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