-
Notifications
You must be signed in to change notification settings - Fork 529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Contractkit upgradeable smart contracts #269
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
c6f5454
Add extension: Upgradeable
nkrishang 7f24b09
docs update
nkrishang 0659962
pkg release
nkrishang 12f2d4f
Add oz proxy presets
nkrishang c18f606
Upgradeable is OZ UUPSUpgradeable
nkrishang b25f403
Fix Address -> TWAddress import
nkrishang 8cb6917
docs update
nkrishang acbb36a
pkg release
nkrishang 0d9fbd9
git status
nkrishang 92f52df
pkg release
nkrishang 98ff89f
Add extension: Initializable
nkrishang 9da7339
pkg release
nkrishang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
// SPDX-License-Identifier: Apache 2.0 | ||
// Cerdits: OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) | ||
|
||
pragma solidity ^0.8.2; | ||
|
||
import "../lib/TWAddress.sol"; | ||
|
||
/** | ||
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed | ||
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an | ||
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer | ||
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. | ||
* | ||
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be | ||
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in | ||
* case an upgrade adds a module that needs to be initialized. | ||
* | ||
* For example: | ||
* | ||
* [.hljs-theme-light.nopadding] | ||
* ``` | ||
* contract MyToken is ERC20Upgradeable { | ||
* function initialize() initializer public { | ||
* __ERC20_init("MyToken", "MTK"); | ||
* } | ||
* } | ||
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { | ||
* function initializeV2() reinitializer(2) public { | ||
* __ERC20Permit_init("MyToken"); | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as | ||
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. | ||
* | ||
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure | ||
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. | ||
* | ||
* [CAUTION] | ||
* ==== | ||
* Avoid leaving a contract uninitialized. | ||
* | ||
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation | ||
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke | ||
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: | ||
* | ||
* [.hljs-theme-light.nopadding] | ||
* ``` | ||
* /// @custom:oz-upgrades-unsafe-allow constructor | ||
* constructor() { | ||
* _disableInitializers(); | ||
* } | ||
* ``` | ||
* ==== | ||
*/ | ||
abstract contract Initializable { | ||
/** | ||
* @dev Indicates that the contract has been initialized. | ||
* @custom:oz-retyped-from bool | ||
*/ | ||
uint8 private _initialized; | ||
|
||
/** | ||
* @dev Indicates that the contract is in the process of being initialized. | ||
*/ | ||
bool private _initializing; | ||
|
||
/** | ||
* @dev Triggered when the contract has been initialized or reinitialized. | ||
*/ | ||
event Initialized(uint8 version); | ||
|
||
/** | ||
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, | ||
* `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. | ||
*/ | ||
modifier initializer() { | ||
bool isTopLevelCall = !_initializing; | ||
require( | ||
(isTopLevelCall && _initialized < 1) || (!TWAddress.isContract(address(this)) && _initialized == 1), | ||
"Initializable: contract is already initialized" | ||
); | ||
_initialized = 1; | ||
if (isTopLevelCall) { | ||
_initializing = true; | ||
} | ||
_; | ||
if (isTopLevelCall) { | ||
_initializing = false; | ||
emit Initialized(1); | ||
} | ||
} | ||
|
||
/** | ||
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the | ||
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be | ||
* used to initialize parent contracts. | ||
* | ||
* `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original | ||
* initialization step. This is essential to configure modules that are added through upgrades and that require | ||
* initialization. | ||
* | ||
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in | ||
* a contract, executing them in the right order is up to the developer or operator. | ||
*/ | ||
modifier reinitializer(uint8 version) { | ||
require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); | ||
_initialized = version; | ||
_initializing = true; | ||
_; | ||
_initializing = false; | ||
emit Initialized(version); | ||
} | ||
|
||
/** | ||
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the | ||
* {initializer} and {reinitializer} modifiers, directly or indirectly. | ||
*/ | ||
modifier onlyInitializing() { | ||
require(_initializing, "Initializable: contract is not initializing"); | ||
_; | ||
} | ||
|
||
/** | ||
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. | ||
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized | ||
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called | ||
* through proxies. | ||
*/ | ||
function _disableInitializers() internal virtual { | ||
require(!_initializing, "Initializable: contract is initializing"); | ||
if (_initialized < type(uint8).max) { | ||
_initialized = type(uint8).max; | ||
emit Initialized(type(uint8).max); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// SPDX-License-Identifier: Apache 2.0 | ||
// Credits: OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol) | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
/** | ||
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM | ||
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to | ||
* be specified by overriding the virtual {_implementation} function. | ||
* | ||
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a | ||
* different contract through the {_delegate} function. | ||
* | ||
* The success and return data of the delegated call will be returned back to the caller of the proxy. | ||
*/ | ||
abstract contract Proxy { | ||
/** | ||
* @dev Delegates the current call to `implementation`. | ||
* | ||
* This function does not return to its internal call site, it will return directly to the external caller. | ||
*/ | ||
function _delegate(address implementation) internal virtual { | ||
assembly { | ||
// Copy msg.data. We take full control of memory in this inline assembly | ||
// block because it will not return to Solidity code. We overwrite the | ||
// Solidity scratch pad at memory position 0. | ||
calldatacopy(0, 0, calldatasize()) | ||
|
||
// Call the implementation. | ||
// out and outsize are 0 because we don't know the size yet. | ||
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) | ||
|
||
// Copy the returned data. | ||
returndatacopy(0, 0, returndatasize()) | ||
|
||
switch result | ||
// delegatecall returns 0 on error. | ||
case 0 { | ||
revert(0, returndatasize()) | ||
} | ||
default { | ||
return(0, returndatasize()) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @dev This is a virtual function that should be overridden so it returns the address to which the fallback function | ||
* and {_fallback} should delegate. | ||
*/ | ||
function _implementation() internal view virtual returns (address); | ||
|
||
/** | ||
* @dev Delegates the current call to the address returned by `_implementation()`. | ||
* | ||
* This function does not return to its internal call site, it will return directly to the external caller. | ||
*/ | ||
function _fallback() internal virtual { | ||
_beforeFallback(); | ||
_delegate(_implementation()); | ||
} | ||
|
||
/** | ||
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other | ||
* function in the contract matches the call data. | ||
*/ | ||
fallback() external payable virtual { | ||
_fallback(); | ||
} | ||
|
||
/** | ||
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data | ||
* is empty. | ||
*/ | ||
receive() external payable virtual { | ||
_fallback(); | ||
} | ||
|
||
/** | ||
* @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` | ||
* call, or as part of the Solidity `fallback` or `receive` functions. | ||
* | ||
* If overridden should call `super._beforeFallback()`. | ||
*/ | ||
function _beforeFallback() internal virtual {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// SPDX-License-Identifier: Apache 2.0 | ||
// Credits: OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol) | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import "./Proxy.sol"; | ||
import "../openzeppelin-presets/proxy/ERC1967/ERC1967Upgrade.sol"; | ||
|
||
/** | ||
* @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an | ||
* implementation address that can be changed. This address is stored in storage in the location specified by | ||
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the | ||
* implementation behind the proxy. | ||
*/ | ||
contract ProxyForUpgradeable is Proxy, ERC1967Upgrade { | ||
/** | ||
* @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. | ||
* | ||
* If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded | ||
* function call, and allows initializing the storage of the proxy like a Solidity constructor. | ||
*/ | ||
constructor(address _logic, bytes memory _data) payable { | ||
_upgradeToAndCall(_logic, _data, false); | ||
} | ||
|
||
/** | ||
* @dev Returns the current implementation address. | ||
*/ | ||
function _implementation() internal view virtual override returns (address impl) { | ||
return ERC1967Upgrade._getImplementation(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// SPDX-License-Identifier: Apache 2.0 | ||
// Credits: OpenZeppelin Contracts | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import "../openzeppelin-presets/proxy/IERC1822Proxiable.sol"; | ||
import "../openzeppelin-presets/proxy/ERC1967/ERC1967Upgrade.sol"; | ||
|
||
/** | ||
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an | ||
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy. | ||
* | ||
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is | ||
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing | ||
* `UUPSUpgradeable` with a custom implementation of upgrades. | ||
* | ||
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism. | ||
* | ||
* _Available since v4.1._ | ||
*/ | ||
abstract contract Upgradeable is IERC1822Proxiable, ERC1967Upgrade { | ||
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment | ||
address private immutable __self = address(this); | ||
|
||
/** | ||
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is | ||
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case | ||
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a | ||
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to | ||
* fail. | ||
*/ | ||
modifier onlyProxy() { | ||
require(address(this) != __self, "Function must be called through delegatecall"); | ||
require(_getImplementation() == __self, "Function must be called through active proxy"); | ||
_; | ||
} | ||
|
||
/** | ||
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be | ||
* callable on the implementing contract but not through proxies. | ||
*/ | ||
modifier notDelegated() { | ||
require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall"); | ||
_; | ||
} | ||
|
||
/** | ||
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the | ||
* implementation. It is used to validate that the this implementation remains valid after an upgrade. | ||
* | ||
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks | ||
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this | ||
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier. | ||
*/ | ||
function proxiableUUID() external view virtual override notDelegated returns (bytes32) { | ||
return _IMPLEMENTATION_SLOT; | ||
} | ||
|
||
/** | ||
* @dev Upgrade the implementation of the proxy to `newImplementation`. | ||
* | ||
* Calls {_authorizeUpgrade}. | ||
* | ||
* Emits an {Upgraded} event. | ||
*/ | ||
function upgradeTo(address newImplementation) external virtual onlyProxy { | ||
_authorizeUpgrade(newImplementation); | ||
_upgradeToAndCallUUPS(newImplementation, new bytes(0), false); | ||
} | ||
|
||
/** | ||
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call | ||
* encoded in `data`. | ||
* | ||
* Calls {_authorizeUpgrade}. | ||
* | ||
* Emits an {Upgraded} event. | ||
*/ | ||
function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy { | ||
_authorizeUpgrade(newImplementation); | ||
_upgradeToAndCallUUPS(newImplementation, data, true); | ||
} | ||
|
||
/** | ||
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by | ||
* {upgradeTo} and {upgradeToAndCall}. | ||
* | ||
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}. | ||
* | ||
* ```solidity | ||
* function _authorizeUpgrade(address) internal override onlyOwner {} | ||
* ``` | ||
*/ | ||
function _authorizeUpgrade(address newImplementation) internal virtual; | ||
} | ||
Comment on lines
+21
to
+95
Check warning Code scanning / Slither Unimplemented functions
Upgradeable (contracts/extension/Upgradeable.sol#21-95) does not implement functions:
- Upgradeable._authorizeUpgrade(address) (contracts/extension/Upgradeable.sol#94)
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Check warning
Code scanning / Slither
Assembly usage