Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -164,17 +164,17 @@ RolesAuthorityTest:testSetPublicCapabilities() (gas: 29183)
RolesAuthorityTest:testSetRoleCapabilities() (gas: 30258)
RolesAuthorityTest:testSetRoles() (gas: 28986)
SSTORE2Test:testFailReadInvalidPointer() (gas: 2927)
SSTORE2Test:testFailReadInvalidPointerCustomBounds() (gas: 3099)
SSTORE2Test:testFailReadInvalidPointerCustomBounds() (gas: 3033)
SSTORE2Test:testFailReadInvalidPointerCustomStartBound() (gas: 3004)
SSTORE2Test:testFailWriteReadEmptyOutOfBounds() (gas: 34470)
SSTORE2Test:testFailWriteReadOutOfBounds() (gas: 34426)
SSTORE2Test:testFailWriteReadOutOfStartBound() (gas: 34362)
SSTORE2Test:testWriteRead() (gas: 53497)
SSTORE2Test:testWriteReadCustomBounds() (gas: 34869)
SSTORE2Test:testWriteReadCustomStartBound() (gas: 34740)
SSTORE2Test:testWriteReadEmptyBound() (gas: 34677)
SSTORE2Test:testWriteReadFullBoundedRead() (gas: 53672)
SSTORE2Test:testWriteReadFullStartBound() (gas: 34764)
SSTORE2Test:testFailWriteReadEmptyOutOfBounds() (gas: 33784)
SSTORE2Test:testFailWriteReadOutOfBounds() (gas: 33740)
SSTORE2Test:testFailWriteReadOutOfStartBound() (gas: 33736)
SSTORE2Test:testWriteRead() (gas: 52536)
SSTORE2Test:testWriteReadCustomBounds() (gas: 34246)
SSTORE2Test:testWriteReadCustomStartBound() (gas: 34117)
SSTORE2Test:testWriteReadEmptyBound() (gas: 34054)
SSTORE2Test:testWriteReadFullBoundedRead() (gas: 52711)
SSTORE2Test:testWriteReadFullStartBound() (gas: 34141)
SafeCastLibTest:testFailSafeCastTo128() (gas: 321)
SafeCastLibTest:testFailSafeCastTo160() (gas: 342)
SafeCastLibTest:testFailSafeCastTo192() (gas: 344)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@rari-capital/solmate",
"license": "AGPL-3.0-only",
"version": "6.4.0",
"version": "6.5.0",
"description": "Modern, opinionated and gas optimized building blocks for smart contract development.",
"files": [
"src/**/*.sol"
Expand Down
79 changes: 49 additions & 30 deletions src/utils/SSTORE2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,58 @@ pragma solidity >=0.8.0;
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
library SSTORE2 {
uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called.
error SSTORE2_DEPLOYMENT_FAILED();
error SSTORE2_READ_OUT_OF_BOUNDS();

// We skip the first byte as it's a STOP opcode to ensure the contract can't be called.
uint256 internal constant DATA_OFFSET = 1;

/*//////////////////////////////////////////////////////////////
WRITE LOGIC
//////////////////////////////////////////////////////////////*/

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.
);

// Note: The assembly block below does not expand the memory.
assembly {
let originalDataLength := mload(data)

// Add 1 to data size since we are prefixing it with a STOP opcode.
let dataSize := add(originalDataLength, 1)

/**
* ------------------------------------------------------------------------------------+
* Opcode | Opcode + Arguments | Description | Stack View |
* ------------------------------------------------------------------------------------|
* 0x61 | 0x61XXXX | PUSH2 codeSize | codeSize |
* 0x80 | 0x80 | DUP1 | codeSize codeSize |
* 0x60 | 0x600A | PUSH1 10 | 10 codeSize codeSize |
* 0x3D | 0x3D | RETURNDATASIZE | 0 10 codeSize codeSize |
* 0x39 | 0x39 | CODECOPY | codeSize |
* 0x3D | 0x3D | RETURNDATASZIE | 0 codeSize |
* 0xF3 | 0xF3 | RETURN | |
* 0x00 | 0x00 | STOP | |
* ------------------------------------------------------------------------------------+
* @dev Prefix the bytecode with a STOP opcode to ensure it cannot be called. Also PUSH2 is
* used since max contract size cap is 24,576 bytes which is less than 2 ** 16.
*/
mstore(
data,
or(
0x61000080600a3d393df300,
shl(64, dataSize) // shift `dataSize` so that it lines up with the 0000 after PUSH2
)
)

// 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))
pointer := create(0, add(data, 21), add(dataSize, 10))

// Restore original length of the variable size `data`
mstore(data, originalDataLength)
}

require(pointer != address(0), "DEPLOYMENT_FAILED");
if (pointer == address(0)) {
revert SSTORE2_DEPLOYMENT_FAILED();
}
}

/*//////////////////////////////////////////////////////////////
Expand All @@ -65,7 +81,9 @@ library SSTORE2 {
start += DATA_OFFSET;
end += DATA_OFFSET;

require(pointer.code.length >= end, "OUT_OF_BOUNDS");
if (pointer.code.length < end) {
revert SSTORE2_READ_OUT_OF_BOUNDS();
}

return readBytecode(pointer, start, end - start);
}
Expand All @@ -85,9 +103,10 @@ library SSTORE2 {

// Update the free memory pointer to prevent overriding our data.
// We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)).
// Adding 31 to size and running the result through the logic above ensures
// the memory pointer remains word-aligned, following the Solidity convention.
mstore(0x40, add(data, and(add(add(size, 32), 31), not(31))))
// Adding 63 (32 + 31) to size and running the result through the logic
// above ensures the memory pointer remains word-aligned, following
// the Solidity convention.
mstore(0x40, add(data, and(add(size, 63), not(31))))

// Store the size of the data in the first 32 byte chunk of free memory.
mstore(data, size)
Expand Down