false
false
0
The new Blockscout UI is now open source! Learn how to deploy it here
- We're indexing this chain right now. Some of the counts may be inaccurate.

Contract Address Details

0xEC2cB49B558307CB8c7130d63dC0c9A92550c766

Contract Name
EVMFS
Creator
0xde984b–d986ce at 0x035af9–cd5491
Balance
0 ETH
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
5804305
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
EVMFS




Optimization enabled
true
Compiler version
v0.8.28+commit.7893614a




Optimization runs
200
EVM Version
prague




Verified at
2026-03-10T21:51:45.398439Z

Constructor Arguments

0x000000000000000000000000d3a75ce3ce12a0aaa74e18e82a1714a21e7d7a13000000000000000000000000b4ac16cb7d97d200e1b474f985c367d8e870e72e000000000000000000000000de984be08167cc4b47ce70be71b18cbe95d986ce

Arg [0] (address) : 0xd3a75ce3ce12a0aaa74e18e82a1714a21e7d7a13
Arg [1] (address) : 0xb4ac16cb7d97d200e1b474f985c367d8e870e72e
Arg [2] (address) : 0xde984be08167cc4b47ce70be71b18cbe95d986ce

              

contracts/EVMFS.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "./Block.sol";
import "./IEVMFSSubscription.sol";

/**
 * @notice EVMFS – stores ZIP archives on-chain as linked Block chunks.
 *
 * CID = rolling sha256 computed over successive chunks:
 *   hash_1 = sha256(chunk_1)
 *   hash_N = sha256(hash_{N-1} ++ chunk_N)
 *
 * The final hash is stored as a 32-byte CID (compatible with the
 * evmfs-web-loader JS ABI: getFileByCID(bytes) / getFileBlocks()).
 *
 * Features:
 *   - Implicit resumability: getUploadProgress() returns block count + status
 *     so clients can resume interrupted uploads from the right offset.
 *   - Block reuse: addExistingBlock() allows composing files from blocks that
 *     were already uploaded (e.g. appending to a log without re-uploading
 *     existing content). Different files can share blocks, resulting in
 *     different CIDs that reference overlapping block sets.
 *   - Subscription tokenomics: holders of an active EVMFSSubscription NFT
 *     upload for free; non-subscribers pay a small per-block premium
 *     forwarded to the immutable beneficiary. Fully permissionless.
 */
