From e8b05aa9f5dafa2b76b68aa557961608ab073154 Mon Sep 17 00:00:00 2001 From: ZeframLou Date: Sun, 23 Jan 2022 13:50:16 -0800 Subject: [PATCH] refactor: replace ClonesWithCallData with ClonesWithImmutableArgs --- .gas-snapshot | 18 ++--- src/VestedERC20.sol | 15 +--- src/VestedERC20Factory.sol | 9 +-- src/lib/ClonesWithCallData.sol | 142 --------------------------------- src/lib/ERC20.sol | 25 ++---- 5 files changed, 23 insertions(+), 186 deletions(-) delete mode 100644 src/lib/ClonesWithCallData.sol diff --git a/.gas-snapshot b/.gas-snapshot index de6624e..9626148 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,9 +1,9 @@ -testCorrectness_createVestedERC20(bytes32,bytes32,uint8,address,uint56) (runs: 256, μ: 78560, ~: 78565) -testGas_createVestedERC20(bytes32,bytes32,uint8,address,uint56) (runs: 256, μ: 69628, ~: 69633) -testCorrectness_wrapAndClaim_beforeVestStart(uint224) (runs: 256, μ: 15857, ~: 15859) -testCorrectness_wrapAndClaim_duringVest(uint224) (runs: 256, μ: 103410, ~: 103412) -testCorrectness_wrapAndClaim_transfer(uint224) (runs: 256, μ: 173705, ~: 173707) -testFail_wrap_afterVestEnd(uint224) (runs: 256, μ: 8375, ~: 8377) -testGas_redeem() (gas: 31018) -testGas_transfer() (gas: 28219) -testGas_wrap() (gas: 8057) +testCorrectness_createVestedERC20(bytes32,bytes32,uint8,address,uint56) (runs: 256, μ: 76483, ~: 76485) +testGas_createVestedERC20(bytes32,bytes32,uint8,address,uint56) (runs: 256, μ: 67551, ~: 67553) +testCorrectness_wrapAndClaim_beforeVestStart(uint224) (runs: 256, μ: 15314, ~: 15315) +testCorrectness_wrapAndClaim_duringVest(uint224) (runs: 256, μ: 102867, ~: 102868) +testCorrectness_wrapAndClaim_transfer(uint224) (runs: 256, μ: 153262, ~: 153263) +testFail_wrap_afterVestEnd(uint224) (runs: 256, μ: 7832, ~: 7833) +testGas_redeem() (gas: 30954) +testGas_transfer() (gas: 28155) +testGas_wrap() (gas: 7993) diff --git a/src/VestedERC20.sol b/src/VestedERC20.sol index 84b8f2f..d4b406d 100644 --- a/src/VestedERC20.sol +++ b/src/VestedERC20.sol @@ -40,28 +40,19 @@ contract VestedERC20 is ERC20 { /// @notice The token that is vested /// @return _underlying The address of the underlying token function underlying() public pure returns (address _underlying) { - uint256 offset = _getImmutableVariablesOffset(); - assembly { - _underlying := shr(0x60, calldataload(add(offset, 0x41))) - } + return _getArgAddress(0x41); } /// @notice The Unix timestamp (in seconds) of the start of the vest /// @return _startTimestamp The vest start timestamp function startTimestamp() public pure returns (uint64 _startTimestamp) { - uint256 offset = _getImmutableVariablesOffset(); - assembly { - _startTimestamp := shr(0xc0, calldataload(add(offset, 0x55))) - } + return _getArgUint64(0x55); } /// @notice The Unix timestamp (in seconds) of the end of the vest /// @return _endTimestamp The vest end timestamp function endTimestamp() public pure returns (uint64 _endTimestamp) { - uint256 offset = _getImmutableVariablesOffset(); - assembly { - _endTimestamp := shr(0xc0, calldataload(add(offset, 0x5d))) - } + return _getArgUint64(0x5d); } /// ----------------------------------------------------------------------- diff --git a/src/VestedERC20Factory.sol b/src/VestedERC20Factory.sol index 1fc6ae5..286e33b 100644 --- a/src/VestedERC20Factory.sol +++ b/src/VestedERC20Factory.sol @@ -2,9 +2,10 @@ pragma solidity ^0.8.11; +import {ClonesWithImmutableArgs} from "@clones/ClonesWithImmutableArgs.sol"; + import {ERC20} from "./lib/ERC20.sol"; import {VestedERC20} from "./VestedERC20.sol"; -import {ClonesWithCallData} from "./lib/ClonesWithCallData.sol"; /// @title VestedERC20Factory /// @author zefram.eth @@ -14,7 +15,7 @@ contract VestedERC20Factory { /// Library usage /// ----------------------------------------------------------------------- - using ClonesWithCallData for address; + using ClonesWithImmutableArgs for address; /// ----------------------------------------------------------------------- /// Errors @@ -70,9 +71,7 @@ contract VestedERC20Factory { mstore(add(ptr, 0x75), shl(0xc0, startTimestamp)) mstore(add(ptr, 0x7d), shl(0xc0, endTimestamp)) } - vestedERC20 = VestedERC20( - address(implementation).cloneWithCallDataProvision(ptr) - ); + vestedERC20 = VestedERC20(address(implementation).clone(ptr)); emit CreateVestedERC20(vestedERC20); } } diff --git a/src/lib/ClonesWithCallData.sol b/src/lib/ClonesWithCallData.sol deleted file mode 100644 index 95e0b11..0000000 --- a/src/lib/ClonesWithCallData.sol +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -library ClonesWithCallData { - function cloneWithCallDataProvision( - address implementation, - bytes memory data - ) internal returns (address instance) { - // unrealistic for memory ptr or data length to exceed 256 bits - unchecked { - uint256 extraLength = data.length + 2; // +2 bytes for telling how much data there is appended to the call - uint256 creationSize = 0x43 + extraLength; - uint256 runSize = creationSize - 11; - uint256 dataPtr; - uint256 ptr; - // solhint-disable-next-line no-inline-assembly - assembly { - ptr := mload(0x40) - - // ------------------------------------------------------------------------------------------------------------- - // CREATION (11 bytes) - // ------------------------------------------------------------------------------------------------------------- - - // 3d | RETURNDATASIZE | 0 | – - // 61 runtime | PUSH2 runtime (r) | r 0 | – - mstore( - ptr, - 0x3d61000000000000000000000000000000000000000000000000000000000000 - ) - mstore(add(ptr, 0x02), shl(240, runSize)) // size of the contract running bytecode (16 bits) - - // creation size = 0b - // 80 | DUP1 | r r 0 | – - // 60 creation | PUSH1 creation (c) | c r r 0 | – - // 3d | RETURNDATASIZE | 0 c r r 0 | – - // 39 | CODECOPY | r 0 | [0-2d]: runtime code - // 81 | DUP2 | 0 c 0 | [0-2d]: runtime code - // f3 | RETURN | 0 | [0-2d]: runtime code - mstore( - add(ptr, 0x04), - 0x80600b3d3981f300000000000000000000000000000000000000000000000000 - ) - - // ------------------------------------------------------------------------------------------------------------- - // RUNTIME - // ------------------------------------------------------------------------------------------------------------- - - // 36 | CALLDATASIZE | cds | – - // 3d | RETURNDATASIZE | 0 cds | – - // 3d | RETURNDATASIZE | 0 0 cds | – - // 37 | CALLDATACOPY | – | [0, cds] = calldata - // 61 | PUSH2 extra | extra | [0, cds] = calldata - mstore( - add(ptr, 0x0b), - 0x363d3d3761000000000000000000000000000000000000000000000000000000 - ) - mstore(add(ptr, 0x10), shl(240, extraLength)) - - // 60 0x38 | PUSH1 0x38 | 0x38 extra | [0, cds] = calldata // 0x38 (56) is runtime size - data - // 36 | CALLDATASIZE | cds 0x38 extra | [0, cds] = calldata - // 39 | CODECOPY | _ | [0, cds] = calldata - // 3d | RETURNDATASIZE | 0 | [0, cds] = calldata - // 3d | RETURNDATASIZE | 0 0 | [0, cds] = calldata - // 3d | RETURNDATASIZE | 0 0 0 | [0, cds] = calldata - // 36 | CALLDATASIZE | cds 0 0 0 | [0, cds] = calldata - // 61 extra | PUSH2 extra | extra cds 0 0 0 | [0, cds] = calldata - mstore( - add(ptr, 0x12), - 0x603836393d3d3d36610000000000000000000000000000000000000000000000 - ) - mstore(add(ptr, 0x1b), shl(240, extraLength)) - - // 01 | ADD | cds+extra 0 0 0 | [0, cds] = calldata - // 3d | RETURNDATASIZE | 0 cds 0 0 0 | [0, cds] = calldata - // 73 addr | PUSH20 0x123… | addr 0 cds 0 0 0 | [0, cds] = calldata - mstore( - add(ptr, 0x1d), - 0x013d730000000000000000000000000000000000000000000000000000000000 - ) - mstore(add(ptr, 0x20), shl(0x60, implementation)) - - // 5a | GAS | gas addr 0 cds 0 0 0 | [0, cds] = calldata - // f4 | DELEGATECALL | success 0 | [0, cds] = calldata - // 3d | RETURNDATASIZE | rds success 0 | [0, cds] = calldata - // 82 | DUP3 | 0 rds success 0 | [0, cds] = calldata - // 80 | DUP1 | 0 0 rds success 0 | [0, cds] = calldata - // 3e | RETURNDATACOPY | success 0 | [0, rds] = return data (there might be some irrelevant leftovers in memory [rds, cds] when rds < cds) - // 90 | SWAP1 | 0 success | [0, rds] = return data - // 3d | RETURNDATASIZE | rds 0 success | [0, rds] = return data - // 91 | SWAP2 | success 0 rds | [0, rds] = return data - // 60 0x36 | PUSH1 0x36 | 0x36 sucess 0 rds | [0, rds] = return data - // 57 | JUMPI | 0 rds | [0, rds] = return data - // fd | REVERT | – | [0, rds] = return data - // 5b | JUMPDEST | 0 rds | [0, rds] = return data - // f3 | RETURN | – | [0, rds] = return data - - mstore( - add(ptr, 0x34), - 0x5af43d82803e903d91603657fd5bf30000000000000000000000000000000000 - ) - } - - // ------------------------------------------------------------------------------------------------------------- - // APPENDED DATA (Accessible from extcodecopy) - // (but also send as appended data to the delegatecall) - // ------------------------------------------------------------------------------------------------------------- - - extraLength -= 2; - uint256 counter = extraLength; - uint256 copyPtr = ptr + 0x43; - // solhint-disable-next-line no-inline-assembly - assembly { - dataPtr := add(data, 32) - } - for (; counter >= 32; counter -= 32) { - // solhint-disable-next-line no-inline-assembly - assembly { - mstore(copyPtr, mload(dataPtr)) - } - - copyPtr += 32; - dataPtr += 32; - } - uint256 mask = ~(256**(32 - counter) - 1); - // solhint-disable-next-line no-inline-assembly - assembly { - mstore(copyPtr, and(mload(dataPtr), mask)) - } - copyPtr += counter; - // solhint-disable-next-line no-inline-assembly - assembly { - mstore(copyPtr, shl(240, extraLength)) - } - // solhint-disable-next-line no-inline-assembly - assembly { - instance := create(0, ptr, creationSize) - } - require(instance != address(0), "create failed"); - } - } -} diff --git a/src/lib/ERC20.sol b/src/lib/ERC20.sol index 8fb4dde..4f1ff4d 100644 --- a/src/lib/ERC20.sol +++ b/src/lib/ERC20.sol @@ -1,11 +1,13 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; +import {Clone} from "@clones/Clone.sol"; + /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. -abstract contract ERC20 { +abstract contract ERC20 is Clone { /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ @@ -33,28 +35,15 @@ abstract contract ERC20 { //////////////////////////////////////////////////////////////*/ function name() external pure returns (string memory) { - uint256 offset = _getImmutableVariablesOffset(); - bytes32 nameBytes; - assembly { - nameBytes := calldataload(offset) - } - return string(abi.encodePacked(nameBytes)); + return string(abi.encodePacked(_getArgUint256(0))); } function symbol() external pure returns (string memory) { - uint256 offset = _getImmutableVariablesOffset(); - bytes32 symbolBytes; - assembly { - symbolBytes := calldataload(add(offset, 0x20)) - } - return string(abi.encodePacked(symbolBytes)); + return string(abi.encodePacked(_getArgUint256(0x20))); } - function decimals() external pure returns (uint8 _decimals) { - uint256 offset = _getImmutableVariablesOffset(); - assembly { - _decimals := shr(0xf8, calldataload(add(offset, 0x40))) - } + function decimals() external pure returns (uint8) { + return _getArgUint8(0x40); } /*///////////////////////////////////////////////////////////////