-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: use solady's minimal ERC-1967 proxy and UUPSUpgradeable
- Loading branch information
Showing
7 changed files
with
1,404 additions
and
67 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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,142 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.4; | ||
|
||
/// @notice UUPS proxy mixin. | ||
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol) | ||
/// @author Modified from OpenZeppelin | ||
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol) | ||
/// | ||
/// Note: | ||
/// - This implementation is intended to be used with ERC1967 proxies. | ||
/// See: `LibClone.deployERC1967` and related functions. | ||
/// - This implementation is NOT compatible with legacy OpenZeppelin proxies | ||
/// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`. | ||
abstract contract UUPSUpgradeable { | ||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* CUSTOM ERRORS */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
/// @dev The upgrade failed. | ||
error UpgradeFailed(); | ||
|
||
/// @dev The call is from an unauthorized call context. | ||
error UnauthorizedCallContext(); | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* IMMUTABLES */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
/// @dev For checking if the context is a delegate call. | ||
uint256 private immutable __self = uint256(uint160(address(this))); | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* EVENTS */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
/// @dev Emitted when the proxy's implementation is upgraded. | ||
event Upgraded(address indexed implementation); | ||
|
||
/// @dev `keccak256(bytes("Upgraded(address)"))`. | ||
uint256 private constant _UPGRADED_EVENT_SIGNATURE = | ||
0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b; | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* STORAGE */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
/// @dev The ERC-1967 storage slot for the implementation in the proxy. | ||
/// `uint256(keccak256("eip1967.proxy.implementation")) - 1`. | ||
bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT = | ||
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* UUPS OPERATIONS */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
/// @dev Please override this function to check if `msg.sender` is authorized | ||
/// to upgrade the proxy to `newImplementation`, reverting if not. | ||
/// ``` | ||
/// function _authorizeUpgrade(address) internal override onlyOwner {} | ||
/// ``` | ||
function _authorizeUpgrade(address newImplementation) internal virtual; | ||
|
||
/// @dev Returns the storage slot used by the implementation, | ||
/// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822). | ||
/// | ||
/// Note: The `notDelegated` modifier prevents accidental upgrades to | ||
/// an implementation that is a proxy contract. | ||
function proxiableUUID() public view virtual notDelegated returns (bytes32) { | ||
// This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967. | ||
return _ERC1967_IMPLEMENTATION_SLOT; | ||
} | ||
|
||
/// @dev Upgrades the proxy's implementation to `newImplementation`. | ||
/// Emits a {Upgraded} event. | ||
/// | ||
/// Note: Passing in empty `data` skips the delegatecall to `newImplementation`. | ||
function upgradeToAndCall(address newImplementation, bytes calldata data) | ||
public | ||
payable | ||
virtual | ||
onlyProxy | ||
{ | ||
_authorizeUpgrade(newImplementation); | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits. | ||
mstore(0x01, 0x52d1902d) // `proxiableUUID()`. | ||
let s := _ERC1967_IMPLEMENTATION_SLOT | ||
// Check if `newImplementation` implements `proxiableUUID` correctly. | ||
if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) { | ||
mstore(0x01, 0x55299b49) // `UpgradeFailed()`. | ||
revert(0x1d, 0x04) | ||
} | ||
// Emit the {Upgraded} event. | ||
log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation) | ||
sstore(s, newImplementation) // Updates the implementation. | ||
|
||
// Perform a delegatecall to `newImplementation` if `data` is non-empty. | ||
if data.length { | ||
// Forwards the `data` to `newImplementation` via delegatecall. | ||
let m := mload(0x40) | ||
calldatacopy(m, data.offset, data.length) | ||
if iszero(delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00)) | ||
{ | ||
// Bubble up the revert if the call reverts. | ||
returndatacopy(m, 0x00, returndatasize()) | ||
revert(m, returndatasize()) | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// @dev Requires that the execution is performed through a proxy. | ||
modifier onlyProxy() { | ||
uint256 s = __self; | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
// To enable use cases with an immutable default implementation in the bytecode, | ||
// (see: ERC6551Proxy), we don't require that the proxy address must match the | ||
// value stored in the implementation slot, which may not be initialized. | ||
if eq(s, address()) { | ||
mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`. | ||
revert(0x1c, 0x04) | ||
} | ||
} | ||
_; | ||
} | ||
|
||
/// @dev Requires that the execution is NOT performed via delegatecall. | ||
/// This is the opposite of `onlyProxy`. | ||
modifier notDelegated() { | ||
uint256 s = __self; | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
if iszero(eq(s, address())) { | ||
mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`. | ||
revert(0x1c, 0x04) | ||
} | ||
} | ||
_; | ||
} | ||
} |
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
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
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
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
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