Skip to content

Commit

Permalink
Add minimal 1967 to LibClone
Browse files Browse the repository at this point in the history
  • Loading branch information
Vectorized committed Oct 7, 2023
1 parent 9947394 commit fcc43ae
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 9 deletions.
18 changes: 9 additions & 9 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ LibCloneTest:testCloneDeterministicRevertsIfAddressAlreadyUsed() (gas: 96882825)
LibCloneTest:testCloneWithImmutableArgs() (gas: 120581)
LibCloneTest:testCloneWithImmutableArgs(uint256,address,uint256,uint256[],uint64,uint8) (runs: 256, μ: 981643, ~: 970078)
LibCloneTest:testCloneWithImmutableArgsRevertsIfDataTooBig() (gas: 97305784)
LibCloneTest:testStartsWithCaller(uint256) (runs: 256, μ: 28152, ~: 28382)
LibCloneTest:testStartsWithCaller(uint256) (runs: 256, μ: 28177, ~: 28382)
LibCloneTest:test__codesize() (gas: 15743)
LibMapTest:testFoundStatementDifferential(uint256,uint256,uint256) (runs: 256, μ: 499, ~: 499)
LibMapTest:testGeneralMapFunctionsGas() (gas: 3304775)
Expand Down Expand Up @@ -706,17 +706,17 @@ LibStringTest:testToStringZeroBrutalized() (gas: 613803)
LibStringTest:testToStringZeroRightPadded(uint256) (runs: 256, μ: 718269, ~: 613726)
LibStringTest:test__codesize() (gas: 40236)
LibZipTest:testCdCompress() (gas: 166411)
LibZipTest:testCdCompressDecompress(bytes) (runs: 256, μ: 728307, ~: 648421)
LibZipTest:testCdCompressDecompress(uint256) (runs: 256, μ: 812673, ~: 710419)
LibZipTest:testCdCompressDecompress(bytes) (runs: 256, μ: 719753, ~: 648252)
LibZipTest:testCdCompressDecompress(uint256) (runs: 256, μ: 766724, ~: 706715)
LibZipTest:testCdDecompressOnInvalidInput() (gas: 34920)
LibZipTest:testCdFallback() (gas: 5684253)
LibZipTest:testCdFallback(bytes,uint256) (runs: 256, μ: 1226089, ~: 1043747)
LibZipTest:testCdFallbackDecompressor(bytes) (runs: 256, μ: 121253, ~: 117213)
LibZipTest:testCdFallbackDecompressor(uint256) (runs: 256, μ: 168196, ~: 154885)
LibZipTest:testCdFallbackMaskTrick(uint256,uint256) (runs: 256, μ: 688, ~: 663)
LibZipTest:testDecompressWontRevert(bytes) (runs: 256, μ: 714186, ~: 629478)
LibZipTest:testCdFallback(bytes,uint256) (runs: 256, μ: 1218412, ~: 1046247)
LibZipTest:testCdFallbackDecompressor(bytes) (runs: 256, μ: 121259, ~: 117480)
LibZipTest:testCdFallbackDecompressor(uint256) (runs: 256, μ: 168777, ~: 155401)
LibZipTest:testCdFallbackMaskTrick(uint256,uint256) (runs: 256, μ: 690, ~: 663)
LibZipTest:testDecompressWontRevert(bytes) (runs: 256, μ: 730451, ~: 629912)
LibZipTest:testFlzCompressDecompress() (gas: 2161289)
LibZipTest:testFlzCompressDecompress(bytes) (runs: 256, μ: 839698, ~: 683883)
LibZipTest:testFlzCompressDecompress(bytes) (runs: 256, μ: 862119, ~: 684336)
LibZipTest:testFlzCompressDecompress2() (gas: 1013575)
LibZipTest:test__codesize() (gas: 21730)
MerkleProofLibTest:testEmptyCalldataHelpers() (gas: 1086)
Expand Down
146 changes: 146 additions & 0 deletions src/utils/LibClone.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pragma solidity ^0.8.4;
/// @author Minimal proxy by 0age (https://github.com/0age)
/// @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie
/// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args)
/// @author Minimal ERC1967 proxy by jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy)
///
/// @dev Minimal proxy:
/// Although the sw0nt pattern saves 5 gas over the erc-1167 pattern during runtime,
Expand All @@ -24,6 +25,9 @@ pragma solidity ^0.8.4;
/// `ReceiveETH(uint256)` event. This skips the `DELEGATECALL` when there is no calldata,
/// enabling us to accept hard gas-capped `sends` & `transfers` for maximum backwards
/// composability. The minimal proxy implementation does not offer this feature.
///
/// @dev Minimal ERC1967 proxy:
/// An minimal ERC1967 proxy, intended to be used with UUPSUpgradeable.
library LibClone {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
Expand Down Expand Up @@ -569,6 +573,148 @@ library LibClone {
predicted = predictDeterministicAddress(hash, salt, deployer);
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL ERC1967 PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

function deployERC1967(address implementation) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------------+
* CREATION (34 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code |
* 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code |
* 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code |
* 55 | SSTORE | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------|
* RUNTIME (62 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds 0 0 | |
* 3d | RETURNDATASIZE | 0 cds 0 0 | |
* 3d | RETURNDATASIZE | 0 0 cds 0 0 | |
* 37 | CALLDATACOPY | 0 0 | [0..calldatasize): calldata |
* |
* ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata |
* 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata |
* 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata |
* 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata |
* f4 | DELEGATECALL | succ | [0..calldatasize): calldata |
* |
* ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata |
* 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata |
* 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata |
* 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata |
* |
* ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
* 60 0x39 | PUSH1 0x39 | dest succ | [0..returndatasize): returndata |
* 57 | JUMPI | | [0..returndatasize): returndata |
* |
* ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* fd | REVERT | | [0..returndatasize): returndata |
* |
* ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [0..returndatasize): returndata |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* f3 | RETURN | | [0..returndatasize): returndata |
* ---------------------------------------------------------------------------------+
*/

let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3735a920a3ca505d382bbc545af43d6000803e610039573d6000fd5b3d6000f3)
mstore(0x40, 0x55f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc)
mstore(0x20, 0x600951)
mstore(0x1d, implementation)
mstore(0x09, 0x60623d8160343d3973)
instance := create(0, 0x20, 0x60)
// If `instance` is zero, revert.
if iszero(instance) {
// Store the function selector of `DeploymentFailed()`.
mstore(0x00, 0x30116425)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}

function deployDeterministicERC1967(address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3735a920a3ca505d382bbc545af43d6000803e610039573d6000fd5b3d6000f3)
mstore(0x40, 0x55f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc)
mstore(0x20, 0x600951)
mstore(0x1d, implementation)
mstore(0x09, 0x60623d8160343d3973)
instance := create2(0, 0x20, 0x60, salt)
// If `instance` is zero, revert.
if iszero(instance) {
// Store the function selector of `DeploymentFailed()`.
mstore(0x00, 0x30116425)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}

/// @dev Returns the initialization code hash of the clone of `implementation`
/// using immutable arguments encoded in `data`.
/// Used for mining vanity addresses with create2crunch.
function initCodeHashERC1967(address implementation) internal pure returns (bytes32 hash) {
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3735a920a3ca505d382bbc545af43d6000803e610039573d6000fd5b3d6000f3)
mstore(0x40, 0x55f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc)
mstore(0x20, 0x600951)
mstore(0x1d, implementation)
mstore(0x09, 0x60623d8160343d3973)
hash := keccak256(0x20, 0x60)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}

/// @dev Returns the address of the deterministic clone of
/// `implementation` using immutable arguments encoded in `data`, with `salt`, by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967(implementation);
predicted = predictDeterministicAddress(hash, salt, deployer);
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OTHER OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down

0 comments on commit fcc43ae

Please sign in to comment.