Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0x25CBB4e36Db597F63c8376d15302c0d9CC6CE2De
Balance 0 ETH
Nonce 64
Code Size 4956 bytes
Indexed Transactions 0
External Etherscan · Sourcify

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