contract EVMFS {
    enum Status {
        Uploading, // 0
        Ready      // 1
    }

    struct FileData {
        uint256 id;
        Status  status;
        bytes   cid;
        uint256[] blockIds;
    }

    Block public blockContract;
    IEVMFSSubscription public immutable subscription;
    address public immutable beneficiary;
    uint256 public constant BLOCK_WRITE_PREMIUM = 0.00005 ether;

    mapping(uint256 => FileData) private _files;
    /// @dev keyed by the raw bytes32 CID
    mapping(bytes32 => uint256) private _cidToFileId;
    mapping(uint256 => bytes32) private _incrementalHash;
    mapping(uint256 => address) private _fileOwners;
    uint256 private _fileCount;

    event FileCreated(uint256 indexed fileId, address indexed owner);
    event BlockUploaded(uint256 indexed fileId, uint256 indexed blockId);
    event FileFinalized(uint256 indexed fileId, bytes cid);
    event FileExtended(uint256 indexed fileId, address indexed owner);

    error FileNotFound();
    error FileAlreadyFinalized();
    error NotFileOwner();
    error InvalidCID();
    error EmptyData();
    error BlockNotFound();
    error FileNotFinalized();
    error InsufficientPremium();
    error PremiumTransferFailed();

    constructor(address _blockContract, address _subscription, address _beneficiary) {
        blockContract = Block(_blockContract);
        subscription = IEVMFSSubscription(_subscription);
        beneficiary = _beneficiary;
    }

    // ─────────────────────────────────────────────────────
    //  Write
    // ─────────────────────────────────────────────────────

    /**
     * @notice Start a new file upload with the first chunk of data.
     * @param data  First (or only) chunk bytes. Must not be empty.
     * @param end   True when this is also the last chunk.
     * @return fileId The new file ID.
     */
    function createFile(bytes calldata data, bool end) external payable returns (uint256 fileId) {
        if (data.length == 0) revert EmptyData();
        _chargePremium();

        fileId = ++_fileCount;

        _files[fileId].id = fileId;
        _files[fileId].status = Status.Uploading;
        _fileOwners[fileId] = msg.sender;
        emit FileCreated(fileId, msg.sender);

        uint256 blockId = blockContract.createBlock(data);
        _files[fileId].blockIds.push(blockId);
        _incrementalHash[fileId] = sha256(data);
        emit BlockUploaded(fileId, blockId);

        if (end) _finalize(fileId);
    }

    /**
     * @notice Append a new chunk to an in-progress upload.
     * @param fileId The file ID returned by createFile().
     * @param data   Chunk bytes.
     * @param end    True when this is the last chunk.
     * @return blockId The block ID of this chunk.
     */
    function upload(uint256 fileId, bytes calldata data, bool end) external payable returns (uint256 blockId) {
        if (fileId == 0 || fileId > _fileCount) revert FileNotFound();
        if (_fileOwners[fileId] != msg.sender)   revert NotFileOwner();
        if (_files[fileId].status == Status.Ready) revert FileAlreadyFinalized();
        _chargePremium();

        blockId = blockContract.createBlock(data);
        _files[fileId].blockIds.push(blockId);
        _incrementalHash[fileId] = sha256(abi.encodePacked(_incrementalHash[fileId], data));
        emit BlockUploaded(fileId, blockId);

        if (end) _finalize(fileId);
    }

    /**
     * @notice Reference an existing block (already uploaded) instead of uploading
     *         new data. Useful for composing files from overlapping block sets —
     *         e.g. appending new content to an existing log without re-uploading
     *         the unchanged prefix.
     * @param fileId  The file being assembled (must be in Uploading status).
     * @param blockId An existing block ID (from this or any other file).
     * @param end     True to finalize the file after adding this block.
     */
    function addExistingBlock(uint256 fileId, uint256 blockId, bool end) external {
        if (fileId == 0 || fileId > _fileCount) revert FileNotFound();
        if (_fileOwners[fileId] != msg.sender)   revert NotFileOwner();
        if (_files[fileId].status == Status.Ready) revert FileAlreadyFinalized();

        // Read the block data for CID computation (reverts if block doesn't exist)
        Block.BlockData memory blk = blockContract.getBlock(blockId);

        _files[fileId].blockIds.push(blockId);

        // Update rolling hash the same way as upload() — the CID depends on
        // data content, not on whether the block was freshly created or reused.
        if (_files[fileId].blockIds.length == 1) {
            // First block added (file was created with createFileEmpty or similar)
            _incrementalHash[fileId] = sha256(blk.data);
        } else {
            _incrementalHash[fileId] = sha256(abi.encodePacked(_incrementalHash[fileId], blk.data));
        }
        emit BlockUploaded(fileId, blockId);

        if (end) _finalize(fileId);
    }

    /**
     * @notice Create an empty file placeholder (no first chunk).
     *         Use with addExistingBlock() to compose files entirely from
     *         pre-existing blocks.
     * @return fileId The new file ID.
     */
    function createFileEmpty() external returns (uint256 fileId) {
        fileId = ++_fileCount;
        _files[fileId].id = fileId;
        _files[fileId].status = Status.Uploading;
        _fileOwners[fileId] = msg.sender;
        emit FileCreated(fileId, msg.sender);
    }

    /**
     * @notice Reopen a finalized file for appending.
     *         Sets status back to Uploading and re-seeds the incremental hash
     *         from the existing CID so subsequent upload() / addExistingBlock()
     *         calls continue the rolling hash. After appending, finalize again
     *         (pass end=true) to get a new CID.
     * @param fileId The file to extend (must be Ready and owned by caller).
     */
    function extendFile(uint256 fileId) external {
        if (fileId == 0 || fileId > _fileCount) revert FileNotFound();
        if (_fileOwners[fileId] != msg.sender)   revert NotFileOwner();
        if (_files[fileId].status != Status.Ready) revert FileNotFinalized();

        // Remove old CID → fileId mapping (CID will change after new blocks)
        bytes32 oldHash;
        bytes memory oldCid = _files[fileId].cid;
        assembly {
            oldHash := mload(add(oldCid, 32))
        }
        delete _cidToFileId[oldHash];

        // Re-seed incremental hash from the existing CID and set back to Uploading
        _incrementalHash[fileId] = oldHash;
        _files[fileId].status = Status.Uploading;
        delete _files[fileId].cid;

        emit FileExtended(fileId, msg.sender);
    }

    // ─────────────────────────────────────────────────────
    //  Read  (ABI-compatible with evmfs-web-loader.js)
    // ─────────────────────────────────────────────────────

    /**
     * @notice Look up a file by its 32-byte CID (raw sha256 bytes).
     * @param cid  Exactly 32 bytes (the sha256 hash used as CID).
     */
    function getFileByCID(bytes calldata cid) external view returns (FileData memory) {
        if (cid.length != 32) revert InvalidCID();
        bytes32 key;
        assembly {
            key := calldataload(cid.offset)
        }
        uint256 fileId = _cidToFileId[key];
        if (fileId == 0) revert FileNotFound();
        return _files[fileId];
    }

    /**
     * @notice Return all block payloads for a finalized file.
     * @dev Return type matches the loader's ABI: tuple(uint256 id, bytes data)[].
     */
    function getFileBlocks(uint256 fileId) external view returns (Block.BlockData[] memory blocks) {
        if (fileId == 0 || fileId > _fileCount) revert FileNotFound();

        uint256[] storage ids = _files[fileId].blockIds;
        blocks = new Block.BlockData[](ids.length);
        for (uint256 i; i < ids.length; ++i) {
            blocks[i] = blockContract.getBlock(ids[i]);
        }
    }

    /**
     * @notice Query upload progress for implicit resumability.
     *         Clients can call this after a failed/interrupted upload to
     *         determine how many blocks were already committed, then resume
     *         by calling upload() / addExistingBlock() from that offset.
     * @param fileId The file ID to query.
     * @return status     Current file status (Uploading or Ready).
     * @return blockCount Number of blocks already committed.
     * @return fileOwner  Address that owns this file upload.
     */
    function getUploadProgress(uint256 fileId) external view returns (
        Status status,
        uint256 blockCount,
        address fileOwner
    ) {
        if (fileId == 0 || fileId > _fileCount) revert FileNotFound();
        return (
            _files[fileId].status,
            _files[fileId].blockIds.length,
            _fileOwners[fileId]
        );
    }

    /// @notice Total number of files created (including in-progress uploads).
    function fileCount() external view returns (uint256) {
        return _fileCount;
    }

    // ─────────────────────────────────────────────────────
    //  Internal
    // ─────────────────────────────────────────────────────

    /**
     * @dev Charge the per-block premium if the caller has no active subscription.
     *      Subscribers pass through for free. Non-subscribers must attach at least
     *      BLOCK_WRITE_PREMIUM; the premium is forwarded to the beneficiary and
     *      any excess is refunded.
     */
    function _chargePremium() internal {
        if (subscription.hasActiveSubscription(msg.sender)) return;

        if (msg.value < BLOCK_WRITE_PREMIUM) revert InsufficientPremium();
        (bool ok, ) = beneficiary.call{value: BLOCK_WRITE_PREMIUM}("");
        if (!ok) revert PremiumTransferFailed();

        uint256 excess = msg.value - BLOCK_WRITE_PREMIUM;
        if (excess > 0) {
            (bool refundOk, ) = msg.sender.call{value: excess}("");
            if (!refundOk) revert PremiumTransferFailed();
        }
    }

    function _finalize(uint256 fileId) internal {
        bytes32 hash = _incrementalHash[fileId];
        _files[fileId].cid = abi.encodePacked(hash);
        _files[fileId].status = Status.Ready;
        _cidToFileId[hash] = fileId;
        delete _incrementalHash[fileId];
        emit FileFinalized(fileId, _files[fileId].cid);
    }
}
        

