From 97685eaea072fedb11289cc2dda791b2f5135e0c Mon Sep 17 00:00:00 2001 From: George Carder Date: Wed, 4 Jan 2023 15:40:22 -0800 Subject: [PATCH 1/3] gasopt: _append cheaper than abi.encodePacked --- src/utils/SSTORE2.sol | 72 ++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/src/utils/SSTORE2.sol b/src/utils/SSTORE2.sol index 23d69803..4ae88681 100644 --- a/src/utils/SSTORE2.sol +++ b/src/utils/SSTORE2.sol @@ -12,27 +12,32 @@ library SSTORE2 { //////////////////////////////////////////////////////////////*/ function write(bytes memory data) internal returns (address pointer) { - // Prefix the bytecode with a STOP opcode to ensure it cannot be called. - bytes memory runtimeCode = abi.encodePacked(hex"00", data); - - bytes memory creationCode = abi.encodePacked( - //---------------------------------------------------------------------------------------------------------------// - // Opcode | Opcode + Arguments | Description | Stack View // - //---------------------------------------------------------------------------------------------------------------// - // 0x60 | 0x600B | PUSH1 11 | codeOffset // - // 0x59 | 0x59 | MSIZE | 0 codeOffset // - // 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset // - // 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset // - // 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset // - // 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset // - // 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // - // 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // - // 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) // - // 0xf3 | 0xf3 | RETURN | // - //---------------------------------------------------------------------------------------------------------------// - hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes. - runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit. - ); + //---------------------------------------------------------------------------------------------------------------// + // Opcode | Opcode + Arguments | Description | Stack View // + //---------------------------------------------------------------------------------------------------------------// + // 0x60 | 0x600B | PUSH1 11 | codeOffset // + // 0x59 | 0x59 | MSIZE | 0 codeOffset // + // 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset // + // 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset // + // 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset // + // 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset // + // 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // + // 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // + // 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) // + // 0xf3 | 0xf3 | RETURN | // + //---------------------------------------------------------------------------------------------------------------// + + unchecked{ + // allocate + bytes memory creationCode = new bytes(12+data.length); // 11+1+data.length + assembly { + mstore(creationCode, 0) // set length to 0 + } + // hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes. + // Prefix the bytecode with a STOP (00) opcode to ensure it cannot be called. + _append(creationCode, hex"60_0B_59_81_38_03_80_92_59_39_F3_00"); + // runtimeCode = hex"00"||data is the bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit. + _append(creationCode, data); /// @solidity memory-safe-assembly assembly { @@ -42,6 +47,7 @@ library SSTORE2 { } require(pointer != address(0), "DEPLOYMENT_FAILED"); + } } /*////////////////////////////////////////////////////////////// @@ -98,4 +104,28 @@ library SSTORE2 { extcodecopy(pointer, add(data, 32), start, size) } } + + // cheaper than bytes concat :) + function _append(bytes memory dst, bytes memory src) private view { + + assembly { + // resize + + let priorLength := mload(dst) + + mstore(dst, add(priorLength, mload(src))) + + // copy + + pop( + staticcall( + gas(), 4, + add(src, 32), // src data start + mload(src), // src length + add(dst, add(32, priorLength)), // dst write ptr + mload(dst) + ) + ) + } + } } From b0abd7a3e828a807d522b29a6035c0333015e240 Mon Sep 17 00:00:00 2001 From: George Carder Date: Wed, 4 Jan 2023 15:53:25 -0800 Subject: [PATCH 2/3] gas-snapshot --- .gas-snapshot | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index c4c5c840..fb1acfe8 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -333,20 +333,20 @@ SSTORE2Test:testFailReadInvalidPointerCustomBounds() (gas: 3099) SSTORE2Test:testFailReadInvalidPointerCustomBounds(address,uint256,uint256,bytes) (runs: 256, μ: 4107, ~: 4130) SSTORE2Test:testFailReadInvalidPointerCustomStartBound() (gas: 3004) SSTORE2Test:testFailReadInvalidPointerCustomStartBound(address,uint256,bytes) (runs: 256, μ: 3980, ~: 3988) -SSTORE2Test:testFailWriteReadCustomBoundsOutOfRange(bytes,uint256,uint256,bytes) (runs: 256, μ: 46236, ~: 43603) -SSTORE2Test:testFailWriteReadCustomStartBoundOutOfRange(bytes,uint256,bytes) (runs: 256, μ: 46017, ~: 43452) -SSTORE2Test:testFailWriteReadEmptyOutOfBounds() (gas: 34470) -SSTORE2Test:testFailWriteReadOutOfBounds() (gas: 34426) +SSTORE2Test:testFailWriteReadCustomBoundsOutOfRange(bytes,uint256,uint256,bytes) (runs: 256, μ: 46128, ~: 43557) +SSTORE2Test:testFailWriteReadCustomStartBoundOutOfRange(bytes,uint256,bytes) (runs: 256, μ: 45907, ~: 43400) +SSTORE2Test:testFailWriteReadEmptyOutOfBounds() (gas: 34476) +SSTORE2Test:testFailWriteReadOutOfBounds() (gas: 34432) SSTORE2Test:testFailWriteReadOutOfStartBound() (gas: 34362) -SSTORE2Test:testWriteRead() (gas: 53497) -SSTORE2Test:testWriteRead(bytes,bytes) (runs: 256, μ: 44019, ~: 41555) -SSTORE2Test:testWriteReadCustomBounds() (gas: 34869) -SSTORE2Test:testWriteReadCustomBounds(bytes,uint256,uint256,bytes) (runs: 256, μ: 29483, ~: 44592) -SSTORE2Test:testWriteReadCustomStartBound() (gas: 34740) -SSTORE2Test:testWriteReadCustomStartBound(bytes,uint256,bytes) (runs: 256, μ: 46484, ~: 44053) -SSTORE2Test:testWriteReadEmptyBound() (gas: 34677) -SSTORE2Test:testWriteReadFullBoundedRead() (gas: 53672) -SSTORE2Test:testWriteReadFullStartBound() (gas: 34764) +SSTORE2Test:testWriteRead() (gas: 53186) +SSTORE2Test:testWriteRead(bytes,bytes) (runs: 256, μ: 43914, ~: 41506) +SSTORE2Test:testWriteReadCustomBounds() (gas: 34872) +SSTORE2Test:testWriteReadCustomBounds(bytes,uint256,uint256,bytes) (runs: 256, μ: 28155, ~: 41182) +SSTORE2Test:testWriteReadCustomStartBound() (gas: 34743) +SSTORE2Test:testWriteReadCustomStartBound(bytes,uint256,bytes) (runs: 256, μ: 46379, ~: 44004) +SSTORE2Test:testWriteReadEmptyBound() (gas: 34680) +SSTORE2Test:testWriteReadFullBoundedRead() (gas: 53361) +SSTORE2Test:testWriteReadFullStartBound() (gas: 34767) SafeCastLibTest:testFailSafeCastTo128() (gas: 321) SafeCastLibTest:testFailSafeCastTo128(uint256) (runs: 256, μ: 443, ~: 443) SafeCastLibTest:testFailSafeCastTo16() (gas: 343) From 0f6cda8ada532742bab57137438dfcbe0a8dcfbb Mon Sep 17 00:00:00 2001 From: George Carder Date: Wed, 4 Jan 2023 16:02:17 -0800 Subject: [PATCH 3/3] linting --- src/utils/SSTORE2.sol | 62 +++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/utils/SSTORE2.sol b/src/utils/SSTORE2.sol index 4ae88681..b76ce60d 100644 --- a/src/utils/SSTORE2.sol +++ b/src/utils/SSTORE2.sol @@ -27,26 +27,26 @@ library SSTORE2 { // 0xf3 | 0xf3 | RETURN | // //---------------------------------------------------------------------------------------------------------------// - unchecked{ - // allocate - bytes memory creationCode = new bytes(12+data.length); // 11+1+data.length - assembly { - mstore(creationCode, 0) // set length to 0 - } - // hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes. - // Prefix the bytecode with a STOP (00) opcode to ensure it cannot be called. - _append(creationCode, hex"60_0B_59_81_38_03_80_92_59_39_F3_00"); - // runtimeCode = hex"00"||data is the bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit. - _append(creationCode, data); - - /// @solidity memory-safe-assembly - assembly { - // Deploy a new contract with the generated creation code. - // We start 32 bytes into the code to avoid copying the byte length. - pointer := create(0, add(creationCode, 32), mload(creationCode)) - } - - require(pointer != address(0), "DEPLOYMENT_FAILED"); + unchecked { + // allocate + bytes memory creationCode = new bytes(12 + data.length); // 11+1+data.length + assembly { + mstore(creationCode, 0) // set length to 0 + } + // hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes. + // Prefix the bytecode with a STOP (00) opcode to ensure it cannot be called. + _append(creationCode, hex"60_0B_59_81_38_03_80_92_59_39_F3_00"); + // runtimeCode = hex"00"||data is the bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit. + _append(creationCode, data); + + /// @solidity memory-safe-assembly + assembly { + // Deploy a new contract with the generated creation code. + // We start 32 bytes into the code to avoid copying the byte length. + pointer := create(0, add(creationCode, 32), mload(creationCode)) + } + + require(pointer != address(0), "DEPLOYMENT_FAILED"); } } @@ -104,27 +104,27 @@ library SSTORE2 { extcodecopy(pointer, add(data, 32), start, size) } } - + // cheaper than bytes concat :) function _append(bytes memory dst, bytes memory src) private view { - assembly { // resize let priorLength := mload(dst) - + mstore(dst, add(priorLength, mload(src))) - - // copy + + // copy pop( staticcall( - gas(), 4, - add(src, 32), // src data start - mload(src), // src length - add(dst, add(32, priorLength)), // dst write ptr - mload(dst) - ) + gas(), + 4, + add(src, 32), // src data start + mload(src), // src length + add(dst, add(32, priorLength)), // dst write ptr + mload(dst) + ) ) } }