Address Contract Verified
Address
0x25CBB4e36Db597F63c8376d15302c0d9CC6CE2De
Balance
0 ETH
Nonce
64
Code Size
4956 bytes
Creator
0xd7dD9612...162b at tx 0x1ab902c6...5cdf23
Indexed Transactions
0
Contract Bytecode
4956 bytes
0x608060405260043610610110575f3560e01c806379627fa11161009d578063b7c763b511610062578063b7c763b5146102df578063cf6dc0fa146102fe578063f04e283e1461031d578063f2fde38b14610330578063fee81cf414610343575f5ffd5b806379627fa11461022b5780638da5cb5b1461024a5780639507d39a146102755780639a8c17dd146102a1578063ad88b6e4146102c0575f5ffd5b8063294bd5ce116100e3578063294bd5ce146101a75780634f558e79146101c657806354d1f13d1461020657806361b8ce8c1461020e578063715018a614610223575f5ffd5b80630f11e5471461011457806316d5002a146101355780631a3cd59a14610167578063256929621461019f575b5f5ffd5b34801561011f575f5ffd5b5061013361012e366004610f8d565b610374565b005b348015610140575f5ffd5b5061015461014f366004610f8d565b61053e565b6040519081526020015b60405180910390f35b348015610172575f5ffd5b50610186610181366004610fd5565b6106b1565b6040805192835262ffffff90911660208301520161015e565b6101336106fa565b3480156101b2575f5ffd5b506101546101c136600461102a565b610747565b3480156101d1575f5ffd5b506101f66101e0366004610fd5565b5f90815260208190526040902060010154151590565b604051901515815260200161015e565b61013361087e565b348015610219575f5ffd5b5061015460025481565b6101336108b7565b348015610236575f5ffd5b5061013361024536600461102a565b6108ca565b348015610255575f5ffd5b50638b78c6d819546040516001600160a01b03909116815260200161015e565b348015610280575f5ffd5b5061029461028f366004610fd5565b610a1d565b60405161015e9190611093565b3480156102ac575f5ffd5b506101336102bb3660046110ac565b610b3e565b3480156102cb575f5ffd5b506102946102da3660046110ac565b610b96565b3480156102ea575f5ffd5b506102946102f9366004610fd5565b610c1b565b348015610309575f5ffd5b506101336103183660046110cc565b610c7e565b61013361032b36600461111b565b610db6565b61013361033e36600461111b565b610df3565b34801561034e575f5ffd5b5061015461035d36600461111b565b63389a75e1600c9081525f91909152602090205490565b61037c610e19565b5f838152602081905260409020600101546103aa576040516315f2470160e31b815260040160405180910390fd5b5f8381526020819052604081205462ffffff16905b828110156104f9578383828181106103d9576103d9611141565b90506020028101906103eb9190611155565b90505f0361040c576040516399d8fec960e01b815260040160405180910390fd5b61046c84848381811061042157610421611141565b90506020028101906104339190611155565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610e3392505050565b5f8681526020819052604081206002019061048784866111ac565b815260208101919091526040015f2080546001600160a01b0319166001600160a01b0392909216919091179055847faa65dca3f562832c0ba4bbde8d03d3d978faaf4da00e3d88cc93ad26d927096d6104e083856111ac565b60405190815260200160405180910390a26001016103bf565b505f848152602081905260408120805484929061051c90849062ffffff166111bf565b92506101000a81548162ffffff021916908362ffffff16021790555050505050565b5f610547610e19565b506002545f5b828110156106a95783838281811061056757610567611141565b90506020028101906105799190611155565b90505f0361059a576040516399d8fec960e01b815260040160405180910390fd5b600280545f91826105aa836111da565b9190505590505f6105c686868581811061042157610421611141565b604080516060810182526001600160a01b03831681525f602082015291925081018787868181106105f9576105f9611141565b905060200281019061060b9190611155565b65ffffffffffff908116909252505f8481526001602081815260408084208651815488850151988401518816600160d01b026001600160d01b0399909816600160a01b026001600160d01b03199091166001600160a01b039092169190911717969096169490941790945581845290829020018990555188815283915f5160206113075f395f51905f52910160405180910390a2505060010161054d565b509392505050565b5f81815260208190526040812060018101548291906106e3576040516315f2470160e31b815260040160405180910390fd5b60018101549054909462ffffff9091169350915050565b5f6202a30067ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f5fa250565b5f610750610e19565b5f829003610771576040516399d8fec960e01b815260040160405180910390fd5b60028054905f610780836111da565b9190505590505f6107c584848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610e3392505050565b604080516060810182526001600160a01b0380841682525f602080840182815265ffffffffffff8a81168688019081528a8552600180855288862097518854945192518416600160d01b026001600160d01b0393909416600160a01b026001600160d01b03199095169716969096179290921791909116179093559182905290829020018790555190915082905f5160206113075f395f51905f529061086e9088815260200190565b60405180910390a2509392505050565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f5fa2565b6108bf610e19565b6108c85f610e74565b565b6108d2610e19565b5f83815260208190526040902060010154610900576040516315f2470160e31b815260040160405180910390fd5b5f819003610921576040516399d8fec960e01b815260040160405180910390fd5b5f8381526020818152604091829020548251601f850183900483028101830190935283835262ffffff169161096f9185908590819084018382808284375f92019190915250610e3392505050565b5f8581526020818152604080832085845260028101835290832080546001600160a01b0319166001600160a01b039590951694909417909355868252819052815462ffffff1691906109c0836111f2565b91906101000a81548162ffffff021916908362ffffff16021790555050837faa65dca3f562832c0ba4bbde8d03d3d978faaf4da00e3d88cc93ad26d927096d82604051610a0f91815260200190565b60405180910390a250505050565b5f818152602081905260409020600181015460609190610a50576040516315f2470160e31b815260040160405180910390fd5b5f83815260016020908152604091829020825160608101845290546001600160a01b03811680835265ffffffffffff600160a01b8304811694840194909452600160d01b9091049092169281019290925215610ad457610acc815f0151826020015165ffffffffffff16836040015165ffffffffffff16610eb1565b949350505050565b60605f5b835462ffffff16811015610b35575f8181526002850160205260409020548290610b0a906001600160a01b0316610f19565b604051602001610b1b92919061122b565b60408051601f198184030181529190529150600101610ad8565b50949350505050565b610b46610e19565b6002548210610b5e57610b5a8260016111ac565b6002555b5f8281526020818152604091829020600101839055905182815283915f5160206113075f395f51905f52910160405180910390a25050565b5f828152602081905260409020600181015460609190610bc9576040516315f2470160e31b815260040160405180910390fd5b805462ffffff168310610bef576040516376fb3d3160e11b815260040160405180910390fd5b5f838152600282016020526040902054610c11906001600160a01b0316610f19565b9150505b92915050565b604051634a83e9cd60e11b8152600481018290526060903090639507d39a906024015f60405180830381865afa158015610c57573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610c159190810190611253565b610c86610e19565b5f819003610ca7576040516399d8fec960e01b815260040160405180910390fd5b6002548410610cbf57610cbb8460016111ac565b6002555b5f610cfe83838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610e3392505050565b604080516060810182526001600160a01b0380841682525f602080840182815265ffffffffffff8981168688019081528d8552600180855288862097518854945192518416600160d01b026001600160d01b0393909416600160a01b026001600160d01b03199095169716969096179290921791909116179093559182905290829020018690555190915085905f5160206113075f395f51905f5290610da79087815260200190565b60405180910390a25050505050565b610dbe610e19565b63389a75e1600c52805f526020600c208054421115610de457636f5e88185f526004601cfd5b5f9055610df081610e74565b50565b610dfb610e19565b8060601b610e1057637448fbae5f526004601cfd5b610df081610e74565b638b78c6d8195433146108c8576382b429005f526004601cfd5b5f81518060401b6bfe61000180600a3d393df3000161fffe8211840152600b8101601584015ff0915081610e6e5763301164255f526004601cfd5b90915290565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b60405161ffff8210610ec35761ffff91505b818310838303026001810184601f8401873c8082015160ff16610efd576001853b038060281c3d3d3e808403818511028203858211029150505b8082525f81602084010152806040830101604052509392505050565b60405164ffffffffff5f19833b0116602181015f601f8401853c80825260408201810160405250919050565b5f5f83601f840112610f55575f5ffd5b50813567ffffffffffffffff811115610f6c575f5ffd5b6020830191508360208260051b8501011115610f86575f5ffd5b9250929050565b5f5f5f60408486031215610f9f575f5ffd5b83359250602084013567ffffffffffffffff811115610fbc575f5ffd5b610fc886828701610f45565b9497909650939450505050565b5f60208284031215610fe5575f5ffd5b5035919050565b5f5f83601f840112610ffc575f5ffd5b50813567ffffffffffffffff811115611013575f5ffd5b602083019150836020828501011115610f86575f5ffd5b5f5f5f6040848603121561103c575f5ffd5b83359250602084013567ffffffffffffffff811115611059575f5ffd5b610fc886828701610fec565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6110a56020830184611065565b9392505050565b5f5f604083850312156110bd575f5ffd5b50508035926020909101359150565b5f5f5f5f606085870312156110df575f5ffd5b8435935060208501359250604085013567ffffffffffffffff811115611103575f5ffd5b61110f87828801610fec565b95989497509550505050565b5f6020828403121561112b575f5ffd5b81356001600160a01b03811681146110a5575f5ffd5b634e487b7160e01b5f52603260045260245ffd5b5f5f8335601e1984360301811261116a575f5ffd5b83018035915067ffffffffffffffff821115611184575f5ffd5b602001915036819003821315610f86575f5ffd5b634e487b7160e01b5f52601160045260245ffd5b80820180821115610c1557610c15611198565b62ffffff8181168382160190811115610c1557610c15611198565b5f600182016111eb576111eb611198565b5060010190565b5f62ffffff821662ffffff810361120b5761120b611198565b60010192915050565b5f81518060208401855e5f93019283525090919050565b5f610acc6112398386611214565b84611214565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215611263575f5ffd5b815167ffffffffffffffff811115611279575f5ffd5b8201601f81018413611289575f5ffd5b805167ffffffffffffffff8111156112a3576112a361123f565b604051601f8201601f19908116603f0116810167ffffffffffffffff811182821017156112d2576112d261123f565b6040528181528282016020018610156112e9575f5ffd5b8160208401602083015e5f9181016020019190915294935050505056fef0d86e4da46a3c60e2dc0f8453bea09697e36bbb29e5a5fb05404b14387c7c2ea264697066735822122001ce30d100ea93568ded9489354569ab46461c3c38e084ad5eeea9fa21cdf94c64736f6c634300081c0033
Verified Source Code Full Match
Compiler: v0.8.28+commit.7893614a
EVM: cancun
Optimization: Yes (200 runs)
ContentStore.sol 183 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "solady/auth/Ownable.sol";
import "solady/utils/SSTORE2.sol";
/// @title ContentStore
/// @notice Stores chunked content (images, game data) in contract bytecode
/// @dev Uses SSTORE2 for gas-efficient storage (~200 gas/byte vs 20000 gas/slot)
contract ContentStore is Ownable {
/// @dev Location of stored data
struct DataLoc {
address pointer;
uint48 start;
uint48 end;
}
/// @dev Content entry with optional chunking
struct Content {
uint24 chunkCount;
bytes32 contentType; // e.g., keccak256("image/webp"), keccak256("application/javascript")
mapping(uint256 => address) chunks;
}
mapping(uint256 => Content) internal contents;
mapping(uint256 => DataLoc) internal locations; // For single-pointer small content
uint256 public nextId = 1;
event ContentStored(uint256 indexed id, bytes32 contentType);
event ChunkStored(uint256 indexed id, uint256 index);
error ContentNotFound();
error ChunkOutOfBounds();
error EmptyData();
constructor(address owner_) {
_initializeOwner(owner_);
}
/// @notice Store small content in single transaction
/// @param contentType Type identifier
/// @param data Content bytes
/// @return id Content ID
function store(bytes32 contentType, bytes calldata data) external onlyOwner returns (uint256 id) {
if (data.length == 0) revert EmptyData();
id = nextId++;
address pointer = SSTORE2.write(data);
locations[id] = DataLoc(pointer, 0, uint48(data.length));
contents[id].contentType = contentType;
emit ContentStored(id, contentType);
}
/// @notice Store content at specific ID
/// @param id Content ID
/// @param contentType Type identifier
/// @param data Content bytes
function storeAt(uint256 id, bytes32 contentType, bytes calldata data) external onlyOwner {
if (data.length == 0) revert EmptyData();
if (id >= nextId) nextId = id + 1;
address pointer = SSTORE2.write(data);
locations[id] = DataLoc(pointer, 0, uint48(data.length));
contents[id].contentType = contentType;
emit ContentStored(id, contentType);
}
/// @notice Initialize chunked content
/// @param id Content ID
/// @param contentType Type identifier
function initChunked(uint256 id, bytes32 contentType) external onlyOwner {
if (id >= nextId) nextId = id + 1;
contents[id].contentType = contentType;
emit ContentStored(id, contentType);
}
/// @notice Add chunk to content
/// @param id Content ID
/// @param chunk Chunk data
function addChunk(uint256 id, bytes calldata chunk) external onlyOwner {
if (contents[id].contentType == bytes32(0)) revert ContentNotFound();
if (chunk.length == 0) revert EmptyData();
uint256 index = contents[id].chunkCount;
contents[id].chunks[index] = SSTORE2.write(chunk);
contents[id].chunkCount++;
emit ChunkStored(id, index);
}
/// @notice Add multiple chunks
/// @param id Content ID
/// @param chunks Array of chunk data
function addChunks(uint256 id, bytes[] calldata chunks) external onlyOwner {
if (contents[id].contentType == bytes32(0)) revert ContentNotFound();
uint256 startIndex = contents[id].chunkCount;
for (uint256 i; i < chunks.length; ++i) {
if (chunks[i].length == 0) revert EmptyData();
contents[id].chunks[startIndex + i] = SSTORE2.write(chunks[i]);
emit ChunkStored(id, startIndex + i);
}
contents[id].chunkCount += uint24(chunks.length);
}
/// @notice Batch store multiple small contents
/// @param contentType Type identifier for all
/// @param dataArray Array of content bytes
/// @return startId First content ID
function storeBatch(bytes32 contentType, bytes[] calldata dataArray) external onlyOwner returns (uint256 startId) {
startId = nextId;
for (uint256 i; i < dataArray.length; ++i) {
if (dataArray[i].length == 0) revert EmptyData();
uint256 id = nextId++;
address pointer = SSTORE2.write(dataArray[i]);
locations[id] = DataLoc(pointer, 0, uint48(dataArray[i].length));
contents[id].contentType = contentType;
emit ContentStored(id, contentType);
}
}
/// @notice Get content bytes
/// @param id Content ID
/// @return Content as bytes
function get(uint256 id) external view returns (bytes memory) {
Content storage content = contents[id];
if (content.contentType == bytes32(0)) revert ContentNotFound();
// Single storage
DataLoc memory loc = locations[id];
if (loc.pointer != address(0)) {
return SSTORE2.read(loc.pointer, loc.start, loc.end);
}
// Chunked storage
bytes memory result;
for (uint256 i; i < content.chunkCount; ++i) {
result = bytes.concat(result, SSTORE2.read(content.chunks[i]));
}
return result;
}
/// @notice Get content as string
/// @param id Content ID
/// @return Content as string
function getString(uint256 id) external view returns (string memory) {
return string(this.get(id));
}
/// @notice Get single chunk
/// @param id Content ID
/// @param index Chunk index
/// @return Chunk bytes
function getChunk(uint256 id, uint256 index) external view returns (bytes memory) {
Content storage content = contents[id];
if (content.contentType == bytes32(0)) revert ContentNotFound();
if (index >= content.chunkCount) revert ChunkOutOfBounds();
return SSTORE2.read(content.chunks[index]);
}
/// @notice Get content info
/// @param id Content ID
/// @return contentType Content type
/// @return chunkCount Number of chunks (0 if single storage)
function getInfo(uint256 id) external view returns (bytes32 contentType, uint24 chunkCount) {
Content storage content = contents[id];
if (content.contentType == bytes32(0)) revert ContentNotFound();
return (content.contentType, content.chunkCount);
}
/// @notice Check if content exists
/// @param id Content ID
/// @return True if exists
function exists(uint256 id) external view returns (bool) {
return contents[id].contentType != bytes32(0);
}
}
SSTORE2.sol 259 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SSTORE2.sol)
/// @author Saw-mon-and-Natalie (https://github.com/Saw-mon-and-Natalie)
/// @author Modified from 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)
/// @author Modified from SSTORE3 (https://github.com/Philogy/sstore3)
library SSTORE2 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The proxy initialization code.
uint256 private constant _CREATE3_PROXY_INITCODE = 0x67363d3d37363d34f03d5260086018f3;
/// @dev Hash of the `_CREATE3_PROXY_INITCODE`.
/// Equivalent to `keccak256(abi.encodePacked(hex"67363d3d37363d34f03d5260086018f3"))`.
bytes32 internal constant CREATE3_PROXY_INITCODE_HASH =
0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unable to deploy the storage contract.
error DeploymentFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WRITE LOGIC */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Writes `data` into the bytecode of a storage contract and returns its address.
function write(bytes memory data) internal returns (address pointer) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(data) // Let `l` be `n + 1`. +1 as we prefix a STOP opcode.
/**
* ---------------------------------------------------+
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------|
* 61 l | PUSH2 l | l | |
* 80 | DUP1 | l l | |
* 60 0xa | PUSH1 0xa | 0xa l l | |
* 3D | RETURNDATASIZE | 0 0xa l l | |
* 39 | CODECOPY | l | [0..l): code |
* 3D | RETURNDATASIZE | 0 l | [0..l): code |
* F3 | RETURN | | [0..l): code |
* 00 | 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.
*/
// Do a out-of-gas revert if `n + 1` is more than 2 bytes.
mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
// Deploy a new contract with the generated creation code.
pointer := create(0, add(data, 0x15), add(n, 0xb))
if iszero(pointer) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(data, n) // Restore the length of `data`.
}
}
/// @dev Writes `data` into the bytecode of a storage contract with `salt`
/// and returns its normal CREATE2 deterministic address.
function writeCounterfactual(bytes memory data, bytes32 salt)
internal
returns (address pointer)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(data)
// Do a out-of-gas revert if `n + 1` is more than 2 bytes.
mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
// Deploy a new contract with the generated creation code.
pointer := create2(0, add(data, 0x15), add(n, 0xb), salt)
if iszero(pointer) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(data, n) // Restore the length of `data`.
}
}
/// @dev Writes `data` into the bytecode of a storage contract and returns its address.
/// This uses the so-called "CREATE3" workflow,
/// which means that `pointer` is agnostic to `data, and only depends on `salt`.
function writeDeterministic(bytes memory data, bytes32 salt)
internal
returns (address pointer)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(data)
mstore(0x00, _CREATE3_PROXY_INITCODE) // Store the `_PROXY_INITCODE`.
let proxy := create2(0, 0x10, 0x10, salt)
if iszero(proxy) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, proxy) // Store the proxy's address.
// 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
// 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
mstore(0x00, 0xd694)
mstore8(0x34, 0x01) // Nonce of the proxy contract (1).
pointer := keccak256(0x1e, 0x17)
// Do a out-of-gas revert if `n + 1` is more than 2 bytes.
mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
if iszero(
mul( // The arguments of `mul` are evaluated last to first.
extcodesize(pointer),
call(gas(), proxy, 0, add(data, 0x15), add(n, 0xb), codesize(), 0x00)
)
) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(data, n) // Restore the length of `data`.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ADDRESS CALCULATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the initialization code hash of the storage contract for `data`.
/// Used for mining vanity addresses with create2crunch.
function initCodeHash(bytes memory data) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(data)
// Do a out-of-gas revert if `n + 1` is more than 2 bytes.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xfffe))
mstore(data, add(0x61000180600a3d393df300, shl(0x40, n)))
hash := keccak256(add(data, 0x15), add(n, 0xb))
mstore(data, n) // Restore the length of `data`.
}
}
/// @dev Equivalent to `predictCounterfactualAddress(data, salt, address(this))`
function predictCounterfactualAddress(bytes memory data, bytes32 salt)
internal
view
returns (address pointer)
{
pointer = predictCounterfactualAddress(data, salt, address(this));
}
/// @dev Returns the CREATE2 address of the storage contract for `data`
/// deployed with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictCounterfactualAddress(bytes memory data, bytes32 salt, address deployer)
internal
pure
returns (address predicted)
{
bytes32 hash = initCodeHash(data);
/// @solidity memory-safe-assembly
assembly {
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, hash)
mstore(0x01, shl(96, deployer))
mstore(0x15, salt)
predicted := keccak256(0x00, 0x55)
// Restore the part of the free memory pointer that has been overwritten.
mstore(0x35, 0)
}
}
/// @dev Equivalent to `predictDeterministicAddress(salt, address(this))`.
function predictDeterministicAddress(bytes32 salt) internal view returns (address pointer) {
pointer = predictDeterministicAddress(salt, address(this));
}
/// @dev Returns the "CREATE3" deterministic address for `salt` with `deployer`.
function predictDeterministicAddress(bytes32 salt, address deployer)
internal
pure
returns (address pointer)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, deployer) // Store `deployer`.
mstore8(0x0b, 0xff) // Store the prefix.
mstore(0x20, salt) // Store the salt.
mstore(0x40, CREATE3_PROXY_INITCODE_HASH) // Store the bytecode hash.
mstore(0x14, keccak256(0x0b, 0x55)) // Store the proxy's address.
mstore(0x40, m) // Restore the free memory pointer.
// 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
// 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
mstore(0x00, 0xd694)
mstore8(0x34, 0x01) // Nonce of the proxy contract (1).
pointer := keccak256(0x1e, 0x17)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* READ LOGIC */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `read(pointer, 0, 2 ** 256 - 1)`.
function read(address pointer) internal view returns (bytes memory data) {
/// @solidity memory-safe-assembly
assembly {
data := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(pointer), 0x01))
extcodecopy(pointer, add(data, 0x1f), 0x00, add(n, 0x21))
mstore(data, n) // Store the length.
mstore(0x40, add(n, add(data, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `read(pointer, start, 2 ** 256 - 1)`.
function read(address pointer, uint256 start) internal view returns (bytes memory data) {
/// @solidity memory-safe-assembly
assembly {
data := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(pointer), 0x01))
let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
extcodecopy(pointer, add(data, 0x1f), start, add(l, 0x21))
mstore(data, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(data, add(0x40, mload(data)))) // Allocate memory.
}
}
/// @dev Returns a slice of the data on `pointer` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `pointer` MUST be deployed via the SSTORE2 write functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `pointer` does not have any code.
function read(address pointer, uint256 start, uint256 end)
internal
view
returns (bytes memory data)
{
/// @solidity memory-safe-assembly
assembly {
data := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(pointer, add(data, 0x1f), start, add(d, 0x01))
if iszero(and(0xff, mload(add(data, d)))) {
let n := sub(extcodesize(pointer), 0x01)
returndatacopy(returndatasize(), returndatasize(), shr(40, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(data, d) // Store the length.
mstore(add(add(data, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(data, 0x40), d)) // Allocate memory.
}
}
}
Ownable.sol 278 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(_OWNER_SLOT))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
/// Override to return a different value if needed.
/// Made internal to conserve bytecode. Wrap it in a public function if needed.
function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + _ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_OWNER_SLOT)
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}
Read Contract
exists 0x4f558e79 → bool
get 0x9507d39a → bytes
getChunk 0xad88b6e4 → bytes
getInfo 0x1a3cd59a → bytes32, uint24
getString 0xb7c763b5 → string
nextId 0x61b8ce8c → uint256
owner 0x8da5cb5b → address
ownershipHandoverExpiresAt 0xfee81cf4 → uint256
Write Contract 11 functions
These functions modify contract state and require a wallet transaction to execute.
addChunk 0x79627fa1
uint256 id
bytes chunk
addChunks 0x0f11e547
uint256 id
bytes[] chunks
cancelOwnershipHandover 0x54d1f13d
No parameters
completeOwnershipHandover 0xf04e283e
address pendingOwner
initChunked 0x9a8c17dd
uint256 id
bytes32 contentType
renounceOwnership 0x715018a6
No parameters
requestOwnershipHandover 0x25692962
No parameters
store 0x294bd5ce
bytes32 contentType
bytes data
returns: uint256
storeAt 0xcf6dc0fa
uint256 id
bytes32 contentType
bytes data
storeBatch 0x16d5002a
bytes32 contentType
bytes[] dataArray
returns: uint256
transferOwnership 0xf2fde38b
address newOwner
Recent Transactions
No transactions found for this address