contracts/Block.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @notice Dead-simple chunk store. Each block holds up to CHUNK_SIZE bytes.
contract Block {
    struct BlockData {
        uint256 id;
        bytes data;
    }

    mapping(uint256 => bytes) private _blocks;
    uint256 private _blockCount;

    event BlockCreated(uint256 indexed blockId);

    function createBlock(bytes calldata data) external returns (uint256 blockId) {
        blockId = ++_blockCount;
        _blocks[blockId] = data;
        emit BlockCreated(blockId);
    }

    function getBlock(uint256 blockId) external view returns (BlockData memory) {
        require(blockId > 0 && blockId <= _blockCount, "Block: not found");
        return BlockData(blockId, _blocks[blockId]);
    }

    function getBlockCount() external view returns (uint256) {
        return _blockCount;
    }
}
          

contracts/IEVMFSSubscription.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/// @title IEVMFSSubscription — minimal interface for EVMFS premium checks
/// @notice EVMFS.sol queries this to decide whether to charge the block-write premium.
interface IEVMFSSubscription {
    /// @notice Returns true if `account` has an active (non-expired) subscription.
    function hasActiveSubscription(address account) external view returns (bool);

    /// @notice Returns the expiry timestamp for `account`.
    ///         0 = never subscribed, type(uint256).max = lifetime.
    function subscriptionExpiry(address account) external view returns (uint256);
}
          

Compiler Settings

