// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solmate (https://github.com/transmissions11/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.
/*//////////////////////////////////////////////////////////////
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.
);
/// @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");
}
/*//////////////////////////////////////////////////////////////
READ LOGIC
//////////////////////////////////////////////////////////////*/
function read(address pointer) internal view returns (bytes memory) {
return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET);
}
function read(address pointer, uint256 start) internal view returns (bytes memory) {
start += DATA_OFFSET;
return readBytecode(pointer, start, pointer.code.length - start);
}
function read(
address pointer,
uint256 start,
uint256 end
) internal view returns (bytes memory) {
start += DATA_OFFSET;
end += DATA_OFFSET;
require(pointer.code.length >= end, "OUT_OF_BOUNDS");
return readBytecode(pointer, start, end - start);
}
/*//////////////////////////////////////////////////////////////
INTERNAL HELPER LOGIC
//////////////////////////////////////////////////////////////*/
function readBytecode(
address pointer,
uint256 start,
uint256 size
) private view returns (bytes memory data) {
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
data := mload(0x40)
// 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))))
// Store the size of the data in the first 32 byte chunk of free memory.
mstore(data, size)
// Copy the code into memory right after the 32 bytes we used to store the size.
extcodecopy(pointer, add(data, 32), start, size)
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { SSTORE2 } from "solmate/src/utils/SSTORE2.sol";
import { BytesSliceAndConcat } from "../../../../libraries/bytesSliceAndConcat.sol";
/// @notice This contract is open and can be called by any address.
/// It provides functionality to deploy and read code using SSTORE2.
contract SStore2Deployer {
/// @dev deploys code and emits an event with the pointer and code hash
/// @param code_ code to deploy
/// @return pointer_ pointer to the deployed code
function deployCode(bytes memory code_) external returns (address pointer_) {
pointer_ = SSTORE2.write(code_);
emit LogCodeDeployed(pointer_, keccak256(code_));
}
/// @dev deploys code and emits an event with the pointer and code hash
/// @param code_ code to deploy
/// @return pointer1_ pointer to the first part of the deployed code
/// @return pointer2_ pointer to the second part of the deployed code
function deployCodeSplit(bytes memory code_) external returns (address pointer1_, address pointer2_) {
// split storing creation code into two SSTORE2 pointers, because:
// due to contract code limits 24576 bytes is the maximum amount of data that can be written in a single pointer / key.
// Attempting to write more will result in failure.
// So by splitting in two parts we can make sure that the contract bytecode size can use up the full limit of 24576 bytes.
bytes memory code1_ = BytesSliceAndConcat.bytesSlice(code_, 0, code_.length / 2);
// slice lengths:
// when even length, e.g. 250:
// part 1 = 0 -> 250 / 2, so 0 until 125 length, so 0 -> 125
// part 2 = 250 / 2 -> 250 - 250 / 2, so 125 until 125 length, so 125 -> 250
// when odd length: e.g. 251:
// part 1 = 0 -> 251 / 2, so 0 until 125 length, so 0 -> 125
// part 2 = 251 / 2 -> 251 - 251 / 2, so 125 until 126 length, so 125 -> 251
bytes memory code2_ = BytesSliceAndConcat.bytesSlice(code_, code_.length / 2, code_.length - code_.length / 2);
pointer1_ = SSTORE2.write(code1_);
pointer2_ = SSTORE2.write(code2_);
emit LogCodeDeployedSplit(pointer1_, pointer2_, keccak256(code_));
}
/// @dev reads code from a pointer
/// @param pointer_ pointer to the code
/// @return code_ code
function readCode(address pointer_) external view returns (bytes memory code_) {
code_ = SSTORE2.read(pointer_);
}
function readCodeSplit(address pointer1_, address pointer2_) external view returns (bytes memory code_) {
code_ = BytesSliceAndConcat.bytesConcat(SSTORE2.read(pointer1_), SSTORE2.read(pointer2_));
}
event LogCodeDeployed(address pointer_, bytes32 codeHash_);
event LogCodeDeployedSplit(address pointer1_, address pointer2_, bytes32 codeHash_);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
library BytesSliceAndConcat {
// @dev taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
function bytesConcat(
bytes memory _preBytes,
bytes memory _postBytes
) internal pure returns (bytes memory tempBytes) {
assembly {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// Store the length of the first bytes array at the beginning of
// the memory for tempBytes.
let length := mload(_preBytes)
mstore(tempBytes, length)
// Maintain a memory counter for the current write location in the
// temp bytes array by adding the 32 bytes for the array length to
// the starting location.
let mc := add(tempBytes, 0x20)
// Stop copying when the memory counter reaches the length of the
// first bytes array.
let end := add(mc, length)
for {
// Initialize a copy counter to the start of the _preBytes data,
// 32 bytes into its memory.
let cc := add(_preBytes, 0x20)
} lt(mc, end) {
// Increase both counters by 32 bytes each iteration.
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
// Write the _preBytes data into the tempBytes memory 32 bytes
// at a time.
mstore(mc, mload(cc))
}
// Add the length of _postBytes to the current length of tempBytes
// and store it as the new length in the first 32 bytes of the
// tempBytes memory.
length := mload(_postBytes)
mstore(tempBytes, add(length, mload(tempBytes)))
// Move the memory counter back from a multiple of 0x20 to the
// actual end of the _preBytes data.
mc := end
// Stop copying when the memory counter reaches the new combined
// length of the arrays.
end := add(mc, length)
for {
let cc := add(_postBytes, 0x20)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
// Update the free-memory pointer by padding our last write location
// to 32 bytes: add 31 bytes to the end of tempBytes to move to the
// next 32 byte block, then round down to the nearest multiple of
// 32. If the sum of the length of the two arrays is zero then add
// one before rounding down to leave a blank 32 bytes (the length block with 0).
mstore(
0x40,
and(
add(add(end, iszero(add(length, mload(_preBytes)))), 31),
not(31) // Round down to the nearest 32 bytes.
)
)
}
return tempBytes;
}
// @dev taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol
function bytesSlice(
bytes memory _bytes,
uint256 _start,
uint256 _length
) internal pure returns (bytes memory tempBytes) {
require(_length + 31 >= _length, "slice_overflow");
require(_bytes.length >= _start + _length, "slice_outOfBounds");
assembly {
switch iszero(_length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(_length, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
//zero out the 32 bytes slice we are about to return
//we need to do it because Solidity does not garbage collect
mstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
}
{
"compilationTarget": {
"contracts/protocols/dex/factory/deploymentHelpers/SSTORE2Deployer.sol": "SStore2Deployer"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 10000000
},
"remappings": []
}
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pointer_","type":"address"},{"indexed":false,"internalType":"bytes32","name":"codeHash_","type":"bytes32"}],"name":"LogCodeDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pointer1_","type":"address"},{"indexed":false,"internalType":"address","name":"pointer2_","type":"address"},{"indexed":false,"internalType":"bytes32","name":"codeHash_","type":"bytes32"}],"name":"LogCodeDeployedSplit","type":"event"},{"inputs":[{"internalType":"bytes","name":"code_","type":"bytes"}],"name":"deployCode","outputs":[{"internalType":"address","name":"pointer_","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"code_","type":"bytes"}],"name":"deployCodeSplit","outputs":[{"internalType":"address","name":"pointer1_","type":"address"},{"internalType":"address","name":"pointer2_","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pointer_","type":"address"}],"name":"readCode","outputs":[{"internalType":"bytes","name":"code_","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pointer1_","type":"address"},{"internalType":"address","name":"pointer2_","type":"address"}],"name":"readCodeSplit","outputs":[{"internalType":"bytes","name":"code_","type":"bytes"}],"stateMutability":"view","type":"function"}]