/// @title SSTORE2
/// @notice SPDX-License-Identifier: MIT
/// @author asnared <https://github.com/abigger87>
/// @notice Faster & cheaper contract key-value storage for Ethereum Contracts
/// @notice Adapted from 0xsequence/sstore2 (https://github.com/0xsequence/sstore2)
#include "./Bytecode.huff"
#include "./CommonErrors.huff"
#define constant TYPE_UINT_256_MAX = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
/// @notice Stores `_data` and returns `pointer` as key for later retrieval
/// @dev The pointer is a contract address with `_data` as code
/// @param {_data} [bytes memory] to be written
/// @return pointer Pointer to the written `_data`
#define macro SSTORE2_WRITE() = takes (0) returns (1) {
// Load the data memory pointer
0x04 calldataload // [&_data]
dup1 0x04 add calldataload swap1 // [&_data, _data.length]
// Create the contract creation code
CREATION_CODE_FOR() // [&_data, _data.length]
// Deploy the contract
swap1 // [_data.length, &_data]
0x0f add // [size, &_data]
0x00 // [offset, size, &_data]
0x00 // [value, offset, size, &_data]
create // [address, &_data]
// Check that the address is non-zero
dup1 iszero iszero success jumpi // [address, &_data]
CREATE_FAILED(0x00)
success:
// Clean the stack and return the address
swap1 pop // [address]
}
/// @notice Reads the contents of the `_pointer` code as data, skips the first byte
/// @dev The function is intended for reading pointers generated by `write`
/// @param _pointer to be read
/// @return data read from `_pointer` contract
#define macro SSTORE2_READ() = takes (1) returns (1) {
// Input Stack: [_pointer]
// We start at 1 because the first byte are zeros to prevent the contract from being called
0x01 // [_start, _pointer]
[TYPE_UINT_256_MAX] // [_end, _start, _pointer]
swap2 // [_pointer, _start, _end]
CODE_AT() // [code]
}
/// @notice Reads the contents of the `_pointer` code as data, skips the first byte
/// @dev The function is intended for reading pointers generated by `write`
/// @param _pointer to be read
/// @param _start number of bytes to skip
/// @return data read from `_pointer` contract
#define macro SSTORE2_READ_AT() = takes (2) returns (1) {
// Input Stack: [_pointer, _start]
swap1 // [_start, _pointer]
[TYPE_UINT_256_MAX] // [_end, _start, _pointer]
swap2 // [_pointer, _start, _end]
CODE_AT() // [code]
}
/// @notice Reads the contents of the `_pointer` code as data, skips the first byte
/// @dev The function is intended for reading pointers generated by `write`
/// @param _pointer to be read
/// @param _start number of bytes to skip
/// @param _end index before which to end extraction
/// @return data read from `_pointer` contract
#define macro SSTORE2_READ_BETWEEN() = takes (3) returns (1) {
// Input Stack: [_pointer, _start, _end]
CODE_AT() // [code]
}