{"viaIR":true,"remappings":["@openzeppelin/contracts/=../../lib/openzeppelin-contracts/contracts/","forge-std/=../../lib/forge-std/src/","erc4626-tests/=/Users/Onix.Martinez/BlockProjects/web3-twitter/new_monorepo/lib/openzeppelin-contracts/lib/erc4626-tests/","halmos-cheatcodes/=/Users/Onix.Martinez/BlockProjects/web3-twitter/new_monorepo/lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=/Users/Onix.Martinez/BlockProjects/web3-twitter/new_monorepo/lib/openzeppelin-contracts/contracts/"],"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"libraries":{},"evmVersion":"prague"}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_blockContract","internalType":"address"},{"type":"address","name":"_subscription","internalType":"address"},{"type":"address","name":"_beneficiary","internalType":"address"}]},{"type":"error","name":"BlockNotFound","inputs":[]},{"type":"error","name":"EmptyData","inputs":[]},{"type":"error","name":"FileAlreadyFinalized","inputs":[]},{"type":"error","name":"FileNotFinalized","inputs":[]},{"type":"error","name":"FileNotFound","inputs":[]},{"type":"error","name":"InsufficientPremium","inputs":[]},{"type":"error","name":"InvalidCID","inputs":[]},{"type":"error","name":"NotFileOwner","inputs":[]},{"type":"error","name":"PremiumTransferFailed","inputs":[]},{"type":"event","name":"BlockUploaded","inputs":[{"type":"uint256","name":"fileId","internalType":"uint256","indexed":true},{"type":"uint256","name":"blockId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"FileCreated","inputs":[{"type":"uint256","name":"fileId","internalType":"uint256","indexed":true},{"type":"address","name":"owner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"FileExtended","inputs":[{"type":"uint256","name":"fileId","internalType":"uint256","indexed":true},{"type":"address","name":"owner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"FileFinalized","inputs":[{"type":"uint256","name":"fileId","internalType":"uint256","indexed":true},{"type":"bytes","name":"cid","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BLOCK_WRITE_PREMIUM","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addExistingBlock","inputs":[{"type":"uint256","name":"fileId","internalType":"uint256"},{"type":"uint256","name":"blockId","internalType":"uint256"},{"type":"bool","name":"end","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"beneficiary","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract Block"}],"name":"blockContract","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"fileId","internalType":"uint256"}],"name":"createFile","inputs":[{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bool","name":"end","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"fileId","internalType":"uint256"}],"name":"createFileEmpty","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"extendFile","inputs":[{"type":"uint256","name":"fileId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"fileCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"blocks","internalType":"struct Block.BlockData[]","components":[{"type":"uint256","name":"id","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"}]}],"name":"getFileBlocks","inputs":[{"type":"uint256","name":"fileId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct EVMFS.FileData","components":[{"type":"uint256","name":"id","internalType":"uint256"},{"type":"uint8","name":"status","internalType":"enum EVMFS.Status"},{"type":"bytes","name":"cid","internalType":"bytes"},{"type":"uint256[]","name":"blockIds","internalType":"uint256[]"}]}],"name":"getFileByCID","inputs":[{"type":"bytes","name":"cid","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"status","internalType":"enum EVMFS.Status"},{"type":"uint256","name":"blockCount","internalType":"uint256"},{"type":"address","name":"fileOwner","internalType":"address"}],"name":"getUploadProgress","inputs":[{"type":"uint256","name":"fileId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IEVMFSSubscription"}],"name":"subscription","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"blockId","internalType":"uint256"}],"name":"upload","inputs":[{"type":"uint256","name":"fileId","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bool","name":"end","internalType":"bool"}]}]
              

Contract Creation Code

0x60c0346100b257601f61169e38819003918201601f19168301916001600160401b038311848410176100b6578084926060946040528339810103126100b257610047816100ca565b906100606040610059602084016100ca565b92016100ca565b5f80546001600160a01b0319166001600160a01b03948516179055911660805260a0526040516115bf90816100df82396080518181816108ba01526112a5015260a05181818161085901526112f40152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036100b25756fe60806040526004361015610011575f80fd5b5f3560e01c8063242912eb146100e457806326865c6e146100df5780632cd53bec146100da5780632fedf4ff146100d557806338af3eed146100d057806343953519146100cb57806344387cc2146100c657806346d29c15146100c157806349d10f5b146100bc5780634ad18ed1146100b7578063da5f2cbc146100b2578063e37e8f1f146100ad5763eb90fff8146100a8575f80fd5b610d53565b610cb5565b610baa565b610b3b565b610909565b6108e9565b6108a5565b610888565b610844565b610739565b6104a3565b610283565b34610247576020366003190112610247576004358015801561023c575b61022d5761012761011a825f52600460205260405f2090565b546001600160a01b031690565b336001600160a01b039091160361021e57600161015881610150845f52600160205260405f2090565b015460ff1690565b61016181610ae8565b0361020f576020610185600261017f845f52600160205260405f2090565b01610ea6565b01515f818152600260205260408120556101a7825f52600360205260405f2090565b556101ca60016101bf835f52600160205260405f2090565b01805460ff19169055565b6101e760026101e1835f52600160205260405f2090565b01610eed565b33907fec44657a88c284e0463cf9e5a9ee86de49e5ef6fb9e84ea991fe5623d66bcae75f80a3005b630236afc760e11b5f5260045ffd5b63abea322960e01b5f5260045ffd5b633b4835d960e21b5f5260045ffd5b506005548111610101565b5f80fd5b9181601f840112156102475782359167ffffffffffffffff8311610247576020838186019501011161024757565b8015150361024757565b60403660031901126102475760043567ffffffffffffffff8111610247576102af90369060040161024b565b6024356102bb81610279565b8115610494576102c9611282565b6102d4600554610f4c565b916102de83600555565b826102f1815f52600160205260405f2090565b5561030960016101bf855f52600160205260405f2090565b61033e3361031f855f52600460205260405f2090565b80546001600160a01b0319166001600160a01b03909216919091179055565b6040519333847ff05ef8f0487c80ce3185f3d26a1e9fb91de62d6a7ac64bdac806f66dd14964865f80a35f546020908690610389906001600160a01b03165b6001600160a01b031690565b631340c99d60e11b8252815f816103a4888860048401610f6e565b03925af1948515610459575f9561045e575b506020915f916103da8760036103d4895f52600160205260405f2090565b01610fce565b6103e960405180938193611007565b039060025afa1561045957610447925f5161040c845f52600360205260405f2090565b55827f03a38763d8cb7ecfb09729a00aea2b4c74cae3564888e83978c7aea00a286c495f80a361044b575b6040519081529081906020820190565b0390f35b610454816113ed565b610437565b610f95565b5f91955091610484602093843d861161048d575b61047c8183610e84565b810190610f5f565b959150916103b6565b503d610472565b6399d8fec960e01b5f5260045ffd5b60603660031901126102475760043560243567ffffffffffffffff8111610247576104d290369060040161024b565b9091604435926104e184610279565b8115801561069a575b61022d5761050361011a835f52600460205260405f2090565b336001600160a01b039091160361021e57600161052c81610150855f52600160205260405f2090565b61053581610ae8565b1461068b57610542611282565b5f54610556906001600160a01b031661037d565b9260206040518095631340c99d60e11b8252815f81610579878960048401610f6e565b03925af1938415610459575f9461065d575b506105ee6020926105e25f936105af8860036103d48a5f52600160205260405f2090565b6105d46105c4885f52600360205260405f2090565b5491604051948593898501611014565b03601f198101835282610e84565b60405191828092611029565b039060025afa1561045957610447925f51610611835f52600360205260405f2090565b5582827f03a38763d8cb7ecfb09729a00aea2b4c74cae3564888e83978c7aea00a286c495f80a361064e575b506040519081529081906020820190565b610657906113ed565b5f61063d565b5f9194506020926105e26106806105ee93863d881161048d5761047c8183610e84565b96935050925061058b565b6368ce659760e01b5f5260045ffd5b5060055482116104ea565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b602081016020825282518091526040820191602060408360051b8301019401925f915b8383106106fb57505050505090565b909192939460208061072a600193603f198682030187526040838b5180518452015191818582015201906106a5565b970193019301919392906106ec565b346102475760203660031901126102475760043580158015610839575b61022d5761076e6003915f52600160205260405f2090565b01805461077a81611053565b5f805491939091610793906001600160a01b031661037d565b915b8381106107aa576040518061044787826106c9565b805f6107c66107bc6107e59486610fb4565b90549060031b1c90565b604051809481926304c0756960e01b8352600483019190602083019252565b0381875afa8015610459576001925f91610817575b506108058288611165565b526108108187611165565b5001610795565b61083391503d805f833e61082b8183610e84565b8101906110cb565b5f6107fa565b506005548111610756565b34610247575f366003190112610247576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610247575f366003190112610247576020600554604051908152f35b34610247575f366003190112610247576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610247575f366003190112610247576020604051652d79883d20008152f35b346102475760603660031901126102475760043560443560243561092c82610279565b82158015610add575b61022d5761094e61011a845f52600460205260405f2090565b336001600160a01b039091160361021e57600161097781610150865f52600160205260405f2090565b61098081610ae8565b1461068b575f54610999906001600160a01b031661037d565b6040516304c0756960e01b815260048101839052905f90829060249082905afa908115610459576020915f918291610ac3575b506109e58460036103d4895f52600160205260405f2090565b600160036109fb885f52600160205260405f2090565b0154148214610a6e5782610a1791015160405191828092611029565b039060025afa15610459575f51610a36845f52600360205260405f2090565b555b827f03a38763d8cb7ecfb09729a00aea2b4c74cae3564888e83978c7aea00a286c495f80a3610a6357005b610a6c906113ed565b005b610a9e906105e284610a88895f52600360205260405f2090565b54920151916105d4604051938492888401611179565b039060025afa15610459575f51610abd845f52600360205260405f2090565b55610a38565b610ad791503d8084833e61082b8183610e84565b5f6109cc565b506005548311610935565b60021115610af257565b634e487b7160e01b5f52602160045260245ffd5b906002821015610af25752565b91926040919493610b28846060810197610b06565b60208401526001600160a01b0316910152565b346102475760203660031901126102475760043580158015610b9f575b61022d575f90815260016020818152604080842092830154600390930154600490925292839020549251928392610447926001600160a01b03909216919060ff1684610b13565b506005548111610b58565b34610247575f36600319011261024757610447610bc8600554610f4c565b60058190555f81815260016020819052604090912082815501805460ff191690555f81815260046020526040902080546001600160a01b0319163317905560405191829133817ff05ef8f0487c80ce3185f3d26a1e9fb91de62d6a7ac64bdac806f66dd14964865f80a382526020820190565b906020825280516020830152610c5960208201516040840190610b06565b6060610c73604083015160808386015260a08501906106a5565b910151916080601f1982840301910152602080835192838152019201905f5b818110610c9f5750505090565b8251845260209384019390920191600101610c92565b346102475760203660031901126102475760043567ffffffffffffffff811161024757610ce8602091369060040161024b565b606080604094939451610cfa81610e47565b5f81525f85820152816040820152015203610d4457355f52600260205260405f2054801561022d575f526001602052610447610d3860405f2061118d565b60405191829182610c3b565b633875d9bd60e21b5f5260045ffd5b34610247575f366003190112610247575f546040516001600160a01b039091168152602090f35b90600182811c92168015610da8575b6020831014610d9457565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610d89565b5f9291815491610dc183610d7a565b8083529260018116908115610e165750600114610ddd57505050565b5f9081526020812093945091925b838310610dfc575060209250010190565b600181602092949394548385870101520191019190610deb565b915050602093945060ff929192191683830152151560051b010190565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff821117610e6357604052565b610e33565b6040810190811067ffffffffffffffff821117610e6357604052565b90601f8019910116810190811067ffffffffffffffff821117610e6357604052565b90610ec1610eba9260405193848092610db2565b0383610e84565b565b818110610ece575050565b5f8155600101610ec3565b634e487b7160e01b5f52601160045260245ffd5b610ef78154610d7a565b9081610f01575050565b81601f5f9311600114610f12575055565b81835260208320610f2e91601f0160051c810190600101610ec3565b808252602082209081548360011b9084198560031b1c191617905555565b5f198114610f5a5760010190565b610ed9565b90816020910312610247575190565b90918060409360208452816020850152848401375f828201840152601f01601f1916010190565b6040513d5f823e3d90fd5b634e487b7160e01b5f52603260045260245ffd5b8054821015610fc9575f5260205f2001905f90565b610fa0565b805468010000000000000000811015610e6357610ff091600182018155610fb4565b819291549060031b91821b915f19901b1916179055565b908092918237015f815290565b9092809260209483528483013701015f815290565b805191908290602001825e015f815290565b67ffffffffffffffff8111610e635760051b60200190565b9061105d8261103b565b61106a6040519182610e84565b828152809261107b601f199161103b565b01905f5b82811061108b57505050565b60209060405161109a81610e68565b5f81526060838201528282850101520161107f565b67ffffffffffffffff8111610e6357601f01601f191660200190565b6020818303126102475780519067ffffffffffffffff8211610247570190604082820312610247576040519161110083610e68565b8051835260208101519067ffffffffffffffff8211610247570181601f8201121561024757805190611131826110af565b9261113f6040519485610e84565b8284526020838301011161024757815f9260208093018386015e83010152602082015290565b8051821015610fc95760209160051b010190565b60209061118a939281520190611029565b90565b9060405161119a81610e47565b80928054825260ff600182015416906002821015610af25760039160208401526040516111d5816111ce8160028601610db2565b0382610e84565b604084015201906040518092805480835260208301915f5260205f20905f5b818110611211575050506060929161120d910384610e84565b0152565b82548452869450602090930192600192830192016111f4565b90816020910312610247575161118a81610279565b3d15611269573d90611250826110af565b9161125e6040519384610e84565b82523d5f602084013e565b606090565b652d79883d1fff19810191908211610f5a57565b60405163bebe4a5760e01b81523360048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115610459575f9161136a575b50610ec157652d79883d2000341061135b575f808080652d79883d20007f00000000000000000000000000000000000000000000000000000000000000005af161131d61123f565b501561134c5761132c3461126e565b806113345750565b5f80808093335af161134461123f565b501561134c57565b6377d9354960e01b5f5260045ffd5b6330cc5d5d60e01b5f5260045ffd5b61138c915060203d602011611392575b6113848183610e84565b81019061122a565b5f6112d5565b503d61137a565b9190601f81116113a857505050565b610ec1925f5260205f20906020601f840160051c830193106113d2575b601f0160051c0190610ec3565b90915081906113c5565b90602061118a928181520190610db2565b805f52600360205260405f20546040519080602083015260208252611413604083610e84565b825f526001602052600260405f20019082519167ffffffffffffffff8311610e63578261144b86946114458454610d7a565b84611399565b602094601f82116001146115195790806114b5959661147f935f9261150e575b50508160011b915f199060031b1c19161790565b90555b6114a76001611499855f52600160205260405f2090565b01805460ff19166001179055565b5f52600260205260405f2090565b555f818152600360205260408120557fbd2e6d41a5e154d79f618262e33b20e68969c12a037078c5927fcedfa724c91761150960026114fc845f52600160205260405f2090565b01604051918291826113dc565b0390a2565b015190505f8061146b565b601f1982169061152c845f5260205f2090565b915f5b81811061156e5750968392916001946114b5989910611556575b505050811b019055611482565b01515f1960f88460031b161c191690555f8080611549565b8289015184556020988901988a98506001909401930161152f56fea2646970667358221220c2ab660ebe655baeadd7da6b3fa718ee43e408a3bcf58211f96b45d017b4841964736f6c634300081c0033000000000000000000000000d3a75ce3ce12a0aaa74e18e82a1714a21e7d7a13000000000000000000000000b4ac16cb7d97d200e1b474f985c367d8e870e72e000000000000000000000000de984be08167cc4b47ce70be71b18cbe95d986ce

Deployed ByteCode

0x60806040526004361015610011575f80fd5b5f3560e01c8063242912eb146100e457806326865c6e146100df5780632cd53bec146100da5780632fedf4ff146100d557806338af3eed146100d057806343953519146100cb57806344387cc2146100c657806346d29c15146100c157806349d10f5b146100bc5780634ad18ed1146100b7578063da5f2cbc146100b2578063e37e8f1f146100ad5763eb90fff8146100a8575f80fd5b610d53565b610cb5565b610baa565b610b3b565b610909565b6108e9565b6108a5565b610888565b610844565b610739565b6104a3565b610283565b34610247576020366003190112610247576004358015801561023c575b61022d5761012761011a825f52600460205260405f2090565b546001600160a01b031690565b336001600160a01b039091160361021e57600161015881610150845f52600160205260405f2090565b015460ff1690565b61016181610ae8565b0361020f576020610185600261017f845f52600160205260405f2090565b01610ea6565b01515f818152600260205260408120556101a7825f52600360205260405f2090565b556101ca60016101bf835f52600160205260405f2090565b01805460ff19169055565b6101e760026101e1835f52600160205260405f2090565b01610eed565b33907fec44657a88c284e0463cf9e5a9ee86de49e5ef6fb9e84ea991fe5623d66bcae75f80a3005b630236afc760e11b5f5260045ffd5b63abea322960e01b5f5260045ffd5b633b4835d960e21b5f5260045ffd5b506005548111610101565b5f80fd5b9181601f840112156102475782359167ffffffffffffffff8311610247576020838186019501011161024757565b8015150361024757565b60403660031901126102475760043567ffffffffffffffff8111610247576102af90369060040161024b565b6024356102bb81610279565b8115610494576102c9611282565b6102d4600554610f4c565b916102de83600555565b826102f1815f52600160205260405f2090565b5561030960016101bf855f52600160205260405f2090565b61033e3361031f855f52600460205260405f2090565b80546001600160a01b0319166001600160a01b03909216919091179055565b6040519333847ff05ef8f0487c80ce3185f3d26a1e9fb91de62d6a7ac64bdac806f66dd14964865f80a35f546020908690610389906001600160a01b03165b6001600160a01b031690565b631340c99d60e11b8252815f816103a4888860048401610f6e565b03925af1948515610459575f9561045e575b506020915f916103da8760036103d4895f52600160205260405f2090565b01610fce565b6103e960405180938193611007565b039060025afa1561045957610447925f5161040c845f52600360205260405f2090565b55827f03a38763d8cb7ecfb09729a00aea2b4c74cae3564888e83978c7aea00a286c495f80a361044b575b6040519081529081906020820190565b0390f35b610454816113ed565b610437565b610f95565b5f91955091610484602093843d861161048d575b61047c8183610e84565b810190610f5f565b959150916103b6565b503d610472565b6399d8fec960e01b5f5260045ffd5b60603660031901126102475760043560243567ffffffffffffffff8111610247576104d290369060040161024b565b9091604435926104e184610279565b8115801561069a575b61022d5761050361011a835f52600460205260405f2090565b336001600160a01b039091160361021e57600161052c81610150855f52600160205260405f2090565b61053581610ae8565b1461068b57610542611282565b5f54610556906001600160a01b031661037d565b9260206040518095631340c99d60e11b8252815f81610579878960048401610f6e565b03925af1938415610459575f9461065d575b506105ee6020926105e25f936105af8860036103d48a5f52600160205260405f2090565b6105d46105c4885f52600360205260405f2090565b5491604051948593898501611014565b03601f198101835282610e84565b60405191828092611029565b039060025afa1561045957610447925f51610611835f52600360205260405f2090565b5582827f03a38763d8cb7ecfb09729a00aea2b4c74cae3564888e83978c7aea00a286c495f80a361064e575b506040519081529081906020820190565b610657906113ed565b5f61063d565b5f9194506020926105e26106806105ee93863d881161048d5761047c8183610e84565b96935050925061058b565b6368ce659760e01b5f5260045ffd5b5060055482116104ea565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b602081016020825282518091526040820191602060408360051b8301019401925f915b8383106106fb57505050505090565b909192939460208061072a600193603f198682030187526040838b5180518452015191818582015201906106a5565b970193019301919392906106ec565b346102475760203660031901126102475760043580158015610839575b61022d5761076e6003915f52600160205260405f2090565b01805461077a81611053565b5f805491939091610793906001600160a01b031661037d565b915b8381106107aa576040518061044787826106c9565b805f6107c66107bc6107e59486610fb4565b90549060031b1c90565b604051809481926304c0756960e01b8352600483019190602083019252565b0381875afa8015610459576001925f91610817575b506108058288611165565b526108108187611165565b5001610795565b61083391503d805f833e61082b8183610e84565b8101906110cb565b5f6107fa565b506005548111610756565b34610247575f366003190112610247576040517f000000000000000000000000de984be08167cc4b47ce70be71b18cbe95d986ce6001600160a01b03168152602090f35b34610247575f366003190112610247576020600554604051908152f35b34610247575f366003190112610247576040517f000000000000000000000000b4ac16cb7d97d200e1b474f985c367d8e870e72e6001600160a01b03168152602090f35b34610247575f366003190112610247576020604051652d79883d20008152f35b346102475760603660031901126102475760043560443560243561092c82610279565b82158015610add575b61022d5761094e61011a845f52600460205260405f2090565b336001600160a01b039091160361021e57600161097781610150865f52600160205260405f2090565b61098081610ae8565b1461068b575f54610999906001600160a01b031661037d565b6040516304c0756960e01b815260048101839052905f90829060249082905afa908115610459576020915f918291610ac3575b506109e58460036103d4895f52600160205260405f2090565b600160036109fb885f52600160205260405f2090565b0154148214610a6e5782610a1791015160405191828092611029565b039060025afa15610459575f51610a36845f52600360205260405f2090565b555b827f03a38763d8cb7ecfb09729a00aea2b4c74cae3564888e83978c7aea00a286c495f80a3610a6357005b610a6c906113ed565b005b610a9e906105e284610a88895f52600360205260405f2090565b54920151916105d4604051938492888401611179565b039060025afa15610459575f51610abd845f52600360205260405f2090565b55610a38565b610ad791503d8084833e61082b8183610e84565b5f6109cc565b506005548311610935565b60021115610af257565b634e487b7160e01b5f52602160045260245ffd5b906002821015610af25752565b91926040919493610b28846060810197610b06565b60208401526001600160a01b0316910152565b346102475760203660031901126102475760043580158015610b9f575b61022d575f90815260016020818152604080842092830154600390930154600490925292839020549251928392610447926001600160a01b03909216919060ff1684610b13565b506005548111610b58565b34610247575f36600319011261024757610447610bc8600554610f4c565b60058190555f81815260016020819052604090912082815501805460ff191690555f81815260046020526040902080546001600160a01b0319163317905560405191829133817ff05ef8f0487c80ce3185f3d26a1e9fb91de62d6a7ac64bdac806f66dd14964865f80a382526020820190565b906020825280516020830152610c5960208201516040840190610b06565b6060610c73604083015160808386015260a08501906106a5565b910151916080601f1982840301910152602080835192838152019201905f5b818110610c9f5750505090565b8251845260209384019390920191600101610c92565b346102475760203660031901126102475760043567ffffffffffffffff811161024757610ce8602091369060040161024b565b606080604094939451610cfa81610e47565b5f81525f85820152816040820152015203610d4457355f52600260205260405f2054801561022d575f526001602052610447610d3860405f2061118d565b60405191829182610c3b565b633875d9bd60e21b5f5260045ffd5b34610247575f366003190112610247575f546040516001600160a01b039091168152602090f35b90600182811c92168015610da8575b6020831014610d9457565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610d89565b5f9291815491610dc183610d7a565b8083529260018116908115610e165750600114610ddd57505050565b5f9081526020812093945091925b838310610dfc575060209250010190565b600181602092949394548385870101520191019190610deb565b915050602093945060ff929192191683830152151560051b010190565b634e487b7160e01b5f52604160045260245ffd5b6080810190811067ffffffffffffffff821117610e6357604052565b610e33565b6040810190811067ffffffffffffffff821117610e6357604052565b90601f8019910116810190811067ffffffffffffffff821117610e6357604052565b90610ec1610eba9260405193848092610db2565b0383610e84565b565b818110610ece575050565b5f8155600101610ec3565b634e487b7160e01b5f52601160045260245ffd5b610ef78154610d7a565b9081610f01575050565b81601f5f9311600114610f12575055565b81835260208320610f2e91601f0160051c810190600101610ec3565b808252602082209081548360011b9084198560031b1c191617905555565b5f198114610f5a5760010190565b610ed9565b90816020910312610247575190565b90918060409360208452816020850152848401375f828201840152601f01601f1916010190565b6040513d5f823e3d90fd5b634e487b7160e01b5f52603260045260245ffd5b8054821015610fc9575f5260205f2001905f90565b610fa0565b805468010000000000000000811015610e6357610ff091600182018155610fb4565b819291549060031b91821b915f19901b1916179055565b908092918237015f815290565b9092809260209483528483013701015f815290565b805191908290602001825e015f815290565b67ffffffffffffffff8111610e635760051b60200190565b9061105d8261103b565b61106a6040519182610e84565b828152809261107b601f199161103b565b01905f5b82811061108b57505050565b60209060405161109a81610e68565b5f81526060838201528282850101520161107f565b67ffffffffffffffff8111610e6357601f01601f191660200190565b6020818303126102475780519067ffffffffffffffff8211610247570190604082820312610247576040519161110083610e68565b8051835260208101519067ffffffffffffffff8211610247570181601f8201121561024757805190611131826110af565b9261113f6040519485610e84565b8284526020838301011161024757815f9260208093018386015e83010152602082015290565b8051821015610fc95760209160051b010190565b60209061118a939281520190611029565b90565b9060405161119a81610e47565b80928054825260ff600182015416906002821015610af25760039160208401526040516111d5816111ce8160028601610db2565b0382610e84565b604084015201906040518092805480835260208301915f5260205f20905f5b818110611211575050506060929161120d910384610e84565b0152565b82548452869450602090930192600192830192016111f4565b90816020910312610247575161118a81610279565b3d15611269573d90611250826110af565b9161125e6040519384610e84565b82523d5f602084013e565b606090565b652d79883d1fff19810191908211610f5a57565b60405163bebe4a5760e01b81523360048201526020816024816001600160a01b037f000000000000000000000000b4ac16cb7d97d200e1b474f985c367d8e870e72e165afa908115610459575f9161136a575b50610ec157652d79883d2000341061135b575f808080652d79883d20007f000000000000000000000000de984be08167cc4b47ce70be71b18cbe95d986ce5af161131d61123f565b501561134c5761132c3461126e565b806113345750565b5f80808093335af161134461123f565b501561134c57565b6377d9354960e01b5f5260045ffd5b6330cc5d5d60e01b5f5260045ffd5b61138c915060203d602011611392575b6113848183610e84565b81019061122a565b5f6112d5565b503d61137a565b9190601f81116113a857505050565b610ec1925f5260205f20906020601f840160051c830193106113d2575b601f0160051c0190610ec3565b90915081906113c5565b90602061118a928181520190610db2565b805f52600360205260405f20546040519080602083015260208252611413604083610e84565b825f526001602052600260405f20019082519167ffffffffffffffff8311610e63578261144b86946114458454610d7a565b84611399565b602094601f82116001146115195790806114b5959661147f935f9261150e575b50508160011b915f199060031b1c19161790565b90555b6114a76001611499855f52600160205260405f2090565b01805460ff19166001179055565b5f52600260205260405f2090565b555f818152600360205260408120557fbd2e6d41a5e154d79f618262e33b20e68969c12a037078c5927fcedfa724c91761150960026114fc845f52600160205260405f2090565b01604051918291826113dc565b0390a2565b015190505f8061146b565b601f1982169061152c845f5260205f2090565b915f5b81811061156e5750968392916001946114b5989910611556575b505050811b019055611482565b01515f1960f88460031b161c191690555f8080611549565b8289015184556020988901988a98506001909401930161152f56fea2646970667358221220c2ab660ebe655baeadd7da6b3fa718ee43e408a3bcf58211f96b45d017b4841964736f6c634300081c0033