Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x61010060 | 15492199 | 690 days ago | IN | 0 ETH | 0.07714297 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
CRFTDStakingToken
Compiler Version
v0.8.15+commit.e14f2714
Optimization Enabled:
Yes with 100000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {utils} from "./utils/utils.sol"; import {ERC20UDS} from "UDS/tokens/ERC20UDS.sol"; import {ERC721UDS} from "UDS/tokens/ERC721UDS.sol"; import {OwnableUDS} from "UDS/auth/OwnableUDS.sol"; import {UUPSUpgrade} from "UDS/proxy/UUPSUpgrade.sol"; import {MINT_ERC20_SIG} from "fx-contracts/FxERC20RootUDS.sol"; import {ERC20RewardUDS} from "UDS/tokens/extensions/ERC20RewardUDS.sol"; import {FxERC721sRootTunnelUDS} from "fx-contracts/FxERC721sRootTunnelUDS.sol"; // ------------- storage bytes32 constant DIAMOND_STORAGE_CRFTD_TOKEN = keccak256("diamond.storage.crftd.token"); function s() pure returns (CRFTDTokenDS storage diamondStorage) { bytes32 slot = DIAMOND_STORAGE_CRFTD_TOKEN; assembly { diamondStorage.slot := slot } // prettier-ignore } struct CRFTDTokenDS { uint256 rewardEndDate; mapping(address => uint256) rewardRate; mapping(address => mapping(uint256 => address)) ownerOf; } // ------------- errors error ZeroReward(); error IncorrectOwner(); error InvalidFxChild(); error MigrationRequired(); error MigrationIncomplete(); error MigrationNotStarted(); error CollectionNotRegistered(); error CollectionAlreadyRegistered(); // ___ ___ ___ _____ // / /\ / /\ / /\ ___ / /::\ // / /:/ / /::\ / /:/_ /__/\ / /:/\:\ // / /:/ / /:/\:\ / /:/ /\ \ \:\ / /:/ \:\ // / /:/ ___ / /::\ \:\ / /:/ /:/ \__\:\ /__/:/ \__\:| // /__/:/ / /\ /__/:/\:\_\:\ /__/:/ /:/ / /::\ \ \:\ / /:/ // \ \:\ / /:/ \__\/~|::\/:/ \ \:\/:/ / /:/\:\ \ \:\ /:/ // \ \:\ /:/ | |:|::/ \ \::/ / /:/__\/ \ \:\/:/ // \ \:\/:/ | |:|\/ \ \:\ /__/:/ \ \::/ // \ \::/ |__|:| \ \:\ \__\/ \__\/ // \__\/ \__\| \__\/ /// @title CRFTDStakingToken (Cross-Chain) /// @author phaze (https://github.com/0xPhaze) /// @notice Minimal cross-chain ERC721 staking contract supporting multiple collections /// @notice Token ids are registered with child ERC20 token contract CRFTDStakingToken is FxERC721sRootTunnelUDS, ERC20RewardUDS, OwnableUDS, UUPSUpgrade { event CollectionRegistered(address indexed collection, uint256 rewardRate); /// @dev Setting `rewardEndDate` to this date enables migration. uint256 constant MIGRATION_START_DATE = (1 << 42) - 1; constructor(address checkpointManager, address fxRoot) FxERC721sRootTunnelUDS(checkpointManager, fxRoot) { __ERC20_init("CRFTD", "CRFTD", 18); } /* ------------- init ------------- */ function init(string calldata name, string calldata symbol) external initializer { __Ownable_init(); __ERC20_init(name, symbol, 18); } /* ------------- view ------------- */ function rewardEndDate() public view override returns (uint256) { return s().rewardEndDate; } function rewardDailyRate() public pure override returns (uint256) { return 1e18; } function rewardRate(address collection) public view returns (uint256) { return s().rewardRate[collection]; } function ownerOf(address collection, uint256 id) public view returns (address) { return s().ownerOf[collection][id]; } function getDailyReward(address user) public view returns (uint256) { return _getRewardMultiplier(user) * rewardDailyRate(); } function migrationStarted() public view returns (bool) { if (rewardEndDate() != MIGRATION_START_DATE) return false; if (fxChildTunnel() == address(0)) return false; return true; } /* ------------- external ------------- */ /// @notice Stake ids from approved collections. /// If child is linked and migration enabled, /// this will register the ids with child contract. /// @param collection erc721 collection address. /// @param tokenIds erc721 id array per collection. function stake(address collection, uint256[] calldata tokenIds) external { uint256 rate = s().rewardRate[collection]; if (rate == 0) revert CollectionNotRegistered(); for (uint256 i; i < tokenIds.length; ++i) { ERC721UDS(collection).transferFrom(msg.sender, address(this), tokenIds[i]); s().ownerOf[collection][tokenIds[i]] = msg.sender; } if (migrationStarted()) { if (_getRewardMultiplier(msg.sender) != 0) revert MigrationRequired(); _registerERC721IdsWithChild(collection, msg.sender, tokenIds); } else { _increaseRewardMultiplier(msg.sender, uint216(tokenIds.length * rate)); } } /// @notice Unstake ids. If child is linked and migration enabled, /// this will de-register the ids with child contract. /// @param collection erc721 collection address. /// @param tokenIds erc721 id array per collection. function unstake(address collection, uint256[] calldata tokenIds) external { uint256 rate = s().rewardRate[collection]; if (rate == 0) revert CollectionNotRegistered(); if (migrationStarted()) { if (_getRewardMultiplier(msg.sender) != 0) revert MigrationRequired(); _deregisterERC721IdsWithChild(collection, tokenIds); } else _decreaseRewardMultiplier(msg.sender, uint216(tokenIds.length * rate)); for (uint256 i; i < tokenIds.length; ++i) { if (s().ownerOf[collection][tokenIds[i]] != msg.sender) revert IncorrectOwner(); delete s().ownerOf[collection][tokenIds[i]]; ERC721UDS(collection).transferFrom(address(this), msg.sender, tokenIds[i]); } } /// @notice Synchronizes any erc721 ids with child. Duplicates are caught by child. /// @param collections erc721 collection addresses. /// @param tokenIds erc721 id array per collection. /// @return rewardMultiplier calculated rewardMultiplier to be registered with child. /// Used by `safeMigrate()`. function synchronizeIdsWithChild(address[] calldata collections, uint256[][] calldata tokenIds) public returns (uint256 rewardMultiplier) { if (!migrationStarted()) revert MigrationNotStarted(); if (_getRewardMultiplier(msg.sender) != 0) revert MigrationRequired(); for (uint256 i; i < collections.length; ++i) { uint256 rate = s().rewardRate[collections[i]]; if (rate == 0) revert CollectionNotRegistered(); uint256[] calldata ids = tokenIds[i]; mapping(uint256 => address) storage owners = s().ownerOf[collections[i]]; rewardMultiplier += ids.length * rate; for (uint256 j; j < ids.length; ++j) { if (owners[ids[j]] != msg.sender) revert IncorrectOwner(); } // note: we're optimistic by fore-going unique tokenId/collection checks // duplicates are caught in child registry and could only hurt the user during migration if (ids.length != 0) _registerERC721IdsWithChild(collections[i], msg.sender, ids); } } /// @notice Migrates the current user balance and erc721 ids to layer 2. /// user `rewardMultiplier` MUST be 0 before registering any ids with child, /// as otherwise it would be possible to earn on both chains. /// @param collections erc721 collection addresses. /// @param tokenIds erc721 id array per collection. function safeMigrate(address[] calldata collections, uint256[][] calldata tokenIds) external { if (!migrationStarted()) revert MigrationNotStarted(); _claimReward(msg.sender); uint256 currentRewardMultiplier = _getRewardMultiplier(msg.sender); // migrate erc20 token balance uint256 balance = balanceOf(msg.sender); _burn(msg.sender, balance); _sendMessageToChild(abi.encode(MINT_ERC20_SIG, abi.encode(msg.sender, balance))); _setRewardMultiplier(msg.sender, 0); // migrate erc721 ids uint256 migratedRewardMultiplier = synchronizeIdsWithChild(collections, tokenIds); // this check does not guarantee that the user has migrated all erc721s, // but is an additional safety measure for the user if (currentRewardMultiplier != migratedRewardMultiplier) revert MigrationIncomplete(); } function claimReward() external { _claimReward(msg.sender); } /* ------------- erc20 ------------- */ function transfer(address to, uint256 amount) public virtual override returns (bool) { _claimReward(msg.sender); return ERC20UDS.transfer(to, amount); } function transferFrom( address from, address to, uint256 amount ) public virtual override returns (bool) { _claimReward(from); return ERC20UDS.transferFrom(from, to, amount); } /* ------------- O(n) read-only ------------- */ function stakedIdsOf( address collection, address user, uint256 collectionSize ) external view returns (uint256[] memory) { return utils.getOwnedIds(s().ownerOf[collection], user, collectionSize); } /* ------------- owner ------------- */ function startMigration(address fxChild) public onlyOwner { if (fxChild == address(0)) revert InvalidFxChild(); setFxChildTunnel(fxChild); s().rewardEndDate = MIGRATION_START_DATE; } function setRewardEndDate(uint256 endDate) external onlyOwner { s().rewardEndDate = endDate; } function registerCollection(address collection, uint200 rate) external onlyOwner { if (rate == 0) revert ZeroReward(); if (s().rewardRate[collection] != 0) revert CollectionAlreadyRegistered(); s().rewardRate[collection] = rate; emit CollectionRegistered(collection, rate); } function airdrop(address[] calldata tos, uint256[] calldata amounts) external onlyOwner { for (uint256 i; i < tos.length; ++i) _mint(tos[i], amounts[i]); } /* ------------- override ------------- */ function _authorizeUpgrade() internal override onlyOwner {} function _authorizeTunnelController() internal override onlyOwner {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library utils { function getOwnedIds( mapping(uint256 => address) storage ownerOf, address user, uint256 collectionSize ) internal view returns (uint256[] memory ids) { uint256 memPtr; uint256 idsLength; assembly { ids := mload(0x40) memPtr := add(ids, 32) } unchecked { uint256 end = collectionSize + 1; for (uint256 id = 0; id < end; ++id) { if (ownerOf[id] == user) { assembly { mstore(memPtr, id) memPtr := add(memPtr, 32) idsLength := add(idsLength, 1) } } } } assembly { mstore(ids, idsLength) mstore(0x40, memPtr) } } function indexOf(address[] calldata arr, address addr) internal pure returns (bool found, uint256 index) { unchecked { for (uint256 i; i < arr.length; ++i) if (arr[i] == addr) return (true, i); } return (false, 0); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {Context} from "../utils/Context.sol"; import {Initializable} from "../utils/Initializable.sol"; import {EIP712PermitUDS} from "../auth/EIP712PermitUDS.sol"; // ------------- storage bytes32 constant DIAMOND_STORAGE_ERC20 = keccak256("diamond.storage.erc20"); function s() pure returns (ERC20DS storage diamondStorage) { bytes32 slot = DIAMOND_STORAGE_ERC20; assembly { diamondStorage.slot := slot } // prettier-ignore } struct ERC20DS { string name; string symbol; uint8 decimals; uint256 totalSupply; mapping(address => uint256) balanceOf; mapping(address => mapping(address => uint256)) allowance; } /// @title ERC20 (Upgradeable Diamond Storage) /// @author phaze (https://github.com/0xPhaze/UDS) /// @author Modified from Solmate (https://github.com/Rari-Capital/solmate) abstract contract ERC20UDS is Context, Initializable, EIP712PermitUDS { ERC20DS private __storageLayout; // storage layout for upgrade compatibility checks event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed operator, uint256 amount); /* ------------- init ------------- */ function __ERC20_init( string memory _name, string memory _symbol, uint8 _decimals ) internal initializer { s().name = _name; s().symbol = _symbol; s().decimals = _decimals; } /* ------------- view ------------- */ function name() external view virtual returns (string memory) { return s().name; } function symbol() external view virtual returns (string memory) { return s().symbol; } function decimals() external view virtual returns (uint8) { return s().decimals; } function totalSupply() external view virtual returns (uint256) { return s().totalSupply; } function balanceOf(address owner) public view virtual returns (uint256) { return s().balanceOf[owner]; } function allowance(address owner, address operator) public view virtual returns (uint256) { return s().allowance[owner][operator]; } /* ------------- public ------------- */ function approve(address operator, uint256 amount) public virtual returns (bool) { s().allowance[_msgSender()][operator] = amount; emit Approval(_msgSender(), operator, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { s().balanceOf[_msgSender()] -= amount; unchecked { s().balanceOf[to] += amount; } emit Transfer(_msgSender(), to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = s().allowance[from][_msgSender()]; if (allowed != type(uint256).max) s().allowance[from][_msgSender()] = allowed - amount; s().balanceOf[from] -= amount; unchecked { s().balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } // EIP-2612 permit function permit( address owner, address operator, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s_ ) public virtual { _usePermit(owner, operator, value, deadline, v, r, s_); s().allowance[owner][operator] = value; emit Approval(owner, operator, value); } /* ------------- internal ------------- */ function _mint(address to, uint256 amount) internal virtual { s().totalSupply += amount; unchecked { s().balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { s().balanceOf[from] -= amount; unchecked { s().totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {Context} from "../utils/Context.sol"; import {Initializable} from "../utils/Initializable.sol"; import {EIP712PermitUDS} from "../auth/EIP712PermitUDS.sol"; // ------------- storage bytes32 constant DIAMOND_STORAGE_ERC721 = keccak256("diamond.storage.erc721"); function s() pure returns (ERC721DS storage diamondStorage) { bytes32 slot = DIAMOND_STORAGE_ERC721; assembly { diamondStorage.slot := slot } // prettier-ignore } struct ERC721DS { string name; string symbol; mapping(uint256 => address) ownerOf; mapping(address => uint256) balanceOf; mapping(uint256 => address) getApproved; mapping(address => mapping(address => bool)) isApprovedForAll; } // ------------- errors error NonexistentToken(); error NonERC721Receiver(); error MintExistingToken(); error MintToZeroAddress(); error BalanceOfZeroAddress(); error TransferToZeroAddress(); error CallerNotOwnerNorApproved(); error TransferFromIncorrectOwner(); /// @title ERC721 (Upgradeable Diamond Storage) /// @author phaze (https://github.com/0xPhaze/UDS) /// @author Modified from Solmate (https://github.com/Rari-Capital/solmate) /// @notice Integrates EIP712Permit abstract contract ERC721UDS is Context, Initializable, EIP712PermitUDS { ERC721DS private __storageLayout; // storage layout for upgrade compatibility checks event Transfer(address indexed from, address indexed to, uint256 indexed id); event Approval(address indexed owner, address indexed operator, uint256 indexed id); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /* ------------- init ------------- */ function __ERC721_init(string memory name_, string memory symbol_) internal initializer { s().name = name_; s().symbol = symbol_; } /* ------------- virtual ------------- */ function tokenURI(uint256 id) public view virtual returns (string memory); /* ------------- view ------------- */ function name() external view virtual returns (string memory) { return s().name; } function symbol() external view virtual returns (string memory) { return s().symbol; } function ownerOf(uint256 id) public view virtual returns (address owner) { if ((owner = s().ownerOf[id]) == address(0)) revert NonexistentToken(); } function balanceOf(address owner) public view virtual returns (uint256) { if (owner == address(0)) revert BalanceOfZeroAddress(); return s().balanceOf[owner]; } function getApproved(uint256 id) public view returns (address) { return s().getApproved[id]; } function isApprovedForAll(address owner, address operator) public view returns (bool) { return s().isApprovedForAll[owner][operator]; } function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata } /* ------------- public ------------- */ function approve(address operator, uint256 id) public virtual { address owner = s().ownerOf[id]; if (_msgSender() != owner && !s().isApprovedForAll[owner][_msgSender()]) revert CallerNotOwnerNorApproved(); s().getApproved[id] = operator; emit Approval(owner, operator, id); } function setApprovalForAll(address operator, bool approved) public virtual { s().isApprovedForAll[_msgSender()][operator] = approved; emit ApprovalForAll(_msgSender(), operator, approved); } function transferFrom( address from, address to, uint256 id ) public virtual { if (to == address(0)) revert TransferToZeroAddress(); if (from != s().ownerOf[id]) revert TransferFromIncorrectOwner(); bool isApprovedOrOwner = (_msgSender() == from || s().isApprovedForAll[from][_msgSender()] || s().getApproved[id] == _msgSender()); if (!isApprovedOrOwner) revert CallerNotOwnerNorApproved(); unchecked { s().balanceOf[from]--; s().balanceOf[to]++; } s().ownerOf[id] = to; delete s().getApproved[id]; emit Transfer(from, to, id); } function safeTransferFrom( address from, address to, uint256 id ) public virtual { transferFrom(from, to, id); if ( to.code.length != 0 && ERC721TokenReceiver(to).onERC721Received(_msgSender(), from, id, "") != ERC721TokenReceiver.onERC721Received.selector ) revert NonERC721Receiver(); } function safeTransferFrom( address from, address to, uint256 id, bytes calldata data ) public virtual { transferFrom(from, to, id); if ( to.code.length != 0 && ERC721TokenReceiver(to).onERC721Received(_msgSender(), from, id, data) != ERC721TokenReceiver.onERC721Received.selector ) revert NonERC721Receiver(); } // EIP-4494 permit; differs from the current EIP function permit( address owner, address operator, uint256 deadline, uint8 v, bytes32 r, bytes32 s_ ) public virtual { _usePermit(owner, operator, 1, deadline, v, r, s_); s().isApprovedForAll[owner][operator] = true; emit ApprovalForAll(owner, operator, true); } /* ------------- internal ------------- */ function _mint(address to, uint256 id) internal virtual { if (to == address(0)) revert MintToZeroAddress(); if (s().ownerOf[id] != address(0)) revert MintExistingToken(); unchecked { s().balanceOf[to]++; } s().ownerOf[id] = to; emit Transfer(address(0), to, id); } function _burn(uint256 id) internal virtual { address owner = s().ownerOf[id]; if (owner == address(0)) revert NonexistentToken(); unchecked { s().balanceOf[owner]--; } delete s().ownerOf[id]; delete s().getApproved[id]; emit Transfer(owner, address(0), id); } function _safeMint(address to, uint256 id) internal virtual { _mint(to, id); if ( to.code.length != 0 && ERC721TokenReceiver(to).onERC721Received(_msgSender(), address(0), id, "") != ERC721TokenReceiver.onERC721Received.selector ) revert NonERC721Receiver(); } function _safeMint( address to, uint256 id, bytes memory data ) internal virtual { _mint(to, id); if ( to.code.length != 0 && ERC721TokenReceiver(to).onERC721Received(_msgSender(), address(0), id, data) != ERC721TokenReceiver.onERC721Received.selector ) revert NonERC721Receiver(); } } /// @notice A generic interface for a contract which properly accepts ERC721 tokens. /// @author Solmate (https://github.com/Rari-Capital/solmate/) abstract contract ERC721TokenReceiver { function onERC721Received( address, address, uint256, bytes calldata ) external virtual returns (bytes4) { return ERC721TokenReceiver.onERC721Received.selector; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {Context} from "../utils/Context.sol"; import {Initializable} from "../utils/Initializable.sol"; // ------------- storage bytes32 constant DIAMOND_STORAGE_OWNABLE = keccak256("diamond.storage.ownable"); function s() pure returns (OwnableDS storage diamondStorage) { bytes32 slot = DIAMOND_STORAGE_OWNABLE; assembly { diamondStorage.slot := slot } // prettier-ignore } struct OwnableDS { address owner; } // ------------- errors error CallerNotOwner(); /// @title Ownable (Upgradeable Diamond Storage) /// @author phaze (https://github.com/0xPhaze/UDS) /// @dev Requires `__Ownable_init` to be called in proxy abstract contract OwnableUDS is Context, Initializable { OwnableDS private __storageLayout; // storage layout for upgrade compatibility checks event OwnerChanged(address oldOwner, address newOwner); function __Ownable_init() internal initializer { s().owner = _msgSender(); } /* ------------- external ------------- */ function owner() public view returns (address) { return s().owner; } function transferOwnership(address newOwner) external onlyOwner { s().owner = newOwner; emit OwnerChanged(_msgSender(), newOwner); } /* ------------- modifier ------------- */ modifier onlyOwner() { if (_msgSender() != s().owner) revert CallerNotOwner(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {ERC1967, ERC1967_PROXY_STORAGE_SLOT} from "./ERC1967Proxy.sol"; // ------------- errors error OnlyProxyCallAllowed(); error DelegateCallNotAllowed(); /// @title Minimal UUPSUpgrade /// @author phaze (https://github.com/0xPhaze/UDS) abstract contract UUPSUpgrade is ERC1967 { address private immutable __implementation = address(this); /* ------------- external ------------- */ function upgradeToAndCall(address logic, bytes calldata data) external { _authorizeUpgrade(); _upgradeToAndCall(logic, data); } /* ------------- view ------------- */ function proxiableUUID() external view virtual returns (bytes32) { if (address(this) != __implementation) revert DelegateCallNotAllowed(); return ERC1967_PROXY_STORAGE_SLOT; } /* ------------- virtual ------------- */ function _authorizeUpgrade() internal virtual; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {ERC20UDS} from "UDS/tokens/ERC20UDS.sol"; import {FxBaseRootTunnelUDS} from "./base/FxBaseRootTunnelUDS.sol"; bytes32 constant MINT_ERC20_SIG = keccak256("mintERC20Tokens(address,uint256)"); error InvalidSignature(); abstract contract FxERC20RootUDS is FxBaseRootTunnelUDS, ERC20UDS { constructor(address checkpointManager, address fxRoot) FxBaseRootTunnelUDS(checkpointManager, fxRoot) {} /* ------------- virtual ------------- */ function _authorizeTunnelController() internal virtual override; /* ------------- external ------------- */ function lock(address to, uint256 amount) external virtual { _burn(msg.sender, amount); _sendMessageToChild(abi.encode(MINT_ERC20_SIG, abi.encode(to, amount))); } function unlock(bytes calldata proofData) external virtual { bytes memory message = _validateAndExtractMessage(proofData); (bytes32 sig, bytes memory args) = abi.decode(message, (bytes32, bytes)); (address to, uint256 amount) = abi.decode(args, (address, uint256)); if (sig != MINT_ERC20_SIG) revert InvalidSignature(); _mint(to, amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {ERC20UDS, s as erc20ds} from "../ERC20UDS.sol"; // ------------- storage bytes32 constant DIAMOND_STORAGE_ERC20_REWARD = keccak256("diamond.storage.erc20.reward"); function s() pure returns (ERC20RewardDS storage diamondStorage) { bytes32 slot = DIAMOND_STORAGE_ERC20_REWARD; assembly { diamondStorage.slot := slot } // prettier-ignore } struct UserData { uint216 multiplier; uint40 lastClaimed; } struct ERC20RewardDS { mapping(address => UserData) userData; } /// @title ERC20Reward (Upgradeable Diamond Storage) /// @author phaze (https://github.com/0xPhaze/UDS) /// @notice Allows for ERC20 reward accrual /// @notice at a rate of rewardDailyRate() * multiplier[user] per day /// @notice Tokens are automatically claimed before any multiplier update abstract contract ERC20RewardUDS is ERC20UDS { ERC20RewardDS private __storageLayout; // storage layout for upgrade compatibility checks /* ------------- virtual ------------- */ function rewardEndDate() public view virtual returns (uint256); function rewardDailyRate() public view virtual returns (uint256); /* ------------- view ------------- */ function totalBalanceOf(address owner) public view virtual returns (uint256) { return ERC20UDS.balanceOf(owner) + pendingReward(owner); } function pendingReward(address owner) public view virtual returns (uint256) { UserData storage userData = s().userData[owner]; return _calculateReward(userData.multiplier, userData.lastClaimed); } /* ------------- internal ------------- */ function _getRewardMultiplier(address owner) internal view virtual returns (uint256) { return s().userData[owner].multiplier; } function _calculateReward(uint256 multiplier, uint256 lastClaimed) internal view virtual returns (uint256) { uint256 end = rewardEndDate(); uint256 timestamp = block.timestamp; if (lastClaimed > end) return 0; else if (timestamp > end) timestamp = end; // If multiplier > 0 then lastClaimed > 0 // because _claimReward must have been called return ((timestamp - lastClaimed) * multiplier * rewardDailyRate()) / 1 days; } function _claimReward(address owner) internal virtual { UserData storage userData = s().userData[owner]; uint256 multiplier = userData.multiplier; uint256 lastClaimed = userData.lastClaimed; if (multiplier != 0) { // Only forego minting if multiplier == 0 // checking for amount == 0 can lead to failed transactions // due to too little gas being supplied through estimation. // This is under the assumption that _increaseRewardMultiplier // is unlikely to be called twice in a row. if (multiplier != 0) { uint256 amount = _calculateReward(multiplier, lastClaimed); _mint(owner, amount); } } s().userData[owner].lastClaimed = uint40(block.timestamp); } function _increaseRewardMultiplier(address owner, uint216 quantity) internal virtual { _claimReward(owner); s().userData[owner].multiplier += quantity; } function _decreaseRewardMultiplier(address owner, uint216 quantity) internal virtual { _claimReward(owner); s().userData[owner].multiplier -= quantity; } function _setRewardMultiplier(address owner, uint216 quantity) internal virtual { _claimReward(owner); s().userData[owner].multiplier = quantity; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {FxBaseRootTunnelUDS} from "./base/FxBaseRootTunnelUDS.sol"; bytes32 constant REGISTER_ERC721s_IDS_SIG = keccak256("registerERC721IdsWithChild(address,address,uint256[])"); bytes32 constant DEREGISTER_ERC721s_IDS_SIG = keccak256("deregisterERC721IdsWithChild(address,uint256[])"); error Disabled(); error InvalidSignature(); /// @title ERC721 FxRootTunnel /// @author phaze (https://github.com/0xPhaze/fx-contracts) abstract contract FxERC721sRootTunnelUDS is FxBaseRootTunnelUDS { constructor(address checkpointManager, address fxRoot) FxBaseRootTunnelUDS(checkpointManager, fxRoot) {} /* ------------- virtual ------------- */ function _authorizeTunnelController() internal virtual override; /* ------------- internal ------------- */ function _registerERC721IdsWithChild( address collection, address to, uint256[] calldata ids ) internal virtual { _sendMessageToChild(abi.encode(REGISTER_ERC721s_IDS_SIG, abi.encode(collection, to, ids))); } function _deregisterERC721IdsWithChild(address collection, uint256[] calldata ids) internal virtual { _sendMessageToChild(abi.encode(DEREGISTER_ERC721s_IDS_SIG, abi.encode(collection, ids))); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title Context /// @notice Overridable context for meta-transactions /// @author OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts) abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {s as erc1967ds} from "../proxy/ERC1967Proxy.sol"; // ------------- errors error ProxyCallRequired(); error AlreadyInitialized(); /// @title Initializable /// @author phaze (https://github.com/0xPhaze/UDS) /// @dev functions using the `initializer` modifier are only callable during proxy deployment /// @dev functions using the `reinitializer` modifier are only callable through a proxy /// @dev and only before a proxy upgrade migration has completed /// @dev (only when `upgradeToAndCall`'s `initCalldata` is being executed) /// @dev allows re-initialization during upgrades abstract contract Initializable { address private immutable __implementation = address(this); /* ------------- modifier ------------- */ modifier initializer() { if (address(this).code.length != 0) revert AlreadyInitialized(); _; } modifier reinitializer() { if (address(this) == __implementation) revert ProxyCallRequired(); if (erc1967ds().implementation == __implementation) revert AlreadyInitialized(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // ------------- storage bytes32 constant DIAMOND_STORAGE_EIP_712_PERMIT = keccak256("diamond.storage.eip.712.permit"); function s() pure returns (EIP2612DS storage diamondStorage) { bytes32 slot = DIAMOND_STORAGE_EIP_712_PERMIT; assembly { diamondStorage.slot := slot } // prettier-ignore } struct EIP2612DS { mapping(address => uint256) nonces; } // ------------- errors error InvalidSigner(); error DeadlineExpired(); /// @title EIP712Permit (Upgradeable Diamond Storage) /// @author phaze (https://github.com/0xPhaze/UDS) /// @author Modified from Solmate (https://github.com/Rari-Capital/solmate) /// @dev `DOMAIN_SEPARATOR` needs to be re-computed every time /// @dev for use with a proxy due to `address(this)` abstract contract EIP712PermitUDS { EIP2612DS private __storageLayout; // storage layout for upgrade compatibility checks /* ------------- public ------------- */ function nonces(address owner) public view returns (uint256) { return s().nonces[owner]; } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256("EIP712"), keccak256("1"), block.chainid, address(this) ) ); } /* ------------- internal ------------- */ function _usePermit( address owner, address spender, uint256 value, uint256 deadline, uint8 v_, bytes32 r_, bytes32 s_ ) internal virtual { if (deadline < block.timestamp) revert DeadlineExpired(); unchecked { uint256 nonce = s().nonces[owner]++; address recovered = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonce, deadline ) ) ) ), v_, r_, s_ ); if (recovered == address(0) || recovered != owner) revert InvalidSigner(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // ------------- storage // keccak256("eip1967.proxy.implementation") - 1 bytes32 constant ERC1967_PROXY_STORAGE_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; function s() pure returns (ERC1967UpgradeDS storage diamondStorage) { assembly { diamondStorage.slot := ERC1967_PROXY_STORAGE_SLOT } // prettier-ignore } struct ERC1967UpgradeDS { address implementation; } // ------------- errors error InvalidUUID(); error NotAContract(); /// @title ERC1967 /// @author phaze (https://github.com/0xPhaze/UDS) abstract contract ERC1967 { event Upgraded(address indexed implementation); function _upgradeToAndCall(address logic, bytes memory data) internal { if (logic.code.length == 0) revert NotAContract(); if (ERC1822(logic).proxiableUUID() != ERC1967_PROXY_STORAGE_SLOT) revert InvalidUUID(); if (data.length != 0) { (bool success, ) = logic.delegatecall(data); if (!success) { assembly { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } } } s().implementation = logic; emit Upgraded(logic); } } /// @title Minimal ERC1967Proxy /// @author phaze (https://github.com/0xPhaze/UDS) contract ERC1967Proxy is ERC1967 { constructor(address logic, bytes memory data) payable { _upgradeToAndCall(logic, data); } fallback() external payable { assembly { calldatacopy(0, 0, calldatasize()) let success := delegatecall(gas(), sload(ERC1967_PROXY_STORAGE_SLOT), 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) if success { return(0, returndatasize()) } revert(0, returndatasize()) } } } /// @title ERC1822 /// @author phaze (https://github.com/0xPhaze/UDS) abstract contract ERC1822 { function proxiableUUID() external view virtual returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {Merkle} from "../lib/Merkle.sol"; import {RLPReader} from "../lib/RLPReader.sol"; import {ExitPayloadReader} from "../lib/ExitPayloadReader.sol"; import {MerklePatriciaProof} from "../lib/MerklePatriciaProof.sol"; // ------------- interfaces interface IFxStateSender { function sendMessageToChild(address _receiver, bytes calldata _data) external; } interface ICheckpointManager { function headerBlocks(uint256 headerNumber) external view returns ( bytes32 root, uint256 start, uint256 end, uint256 createdAt, address proposer ); } // ------------- storage bytes32 constant DIAMOND_STORAGE_FX_BASE_ROOT_TUNNEL = keccak256("diamond.storage.fx.base.root.tunnel"); function s() pure returns (FxBaseRootTunnelDS storage diamondStorage) { bytes32 slot = DIAMOND_STORAGE_FX_BASE_ROOT_TUNNEL; assembly { diamondStorage.slot := slot } // prettier-ignore } struct FxBaseRootTunnelDS { address fxChildTunnel; mapping(bytes32 => bool) processedExits; } // ------------- errors error FxChildUnset(); error InvalidHeader(); error InvalidSignature(); error InvalidReceiptProof(); error InvalidFxChildTunnel(); error ExitAlreadyProcessed(); abstract contract FxBaseRootTunnelUDS { using RLPReader for RLPReader.RLPItem; using Merkle for bytes32; using ExitPayloadReader for bytes; using ExitPayloadReader for ExitPayloadReader.ExitPayload; using ExitPayloadReader for ExitPayloadReader.Log; using ExitPayloadReader for ExitPayloadReader.LogTopics; using ExitPayloadReader for ExitPayloadReader.Receipt; bytes32 private constant SEND_MESSAGE_EVENT_SIG = 0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036; IFxStateSender private immutable fxRoot; ICheckpointManager private immutable checkpointManager; constructor(address checkpointManager_, address fxRoot_) { checkpointManager = ICheckpointManager(checkpointManager_); fxRoot = IFxStateSender(fxRoot_); } /* ------------- virtual ------------- */ function _authorizeTunnelController() internal virtual; /* ------------- view ------------- */ function fxChildTunnel() public view returns (address) { return s().fxChildTunnel; } function processedExits(bytes32 exitHash) public view returns (bool) { return s().processedExits[exitHash]; } function setFxChildTunnel(address fxChildTunnel_) public virtual { _authorizeTunnelController(); s().fxChildTunnel = fxChildTunnel_; } /* ------------- internal ------------- */ function _sendMessageToChild(bytes memory message) internal { if (s().fxChildTunnel == address(0)) revert FxChildUnset(); fxRoot.sendMessageToChild(s().fxChildTunnel, message); } /** * @notice receive message from L2 to L1, validated by proof * @dev This function verifies if the transaction actually happened on child chain * * @param proofData RLP encoded data of the reference tx containing following list of fields * 0 - headerNumber - Checkpoint header block number containing the reference tx * 1 - blockProof - Proof that the block header (in the child chain) is a leaf in the submitted merkle root * 2 - blockNumber - Block number containing the reference tx on child chain * 3 - blockTime - Reference tx block time * 4 - txRoot - Transactions root of block * 5 - receiptRoot - Receipts root of block * 6 - receipt - Receipt of the reference transaction * 7 - receiptProof - Merkle proof of the reference receipt * 8 - branchMask - 32 bits denoting the path of receipt in merkle tree * 9 - receiptLogIndex - Log Index to read from the receipt */ function _validateAndExtractMessage(bytes memory proofData) internal returns (bytes memory) { address childTunnel = s().fxChildTunnel; if (childTunnel == address(0)) revert FxChildUnset(); ExitPayloadReader.ExitPayload memory payload = proofData.toExitPayload(); bytes memory branchMaskBytes = payload.getBranchMaskAsBytes(); uint256 blockNumber = payload.getBlockNumber(); // checking if exit has already been processed // unique exit is identified using hash of (blockNumber, branchMask, receiptLogIndex) bytes32 exitHash = keccak256( abi.encodePacked( blockNumber, // first 2 nibbles are dropped while generating nibble array // this allows branch masks that are valid but bypass exitHash check (changing first 2 nibbles only) // so converting to nibble array and then hashing it MerklePatriciaProof._getNibbleArray(branchMaskBytes), payload.getReceiptLogIndex() ) ); if (s().processedExits[exitHash]) revert ExitAlreadyProcessed(); s().processedExits[exitHash] = true; ExitPayloadReader.Receipt memory receipt = payload.getReceipt(); ExitPayloadReader.Log memory log = receipt.getLog(); // check child tunnel if (childTunnel != log.getEmitter()) revert InvalidFxChildTunnel(); bytes32 receiptRoot = payload.getReceiptRoot(); // verify receipt inclusion if (!MerklePatriciaProof.verify(receipt.toBytes(), branchMaskBytes, payload.getReceiptProof(), receiptRoot)) revert InvalidReceiptProof(); (bytes32 headerRoot, uint256 startBlock, , , ) = checkpointManager.headerBlocks(payload.getHeaderNumber()); bytes32 leaf = keccak256( abi.encodePacked(blockNumber, payload.getBlockTime(), payload.getTxRoot(), receiptRoot) ); if (!leaf.checkMembership(blockNumber - startBlock, headerRoot, payload.getBlockProof())) revert InvalidHeader(); ExitPayloadReader.LogTopics memory topics = log.getTopics(); if (bytes32(topics.getField(0).toUint()) != SEND_MESSAGE_EVENT_SIG) revert InvalidSignature(); // received message data bytes memory message = abi.decode(log.getData(), (bytes)); // event decodes params again, so decoding bytes to get message return message; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library Merkle { function checkMembership( bytes32 leaf, uint256 index, bytes32 rootHash, bytes memory proof ) internal pure returns (bool) { require(proof.length % 32 == 0, "Invalid proof length"); uint256 proofHeight = proof.length / 32; // Proof of size n means, height of the tree is n+1. // In a tree of height n+1, max #leafs possible is 2 ^ n require(index < 2**proofHeight, "Leaf index is too big"); bytes32 proofElement; bytes32 computedHash = leaf; for (uint256 i = 32; i <= proof.length; i += 32) { assembly { proofElement := mload(add(proof, i)) } if (index % 2 == 0) { computedHash = keccak256(abi.encodePacked(computedHash, proofElement)); } else { computedHash = keccak256(abi.encodePacked(proofElement, computedHash)); } index = index / 2; } return computedHash == rootHash; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /* * @author Hamdi Allam [email protected] * Please reach out with any questions or concerns */ library RLPReader { uint8 constant STRING_SHORT_START = 0x80; uint8 constant STRING_LONG_START = 0xb8; uint8 constant LIST_SHORT_START = 0xc0; uint8 constant LIST_LONG_START = 0xf8; uint8 constant WORD_SIZE = 32; struct RLPItem { uint256 len; uint256 memPtr; } struct Iterator { RLPItem item; // Item that's being iterated over. uint256 nextPtr; // Position of the next item in the list. } /* * @dev Returns the next element in the iteration. Reverts if it has not next element. * @param self The iterator. * @return The next element in the iteration. */ function next(Iterator memory self) internal pure returns (RLPItem memory) { require(hasNext(self)); uint256 ptr = self.nextPtr; uint256 itemLength = _itemLength(ptr); self.nextPtr = ptr + itemLength; return RLPItem(itemLength, ptr); } /* * @dev Returns true if the iteration has more elements. * @param self The iterator. * @return true if the iteration has more elements. */ function hasNext(Iterator memory self) internal pure returns (bool) { RLPItem memory item = self.item; return self.nextPtr < item.memPtr + item.len; } /* * @param item RLP encoded bytes */ function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { uint256 memPtr; assembly { memPtr := add(item, 0x20) } return RLPItem(item.length, memPtr); } /* * @dev Create an iterator. Reverts if item is not a list. * @param self The RLP item. * @return An 'Iterator' over the item. */ function iterator(RLPItem memory self) internal pure returns (Iterator memory) { require(isList(self)); uint256 ptr = self.memPtr + _payloadOffset(self.memPtr); return Iterator(self, ptr); } /* * @param item RLP encoded bytes */ function rlpLen(RLPItem memory item) internal pure returns (uint256) { return item.len; } /* * @param item RLP encoded bytes */ function payloadLen(RLPItem memory item) internal pure returns (uint256) { return item.len - _payloadOffset(item.memPtr); } /* * @param item RLP encoded list in bytes */ function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { require(isList(item)); uint256 items = numItems(item); RLPItem[] memory result = new RLPItem[](items); uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 dataLen; for (uint256 i = 0; i < items; i++) { dataLen = _itemLength(memPtr); result[i] = RLPItem(dataLen, memPtr); memPtr = memPtr + dataLen; } return result; } // @return indicator whether encoded payload is a list. negate this function call for isData. function isList(RLPItem memory item) internal pure returns (bool) { if (item.len == 0) return false; uint8 byte0; uint256 memPtr = item.memPtr; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < LIST_SHORT_START) return false; return true; } /* * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory. * @return keccak256 hash of RLP encoded bytes. */ function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) { uint256 ptr = item.memPtr; uint256 len = item.len; bytes32 result; assembly { result := keccak256(ptr, len) } return result; } function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) { uint256 offset = _payloadOffset(item.memPtr); uint256 memPtr = item.memPtr + offset; uint256 len = item.len - offset; // data length return (memPtr, len); } /* * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory. * @return keccak256 hash of the item payload. */ function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) { (uint256 memPtr, uint256 len) = payloadLocation(item); bytes32 result; assembly { result := keccak256(memPtr, len) } return result; } /** RLPItem conversions into data types **/ // @returns raw rlp encoding in bytes function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) { bytes memory result = new bytes(item.len); if (result.length == 0) return result; uint256 ptr; assembly { ptr := add(0x20, result) } copy(item.memPtr, ptr, item.len); return result; } // any non-zero byte is considered true function toBoolean(RLPItem memory item) internal pure returns (bool) { require(item.len == 1); uint256 result; uint256 memPtr = item.memPtr; assembly { result := byte(0, mload(memPtr)) } return result == 0 ? false : true; } function toAddress(RLPItem memory item) internal pure returns (address) { // 1 byte for the length prefix require(item.len == 21); return address(uint160(toUint(item))); } function toUint(RLPItem memory item) internal pure returns (uint256) { require(item.len > 0 && item.len <= 33); uint256 offset = _payloadOffset(item.memPtr); uint256 len = item.len - offset; uint256 result; uint256 memPtr = item.memPtr + offset; assembly { result := mload(memPtr) // shfit to the correct location if neccesary if lt(len, 32) { result := div(result, exp(256, sub(32, len))) } } return result; } // enforces 32 byte length function toUintStrict(RLPItem memory item) internal pure returns (uint256) { // one byte prefix require(item.len == 33); uint256 result; uint256 memPtr = item.memPtr + 1; assembly { result := mload(memPtr) } return result; } function toBytes(RLPItem memory item) internal pure returns (bytes memory) { require(item.len > 0); uint256 offset = _payloadOffset(item.memPtr); uint256 len = item.len - offset; // data length bytes memory result = new bytes(len); uint256 destPtr; assembly { destPtr := add(0x20, result) } copy(item.memPtr + offset, destPtr, len); return result; } /* * Private Helpers */ // @return number of payload items inside an encoded list. function numItems(RLPItem memory item) private pure returns (uint256) { if (item.len == 0) return 0; uint256 count = 0; uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); uint256 endPtr = item.memPtr + item.len; while (currPtr < endPtr) { currPtr = currPtr + _itemLength(currPtr); // skip over an item count++; } return count; } // @return entire rlp item byte length function _itemLength(uint256 memPtr) private pure returns (uint256) { uint256 itemLen; uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) itemLen = 1; else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1; else if (byte0 < LIST_SHORT_START) { assembly { let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is memPtr := add(memPtr, 1) // skip over the first byte /* 32 byte word size */ let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len itemLen := add(dataLen, add(byteLen, 1)) } } else if (byte0 < LIST_LONG_START) { itemLen = byte0 - LIST_SHORT_START + 1; } else { assembly { let byteLen := sub(byte0, 0xf7) memPtr := add(memPtr, 1) let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length itemLen := add(dataLen, add(byteLen, 1)) } } return itemLen; } // @return number of bytes until the data function _payloadOffset(uint256 memPtr) private pure returns (uint256) { uint256 byte0; assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) return 0; else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) return 1; else if (byte0 < LIST_SHORT_START) // being explicit return byte0 - (STRING_LONG_START - 1) + 1; else return byte0 - (LIST_LONG_START - 1) + 1; } /* * @param src Pointer to source * @param dest Pointer to destination * @param len Amount of memory to copy from the source */ function copy( uint256 src, uint256 dest, uint256 len ) private pure { if (len == 0) return; // copy as many word sizes as possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } src += WORD_SIZE; dest += WORD_SIZE; } if (len == 0) return; // left over bytes. Mask is used to remove unwanted bytes from the word uint256 mask = 256**(WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) // zero out src let destpart := and(mload(dest), mask) // retrieve the bytes mstore(dest, or(destpart, srcpart)) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {RLPReader} from "./RLPReader.sol"; library ExitPayloadReader { using RLPReader for bytes; using RLPReader for RLPReader.RLPItem; uint8 constant WORD_SIZE = 32; struct ExitPayload { RLPReader.RLPItem[] data; } struct Receipt { RLPReader.RLPItem[] data; bytes raw; uint256 logIndex; } struct Log { RLPReader.RLPItem data; RLPReader.RLPItem[] list; } struct LogTopics { RLPReader.RLPItem[] data; } // copy paste of private copy() from RLPReader to avoid changing of existing contracts function copy( uint256 src, uint256 dest, uint256 len ) private pure { if (len == 0) return; // copy as many word sizes as possible for (; len >= WORD_SIZE; len -= WORD_SIZE) { assembly { mstore(dest, mload(src)) } src += WORD_SIZE; dest += WORD_SIZE; } // left over bytes. Mask is used to remove unwanted bytes from the word uint256 mask = 256**(WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) // zero out src let destpart := and(mload(dest), mask) // retrieve the bytes mstore(dest, or(destpart, srcpart)) } } function toExitPayload(bytes memory data) internal pure returns (ExitPayload memory) { RLPReader.RLPItem[] memory payloadData = data.toRlpItem().toList(); return ExitPayload(payloadData); } function getHeaderNumber(ExitPayload memory payload) internal pure returns (uint256) { return payload.data[0].toUint(); } function getBlockProof(ExitPayload memory payload) internal pure returns (bytes memory) { return payload.data[1].toBytes(); } function getBlockNumber(ExitPayload memory payload) internal pure returns (uint256) { return payload.data[2].toUint(); } function getBlockTime(ExitPayload memory payload) internal pure returns (uint256) { return payload.data[3].toUint(); } function getTxRoot(ExitPayload memory payload) internal pure returns (bytes32) { return bytes32(payload.data[4].toUint()); } function getReceiptRoot(ExitPayload memory payload) internal pure returns (bytes32) { return bytes32(payload.data[5].toUint()); } function getReceipt(ExitPayload memory payload) internal pure returns (Receipt memory receipt) { receipt.raw = payload.data[6].toBytes(); RLPReader.RLPItem memory receiptItem = receipt.raw.toRlpItem(); if (receiptItem.isList()) { // legacy tx receipt.data = receiptItem.toList(); } else { // pop first byte before parsting receipt bytes memory typedBytes = receipt.raw; bytes memory result = new bytes(typedBytes.length - 1); uint256 srcPtr; uint256 destPtr; assembly { srcPtr := add(33, typedBytes) destPtr := add(0x20, result) } copy(srcPtr, destPtr, result.length); receipt.data = result.toRlpItem().toList(); } receipt.logIndex = getReceiptLogIndex(payload); return receipt; } function getReceiptProof(ExitPayload memory payload) internal pure returns (bytes memory) { return payload.data[7].toBytes(); } function getBranchMaskAsBytes(ExitPayload memory payload) internal pure returns (bytes memory) { return payload.data[8].toBytes(); } function getBranchMaskAsUint(ExitPayload memory payload) internal pure returns (uint256) { return payload.data[8].toUint(); } function getReceiptLogIndex(ExitPayload memory payload) internal pure returns (uint256) { return payload.data[9].toUint(); } // Receipt methods function toBytes(Receipt memory receipt) internal pure returns (bytes memory) { return receipt.raw; } function getLog(Receipt memory receipt) internal pure returns (Log memory) { RLPReader.RLPItem memory logData = receipt.data[3].toList()[receipt.logIndex]; return Log(logData, logData.toList()); } // Log methods function getEmitter(Log memory log) internal pure returns (address) { return RLPReader.toAddress(log.list[0]); } function getTopics(Log memory log) internal pure returns (LogTopics memory) { return LogTopics(log.list[1].toList()); } function getData(Log memory log) internal pure returns (bytes memory) { return log.list[2].toBytes(); } function toRlpBytes(Log memory log) internal pure returns (bytes memory) { return log.data.toRlpBytes(); } // LogTopics methods function getField(LogTopics memory topics, uint256 index) internal pure returns (RLPReader.RLPItem memory) { return topics.data[index]; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {RLPReader} from "./RLPReader.sol"; library MerklePatriciaProof { /* * @dev Verifies a merkle patricia proof. * @param value The terminating value in the trie. * @param encodedPath The path in the trie leading to value. * @param rlpParentNodes The rlp encoded stack of nodes. * @param root The root hash of the trie. * @return The boolean validity of the proof. */ function verify( bytes memory value, bytes memory encodedPath, bytes memory rlpParentNodes, bytes32 root ) internal pure returns (bool) { RLPReader.RLPItem memory item = RLPReader.toRlpItem(rlpParentNodes); RLPReader.RLPItem[] memory parentNodes = RLPReader.toList(item); bytes memory currentNode; RLPReader.RLPItem[] memory currentNodeList; bytes32 nodeKey = root; uint256 pathPtr = 0; bytes memory path = _getNibbleArray(encodedPath); if (path.length == 0) { return false; } for (uint256 i = 0; i < parentNodes.length; i++) { if (pathPtr > path.length) { return false; } currentNode = RLPReader.toRlpBytes(parentNodes[i]); if (nodeKey != keccak256(currentNode)) { return false; } currentNodeList = RLPReader.toList(parentNodes[i]); if (currentNodeList.length == 17) { if (pathPtr == path.length) { if (keccak256(RLPReader.toBytes(currentNodeList[16])) == keccak256(value)) { return true; } else { return false; } } uint8 nextPathNibble = uint8(path[pathPtr]); if (nextPathNibble > 16) { return false; } nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[nextPathNibble])); pathPtr += 1; } else if (currentNodeList.length == 2) { uint256 traversed = _nibblesToTraverse(RLPReader.toBytes(currentNodeList[0]), path, pathPtr); if (pathPtr + traversed == path.length) { //leaf node if (keccak256(RLPReader.toBytes(currentNodeList[1])) == keccak256(value)) { return true; } else { return false; } } //extension node if (traversed == 0) { return false; } pathPtr += traversed; nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1])); } else { return false; } } return false; } function _nibblesToTraverse( bytes memory encodedPartialPath, bytes memory path, uint256 pathPtr ) private pure returns (uint256) { uint256 len = 0; // encodedPartialPath has elements that are each two hex characters (1 byte), but partialPath // and slicedPath have elements that are each one hex character (1 nibble) bytes memory partialPath = _getNibbleArray(encodedPartialPath); bytes memory slicedPath = new bytes(partialPath.length); // pathPtr counts nibbles in path // partialPath.length is a number of nibbles for (uint256 i = pathPtr; i < pathPtr + partialPath.length; i++) { bytes1 pathNibble = path[i]; slicedPath[i - pathPtr] = pathNibble; } if (keccak256(partialPath) == keccak256(slicedPath)) { len = partialPath.length; } else { len = 0; } return len; } // bytes b must be hp encoded function _getNibbleArray(bytes memory b) internal pure returns (bytes memory) { bytes memory nibbles = ""; if (b.length > 0) { uint8 offset; uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b)); if (hpNibble == 1 || hpNibble == 3) { nibbles = new bytes(b.length * 2 - 1); bytes1 oddNibble = _getNthNibbleOfBytes(1, b); nibbles[0] = oddNibble; offset = 1; } else { nibbles = new bytes(b.length * 2 - 2); offset = 0; } for (uint256 i = offset; i < nibbles.length; i++) { nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b); } } return nibbles; } function _getNthNibbleOfBytes(uint256 n, bytes memory str) private pure returns (bytes1) { return bytes1(n % 2 == 0 ? uint8(str[n / 2]) / 0x10 : uint8(str[n / 2]) % 0x10); } }
{ "remappings": [ "CRFTD/=src/", "ERC721A/=lib/fx-contracts/lib/ERC721M/lib/ERC721A/contracts/", "ERC721M/=lib/fx-contracts/lib/ERC721M/src/", "UDS/=lib/UDS/src/", "ds-test/=lib/solmate/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "futils/=lib/futils/src/", "fx-contracts/=lib/fx-contracts/src/", "fx-portal/=lib/fx-contracts/lib/ERC721M/lib/fx-portal/contracts/", "openzeppelin/=lib/fx-contracts/lib/ERC721M/lib/openzeppelin-contracts/", "solmate/=lib/solmate/src/", "upgrade-scripts/=lib/upgrade-scripts/src/" ], "optimizer": { "enabled": true, "runs": 100000 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"checkpointManager","type":"address"},{"internalType":"address","name":"fxRoot","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"CallerNotOwner","type":"error"},{"inputs":[],"name":"CollectionAlreadyRegistered","type":"error"},{"inputs":[],"name":"CollectionNotRegistered","type":"error"},{"inputs":[],"name":"DeadlineExpired","type":"error"},{"inputs":[],"name":"DelegateCallNotAllowed","type":"error"},{"inputs":[],"name":"FxChildUnset","type":"error"},{"inputs":[],"name":"IncorrectOwner","type":"error"},{"inputs":[],"name":"InvalidFxChild","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidUUID","type":"error"},{"inputs":[],"name":"MigrationIncomplete","type":"error"},{"inputs":[],"name":"MigrationNotStarted","type":"error"},{"inputs":[],"name":"MigrationRequired","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"ZeroReward","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardRate","type":"uint256"}],"name":"CollectionRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tos","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"airdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fxChildTunnel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getDailyReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"migrationStarted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"pendingReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s_","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"exitHash","type":"bytes32"}],"name":"processedExits","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint200","name":"rate","type":"uint200"}],"name":"registerCollection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardDailyRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"rewardEndDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"}],"name":"rewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"collections","type":"address[]"},{"internalType":"uint256[][]","name":"tokenIds","type":"uint256[][]"}],"name":"safeMigrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fxChildTunnel_","type":"address"}],"name":"setFxChildTunnel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"endDate","type":"uint256"}],"name":"setRewardEndDate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"collectionSize","type":"uint256"}],"name":"stakedIdsOf","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fxChild","type":"address"}],"name":"startMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"collections","type":"address[]"},{"internalType":"uint256[][]","name":"tokenIds","type":"uint256[][]"}],"name":"synchronizeIdsWithChild","outputs":[{"internalType":"uint256","name":"rewardMultiplier","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"totalBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collection","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"logic","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6101006040523060c081905260e0523480156200001b57600080fd5b5060405162003e3338038062003e338339810160408190526200003e9162000164565b6001600160a01b0380831660a052811660805260408051808201825260058082526410d491951160da1b60208381018290528451808601909552918452908301526200008c91601262000094565b50506200030d565b303b15620000b45760405162dc149f60e41b815260040160405180910390fd5b7f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db7620000e1848262000241565b507f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db86200010f838262000241565b507f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db9805460ff191660ff929092169190911790555050565b80516001600160a01b03811681146200015f57600080fd5b919050565b600080604083850312156200017857600080fd5b620001838362000147565b9150620001936020840162000147565b90509250929050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620001c757607f821691505b602082108103620001e857634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200023c57600081815260208120601f850160051c81016020861015620002175750805b601f850160051c820191505b81811015620002385782815560010162000223565b5050505b505050565b81516001600160401b038111156200025d576200025d6200019c565b62000275816200026e8454620001b2565b84620001ee565b602080601f831160018114620002ad5760008415620002945750858301515b600019600386901b1c1916600185901b17855562000238565b600085815260208120601f198616915b82811015620002de57888601518255948401946001909101908401620002bd565b5085821015620002fd5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a05160c05160e051613af2620003416000396000610dc6015260005050600050506000612b730152613af26000f3fe608060405234801561001057600080fd5b50600436106102925760003560e01c806370a0823111610160578063aea4e49e116100d8578063d505accf1161008c578063e4ab593d11610071578063e4ab593d146107a9578063f2fde38b146107c9578063f40f0f52146107dc57600080fd5b8063d505accf14610731578063dd62ed3e1461074457600080fd5b8063c5b41b89116100bd578063c5b41b89146106f8578063c9a3911e1461070b578063cc7010d71461071e57600080fd5b8063aea4e49e146106dd578063b88a802f146106f057600080fd5b80638da5cb5b1161012f578063972c492811610114578063972c4928146106af578063a0f52da0146106b7578063a9059cbb146106ca57600080fd5b80638da5cb5b1461069f57806395d89b41146106a757600080fd5b806370a08231146105da5780637abdab561461062f5780637c6e94e3146106425780637ecebe001461064a57600080fd5b80633644e5151161020e5780635dbe4756116101c2578063607f2d42116101a7578063607f2d421461057257806367243482146105b45780637029144c146105c757600080fd5b80635dbe4756146105385780635f36e2521461054b57600080fd5b80634b0ee02a116101f35780634b0ee02a146105085780634f1ef2861461051b57806352d1902d1461053057600080fd5b80633644e5151461044d5780633d9e7719146104f557600080fd5b80631f29d2dc11610265578063221ca18c1161024a578063221ca18c146103b157806323b872dd14610406578063313ce5671461041957600080fd5b80631f29d2dc146103175780632027a80f1461039e57600080fd5b806306fdde0314610297578063095ea7b3146102b557806316336c65146102d857806318160ddd146102f0575b600080fd5b61029f6107ef565b6040516102ac919061316a565b60405180910390f35b6102c86102c33660046131a6565b6108a0565b60405190151581526020016102ac565b670de0b6b3a76400005b6040519081526020016102ac565b7f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dba546102e2565b6103796103253660046131a6565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facee6020908152604080832093835292905220541690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102ac565b6102e26103ac36600461321c565b610935565b6102e26103bf366004613288565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced602052604090205490565b6102c86104143660046132a3565b610c72565b7f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db95460405160ff90911681526020016102ac565b6102e2604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b6102e2610503366004613288565b610c90565b6102e2610516366004613288565b610d05565b61052e610529366004613321565b610d5f565b005b6102e2610dac565b61052e610546366004613374565b610e42565b7f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec546102e2565b6102c86105803660046133ba565b60009081527f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9485602052604090205460ff1690565b61052e6105c236600461321c565b6111b0565b61052e6105d53660046133d3565b611299565b6102e26105e8366004613288565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb602052604090205490565b61052e61063d366004613433565b611356565b6102c861154a565b6102e2610658366004613288565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b81602052604090205490565b6103796115b7565b61029f6115f7565b610379611628565b61052e6106c5366004613288565b611650565b6102c86106d83660046131a6565b611758565b61052e6106eb366004613288565b611774565b61052e6117e2565b61052e6107063660046133ba565b6117ed565b61052e610719366004613374565b611897565b61052e61072c36600461321c565b611b62565b61052e61073f366004613488565b611d14565b6102e26107523660046134fb565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbc6020908152604080832093909416825291909152205490565b6107bc6107b73660046132a3565b611db5565b6040516102ac919061352e565b61052e6107d7366004613288565b611e07565b6102e26107ea366004613288565b611f2d565b60607f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db7805461081d90613572565b80601f016020809104026020016040519081016040528092919081815260200182805461084990613572565b80156108965780601f1061086b57610100808354040283529160200191610896565b820191906000526020600020905b81548152906001019060200180831161087957829003601f168201915b5050505050905090565b3360008181527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbc6020908152604080832073ffffffffffffffffffffffffffffffffffffffff871680855290835281842086905590518581529293909290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a35060015b92915050565b600061093f61154a565b610975576040517f932234ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260409020547affffffffffffffffffffffffffffffffffffffffffffffffffffff16156109f8576040517f0c74abe800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b84811015610c695760007f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced81888885818110610a3957610a396135c5565b9050602002016020810190610a4e9190613288565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905080600003610ac5576040517fa973256400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b366000868685818110610ada57610ada6135c5565b9050602002810190610aec91906135f4565b915091506000610b197f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec90565b60020160008b8b88818110610b3057610b306135c5565b9050602002016020810190610b459190613288565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000209050610b77848361368b565b610b8190876136c8565b955060005b82811015610c1b5733826000868685818110610ba457610ba46135c5565b602090810292909201358352508101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff1614610c0b576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c14816136e0565b9050610b86565b508115610c5457610c548a8a87818110610c3757610c376135c5565b9050602002016020810190610c4c9190613288565b338585611fc1565b5050505080610c62906136e0565b90506109fb565b50949350505050565b6000610c7d84611ffc565b610c8884848461213d565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c9602052604081205461092f90670de0b6b3a7640000907affffffffffffffffffffffffffffffffffffffffffffffffffffff1661368b565b6000610d1082611f2d565b73ffffffffffffffffffffffffffffffffffffffff831660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb602052604090205461092f91906136c8565b610d676122f7565b610da78383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061237d92505050565b505050565b60003073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610e1d576040517f0d89438e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b73ffffffffffffffffffffffffffffffffffffffff831660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced602052604081205490819003610ec1576040517fa973256400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ec961154a565b15610f61573360009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260409020547affffffffffffffffffffffffffffffffffffffffffffffffffffff1615610f51576040517f0c74abe800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f5c8484846125aa565b610f74565b610f7433610f6f838561368b565b6125e3565b60005b828110156111a95773ffffffffffffffffffffffffffffffffffffffff851660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facee602052604081203391868685818110610fd757610fd76135c5565b602090810292909201358352508101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff161461103e576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff851660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facee6020526040812090858584818110611095576110956135c5565b6020908102929092013583525081019190915260400160002080547fffffffffffffffffffffffff000000000000000000000000000000000000000016905573ffffffffffffffffffffffffffffffffffffffff85166323b872dd3033878786818110611104576111046135c5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815273ffffffffffffffffffffffffffffffffffffffff958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b15801561118057600080fd5b505af1158015611194573d6000803e3d6000fd5b50505050806111a2906136e0565b9050610f77565b5050505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611236576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b838110156111a957611289858583818110611256576112566135c5565b905060200201602081019061126b9190613288565b84848481811061127d5761127d6135c5565b905060200201356126ad565b611292816136e0565b9050611239565b303b156112d2576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112da612767565b61135084848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f88018190048102820181019092528681529250869150859081908401838280828437600092019190915250601292506127eb915050565b50505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113dc576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8078ffffffffffffffffffffffffffffffffffffffffffffffffff16600003611431576040517f9c1b278400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced6020526040902054156114ad576040517f41ab882d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660008181527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced602090815260409182902078ffffffffffffffffffffffffffffffffffffffffffffffffff851690819055825190815291517fccfc7df822b68cdc8b0d7cd8b0255d24005c6fb4adc390a411ebdf309fa6dc099281900390910190a25050565b60006503ffffffffff61157b7f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec5490565b146115865750600090565b6000611590611628565b73ffffffffffffffffffffffffffffffffffffffff16036115b15750600090565b50600190565b60007f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335b5473ffffffffffffffffffffffffffffffffffffffff16919050565b60607f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db7600101805461081d90613572565b60007f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd94846115db565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146116d6576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116611723576040517f8e3383b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61172c81611774565b6503ffffffffff7f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec5550565b600061176333611ffc565b61176d83836128d1565b9392505050565b61177c6122f7565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd948480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6117eb33611ffc565b565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611873576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec55565b73ffffffffffffffffffffffffffffffffffffffff831660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced602052604081205490819003611916576040517fa973256400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b82811015611aad578473ffffffffffffffffffffffffffffffffffffffff166323b872dd3330878786818110611951576119516135c5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815273ffffffffffffffffffffffffffffffffffffffff958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b1580156119cd57600080fd5b505af11580156119e1573d6000803e3d6000fd5b5050505033611a0d7f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec90565b73ffffffffffffffffffffffffffffffffffffffff871660009081526002919091016020526040812090868685818110611a4957611a496135c5565b90506020020135815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080611aa6906136e0565b9050611919565b50611ab661154a565b15611b4f573360009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260409020547affffffffffffffffffffffffffffffffffffffffffffffffffffff1615611b3e576040517f0c74abe800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b4a84338585611fc1565b611350565b61135033611b5d838561368b565b6129a8565b611b6a61154a565b611ba0576040517f932234ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ba933611ffc565b3360008181527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960209081526040808320547f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb909252909120547affffffffffffffffffffffffffffffffffffffffffffffffffffff90911691611c2d9082612a22565b60408051336020820152908101829052611cb7907f44344735c48ff651708a4f18a84c726f5beda992b79b3b970c63e46fb41f7ae2906060015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052611ca39291602001613718565b604051602081830303815290604052612aee565b611cc2336000612c39565b6000611cd087878787610935565b9050808314611d0b576040517fd24a4efc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050505050565b611d2387878787878787612cd3565b73ffffffffffffffffffffffffffffffffffffffff87811660008181527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbc60209081526040808320948b1680845294825291829020899055815189815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a350505050505050565b73ffffffffffffffffffffffffffffffffffffffff831660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facee60205260409020606090610c88908484612ffe565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611e8d576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea3380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560408051338152602081019290925280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260408120805461176d907affffffffffffffffffffffffffffffffffffffffffffffffffffff8116907b01000000000000000000000000000000000000000000000000000000900464ffffffffff16613069565b6113507fd71b04c34f087affbc01a0d18c25e3d24a365664d06d565a2b97abcd2a61792c85858585604051602001611c679493929190613780565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c96020526040902080547affffffffffffffffffffffffffffffffffffffffffffffffffffff8116907b01000000000000000000000000000000000000000000000000000000900464ffffffffff1681156120ad5781156120ad57600061209f8383613069565b90506120ab85826126ad565b505b50505073ffffffffffffffffffffffffffffffffffffffff1660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c96020526040902080547affffffffffffffffffffffffffffffffffffffffffffffffffffff167b0100000000000000000000000000000000000000000000000000000064ffffffffff421602179055565b73ffffffffffffffffffffffffffffffffffffffff831660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbc602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461220f576121be83826137c4565b73ffffffffffffffffffffffffffffffffffffffff861660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbc602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff851660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb6020526040812080548592906122639084906137c4565b909155505073ffffffffffffffffffffffffffffffffffffffff84811660008181527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb6020908152604091829020805488019055905186815291928816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3506001949350505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146117eb576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff163b6000036123ce576040517f09ee12d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561243d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061246191906137db565b14612498576040517f03ed501d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80511561251b5760008273ffffffffffffffffffffffffffffffffffffffff16826040516124c691906137f4565b600060405180830381855af49150503d8060008114612501576040519150601f19603f3d011682016040523d82523d6000602084013e612506565b606091505b5050905080612519573d6000803e3d6000fd5b505b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040517fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a25050565b610da77fc9f557702f80fbdc4fef46edb068c40d7225ad4563d3cf784c276650c0fbba73848484604051602001611c6793929190613810565b6125ec82611ffc565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260408120805483929061265d9084907affffffffffffffffffffffffffffffffffffffffffffffffffffff16613840565b92506101000a8154817affffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837affffffffffffffffffffffffffffffffffffffffffffffffffffff1602179055505050565b807f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db760030160008282546126e191906136c8565b909155505073ffffffffffffffffffffffffffffffffffffffff821660008181527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb60209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b303b156127a0576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea3380547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055565b303b15612824576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db761284f84826138f9565b507f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db861287b83826138f9565b507f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff929092169190911790555050565b3360009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb60205260408120805483919083906129119084906137c4565b909155505073ffffffffffffffffffffffffffffffffffffffff831660008181527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb602052604090208054840190553373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161092391815260200190565b6129b182611ffc565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260408120805483929061265d9084907affffffffffffffffffffffffffffffffffffffffffffffffffffff16613a13565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb602052604081208054839290612a769084906137c4565b90915550507f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dba8054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200161275b565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd94845473ffffffffffffffffffffffffffffffffffffffff16612b5c576040517fafd0683400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663b47204777f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9484546040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b168152612c0b9173ffffffffffffffffffffffffffffffffffffffff16908590600401613a52565b600060405180830381600087803b158015612c2557600080fd5b505af11580156111a9573d6000803e3d6000fd5b612c4282611ffc565b73ffffffffffffffffffffffffffffffffffffffff9190911660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c96020526040902080547fffffffffff000000000000000000000000000000000000000000000000000000167affffffffffffffffffffffffffffffffffffffffffffffffffffff909216919091179055565b42841015612d0d576040517f1ab7da6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff871660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b8160205260408120805460018082019092559190612e05604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b604080517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9602082015273ffffffffffffffffffffffffffffffffffffffff808e1692820192909252908b166060820152608081018a905260a0810185905260c0810189905260e00160405160208183030381529060405280519060200120604051602001612ec69291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff881690820152606081018690526080810185905260a0016020604051602081039080840390855afa158015612f42573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81161580612fbc57508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b15612ff3576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050565b60405160208101600060018401815b8181101561305b5760008181526020899052604090205473ffffffffffffffffffffffffffffffffffffffff808916911603613053578084526020840193506001830192505b60010161300d565b505082526040529392505050565b6000806130947f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec5490565b905042818411156130aa5760009250505061092f565b818111156130b55750805b62015180670de0b6b3a7640000866130cd87856137c4565b6130d7919061368b565b6130e1919061368b565b6130eb9190613a81565b95945050505050565b60005b8381101561310f5781810151838201526020016130f7565b838111156113505750506000910152565b600081518084526131388160208601602086016130f4565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061176d6020830184613120565b803573ffffffffffffffffffffffffffffffffffffffff811681146131a157600080fd5b919050565b600080604083850312156131b957600080fd5b6131c28361317d565b946020939093013593505050565b60008083601f8401126131e257600080fd5b50813567ffffffffffffffff8111156131fa57600080fd5b6020830191508360208260051b850101111561321557600080fd5b9250929050565b6000806000806040858703121561323257600080fd5b843567ffffffffffffffff8082111561324a57600080fd5b613256888389016131d0565b9096509450602087013591508082111561326f57600080fd5b5061327c878288016131d0565b95989497509550505050565b60006020828403121561329a57600080fd5b61176d8261317d565b6000806000606084860312156132b857600080fd5b6132c18461317d565b92506132cf6020850161317d565b9150604084013590509250925092565b60008083601f8401126132f157600080fd5b50813567ffffffffffffffff81111561330957600080fd5b60208301915083602082850101111561321557600080fd5b60008060006040848603121561333657600080fd5b61333f8461317d565b9250602084013567ffffffffffffffff81111561335b57600080fd5b613367868287016132df565b9497909650939450505050565b60008060006040848603121561338957600080fd5b6133928461317d565b9250602084013567ffffffffffffffff8111156133ae57600080fd5b613367868287016131d0565b6000602082840312156133cc57600080fd5b5035919050565b600080600080604085870312156133e957600080fd5b843567ffffffffffffffff8082111561340157600080fd5b61340d888389016132df565b9096509450602087013591508082111561342657600080fd5b5061327c878288016132df565b6000806040838503121561344657600080fd5b61344f8361317d565b9150602083013578ffffffffffffffffffffffffffffffffffffffffffffffffff8116811461347d57600080fd5b809150509250929050565b600080600080600080600060e0888a0312156134a357600080fd5b6134ac8861317d565b96506134ba6020890161317d565b95506040880135945060608801359350608088013560ff811681146134de57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561350e57600080fd5b6135178361317d565b91506135256020840161317d565b90509250929050565b6020808252825182820181905260009190848201906040850190845b818110156135665783518352928401929184019160010161354a565b50909695505050505050565b600181811c9082168061358657607f821691505b6020821081036135bf577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261362957600080fd5b83018035915067ffffffffffffffff82111561364457600080fd5b6020019150600581901b360382131561321557600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156136c3576136c361365c565b500290565b600082198211156136db576136db61365c565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036137115761371161365c565b5060010190565b828152604060208201526000610c886040830184613120565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561376357600080fd5b8260051b8083602087013760009401602001938452509192915050565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015250606060408301526137ba606083018486613731565b9695505050505050565b6000828210156137d6576137d661365c565b500390565b6000602082840312156137ed57600080fd5b5051919050565b600082516138068184602087016130f4565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff841681526040602082015260006130eb604083018486613731565b60007affffffffffffffffffffffffffffffffffffffffffffffffffffff838116908316818110156138745761387461365c565b039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b601f821115610da757600081815260208120601f850160051c810160208610156138d25750805b601f850160051c820191505b818110156138f1578281556001016138de565b505050505050565b815167ffffffffffffffff8111156139135761391361387c565b613927816139218454613572565b846138ab565b602080601f83116001811461397a57600084156139445750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556138f1565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156139c7578886015182559484019460019091019084016139a8565b5085821015613a0357878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60007affffffffffffffffffffffffffffffffffffffffffffffffffffff808316818516808303821115613a4957613a4961365c565b01949350505050565b73ffffffffffffffffffffffffffffffffffffffff83168152604060208201526000610c886040830184613120565b600082613ab7577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea2646970667358221220d7799d56caf46f4219679ee400f48bf32b0f7afcf83eac5d32616a9e8a711b4d64736f6c634300080f003300000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c287000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a2
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102925760003560e01c806370a0823111610160578063aea4e49e116100d8578063d505accf1161008c578063e4ab593d11610071578063e4ab593d146107a9578063f2fde38b146107c9578063f40f0f52146107dc57600080fd5b8063d505accf14610731578063dd62ed3e1461074457600080fd5b8063c5b41b89116100bd578063c5b41b89146106f8578063c9a3911e1461070b578063cc7010d71461071e57600080fd5b8063aea4e49e146106dd578063b88a802f146106f057600080fd5b80638da5cb5b1161012f578063972c492811610114578063972c4928146106af578063a0f52da0146106b7578063a9059cbb146106ca57600080fd5b80638da5cb5b1461069f57806395d89b41146106a757600080fd5b806370a08231146105da5780637abdab561461062f5780637c6e94e3146106425780637ecebe001461064a57600080fd5b80633644e5151161020e5780635dbe4756116101c2578063607f2d42116101a7578063607f2d421461057257806367243482146105b45780637029144c146105c757600080fd5b80635dbe4756146105385780635f36e2521461054b57600080fd5b80634b0ee02a116101f35780634b0ee02a146105085780634f1ef2861461051b57806352d1902d1461053057600080fd5b80633644e5151461044d5780633d9e7719146104f557600080fd5b80631f29d2dc11610265578063221ca18c1161024a578063221ca18c146103b157806323b872dd14610406578063313ce5671461041957600080fd5b80631f29d2dc146103175780632027a80f1461039e57600080fd5b806306fdde0314610297578063095ea7b3146102b557806316336c65146102d857806318160ddd146102f0575b600080fd5b61029f6107ef565b6040516102ac919061316a565b60405180910390f35b6102c86102c33660046131a6565b6108a0565b60405190151581526020016102ac565b670de0b6b3a76400005b6040519081526020016102ac565b7f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dba546102e2565b6103796103253660046131a6565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facee6020908152604080832093835292905220541690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102ac565b6102e26103ac36600461321c565b610935565b6102e26103bf366004613288565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced602052604090205490565b6102c86104143660046132a3565b610c72565b7f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db95460405160ff90911681526020016102ac565b6102e2604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b6102e2610503366004613288565b610c90565b6102e2610516366004613288565b610d05565b61052e610529366004613321565b610d5f565b005b6102e2610dac565b61052e610546366004613374565b610e42565b7f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec546102e2565b6102c86105803660046133ba565b60009081527f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9485602052604090205460ff1690565b61052e6105c236600461321c565b6111b0565b61052e6105d53660046133d3565b611299565b6102e26105e8366004613288565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb602052604090205490565b61052e61063d366004613433565b611356565b6102c861154a565b6102e2610658366004613288565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b81602052604090205490565b6103796115b7565b61029f6115f7565b610379611628565b61052e6106c5366004613288565b611650565b6102c86106d83660046131a6565b611758565b61052e6106eb366004613288565b611774565b61052e6117e2565b61052e6107063660046133ba565b6117ed565b61052e610719366004613374565b611897565b61052e61072c36600461321c565b611b62565b61052e61073f366004613488565b611d14565b6102e26107523660046134fb565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbc6020908152604080832093909416825291909152205490565b6107bc6107b73660046132a3565b611db5565b6040516102ac919061352e565b61052e6107d7366004613288565b611e07565b6102e26107ea366004613288565b611f2d565b60607f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db7805461081d90613572565b80601f016020809104026020016040519081016040528092919081815260200182805461084990613572565b80156108965780601f1061086b57610100808354040283529160200191610896565b820191906000526020600020905b81548152906001019060200180831161087957829003601f168201915b5050505050905090565b3360008181527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbc6020908152604080832073ffffffffffffffffffffffffffffffffffffffff871680855290835281842086905590518581529293909290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a35060015b92915050565b600061093f61154a565b610975576040517f932234ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260409020547affffffffffffffffffffffffffffffffffffffffffffffffffffff16156109f8576040517f0c74abe800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b84811015610c695760007f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced81888885818110610a3957610a396135c5565b9050602002016020810190610a4e9190613288565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905080600003610ac5576040517fa973256400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b366000868685818110610ada57610ada6135c5565b9050602002810190610aec91906135f4565b915091506000610b197f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec90565b60020160008b8b88818110610b3057610b306135c5565b9050602002016020810190610b459190613288565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000209050610b77848361368b565b610b8190876136c8565b955060005b82811015610c1b5733826000868685818110610ba457610ba46135c5565b602090810292909201358352508101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff1614610c0b576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c14816136e0565b9050610b86565b508115610c5457610c548a8a87818110610c3757610c376135c5565b9050602002016020810190610c4c9190613288565b338585611fc1565b5050505080610c62906136e0565b90506109fb565b50949350505050565b6000610c7d84611ffc565b610c8884848461213d565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c9602052604081205461092f90670de0b6b3a7640000907affffffffffffffffffffffffffffffffffffffffffffffffffffff1661368b565b6000610d1082611f2d565b73ffffffffffffffffffffffffffffffffffffffff831660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb602052604090205461092f91906136c8565b610d676122f7565b610da78383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061237d92505050565b505050565b60003073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f342517ad53b0e43a413a678105a4fe4c85440ef1614610e1d576040517f0d89438e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b73ffffffffffffffffffffffffffffffffffffffff831660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced602052604081205490819003610ec1576040517fa973256400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ec961154a565b15610f61573360009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260409020547affffffffffffffffffffffffffffffffffffffffffffffffffffff1615610f51576040517f0c74abe800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f5c8484846125aa565b610f74565b610f7433610f6f838561368b565b6125e3565b60005b828110156111a95773ffffffffffffffffffffffffffffffffffffffff851660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facee602052604081203391868685818110610fd757610fd76135c5565b602090810292909201358352508101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff161461103e576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff851660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facee6020526040812090858584818110611095576110956135c5565b6020908102929092013583525081019190915260400160002080547fffffffffffffffffffffffff000000000000000000000000000000000000000016905573ffffffffffffffffffffffffffffffffffffffff85166323b872dd3033878786818110611104576111046135c5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815273ffffffffffffffffffffffffffffffffffffffff958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b15801561118057600080fd5b505af1158015611194573d6000803e3d6000fd5b50505050806111a2906136e0565b9050610f77565b5050505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611236576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b838110156111a957611289858583818110611256576112566135c5565b905060200201602081019061126b9190613288565b84848481811061127d5761127d6135c5565b905060200201356126ad565b611292816136e0565b9050611239565b303b156112d2576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112da612767565b61135084848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f88018190048102820181019092528681529250869150859081908401838280828437600092019190915250601292506127eb915050565b50505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113dc576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8078ffffffffffffffffffffffffffffffffffffffffffffffffff16600003611431576040517f9c1b278400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced6020526040902054156114ad576040517f41ab882d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660008181527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced602090815260409182902078ffffffffffffffffffffffffffffffffffffffffffffffffff851690819055825190815291517fccfc7df822b68cdc8b0d7cd8b0255d24005c6fb4adc390a411ebdf309fa6dc099281900390910190a25050565b60006503ffffffffff61157b7f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec5490565b146115865750600090565b6000611590611628565b73ffffffffffffffffffffffffffffffffffffffff16036115b15750600090565b50600190565b60007f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335b5473ffffffffffffffffffffffffffffffffffffffff16919050565b60607f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db7600101805461081d90613572565b60007f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd94846115db565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146116d6576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8116611723576040517f8e3383b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61172c81611774565b6503ffffffffff7f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec5550565b600061176333611ffc565b61176d83836128d1565b9392505050565b61177c6122f7565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd948480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6117eb33611ffc565b565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611873576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec55565b73ffffffffffffffffffffffffffffffffffffffff831660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2faced602052604081205490819003611916576040517fa973256400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b82811015611aad578473ffffffffffffffffffffffffffffffffffffffff166323b872dd3330878786818110611951576119516135c5565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815273ffffffffffffffffffffffffffffffffffffffff958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b1580156119cd57600080fd5b505af11580156119e1573d6000803e3d6000fd5b5050505033611a0d7f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec90565b73ffffffffffffffffffffffffffffffffffffffff871660009081526002919091016020526040812090868685818110611a4957611a496135c5565b90506020020135815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080611aa6906136e0565b9050611919565b50611ab661154a565b15611b4f573360009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260409020547affffffffffffffffffffffffffffffffffffffffffffffffffffff1615611b3e576040517f0c74abe800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b4a84338585611fc1565b611350565b61135033611b5d838561368b565b6129a8565b611b6a61154a565b611ba0576040517f932234ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611ba933611ffc565b3360008181527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960209081526040808320547f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb909252909120547affffffffffffffffffffffffffffffffffffffffffffffffffffff90911691611c2d9082612a22565b60408051336020820152908101829052611cb7907f44344735c48ff651708a4f18a84c726f5beda992b79b3b970c63e46fb41f7ae2906060015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052611ca39291602001613718565b604051602081830303815290604052612aee565b611cc2336000612c39565b6000611cd087878787610935565b9050808314611d0b576040517fd24a4efc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050505050565b611d2387878787878787612cd3565b73ffffffffffffffffffffffffffffffffffffffff87811660008181527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbc60209081526040808320948b1680845294825291829020899055815189815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a350505050505050565b73ffffffffffffffffffffffffffffffffffffffff831660009081527f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facee60205260409020606090610c88908484612ffe565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611e8d576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea3380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560408051338152602081019290925280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260408120805461176d907affffffffffffffffffffffffffffffffffffffffffffffffffffff8116907b01000000000000000000000000000000000000000000000000000000900464ffffffffff16613069565b6113507fd71b04c34f087affbc01a0d18c25e3d24a365664d06d565a2b97abcd2a61792c85858585604051602001611c679493929190613780565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c96020526040902080547affffffffffffffffffffffffffffffffffffffffffffffffffffff8116907b01000000000000000000000000000000000000000000000000000000900464ffffffffff1681156120ad5781156120ad57600061209f8383613069565b90506120ab85826126ad565b505b50505073ffffffffffffffffffffffffffffffffffffffff1660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c96020526040902080547affffffffffffffffffffffffffffffffffffffffffffffffffffff167b0100000000000000000000000000000000000000000000000000000064ffffffffff421602179055565b73ffffffffffffffffffffffffffffffffffffffff831660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbc602090815260408083203384529091528120547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461220f576121be83826137c4565b73ffffffffffffffffffffffffffffffffffffffff861660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbc602090815260408083203384529091529020555b73ffffffffffffffffffffffffffffffffffffffff851660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb6020526040812080548592906122639084906137c4565b909155505073ffffffffffffffffffffffffffffffffffffffff84811660008181527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb6020908152604091829020805488019055905186815291928816917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3506001949350505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146117eb576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff163b6000036123ce576040517f09ee12d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561243d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061246191906137db565b14612498576040517f03ed501d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80511561251b5760008273ffffffffffffffffffffffffffffffffffffffff16826040516124c691906137f4565b600060405180830381855af49150503d8060008114612501576040519150601f19603f3d011682016040523d82523d6000602084013e612506565b606091505b5050905080612519573d6000803e3d6000fd5b505b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040517fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a25050565b610da77fc9f557702f80fbdc4fef46edb068c40d7225ad4563d3cf784c276650c0fbba73848484604051602001611c6793929190613810565b6125ec82611ffc565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260408120805483929061265d9084907affffffffffffffffffffffffffffffffffffffffffffffffffffff16613840565b92506101000a8154817affffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837affffffffffffffffffffffffffffffffffffffffffffffffffffff1602179055505050565b807f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db760030160008282546126e191906136c8565b909155505073ffffffffffffffffffffffffffffffffffffffff821660008181527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb60209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b303b156127a0576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea3380547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055565b303b15612824576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db761284f84826138f9565b507f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db861287b83826138f9565b507f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80db980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff929092169190911790555050565b3360009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb60205260408120805483919083906129119084906137c4565b909155505073ffffffffffffffffffffffffffffffffffffffff831660008181527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb602052604090208054840190553373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161092391815260200190565b6129b182611ffc565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c960205260408120805483929061265d9084907affffffffffffffffffffffffffffffffffffffffffffffffffffff16613a13565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dbb602052604081208054839290612a769084906137c4565b90915550507f0e539be85842d1c3b5b43263a827c1e07ab5a9c9536bf840ece723e480d80dba8054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200161275b565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd94845473ffffffffffffffffffffffffffffffffffffffff16612b5c576040517fafd0683400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a21663b47204777f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9484546040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b168152612c0b9173ffffffffffffffffffffffffffffffffffffffff16908590600401613a52565b600060405180830381600087803b158015612c2557600080fd5b505af11580156111a9573d6000803e3d6000fd5b612c4282611ffc565b73ffffffffffffffffffffffffffffffffffffffff9190911660009081527f2bf76f1229f14879252da90846a528ce52c56d0ade153f3ef6c5b45141fb99c96020526040902080547fffffffffff000000000000000000000000000000000000000000000000000000167affffffffffffffffffffffffffffffffffffffffffffffffffffff909216919091179055565b42841015612d0d576040517f1ab7da6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff871660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b8160205260408120805460018082019092559190612e05604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b604080517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9602082015273ffffffffffffffffffffffffffffffffffffffff808e1692820192909252908b166060820152608081018a905260a0810185905260c0810189905260e00160405160208183030381529060405280519060200120604051602001612ec69291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff881690820152606081018690526080810185905260a0016020604051602081039080840390855afa158015612f42573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81161580612fbc57508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b15612ff3576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050565b60405160208101600060018401815b8181101561305b5760008181526020899052604090205473ffffffffffffffffffffffffffffffffffffffff808916911603613053578084526020840193506001830192505b60010161300d565b505082526040529392505050565b6000806130947f1a092854511578a55ddb9a3e239e5eb710da1c5cb2adb4c4d5c3fe3a7e2facec5490565b905042818411156130aa5760009250505061092f565b818111156130b55750805b62015180670de0b6b3a7640000866130cd87856137c4565b6130d7919061368b565b6130e1919061368b565b6130eb9190613a81565b95945050505050565b60005b8381101561310f5781810151838201526020016130f7565b838111156113505750506000910152565b600081518084526131388160208601602086016130f4565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061176d6020830184613120565b803573ffffffffffffffffffffffffffffffffffffffff811681146131a157600080fd5b919050565b600080604083850312156131b957600080fd5b6131c28361317d565b946020939093013593505050565b60008083601f8401126131e257600080fd5b50813567ffffffffffffffff8111156131fa57600080fd5b6020830191508360208260051b850101111561321557600080fd5b9250929050565b6000806000806040858703121561323257600080fd5b843567ffffffffffffffff8082111561324a57600080fd5b613256888389016131d0565b9096509450602087013591508082111561326f57600080fd5b5061327c878288016131d0565b95989497509550505050565b60006020828403121561329a57600080fd5b61176d8261317d565b6000806000606084860312156132b857600080fd5b6132c18461317d565b92506132cf6020850161317d565b9150604084013590509250925092565b60008083601f8401126132f157600080fd5b50813567ffffffffffffffff81111561330957600080fd5b60208301915083602082850101111561321557600080fd5b60008060006040848603121561333657600080fd5b61333f8461317d565b9250602084013567ffffffffffffffff81111561335b57600080fd5b613367868287016132df565b9497909650939450505050565b60008060006040848603121561338957600080fd5b6133928461317d565b9250602084013567ffffffffffffffff8111156133ae57600080fd5b613367868287016131d0565b6000602082840312156133cc57600080fd5b5035919050565b600080600080604085870312156133e957600080fd5b843567ffffffffffffffff8082111561340157600080fd5b61340d888389016132df565b9096509450602087013591508082111561342657600080fd5b5061327c878288016132df565b6000806040838503121561344657600080fd5b61344f8361317d565b9150602083013578ffffffffffffffffffffffffffffffffffffffffffffffffff8116811461347d57600080fd5b809150509250929050565b600080600080600080600060e0888a0312156134a357600080fd5b6134ac8861317d565b96506134ba6020890161317d565b95506040880135945060608801359350608088013560ff811681146134de57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561350e57600080fd5b6135178361317d565b91506135256020840161317d565b90509250929050565b6020808252825182820181905260009190848201906040850190845b818110156135665783518352928401929184019160010161354a565b50909695505050505050565b600181811c9082168061358657607f821691505b6020821081036135bf577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261362957600080fd5b83018035915067ffffffffffffffff82111561364457600080fd5b6020019150600581901b360382131561321557600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156136c3576136c361365c565b500290565b600082198211156136db576136db61365c565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036137115761371161365c565b5060010190565b828152604060208201526000610c886040830184613120565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561376357600080fd5b8260051b8083602087013760009401602001938452509192915050565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015250606060408301526137ba606083018486613731565b9695505050505050565b6000828210156137d6576137d661365c565b500390565b6000602082840312156137ed57600080fd5b5051919050565b600082516138068184602087016130f4565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff841681526040602082015260006130eb604083018486613731565b60007affffffffffffffffffffffffffffffffffffffffffffffffffffff838116908316818110156138745761387461365c565b039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b601f821115610da757600081815260208120601f850160051c810160208610156138d25750805b601f850160051c820191505b818110156138f1578281556001016138de565b505050505050565b815167ffffffffffffffff8111156139135761391361387c565b613927816139218454613572565b846138ab565b602080601f83116001811461397a57600084156139445750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556138f1565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156139c7578886015182559484019460019091019084016139a8565b5085821015613a0357878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60007affffffffffffffffffffffffffffffffffffffffffffffffffffff808316818516808303821115613a4957613a4961365c565b01949350505050565b73ffffffffffffffffffffffffffffffffffffffff83168152604060208201526000610c886040830184613120565b600082613ab7577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea2646970667358221220d7799d56caf46f4219679ee400f48bf32b0f7afcf83eac5d32616a9e8a711b4d64736f6c634300080f0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c287000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a2
-----Decoded View---------------
Arg [0] : checkpointManager (address): 0x86E4Dc95c7FBdBf52e33D563BbDB00823894C287
Arg [1] : fxRoot (address): 0xfe5e5D361b2ad62c541bAb87C45a0B9B018389a2
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c287
Arg [1] : 000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a2
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.