ETH Price: $3,275.17 (+0.87%)

Contract

0x904aE1a25757E59456a59a49c4dE52D68DcCf9ff
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Approval For...215159412024-12-30 14:38:5912 days ago1735569539IN
0x904aE1a2...68DcCf9ff
0 ETH0.0007792716.91910894
Set Approval For...214540882024-12-21 23:14:2320 days ago1734822863IN
0x904aE1a2...68DcCf9ff
0 ETH0.0004745310.3
Set Approval For...214244842024-12-17 19:55:5924 days ago1734465359IN
0x904aE1a2...68DcCf9ff
0 ETH0.0008690518.83393079
Set Approval For...214217612024-12-17 10:48:5925 days ago1734432539IN
0x904aE1a2...68DcCf9ff
0 ETH0.0004616510.02302091
Set Approval For...214207712024-12-17 7:29:5925 days ago1734420599IN
0x904aE1a2...68DcCf9ff
0 ETH0.000408498.86896765
Set Approval For...213847402024-12-12 6:49:5930 days ago1733986199IN
0x904aE1a2...68DcCf9ff
0 ETH0.0006551314.19796005
Set Approval For...211012882024-11-02 16:56:4769 days ago1730566607IN
0x904aE1a2...68DcCf9ff
0 ETH0.0004174717.27161705
Set Approval For...209851692024-10-17 12:04:3586 days ago1729166675IN
0x904aE1a2...68DcCf9ff
0 ETH0.0010600543.9
Set Approval For...209485872024-10-12 9:19:3591 days ago1728724775IN
0x904aE1a2...68DcCf9ff
0 ETH0.0002656811.0026337
Set Approval For...207161262024-09-09 22:45:35123 days ago1725921935IN
0x904aE1a2...68DcCf9ff
0 ETH0.000087941.90592487
Set Approval For...206316592024-08-29 3:47:59135 days ago1724903279IN
0x904aE1a2...68DcCf9ff
0 ETH0.000057891.25691344
Set Approval For...206156722024-08-26 22:12:35137 days ago1724710355IN
0x904aE1a2...68DcCf9ff
0 ETH0.000028241.16966134
Set Approval For...205188472024-08-13 9:36:11151 days ago1723541771IN
0x904aE1a2...68DcCf9ff
0 ETH0.000058762.43359157
Set Approval For...205183592024-08-13 7:57:47151 days ago1723535867IN
0x904aE1a2...68DcCf9ff
0 ETH0.00003341.38359718
Set Approval For...203488062024-07-20 16:01:59175 days ago1721491319IN
0x904aE1a2...68DcCf9ff
0 ETH0.000275165.96482956
Set Approval For...202630432024-07-08 16:40:23187 days ago1720456823IN
0x904aE1a2...68DcCf9ff
0 ETH0.000364947.90901888
Set Approval For...201539032024-06-23 10:50:35202 days ago1719139835IN
0x904aE1a2...68DcCf9ff
0 ETH0.000054162.23536014
Set Approval For...201539022024-06-23 10:50:23202 days ago1719139823IN
0x904aE1a2...68DcCf9ff
0 ETH0.000053332.20891901
Set Approval For...201093662024-06-17 5:16:59208 days ago1718601419IN
0x904aE1a2...68DcCf9ff
0 ETH0.000060242.49491202
Set Approval For...200184612024-06-04 12:29:11221 days ago1717504151IN
0x904aE1a2...68DcCf9ff
0 ETH0.000390068.46875175
Set Approval For...198939162024-05-18 2:40:11238 days ago1716000011IN
0x904aE1a2...68DcCf9ff
0 ETH0.000143383.11315136
Set Approval For...198859622024-05-16 23:57:11239 days ago1715903831IN
0x904aE1a2...68DcCf9ff
0 ETH0.000153046.33805988
Set Approval For...197020342024-04-21 6:36:47265 days ago1713681407IN
0x904aE1a2...68DcCf9ff
0 ETH0.000153816.34782373
Set Approval For...197009742024-04-21 3:03:11265 days ago1713668591IN
0x904aE1a2...68DcCf9ff
0 ETH0.00015476.3844623
Set Approval For...196848762024-04-18 20:58:47267 days ago1713473927IN
0x904aE1a2...68DcCf9ff
0 ETH0.000209278.66661783
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block
From
To
156826232022-10-05 14:53:35829 days ago1664981615
0x904aE1a2...68DcCf9ff
233.727 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
GMC

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 100000 runs

Other Settings:
default evmVersion
File 1 of 20 : GMCRoot.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {OwnableUDS} from "UDS/auth/OwnableUDS.sol";
import {LibCrumbMap} from "../lib/LibCrumbMap.sol";
import {FxERC721MRoot} from "ERC721M/extensions/FxERC721MRoot.sol";
import {ERC20UDS as ERC20} from "UDS/tokens/ERC20UDS.sol";

import "solady/utils/ECDSA.sol";
import "solady/utils/LibString.sol";

error ExceedsLimit();
error TransferFailed();
error TimelockActive();
error IncorrectValue();
error MaxSupplyLocked();
error InvalidSignature();
error InvalidPriceUnits();
error WhitelistNotActive();
error PublicSaleNotActive();
error ContractCallNotAllowed();

/// @title Gangsta Mice City Root
/// @author phaze (https://github.com/0xPhaze)
contract GMC is OwnableUDS, FxERC721MRoot {
    using ECDSA for bytes32;
    using LibString for uint256;
    using LibCrumbMap for LibCrumbMap.CrumbMap;

    event SaleStateUpdate();
    event FirstLegendaryRaffleEntered(address user);
    event SecondLegendaryRaffleEntered(address user);

    uint16 public constant MAX_PER_WALLET = 20;
    uint256 public constant PURCHASE_LIMIT = 5;
    uint256 public constant BRIDGE_RAFFLE_LOCK_DURATION = 24 hours;
    uint256 private constant PRICE_UNIT = 0.001 ether;
    uint256 private constant GENESIS_CLAIM = 555;
    uint256 private immutable DEPLOY_TIMESTAMP;

    bool public maxSupplyLocked;
    uint16 public supply;
    uint16 public maxSupply;
    uint32 public mintStart;
    uint8 private publicPriceUnits;
    uint8 private whitelistPriceUnits;
    address private signer;

    string private baseURI;
    string private postFixURI = ".json";
    string private unrevealedURI = "ipfs://QmTv9VoXgkZxFcomTW3kN6CRryUPMfgeUkVekFszcd79gK/";

    LibCrumbMap.CrumbMap gangs;

    constructor(address checkpointManager, address fxRoot)
        FxERC721MRoot("Gangsta Mice City", "GMC", checkpointManager, fxRoot)
    {
        __Ownable_init();

        maxSupply = 6666;
        signer = msg.sender;
        DEPLOY_TIMESTAMP = block.timestamp;

        publicPriceUnits = toPriceUnits(0.049 ether);
        whitelistPriceUnits = toPriceUnits(0.039 ether);
    }

    /* ------------- view ------------- */

    function totalSupply() public view override returns (uint256) {
        return supply;
    }

    function publicPrice() public view returns (uint256) {
        return toPrice(publicPriceUnits);
    }

    function whitelistPrice() public view returns (uint256) {
        return toPrice(whitelistPriceUnits);
    }

    function gangOf(uint256 id) public view returns (uint256) {
        return gangs.get(id);
    }

    /* ------------- external ------------- */

    function mint(uint256 quantity, bool lock)
        external
        payable
        onlyEOA
        requireMintableSupply(quantity)
        requireMintableByUser(quantity, MAX_PER_WALLET)
    {
        unchecked {
            if (msg.value != publicPrice() * quantity) revert IncorrectValue();
            if (block.timestamp < mintStart + 2 hours || mintStart == 0) revert PublicSaleNotActive();

            mintWithPerks(msg.sender, quantity, lock);
        }
    }

    function whitelistMint(
        uint256 quantity,
        bool lock,
        uint256 limit,
        bytes calldata signature
    ) external payable onlyEOA requireMintableSupply(quantity) requireMintableByUser(quantity, limit) {
        unchecked {
            if (!validSignature(signature, limit)) revert InvalidSignature();
            if (mintStart + 2 hours < block.timestamp) revert WhitelistNotActive();
            if (msg.value != whitelistPrice() * quantity) revert IncorrectValue();

            mintWithPerks(msg.sender, quantity, lock);
        }
    }

    function lockAndTransmit(address from, uint256[] calldata tokenIds) external {
        unchecked {
            if (tokenIds.length > 20) revert ExceedsLimit();
            // don't repeat an unnecessary sload if we can avoid it
            if (
                tokenIds.length != 0 &&
                block.timestamp < DEPLOY_TIMESTAMP + 1 weeks &&
                block.timestamp < mintStart + 2 hours
            ) {
                emit SecondLegendaryRaffleEntered(from);
            }

            _lockAndTransmit(from, tokenIds);
        }
    }

    function unlockAndTransmit(address from, uint256[] calldata tokenIds) external {
        if (tokenIds.length > 20) revert ExceedsLimit();
        if (block.timestamp < DEPLOY_TIMESTAMP + 1 weeks && block.timestamp < mintStart + BRIDGE_RAFFLE_LOCK_DURATION) {
            revert TimelockActive();
        }

        _unlockAndTransmit(from, tokenIds);
    }

    /* ------------- private ------------- */

    function validSignature(bytes calldata signature, uint256 limit) private view returns (bool) {
        bytes32 hash = keccak256(abi.encode(address(this), msg.sender, limit));
        address recovered = hash.toEthSignedMessageHash().recover(signature);

        return recovered != address(0) && recovered == signer;
    }

    function toPrice(uint16 priceUnits) private pure returns (uint256) {
        unchecked {
            return uint256(priceUnits) * PRICE_UNIT;
        }
    }

    function toPriceUnits(uint256 price) private pure returns (uint8) {
        unchecked {
            uint256 units;

            if (price % PRICE_UNIT != 0) revert InvalidPriceUnits();
            if ((units = price / PRICE_UNIT) > type(uint8).max) revert InvalidPriceUnits();

            return uint8(units);
        }
    }

    function mintWithPerks(
        address to,
        uint256 quantity,
        bool lock
    ) private {
        unchecked {
            if (quantity > 2) {
                emit FirstLegendaryRaffleEntered(to);

                if (supply < 500 + GENESIS_CLAIM) ++quantity;
            }

            if (lock && block.timestamp < mintStart + 2 hours) emit SecondLegendaryRaffleEntered(to);

            if (lock) _mintLockedAndTransmit(to, quantity);
            else _mint(to, quantity);
        }
    }

    /* ------------- owner ------------- */

    function pause() external onlyOwner {
        mintStart = 0;
    }

    function lockMaxSupply() external onlyOwner {
        maxSupplyLocked = true;
    }

    function setSigner(address addr) external onlyOwner {
        signer = addr;
    }

    function setMaxSupply(uint16 value) external onlyOwner {
        if (maxSupplyLocked) revert MaxSupplyLocked();

        maxSupply = value;
    }

    function setMintStart(uint32 time) external onlyOwner {
        mintStart = time;
    }

    function setPublicPrice(uint256 value) external onlyOwner {
        publicPriceUnits = toPriceUnits(value);
    }

    function setBaseURI(string calldata uri) external onlyOwner {
        baseURI = uri;
    }

    function setPostFixURI(string calldata postFix) external onlyOwner {
        postFixURI = postFix;
    }

    function setWhitelistPrice(uint256 value) external onlyOwner {
        whitelistPriceUnits = toPriceUnits(value);
    }

    function setUnrevealedURI(string calldata uri) external onlyOwner {
        unrevealedURI = uri;
    }

    function setGangs(uint256[] calldata chunkIndices, uint256[] calldata chunks) external onlyOwner {
        for (uint256 i; i < chunkIndices.length; ++i) gangs.set32BytesChunk(chunkIndices[i], chunks[i]);
    }

    function airdrop(
        address[] calldata users,
        uint256 quantity,
        bool locked
    ) external onlyOwner requireMintableSupply(quantity * users.length) {
        if (locked) for (uint256 i; i < users.length; ++i) _mintLockedAndTransmit(users[i], quantity);
        else for (uint256 i; i < users.length; ++i) _mint(users[i], quantity);
    }

    function withdraw() external onlyOwner {
        uint256 balance = address(this).balance;
        (bool success, ) = msg.sender.call{value: balance}("");

        if (!success) revert TransferFailed();
    }

    function recoverToken(ERC20 token) external onlyOwner {
        uint256 balance = token.balanceOf(address(this));

        token.transfer(msg.sender, balance);
    }

    /* ------------- override ------------- */

    function _authorizeTunnelController() internal override onlyOwner {}

    function _increaseTotalSupply(uint256 amount) internal override {
        supply += uint16(amount);
    }

    /* ------------- modifier ------------- */

    modifier onlyEOA() {
        if (tx.origin != msg.sender) revert ContractCallNotAllowed();
        _;
    }

    modifier requireMintableByUser(uint256 quantity, uint256 limit) {
        unchecked {
            if (quantity > PURCHASE_LIMIT) revert ExceedsLimit();
            if (quantity + numMinted(msg.sender) > limit) revert ExceedsLimit();
        }
        _;
    }

    modifier requireMintableSupply(uint256 quantity) {
        unchecked {
            if (quantity + supply > maxSupply) revert ExceedsLimit();
        }
        _;
    }

    /* ------------- ERC721 ------------- */

    function tokenURI(uint256 id) public view override returns (string memory) {
        return 
            bytes(baseURI).length == 0 
              ? unrevealedURI 
              : string.concat(baseURI, id.toString(), postFixURI); // prettier-ignore
    }
}

File 2 of 20 : OwnableUDS.sol
// 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();
        _;
    }
}

File 3 of 20 : LibCrumbMap.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

using LibCrumbMap for LibCrumbMap.CrumbMap;


/// @notice Efficient crumb map library for mapping integers to crumbs.
/// @author phaze (https://github.com/0xPhaze)
/// @author adapted from Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBytemap.sol)
library LibCrumbMap {
    struct CrumbMap {
        mapping(uint256 => uint256) map;
    }

    /* ------------- CrumbMap ------------- */

    function get(CrumbMap storage crumbMap, uint256 index) internal view returns (uint256 result) {
        assembly {
            mstore(0x20, crumbMap.slot)
            mstore(0x00, shr(7, index))
            result := and(shr(shl(1, and(index, 0x7f)), sload(keccak256(0x00, 0x20))), 0x03)
        }
    }

    function get32BytesChunk(CrumbMap storage crumbMap, uint256 bytesIndex) internal view returns (uint256 result) {
        assembly {
            mstore(0x20, crumbMap.slot)
            mstore(0x00, bytesIndex)
            result := sload(keccak256(0x00, 0x20))
        }
    }

    function set32BytesChunk(
        CrumbMap storage crumbMap,
        uint256 bytesIndex,
        uint256 value
    ) internal {
        assembly {
            mstore(0x20, crumbMap.slot)
            mstore(0x00, bytesIndex)
            sstore(keccak256(0x00, 0x20), value)
        }
    }

    function set(
        CrumbMap storage crumbMap,
        uint256 index,
        uint256 value
    ) internal {
        require(value < 4);

        assembly {
            mstore(0x20, crumbMap.slot)
            mstore(0x00, shr(7, index))
            let storageSlot := keccak256(0x00, 0x20)
            let shift := shl(1, and(index, 0x7f))
            // Unset crumb at index and store.
            let chunkValue := and(sload(storageSlot), not(shl(shift, 0x03)))
            // Set crumb to `value` at index and store.
            chunkValue := or(chunkValue, shl(shift, value))
            sstore(storageSlot, chunkValue)
        }
    }

    /* ------------- mapping(uint256 => uint256) ------------- */

    function get(mapping(uint256 => uint256) storage crumbMap, uint256 index) internal view returns (uint256 result) {
        assembly {
            mstore(0x20, crumbMap.slot)
            mstore(0x00, shr(7, index))
            result := and(shr(shl(1, and(index, 0x7f)), sload(keccak256(0x00, 0x20))), 0x03)
        }
    }

    function get32BytesChunk(mapping(uint256 => uint256) storage crumbMap, uint256 bytesIndex) internal view returns (uint256 result) {
        assembly {
            mstore(0x20, crumbMap.slot)
            mstore(0x00, bytesIndex)
            result := sload(keccak256(0x00, 0x20))
        }
    }

    function set32BytesChunk(
        mapping(uint256 => uint256) storage crumbMap,
        uint256 bytesIndex,
        uint256 value
    ) internal {
        assembly {
            mstore(0x20, crumbMap.slot)
            mstore(0x00, bytesIndex)
            sstore(keccak256(0x00, 0x20), value)
        }
    }

    function set(
        mapping(uint256 => uint256) storage crumbMap,
        uint256 index,
        uint256 value
    ) internal {
        require(value < 4);

        assembly {
            mstore(0x20, crumbMap.slot)
            mstore(0x00, shr(7, index))
            let storageSlot := keccak256(0x00, 0x20)
            let shift := shl(1, and(index, 0x7f))
            // Unset crumb at index and store.
            let chunkValue := and(sload(storageSlot), not(shl(shift, 0x03)))
            // Set crumb to `value` at index and store.
            chunkValue := or(chunkValue, shl(shift, value))
            sstore(storageSlot, chunkValue)
        }
    }
}

File 4 of 20 : FxERC721MRoot.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ERC721M} from "../ERC721M.sol";
import {ERC721MQuery} from "./ERC721MQuery.sol";
import {FxERC721Root} from "fx-contracts/FxERC721Root.sol";

/// @title ERC721M FxPortal extension
/// @author phaze (https://github.com/0xPhaze/ERC721M)
abstract contract FxERC721MRoot is FxERC721Root, ERC721M, ERC721MQuery {
    constructor(
        string memory name,
        string memory symbol,
        address checkpointManager,
        address fxRoot
    ) ERC721M(name, symbol) FxERC721Root(checkpointManager, fxRoot) {}

    /* ------------- virtual ------------- */

    function tokenURI(uint256 id) external view virtual override returns (string memory);

    function _authorizeTunnelController() internal virtual override;

    /* ------------- internal ------------- */

    function _mintLockedAndTransmit(address to, uint256 quantity) internal virtual {
        _mintLockedAndTransmit(to, quantity, 0);
    }

    function _mintLockedAndTransmit(
        address to,
        uint256 quantity,
        uint48 auxData
    ) internal virtual {
        uint256 startId = _nextTokenId();

        _mintAndLock(to, quantity, true, auxData);

        uint256[] memory ids = new uint256[](quantity);

        unchecked {
            for (uint256 i; i < quantity; ++i) {
                ids[i] = startId + i;
            }
        }

        _registerERC721IdsWithChildMem(to, ids);
    }

    function _lockAndTransmit(address from, uint256[] calldata ids) internal virtual {
        unchecked {
            for (uint256 i; i < ids.length; ++i) {
                _lock(from, ids[i]);
            }
        }

        _registerERC721IdsWithChild(from, ids);
    }

    // @notice using `_unlockAndTransmit` is simple and easy
    // this assumes L1 state as the single source of truth
    // messages are always pushed L1 -> L2 without knowing state on L2
    // this means that NFTs should not be allowed to be traded/sold on L2
    // alternatively `_unlockWithProof` should be implemented requiring
    // a MPT inclusion proof.
    function _unlockAndTransmit(address from, uint256[] calldata ids) internal virtual {
        unchecked {
            for (uint256 i; i < ids.length; ++i) _unlock(from, ids[i]);
        }

        _registerERC721IdsWithChild(address(0), ids);
    }
}

File 5 of 20 : ERC20UDS.sol
// 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);
    }
}

File 6 of 20 : ECDSA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Gas optimized ECDSA wrapper.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
library ECDSA {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The number which `s` must not exceed in order for
    /// the signature to be non-malleable.
    bytes32 private constant _MALLEABILITY_THRESHOLD =
        0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    RECOVERY OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the `signature`.
    ///
    /// This function does NOT accept EIP-2098 short form signatures.
    /// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
    /// short form signatures instead.
    ///
    /// WARNING!
    /// The `result` will be the zero address upon recovery failure.
    /// As such, it is extremely important to ensure that the address which
    /// the `result` is compared against is never zero.
    function recover(bytes32 hash, bytes calldata signature) internal view returns (address result) {
        assembly {
            if eq(signature.length, 65) {
                // Copy the free memory pointer so that we can restore it later.
                let m := mload(0x40)
                // Directly copy `r` and `s` from the calldata.
                calldatacopy(0x40, signature.offset, 0x40)

                // If `s` in lower half order, such that the signature is not malleable.
                if iszero(gt(mload(0x60), _MALLEABILITY_THRESHOLD)) {
                    mstore(0x00, hash)
                    // Compute `v` and store it in the scratch space.
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40))))
                    pop(
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            0x01, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x40, // Start of output.
                            0x20 // Size of output.
                        )
                    )
                    // Restore the zero slot.
                    mstore(0x60, 0)
                    // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                    result := mload(sub(0x60, returndatasize()))
                }
                // Restore the free memory pointer.
                mstore(0x40, m)
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    ///
    /// This function only accepts EIP-2098 short form signatures.
    /// See: https://eips.ethereum.org/EIPS/eip-2098
    ///
    /// To be honest, I do not recommend using EIP-2098 signatures
    /// for simplicity, performance, and security reasons. Most if not
    /// all clients support traditional non EIP-2098 signatures by default.
    /// As such, this method is intentionally not fully inlined.
    /// It is merely included for completeness.
    ///
    /// WARNING!
    /// The `result` will be the zero address upon recovery failure.
    /// As such, it is extremely important to ensure that the address which
    /// the `result` is compared against is never zero.
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal view returns (address result) {
        uint8 v;
        bytes32 s;
        assembly {
            s := shr(1, shl(1, vs))
            v := add(shr(255, vs), 27)
        }
        result = recover(hash, v, r, s);
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    ///
    /// WARNING!
    /// The `result` will be the zero address upon recovery failure.
    /// As such, it is extremely important to ensure that the address which
    /// the `result` is compared against is never zero.
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal view returns (address result) {
        assembly {
            // Copy the free memory pointer so that we can restore it later.
            let m := mload(0x40)

            // If `s` in lower half order, such that the signature is not malleable.
            if iszero(gt(s, _MALLEABILITY_THRESHOLD)) {
                mstore(0x00, hash)
                mstore(0x20, v)
                mstore(0x40, r)
                mstore(0x60, s)
                pop(
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        0x01, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x40, // Start of output.
                        0x20 // Size of output.
                    )
                )
                // Restore the zero slot.
                mstore(0x60, 0)
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(sub(0x60, returndatasize()))
            }
            // Restore the free memory pointer.
            mstore(0x40, m)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HASHING OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an Ethereum Signed Message, created from a `hash`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
        assembly {
            // Store into scratch space for keccak256.
            mstore(0x20, hash)
            mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32")
            // 0x40 - 0x04 = 0x3c
            result := keccak256(0x04, 0x3c)
        }
    }

    /// @dev Returns an Ethereum Signed Message, created from `s`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
        assembly {
            // We need at most 128 bytes for Ethereum signed message header.
            // The max length of the ASCII reprenstation of a uint256 is 78 bytes.
            // The length of "\x19Ethereum Signed Message:\n" is 26 bytes (i.e. 0x1a).
            // The next multiple of 32 above 78 + 26 is 128 (i.e. 0x80).

            // Instead of allocating, we temporarily copy the 128 bytes before the
            // start of `s` data to some variables.
            let m3 := mload(sub(s, 0x60))
            let m2 := mload(sub(s, 0x40))
            let m1 := mload(sub(s, 0x20))
            // The length of `s` is in bytes.
            let sLength := mload(s)

            let ptr := add(s, 0x20)

            // `end` marks the end of the memory which we will compute the keccak256 of.
            let end := add(ptr, sLength)

            // Convert the length of the bytes to ASCII decimal representation
            // and store it into the memory.
            // prettier-ignore
            for { let temp := sLength } 1 {} {
                ptr := sub(ptr, 1)
                mstore8(ptr, add(48, mod(temp, 10)))
                temp := div(temp, 10)
                // prettier-ignore
                if iszero(temp) { break }
            }

            // Copy the header over to the memory.
            mstore(sub(ptr, 0x20), "\x00\x00\x00\x00\x00\x00\x19Ethereum Signed Message:\n")
            // Compute the keccak256 of the memory.
            result := keccak256(sub(ptr, 0x1a), sub(end, sub(ptr, 0x1a)))

            // Restore the previous memory.
            mstore(s, sLength)
            mstore(sub(s, 0x20), m1)
            mstore(sub(s, 0x40), m2)
            mstore(sub(s, 0x60), m3)
        }
    }
}

File 7 of 20 : LibString.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
library LibString {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The `length` of the output is too small to contain all the hex digits.
    error HexLengthInsufficient();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The constant returned when the `search` is not found in the string.
    uint256 internal constant NOT_FOUND = uint256(int256(-1));

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     DECIMAL OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory str) {
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
            let m := add(mload(0x40), 0xa0)
            // Update the free memory pointer to allocate.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end of the memory to calculate the length later.
            let end := str

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 {} {
                str := sub(str, 1)
                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                // Keep dividing `temp` until zero.
                temp := div(temp, 10)
                // prettier-ignore
                if iszero(temp) { break }
            }

            let length := sub(end, str)
            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 0x20)
            // Store the length.
            mstore(str, length)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   HEXADECIMAL OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2 + 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
        assembly {
            let start := mload(0x40)
            // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
            // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
            // We add 0x20 to the total and round down to a multiple of 0x20.
            // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
            let m := add(start, and(add(shl(1, length), 0x62), not(0x1f)))
            // Allocate the memory.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let temp := value
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for {} 1 {} {
                str := sub(str, 2)
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                length := sub(length, 1)
                // prettier-ignore
                if iszero(length) { break }
            }

            if temp {
                // Store the function selector of `HexLengthInsufficient()`.
                mstore(0x00, 0x2194895a)
                // Revert with (offset, size).
                revert(0x1c, 0x04)
            }

            // Compute the string's length.
            let strLength := add(sub(end, str), 2)
            // Move the pointer and write the "0x" prefix.
            str := sub(str, 0x20)
            mstore(str, 0x3078)
            // Move the pointer and write the length.
            str := sub(str, 2)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2 + 2` bytes.
    function toHexString(uint256 value) internal pure returns (string memory str) {
        assembly {
            let start := mload(0x40)
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
            let m := add(start, 0xa0)
            // Allocate the memory.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 {} {
                str := sub(str, 2)
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                // prettier-ignore
                if iszero(temp) { break }
            }

            // Compute the string's length.
            let strLength := add(sub(end, str), 2)
            // Move the pointer and write the "0x" prefix.
            str := sub(str, 0x20)
            mstore(str, 0x3078)
            // Move the pointer and write the length.
            str := sub(str, 2)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    function toHexString(address value) internal pure returns (string memory str) {
        assembly {
            let start := mload(0x40)
            // We need 0x20 bytes for the length, 0x02 bytes for the prefix,
            // and 0x28 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x02 + 0x28) is 0x60.
            str := add(start, 0x60)

            // Allocate the memory.
            mstore(0x40, str)
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let length := 20
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 {} {
                str := sub(str, 2)
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                length := sub(length, 1)
                // prettier-ignore
                if iszero(length) { break }
            }

            // Move the pointer and write the "0x" prefix.
            str := sub(str, 32)
            mstore(str, 0x3078)
            // Move the pointer and write the length.
            str := sub(str, 2)
            mstore(str, 42)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   OTHER STRING OPERATIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // For performance and bytecode compactness, all indices of the following operations
    // are byte (ASCII) offsets, not UTF character offsets.

    /// @dev Returns `subject` all occurances of `search` replaced with `replacement`.
    function replace(
        string memory subject,
        string memory search,
        string memory replacement
    ) internal pure returns (string memory result) {
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)
            let replacementLength := mload(replacement)

            subject := add(subject, 0x20)
            search := add(search, 0x20)
            replacement := add(replacement, 0x20)
            result := add(mload(0x40), 0x20)

            let subjectEnd := add(subject, subjectLength)
            if iszero(gt(searchLength, subjectLength)) {
                let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 32)) {
                    h := keccak256(search, searchLength)
                }
                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(search)
                // prettier-ignore
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of 
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                mstore(result, t)
                                result := add(result, 1)
                                subject := add(subject, 1)
                                // prettier-ignore
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Copy the `replacement` one word at a time.
                        // prettier-ignore
                        for { let o := 0 } 1 {} {
                            mstore(add(result, o), mload(add(replacement, o)))
                            o := add(o, 0x20)
                            // prettier-ignore
                            if iszero(lt(o, replacementLength)) { break }
                        }
                        result := add(result, replacementLength)
                        subject := add(subject, searchLength)
                        if searchLength {
                            // prettier-ignore
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    mstore(result, t)
                    result := add(result, 1)
                    subject := add(subject, 1)
                    // prettier-ignore
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
            }

            let resultRemainder := result
            result := add(mload(0x40), 0x20)
            let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
            // Copy the rest of the string one word at a time.
            // prettier-ignore
            for {} lt(subject, subjectEnd) {} {
                mstore(resultRemainder, mload(subject))
                resultRemainder := add(resultRemainder, 0x20)
                subject := add(subject, 0x20)
            }
            result := sub(result, 0x20)
            // Zeroize the slot after the string.
            let last := add(add(result, 0x20), k)
            mstore(last, 0)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
            // Store the length of the result.
            mstore(result, k)
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(
        string memory subject,
        string memory search,
        uint256 from
    ) internal pure returns (uint256 result) {
        assembly {
            // prettier-ignore
            for { let subjectLength := mload(subject) } 1 {} {
                if iszero(mload(search)) {
                    // `result = min(from, subjectLength)`.
                    result := xor(from, mul(xor(from, subjectLength), lt(subjectLength, from)))
                    break
                }
                let searchLength := mload(search)
                let subjectStart := add(subject, 0x20)    
                
                result := not(0) // Initialize to `NOT_FOUND`.

                subject := add(subjectStart, from)
                let subjectSearchEnd := add(sub(add(subjectStart, subjectLength), searchLength), 1)

                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(add(search, 0x20))

                // prettier-ignore
                if iszero(lt(subject, subjectSearchEnd)) { break }

                if iszero(lt(searchLength, 32)) {
                    // prettier-ignore
                    for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            if eq(keccak256(subject, searchLength), h) {
                                result := sub(subject, subjectStart)
                                break
                            }
                        }
                        subject := add(subject, 1)
                        // prettier-ignore
                        if iszero(lt(subject, subjectSearchEnd)) { break }
                    }
                    break
                }
                // prettier-ignore
                for {} 1 {} {
                    if iszero(shr(m, xor(mload(subject), s))) {
                        result := sub(subject, subjectStart)
                        break
                    }
                    subject := add(subject, 1)
                    // prettier-ignore
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search) internal pure returns (uint256 result) {
        result = indexOf(subject, search, 0);
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(
        string memory subject,
        string memory search,
        uint256 from
    ) internal pure returns (uint256 result) {
        assembly {
            // prettier-ignore
            for {} 1 {} {
                let searchLength := mload(search)
                let fromMax := sub(mload(subject), searchLength)
                if iszero(gt(fromMax, from)) {
                    from := fromMax
                }
                if iszero(mload(search)) {
                    result := from
                    break
                }
                result := not(0) // Initialize to `NOT_FOUND`.

                let subjectSearchEnd := sub(add(subject, 0x20), 1)

                subject := add(add(subject, 0x20), from)
                // prettier-ignore
                if iszero(gt(subject, subjectSearchEnd)) { break }
                // As this function is not too often used,
                // we shall simply use keccak256 for smaller bytecode size.
                // prettier-ignore
                for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                    if eq(keccak256(subject, searchLength), h) {
                        result := sub(subject, add(subjectSearchEnd, 1))
                        break
                    }
                    subject := sub(subject, 1)
                    // prettier-ignore
                    if iszero(gt(subject, subjectSearchEnd)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the index of the first location of `search` in `subject`,
    /// searching from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search) internal pure returns (uint256 result) {
        result = lastIndexOf(subject, search, uint256(int256(-1)));
    }

    /// @dev Returns whether `subject` starts with `search`.
    function startsWith(string memory subject, string memory search) internal pure returns (bool result) {
        assembly {
            let searchLength := mload(search)
            // Just using keccak256 directly is actually cheaper.
            result := and(
                iszero(gt(searchLength, mload(subject))),
                eq(keccak256(add(subject, 0x20), searchLength), keccak256(add(search, 0x20), searchLength))
            )
        }
    }

    /// @dev Returns whether `subject` ends with `search`.
    function endsWith(string memory subject, string memory search) internal pure returns (bool result) {
        assembly {
            let searchLength := mload(search)
            let subjectLength := mload(subject)
            // Whether `search` is not longer than `subject`.
            let withinRange := iszero(gt(searchLength, subjectLength))
            // Just using keccak256 directly is actually cheaper.
            result := and(
                withinRange,
                eq(
                    keccak256(
                        // `subject + 0x20 + max(subjectLength - searchLength, 0)`.
                        add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
                        searchLength
                    ),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns `subject` repeated `times`.
    function repeat(string memory subject, uint256 times) internal pure returns (string memory result) {
        assembly {
            let subjectLength := mload(subject)
            if iszero(or(iszero(times), iszero(subjectLength))) {
                subject := add(subject, 0x20)
                result := mload(0x40)
                let output := add(result, 0x20)
                // prettier-ignore
                for {} 1 {} {
                    // Copy the `subject` one word at a time.
                    // prettier-ignore
                    for { let o := 0 } 1 {} {
                        mstore(add(output, o), mload(add(subject, o)))
                        o := add(o, 0x20)
                        // prettier-ignore
                        if iszero(lt(o, subjectLength)) { break }
                    }
                    output := add(output, subjectLength)
                    times := sub(times, 1)
                    // prettier-ignore
                    if iszero(times) { break }
                }
                // Zeroize the slot after the string.
                mstore(output, 0)
                // Store the length.
                let resultLength := sub(output, add(result, 0x20))
                mstore(result, resultLength)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 63), not(31))))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function slice(
        string memory subject,
        uint256 start,
        uint256 end
    ) internal pure returns (string memory result) {
        assembly {
            let subjectLength := mload(subject)
            if iszero(gt(subjectLength, end)) {
                end := subjectLength
            }
            if iszero(gt(subjectLength, start)) {
                start := subjectLength
            }
            if lt(start, end) {
                result := mload(0x40)
                let resultLength := sub(end, start)
                mstore(result, resultLength)
                subject := add(subject, start)
                // Copy the `subject` one word at a time, backwards.
                // prettier-ignore
                for { let o := and(add(resultLength, 31), not(31)) } 1 {} {
                    mstore(add(result, o), mload(add(subject, o)))
                    o := sub(o, 0x20)
                    // prettier-ignore
                    if iszero(o) { break }
                }
                // Zeroize the slot after the string.
                mstore(add(add(result, 0x20), resultLength), 0)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 63), not(31))))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
    /// `start` is a byte offset.
    function slice(string memory subject, uint256 start) internal pure returns (string memory result) {
        result = slice(subject, start, uint256(int256(-1)));
    }

    /// @dev Returns all the indices of `search` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(string memory subject, string memory search) internal pure returns (uint256[] memory result) {
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)

            if iszero(gt(searchLength, subjectLength)) {
                subject := add(subject, 0x20)
                search := add(search, 0x20)
                result := add(mload(0x40), 0x20)

                let subjectStart := subject
                let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 32)) {
                    h := keccak256(search, searchLength)
                }
                let m := shl(3, sub(32, and(searchLength, 31)))
                let s := mload(search)
                // prettier-ignore
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of 
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                subject := add(subject, 1)
                                // prettier-ignore
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Append to `result`.
                        mstore(result, sub(subject, subjectStart))
                        result := add(result, 0x20)
                        // Advance `subject` by `searchLength`.
                        subject := add(subject, searchLength)
                        if searchLength {
                            // prettier-ignore
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    subject := add(subject, 1)
                    // prettier-ignore
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
                let resultEnd := result
                // Assign `result` to the free memory pointer.
                result := mload(0x40)
                // Store the length of `result`.
                mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
                // Allocate memory for result.
                // We allocate one more word, so this array can be recycled for {split}.
                mstore(0x40, add(resultEnd, 0x20))
            }
        }
    }

    /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
    function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) {
        uint256[] memory indices = indicesOf(subject, delimiter);
        assembly {
            if mload(indices) {
                let indexPtr := add(indices, 0x20)
                let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
                mstore(sub(indicesEnd, 0x20), mload(subject))
                mstore(indices, add(mload(indices), 1))
                let prevIndex := 0
                // prettier-ignore
                for {} 1 {} {
                    let index := mload(indexPtr)
                    mstore(indexPtr, 0x60)                        
                    if iszero(eq(index, prevIndex)) {
                        let element := mload(0x40)
                        let elementLength := sub(index, prevIndex)
                        mstore(element, elementLength)
                        // Copy the `subject` one word at a time, backwards.
                        // prettier-ignore
                        for { let o := and(add(elementLength, 31), not(31)) } 1 {} {
                            mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                            o := sub(o, 0x20)
                            // prettier-ignore
                            if iszero(o) { break }
                        }
                        // Zeroize the slot after the string.
                        mstore(add(add(element, 0x20), elementLength), 0)
                        // Allocate memory for the length and the bytes,
                        // rounded up to a multiple of 32.
                        mstore(0x40, add(element, and(add(elementLength, 63), not(31))))
                        // Store the `element` into the array.
                        mstore(indexPtr, element)                        
                    }
                    prevIndex := add(index, mload(delimiter))
                    indexPtr := add(indexPtr, 0x20)
                    // prettier-ignore
                    if iszero(lt(indexPtr, indicesEnd)) { break }
                }
                result := indices
                if iszero(mload(delimiter)) {
                    result := add(indices, 0x20)
                    mstore(result, sub(mload(indices), 2))
                }
            }
        }
    }

    /// @dev Returns a concatenated string of `a` and `b`.
    /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
    function concat(string memory a, string memory b) internal pure returns (string memory result) {
        assembly {
            result := mload(0x40)
            let aLength := mload(a)
            // Copy `a` one word at a time, backwards.
            // prettier-ignore
            for { let o := and(add(mload(a), 32), not(31)) } 1 {} {
                mstore(add(result, o), mload(add(a, o)))
                o := sub(o, 0x20)
                // prettier-ignore
                if iszero(o) { break }
            }
            let bLength := mload(b)
            let output := add(result, mload(a))
            // Copy `b` one word at a time, backwards.
            // prettier-ignore
            for { let o := and(add(bLength, 32), not(31)) } 1 {} {
                mstore(add(output, o), mload(add(b, o)))
                o := sub(o, 0x20)
                // prettier-ignore
                if iszero(o) { break }
            }
            let totalLength := add(aLength, bLength)
            let last := add(add(result, 0x20), totalLength)
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Stores the length.
            mstore(result, totalLength)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 31), not(31)))
        }
    }

    /// @dev Packs a single string with its length into a single word.
    /// Returns `bytes32(0)` if the length is zero or greater than 31.
    function packOne(string memory a) internal pure returns (bytes32 result) {
        assembly {
            // We don't need to zero right pad the string,
            // since this is our own custom non-standard packing scheme.
            result := mul(
                // Load the length and the bytes.
                mload(add(a, 0x1f)),
                // `length != 0 && length < 32`. Abuses underflow.
                // Assumes that the length is valid and within the block gas limit.
                lt(sub(mload(a), 1), 0x1f)
            )
        }
    }

    /// @dev Unpacks a string packed using {packOne}.
    /// Returns the empty string if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packOne}, the output behaviour is undefined.
    function unpackOne(bytes32 packed) internal pure returns (string memory result) {
        assembly {
            // Grab the free memory pointer.
            result := mload(0x40)
            // Allocate 2 words (1 for the length, 1 for the bytes).
            mstore(0x40, add(result, 0x40))
            // Zeroize the length slot.
            mstore(result, 0)
            // Store the length and bytes.
            mstore(add(result, 0x1f), packed)
            // Right pad with zeroes.
            mstore(add(add(result, 0x20), mload(result)), 0)
        }
    }

    /// @dev Packs two strings with their lengths into a single word.
    /// Returns `bytes32(0)` if combined length is zero or greater than 30.
    function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
        assembly {
            let aLength := mload(a)
            // We don't need to zero right pad the strings,
            // since this is our own custom non-standard packing scheme.
            result := mul(
                // Load the length and the bytes of `a` and `b`.
                or(shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), mload(sub(add(b, 0x1e), aLength))),
                // `totalLength != 0 && totalLength < 31`. Abuses underflow.
                // Assumes that the lengths are valid and within the block gas limit.
                lt(sub(add(aLength, mload(b)), 1), 0x1e)
            )
        }
    }

    /// @dev Unpacks strings packed using {packTwo}.
    /// Returns the empty strings if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packTwo}, the output behaviour is undefined.
    function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) {
        assembly {
            // Grab the free memory pointer.
            resultA := mload(0x40)
            resultB := add(resultA, 0x40)
            // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
            mstore(0x40, add(resultB, 0x40))
            // Zeroize the length slots.
            mstore(resultA, 0)
            mstore(resultB, 0)
            // Store the lengths and bytes.
            mstore(add(resultA, 0x1f), packed)
            mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
            // Right pad with zeroes.
            mstore(add(add(resultA, 0x20), mload(resultA)), 0)
            mstore(add(add(resultB, 0x20), mload(resultB)), 0)
        }
    }

    /// @dev Directly returns `a` without copying.
    function directReturn(string memory a) internal pure {
        assembly {
            // Right pad with zeroes. Just in case the string is produced
            // by a method that doesn't zero right pad.
            mstore(add(add(a, 0x20), mload(a)), 0)
            // Store the return offset.
            // Assumes that the string does not start from the scratch space.
            mstore(sub(a, 0x20), 0x20)
            // End the transaction, returning the string.
            return(sub(a, 0x20), add(mload(a), 0x40))
        }
    }
}

File 8 of 20 : Context.sol
// 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;
    }
}

File 9 of 20 : Initializable.sol
// 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();
        _;
    }
}

File 10 of 20 : ERC721M.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {EIP712PermitUDS} from "UDS/auth/EIP712PermitUDS.sol";
import {UserDataOps, TokenDataOps} from "./ERC721MLibrary.sol";

// ------------- storage

struct ERC721MStorage {
    string name;
    string symbol;
    uint256 totalSupply;
    mapping(address => uint256) userData;
    mapping(uint256 => uint256) tokenData;
    mapping(uint256 => address) getApproved;
    mapping(address => mapping(address => bool)) isApprovedForAll;
}

bytes32 constant DIAMOND_STORAGE_ERC721M_LOCKABLE = keccak256("diamond.storage.erc721m.lockable");

function s() pure returns (ERC721MStorage storage diamondStorage) {
    bytes32 slot = DIAMOND_STORAGE_ERC721M_LOCKABLE;
    assembly { diamondStorage.slot := slot } // prettier-ignore
}

// ------------- errors

error IncorrectOwner();
error TokenIdUnlocked();
error NonexistentToken();
error MintZeroQuantity();
error MintToZeroAddress();
error TransferFromInvalidTo();
error TransferToZeroAddress();
error CallerNotOwnerNorApproved();
error TransferFromIncorrectOwner();
error TransferToNonERC721Receiver();

/// @title ERC721M (Integrated Token Locking)
/// @author phaze (https://github.com/0xPhaze/ERC721M)
/// @author modified from ERC721A (https://github.com/chiru-labs/ERC721A)
/// @author modified from Solmate (https://github.com/Rari-Capital/solmate)
/// @notice Integrates EIP712Permit
abstract contract ERC721M is EIP712PermitUDS {
    using UserDataOps for uint256;
    using TokenDataOps for uint256;

    event Transfer(address indexed from, address indexed to, uint256 indexed id);
    event Approval(address indexed owner, address indexed spender, uint256 indexed id);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    uint256 constant startingIndex = 1;

    constructor(string memory name_, string memory symbol_) {
        __ERC721_init(name_, symbol_);
    }

    /* ------------- init ------------- */

    function __ERC721_init(string memory name_, string memory symbol_) internal {
        s().name = name_;
        s().symbol = symbol_;
    }

    /* ------------- virtual ------------- */

    function tokenURI(uint256 id) external 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 balanceOf(address user) public view virtual returns (uint256) {
        return s().userData[user].balance();
    }

    function getApproved(uint256 id) external view virtual returns (address) {
        return s().getApproved[id];
    }

    function isApprovedForAll(address owner, address spender) external view virtual returns (bool) {
        return s().isApprovedForAll[owner][spender];
    }

    function ownerOf(uint256 id) public view virtual returns (address) {
        return _tokenDataOf(id).owner();
    }

    function totalSupply() public view virtual returns (uint256) {
        return s().totalSupply;
    }

    function getAux(uint256 id) public view returns (uint256) {
        return _tokenDataOf(id).aux();
    }

    function getLockStart(uint256 id) public view returns (uint256) {
        return _tokenDataOf(id).tokenLockStart();
    }

    function numMinted(address user) public view virtual returns (uint256) {
        return s().userData[user].numMinted();
    }

    function numLocked(address user) public view virtual returns (uint256) {
        return s().userData[user].numLocked();
    }

    function getLockStart(address user) public view virtual returns (uint256) {
        return s().userData[user].userLockStart();
    }

    function trueOwnerOf(uint256 id) public view virtual returns (address) {
        return _tokenDataOf(id).trueOwner();
    }

    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 spender, uint256 id) public virtual {
        address owner = _tokenDataOf(id).owner();

        if (msg.sender != owner && !s().isApprovedForAll[owner][msg.sender]) revert CallerNotOwnerNorApproved();

        s().getApproved[id] = spender;

        emit Approval(owner, spender, id);
    }

    function setApprovalForAll(address operator, bool approved) public virtual {
        s().isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function _isApprovedOrOwner(address from, uint256 id) private view returns (bool) {
        return (msg.sender == from || s().isApprovedForAll[from][msg.sender] || s().getApproved[id] == msg.sender);
    }

    function transferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        if (to == address(this)) revert TransferFromInvalidTo();
        if (to == address(0)) revert TransferToZeroAddress();

        uint256 tokenData = _tokenDataOf(id);

        bool isApprovedOrOwner = (msg.sender == from ||
            s().isApprovedForAll[from][msg.sender] ||
            s().getApproved[id] == msg.sender);

        if (!isApprovedOrOwner) revert CallerNotOwnerNorApproved();
        if (tokenData.owner() != from) revert TransferFromIncorrectOwner();

        delete s().getApproved[id];

        unchecked {
            _ensureTokenDataSet(id + 1, tokenData);
        }

        s().tokenData[id] = tokenData.setOwner(to).flagNextTokenDataSet();

        s().userData[to] = s().userData[to].increaseBalance(1);
        s().userData[from] = s().userData[from].decreaseBalance(1);

        emit Transfer(from, to, id);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        safeTransferFrom(from, to, id, "");
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        bytes memory data
    ) public virtual {
        transferFrom(from, to, id);
        if (
            to.code.length != 0 &&
            IERC721Receiver(to).onERC721Received(msg.sender, from, id, data) !=
            IERC721Receiver(to).onERC721Received.selector
        ) revert TransferToNonERC721Receiver();
    }

    // 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 _exists(uint256 id) internal view virtual returns (bool) {
        return startingIndex <= id && id < _nextTokenId();
    }

    function _nextTokenId() internal view virtual returns (uint256) {
        return startingIndex + totalSupply();
    }

    function _increaseTotalSupply(uint256 amount) internal virtual {
        if (amount != 0) s().totalSupply = _nextTokenId() + amount - 1;
    }

    function _tokenDataOf(uint256 id) internal view virtual returns (uint256 out) {
        if (!_exists(id)) revert NonexistentToken();

        unchecked {
            uint256 tokenData;

            for (uint256 curr = id; ; curr--) {
                tokenData = s().tokenData[curr];

                if (tokenData != 0) return tokenData;
            }
        }
    }

    function _ensureTokenDataSet(uint256 id, uint256 tokenData) internal virtual {
        if (!tokenData.nextTokenDataSet() && s().tokenData[id] == 0 && _exists(id)) s().tokenData[id] = tokenData;
    }

    function _mint(address to, uint256 quantity) internal virtual {
        _mintAndLock(to, quantity, false, 0);
    }

    function _mint(
        address to,
        uint256 quantity,
        uint48 auxData
    ) internal virtual {
        _mintAndLock(to, quantity, false, auxData);
    }

    function _mintAndLock(
        address to,
        uint256 quantity,
        bool lock
    ) internal virtual {
        _mintAndLock(to, quantity, lock, 0);
    }

    function _mintAndLock(
        address to,
        uint256 quantity,
        bool lock,
        uint48 auxData
    ) internal virtual {
        unchecked {
            if (quantity == 0) revert MintZeroQuantity();
            if (to == address(0)) revert MintToZeroAddress();

            uint256 startTokenId = _nextTokenId();
            uint256 tokenData = uint256(uint160(to)).setAux(auxData);
            uint256 userData = s().userData[to];

            // don't have to care about next token data if only minting one
            if (quantity == 1) tokenData = tokenData.flagNextTokenDataSet();
            if (lock) {
                tokenData = tokenData.setConsecutiveLocked().lock();

                userData = userData.increaseNumLocked(quantity).setUserLockStart(block.timestamp);

                for (uint256 i; i < quantity; ++i) {
                    emit Transfer(address(0), to, startTokenId + i);
                    emit Transfer(to, address(this), startTokenId + i);
                }
            } else {
                for (uint256 i; i < quantity; ++i) {
                    emit Transfer(address(0), to, startTokenId + i);
                }
            }

            s().userData[to] = userData.increaseNumMinted(quantity).increaseBalance(quantity);
            s().tokenData[startTokenId] = tokenData;

            _increaseTotalSupply(quantity);
        }
    }

    function _setAux(uint256 id, uint48 aux) internal virtual {
        uint256 tokenData = _tokenDataOf(id);

        unchecked {
            _ensureTokenDataSet(id + 1, tokenData);
        }

        s().tokenData[id] = tokenData.setAux(aux);
    }

    function _lock(address from, uint256 id) internal virtual {
        uint256 tokenData = _tokenDataOf(id);

        bool isApprovedOrOwner = (msg.sender == from ||
            s().isApprovedForAll[from][msg.sender] ||
            s().getApproved[id] == msg.sender);

        if (!isApprovedOrOwner) revert CallerNotOwnerNorApproved();
        if (tokenData.owner() != from) revert IncorrectOwner();

        delete s().getApproved[id];

        unchecked {
            _ensureTokenDataSet(id + 1, tokenData);
        }

        s().tokenData[id] = tokenData.lock().unsetConsecutiveLocked().flagNextTokenDataSet();
        s().userData[from] = s().userData[from].increaseNumLocked(1).setUserLockStart(block.timestamp);

        emit Transfer(from, address(this), id);
    }

    function _unlock(address from, uint256 id) internal virtual {
        uint256 tokenData = _tokenDataOf(id);

        bool isApprovedOrOwner = (msg.sender == from ||
            s().isApprovedForAll[from][msg.sender] ||
            s().getApproved[id] == msg.sender);

        if (!isApprovedOrOwner) revert CallerNotOwnerNorApproved();
        if (!tokenData.locked()) revert TokenIdUnlocked();
        if (tokenData.trueOwner() != from) revert IncorrectOwner();

        // if isConsecutiveLocked flag is set, we need to make sure that next tokenData is set
        // because tokenData in this case is implicit and needs to carry over
        if (tokenData.isConsecutiveLocked()) {
            unchecked {
                _ensureTokenDataSet(id + 1, tokenData);

                tokenData = tokenData.unsetConsecutiveLocked().flagNextTokenDataSet();
            }
        }

        s().tokenData[id] = tokenData.unlock();
        s().userData[from] = s().userData[from].decreaseNumLocked(1).setUserLockStart(block.timestamp);

        emit Transfer(address(this), from, id);
    }
}

interface IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 id,
        bytes calldata data
    ) external returns (bytes4);
}

File 11 of 20 : ERC721MQuery.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../ERC721MLibrary.sol";
import {ERC721M, s} from "../ERC721M.sol";

/// @title ERC721M Query Extension
/// @author phaze (https://github.com/0xPhaze/ERC721M)
abstract contract ERC721MQuery is ERC721M {
    using UserDataOps for uint256;
    using TokenDataOps for uint256;

    /* ------------- O(n) read-only ------------- */

    function getOwnedIds(address user) external view returns (uint256[] memory) {
        return utils.getOwnedIds(s().tokenData, user, startingIndex, totalSupply());
    }

    function getLockedIds(address user) external view returns (uint256[] memory) {
        return utils.getLockedIds(s().tokenData, user, startingIndex, totalSupply());
    }

    function getUnlockedIds(address user) external view returns (uint256[] memory) {
        return utils.getUnlockedIds(s().tokenData, user, startingIndex, totalSupply());
    }

    function totalNumLocked() external view returns (uint256) {
        uint256 data;
        uint256 count;
        uint256 endIndex = _nextTokenId();
        uint256 currentData;

        unchecked {
            for (uint256 i = startingIndex; i < endIndex; ++i) {
                data = s().tokenData[i];
                if (data != 0) currentData = data;
                if (currentData.locked()) ++count;
            }
        }

        return count;
    }
}

/// @title ERC721M Query Utils
/// @author phaze (https://github.com/0xPhaze/ERC721M)
library utils {
    using TokenDataOps for uint256;

    function getOwnedIds(
        mapping(uint256 => uint256) storage tokenDataOf,
        address user,
        uint256 start,
        uint256 collectionSize
    ) internal view returns (uint256[] memory ids) {
        uint256 memPtr;

        assembly {
            ids := mload(0x40)
            memPtr := add(ids, 0x20)
        }

        unchecked {
            uint256 data;
            uint256 currentData;
            uint256 end = collectionSize + start;
            for (uint256 id = start; id < end; ++id) {
                data = tokenDataOf[id];
                if (data != 0) currentData = data;
                if (user == address(uint160(currentData))) {
                    assembly {
                        mstore(memPtr, id)
                        memPtr := add(memPtr, 0x20)
                    }
                }
            }
        }

        assembly {
            mstore(ids, shr(5, sub(sub(memPtr, ids), 0x20)))
            mstore(0x40, memPtr)
        }
    }

    function getLockedIds(
        mapping(uint256 => uint256) storage tokenDataOf,
        address user,
        uint256 start,
        uint256 collectionSize
    ) internal view returns (uint256[] memory ids) {
        uint256 memPtr;

        assembly {
            ids := mload(0x40)
            memPtr := add(ids, 0x20)
        }

        unchecked {
            uint256 data;
            uint256 currentData;
            uint256 end = collectionSize + start;
            for (uint256 id = start; id < end; ++id) {
                data = tokenDataOf[id];
                if (data != 0) currentData = data;
                if (user == address(uint160(currentData)) && currentData.locked()) {
                    assembly {
                        mstore(memPtr, id)
                        memPtr := add(memPtr, 0x20)
                    }
                }
            }
        }

        assembly {
            mstore(ids, shr(5, sub(sub(memPtr, ids), 0x20)))
            mstore(0x40, memPtr)
        }
    }

    function getUnlockedIds(
        mapping(uint256 => uint256) storage tokenDataOf,
        address user,
        uint256 start,
        uint256 collectionSize
    ) internal view returns (uint256[] memory ids) {
        uint256 memPtr;

        assembly {
            ids := mload(0x40)
            memPtr := add(ids, 0x20)
        }

        unchecked {
            uint256 data;
            uint256 currentData;
            uint256 end = collectionSize + start;
            for (uint256 id = start; id < end; ++id) {
                data = tokenDataOf[id];
                if (data != 0) currentData = data;
                if (user == address(uint160(currentData)) && !currentData.locked()) {
                    assembly {
                        mstore(memPtr, id)
                        memPtr := add(memPtr, 0x20)
                    }
                }
            }
        }

        assembly {
            mstore(ids, shr(5, sub(sub(memPtr, ids), 0x20)))
            mstore(0x40, memPtr)
        }
    }
}

File 12 of 20 : FxERC721Root.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {FxBaseRootTunnel} from "./base/FxBaseRootTunnel.sol";

bytes4 constant REGISTER_ERC721_IDS_SELECTOR = bytes4(keccak256("registerERC721IdsWithChild(address,uint256[])"));
bytes4 constant DEREGISTER_ERC721_IDS_SELECTOR = bytes4(keccak256("deregisterERC721IdsWithChild(uint256[])"));

/// @title ERC721 FxRootTunnel
/// @author phaze (https://github.com/0xPhaze/fx-contracts)
abstract contract FxERC721Root is FxBaseRootTunnel {
    constructor(address checkpointManager, address fxRoot) FxBaseRootTunnel(checkpointManager, fxRoot) {}

    /* ------------- virtual ------------- */

    function _authorizeTunnelController() internal virtual override;

    /* ------------- internal ------------- */

    function _registerERC721IdsWithChild(address to, uint256[] calldata ids) internal virtual {
        _sendMessageToChild(abi.encodeWithSelector(REGISTER_ERC721_IDS_SELECTOR, to, ids));
    }

    function _registerERC721IdsWithChildMem(address to, uint256[] memory ids) internal virtual {
        _sendMessageToChild(abi.encodeWithSelector(REGISTER_ERC721_IDS_SELECTOR, to, ids));
    }
}

File 13 of 20 : EIP712PermitUDS.sol
// 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();
        }
    }
}

File 14 of 20 : ERC1967Proxy.sol
// 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);
}

File 15 of 20 : ERC721MLibrary.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Library used for bitmap manipulation for ERC721M
/// @author phaze (https://github.com/0xPhaze/ERC721M)
library UserDataOps {
    /* ------------- balance: [0, 20) ------------- */

    function balance(uint256 userData) internal pure returns (uint256) {
        return userData & 0xFFFFF;
    }

    function increaseBalance(uint256 userData, uint256 amount) internal pure returns (uint256) {
        unchecked {
            return userData + amount;
        }
    }

    function decreaseBalance(uint256 userData, uint256 amount) internal pure returns (uint256) {
        unchecked {
            return userData - amount;
        }
    }

    /* ------------- numMinted: [20, 40) ------------- */

    function numMinted(uint256 userData) internal pure returns (uint256) {
        return (userData >> 20) & 0xFFFFF;
    }

    function increaseNumMinted(uint256 userData, uint256 amount) internal pure returns (uint256) {
        unchecked {
            return userData + (amount << 20);
        }
    }

    /* ------------- numLocked: [40, 60) ------------- */

    function numLocked(uint256 userData) internal pure returns (uint256) {
        return (userData >> 40) & 0xFFFFF;
    }

    function increaseNumLocked(uint256 userData, uint256 amount) internal pure returns (uint256) {
        unchecked {
            return userData + (amount << 40);
        }
    }

    function decreaseNumLocked(uint256 userData, uint256 amount) internal pure returns (uint256) {
        unchecked {
            return userData - (amount << 40);
        }
    }

    /* ------------- lockStart: [60, 100) ------------- */

    function userLockStart(uint256 userData) internal pure returns (uint256) {
        return (userData >> 60) & 0xFFFFFFFFFF;
    }

    function setUserLockStart(uint256 userData, uint256 timestamp) internal pure returns (uint256) {
        return (userData & ~uint256(0xFFFFFFFFFF << 60)) | (timestamp << 60);
    }

    // /* ------------- aux: [100, 256) ------------- */

    // function aux(uint256 userData) internal pure returns (uint256) {
    //     return (userData >> 100) & 0xFFFFFFFFFF;
    // }

    // function setAux(uint256 userData, uint256 aux_) internal pure returns (uint256) {
    //     return (userData & ~((uint256(1) << 100) - 1)) | (aux_ << 100);
    // }
}

library TokenDataOps {
    /// @dev Big question whether copy should transfer over data, such as,
    ///      aux data and timestamps
    function copy(uint256 tokenData) internal pure returns (uint256) {
        return tokenData;
    }

    // return tokenData & ((uint256(1) << (160 + (((tokenData >> 160) & 1) << 1))) - 1);
    /// ^ equivalent code:
    // function copy2(uint256 tokenData) internal pure returns (uint256) {
    //     uint256 copiedData = uint160(tokenData);
    //     if (isConsecutiveLocked(tokenData)) {
    //         copiedData = setConsecutiveLocked(copiedData);
    //         if (locked(tokenData)) copiedData = lock(copiedData);
    //     }
    //     return copiedData;
    // }

    /* ------------- owner: [0, 160) ------------- */

    function owner(uint256 tokenData) internal view returns (address) {
        return locked(tokenData) ? address(this) : trueOwner(tokenData);
    }

    function setOwner(uint256 tokenData, address owner_) internal pure returns (uint256) {
        return (tokenData & 0xFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000) | uint160(owner_);
    }

    function trueOwner(uint256 tokenData) internal pure returns (address) {
        return address(uint160(tokenData));
    }

    /* ------------- consecutiveLock: [160, 161) ------------- */

    function isConsecutiveLocked(uint256 tokenData) internal pure returns (bool) {
        return ((tokenData >> 160) & uint256(1)) != 0;
    }

    function setConsecutiveLocked(uint256 tokenData) internal pure returns (uint256) {
        return tokenData | (uint256(1) << 160);
    }

    function unsetConsecutiveLocked(uint256 tokenData) internal pure returns (uint256) {
        return tokenData & ~(uint256(1) << 160);
    }

    /* ------------- locked: [161, 162) ------------- */

    function locked(uint256 tokenData) internal pure returns (bool) {
        return ((tokenData >> 161) & uint256(1)) != 0; // Note: this is not masked and can carry over when calling 'ownerOf'
    }

    function lock(uint256 tokenData) internal view returns (uint256) {
        return setTokenLockStart(tokenData, block.timestamp) | (uint256(1) << 161);
    }

    function unlock(uint256 tokenData) internal view returns (uint256) {
        return setTokenLockStart(tokenData, block.timestamp) & ~(uint256(1) << 161);
    }

    /* ------------- nextTokenDataSet: [162, 163) ------------- */

    function nextTokenDataSet(uint256 tokenData) internal pure returns (bool) {
        return ((tokenData >> 162) & uint256(1)) != 0;
    }

    function flagNextTokenDataSet(uint256 tokenData) internal pure returns (uint256) {
        return tokenData | (uint256(1) << 162); // nextTokenDatatSet flag (don't repeat the read/write)
    }

    /* ------------- lockStart: [168, 208) ------------- */

    function tokenLockStart(uint256 tokenData) internal pure returns (uint256) {
        return (tokenData >> 168) & 0xFFFFFFFFFF;
    }

    function setTokenLockStart(uint256 tokenData, uint256 timestamp) internal pure returns (uint256) {
        return (tokenData & ~uint256(0xFFFFFFFFFF << 168)) | (timestamp << 168);
    }

    /* ------------- aux: [208, 256) ------------- */

    function aux(uint256 tokenData) internal pure returns (uint256) {
        return tokenData >> 208;
    }

    function setAux(uint256 tokenData, uint256 auxData) internal pure returns (uint256) {
        return (tokenData & ~uint256(0xFFFFFFFFFFFF << 208)) | (auxData << 208);
    }
}

File 16 of 20 : FxBaseRootTunnel.sol
// 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 InvalidSelector();
error InvalidReceiptProof();
error InvalidFxChildTunnel();
error ExitAlreadyProcessed();

abstract contract FxBaseRootTunnel {
    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_SELECTOR =
        0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036;

    IFxStateSender public immutable fxRoot;
    ICheckpointManager public immutable checkpointManager;

    constructor(address checkpointManager_, address fxRoot_) {
        checkpointManager = ICheckpointManager(checkpointManager_);
        fxRoot = IFxStateSender(fxRoot_);
    }

    /* ------------- virtual ------------- */

    function _authorizeTunnelController() internal virtual;

    /* ------------- view ------------- */

    function fxChildTunnel() public view virtual returns (address) {
        return s().fxChildTunnel;
    }

    function processedExits(bytes32 exitHash) public view virtual returns (bool) {
        return s().processedExits[exitHash];
    }

    function setFxChildTunnel(address fxChildTunnel_) public virtual {
        _authorizeTunnelController();

        s().fxChildTunnel = fxChildTunnel_;
    }

    /* ------------- internal ------------- */

    function _sendMessageToChild(bytes memory message) internal virtual {
        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_SELECTOR) revert InvalidSelector();

        // received message data
        bytes memory message = abi.decode(log.getData(), (bytes)); // event decodes params again, so decoding bytes to get message

        return message;
    }
}

File 17 of 20 : Merkle.sol
// 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;
    }
}

File 18 of 20 : RLPReader.sol
// 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))
        }
    }
}

File 19 of 20 : ExitPayloadReader.sol
// 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];
    }
}

File 20 of 20 : MerklePatriciaProof.sol
// 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);
    }
}

Settings
{
  "remappings": [
    "/=src/",
    "ERC721M/=lib/ERC721M/src/",
    "UDS/=lib/UDS/src/",
    "ds-test/=lib/ERC721M/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "futils/=lib/futils/src/",
    "fx-contracts/=lib/fx-contracts/src/",
    "fx-portal/=lib/ERC721M/lib/fx-portal/contracts/",
    "solady/=lib/solady/src/",
    "solmate/=lib/solmate/src/",
    "upgrade-scripts/=lib/upgrade-scripts/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 100000
  },
  "metadata": {
    "bytecodeHash": "none"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"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":"CallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ContractCallNotAllowed","type":"error"},{"inputs":[],"name":"DeadlineExpired","type":"error"},{"inputs":[],"name":"ExceedsLimit","type":"error"},{"inputs":[],"name":"FxChildUnset","type":"error"},{"inputs":[],"name":"IncorrectOwner","type":"error"},{"inputs":[],"name":"IncorrectValue","type":"error"},{"inputs":[],"name":"InvalidPriceUnits","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"MaxSupplyLocked","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"NonexistentToken","type":"error"},{"inputs":[],"name":"PublicSaleNotActive","type":"error"},{"inputs":[],"name":"TimelockActive","type":"error"},{"inputs":[],"name":"TokenIdUnlocked","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferFromInvalidTo","type":"error"},{"inputs":[],"name":"TransferToNonERC721Receiver","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"WhitelistNotActive","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"FirstLegendaryRaffleEntered","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":[],"name":"SaleStateUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"SecondLegendaryRaffleEntered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"BRIDGE_RAFFLE_LOCK_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PER_WALLET","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PURCHASE_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bool","name":"locked","type":"bool"}],"name":"airdrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpointManager","outputs":[{"internalType":"contract ICheckpointManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fxChildTunnel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fxRoot","outputs":[{"internalType":"contract IFxStateSender","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"gangOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getAux","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getLockStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getLockStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getLockedIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getOwnedIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUnlockedIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"lockAndTransmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupplyLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bool","name":"lock","type":"bool"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mintStart","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"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":[{"internalType":"address","name":"user","type":"address"}],"name":"numLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"numMinted","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":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"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":"publicPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20UDS","name":"token","type":"address"}],"name":"recoverToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fxChildTunnel_","type":"address"}],"name":"setFxChildTunnel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"chunkIndices","type":"uint256[]"},{"internalType":"uint256[]","name":"chunks","type":"uint256[]"}],"name":"setGangs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"value","type":"uint16"}],"name":"setMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"time","type":"uint32"}],"name":"setMintStart","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"postFix","type":"string"}],"name":"setPostFixURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setPublicPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"setUnrevealedURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setWhitelistPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supply","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalNumLocked","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":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"trueOwnerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"unlockAndTransmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bool","name":"lock","type":"bool"},{"internalType":"uint256","name":"limit","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"whitelistMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"whitelistPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

30608052610140604052600561010090815264173539b7b760d91b610120526004906200002d90826200036b565b5060405180606001604052806036815260200162005764603691396005906200005790826200036b565b503480156200006557600080fd5b506040516200579a3803806200579a833981016040819052620000889162000454565b604080518082018252601181527047616e67737461204d696365204369747960781b60208083019190915282518084019093526003835262474d4360e81b908301526001600160a01b0380851660c052831660a0529083838383620000ee8282620001b4565b505050505050620001046200021460201b60201c565b600280546b01000000000000000000000033027fff0000000000000000000000000000000000000000ffffffffffff0000ffffff90911617641a0a0000001790554260e0526200015b66ae153d89fe800062000267565b6002805460ff9290921669010000000000000000000260ff60481b1990921691909117905562000192668a8e4b1a3d800062000267565b6002600a6101000a81548160ff021916908360ff16021790555050506200048c565b7facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a95599620001e183826200036b565b507facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559a6200020f82826200036b565b505050565b303b15620002345760405162dc149f60e41b815260040160405180910390fd5b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea3380546001600160a01b03191633179055565b60008066038d7ea4c680008306156200029357604051632411db2d60e11b815260040160405180910390fd5b5066038d7ea4c68000820460ff811115620002c157604051632411db2d60e11b815260040160405180910390fd5b92915050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620002f257607f821691505b6020821081036200031357634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200020f57600081815260208120601f850160051c81016020861015620003425750805b601f850160051c820191505b8181101562000363578281556001016200034e565b505050505050565b81516001600160401b03811115620003875762000387620002c7565b6200039f81620003988454620002dd565b8462000319565b602080601f831160018114620003d75760008415620003be5750858301515b600019600386901b1c1916600185901b17855562000363565b600085815260208120601f198616915b828110156200040857888601518255948401946001909101908401620003e7565b5085821015620004275787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b80516001600160a01b03811681146200044f57600080fd5b919050565b600080604083850312156200046857600080fd5b620004738362000437565b9150620004836020840162000437565b90509250929050565b60805160a05160c05160e051615293620004d1600039600081816111b5015261171901526000610b17015260008181610c0201526145910152600050506152936000f3fe6080604052600436106103ad5760003560e01c806388486f86116101e7578063bb58131f1161010d578063de9b771f116100a0578063f2fde38b1161006f578063f2fde38b14610ccc578063fc1a1c3614610cec578063fca76c2614610d1c578063fe2c7fee14610d3157600080fd5b8063de9b771f14610bf0578063e7f371e614610c24578063e985e9c514610c37578063eec1cbb714610cac57600080fd5b8063c90af357116100dc578063c90af35714610b79578063ca9228a114610b99578063d5abeb0114610bb9578063d75e611014610bdb57600080fd5b8063bb58131f14610ae5578063c0857ba014610b05578063c627525514610b39578063c87b56dd14610b5957600080fd5b80639be65a6011610185578063a945bf8011610154578063a945bf8014610a56578063aea4e49e14610a85578063af436af314610aa5578063b88d4fde14610ac557600080fd5b80639be65a60146109dc578063a22cb465146109fc578063a742530814610a1c578063a8d0466c14610a3c57600080fd5b806395e1a8a5116101c157806395e1a8a514610970578063972c4928146109875780639871e2221461099c5780639888eb1b146109bc57600080fd5b806388486f86146109265780638da5cb5b1461094657806395d89b411461095b57600080fd5b80633644e515116102d757806367f68fac1161026a57806370a082311161023957806370a082311461086f578063717d57d31461088f5780637ecebe00146108af5780638456cb591461091157600080fd5b806367f68fac146107ef5780636c19e783146108025780636e8ba803146108225780636e9010381461084257600080fd5b806355f804b3116102a657806355f804b314610740578063607f2d42146107605780636352211e146107af57806364c6cbbd146107cf57600080fd5b80633644e515146106365780633ccfd60b146106eb57806342842e0e1461070057806348613c281461072057600080fd5b806311836f321161034f57806323b872dd1161031e57806323b872dd146105a6578063255e4685146105c6578063257f84ae14610601578063306c76791461061657600080fd5b806311836f321461051a57806318160ddd146105485780631fb161b81461056657806320fc7eb21461058657600080fd5b806306fdde031161038b57806306fdde031461043c578063081812fc1461045e578063095ea7b3146104e55780630f2cdd6c1461050557600080fd5b806301ffc9a7146103b2578063047fc9aa146103e757806306421c2f1461041a575b600080fd5b3480156103be57600080fd5b506103d26103cd3660046146fb565b610d51565b60405190151581526020015b60405180910390f35b3480156103f357600080fd5b5060025461040790610100900461ffff1681565b60405161ffff90911681526020016103de565b34801561042657600080fd5b5061043a610435366004614718565b610e36565b005b34801561044857600080fd5b50610451610f35565b6040516103de91906147b2565b34801561046a57600080fd5b506104c06104793660046147c5565b60009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103de565b3480156104f157600080fd5b5061043a610500366004614800565b610fe6565b34801561051157600080fd5b50610407601481565b34801561052657600080fd5b5061053a6105353660046147c5565b611148565b6040519081526020016103de565b34801561055457600080fd5b50600254610100900461ffff1661053a565b34801561057257600080fd5b5061043a610581366004614878565b61116e565b34801561059257600080fd5b5061053a6105a13660046148cd565b61125e565b3480156105b257600080fd5b5061043a6105c13660046148ea565b6112b0565b3480156105d257600080fd5b506002546105ec9065010000000000900463ffffffff1681565b60405163ffffffff90911681526020016103de565b34801561060d57600080fd5b5061053a611655565b34801561062257600080fd5b5061043a610631366004614878565b6116d9565b34801561064257600080fd5b5061053a604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b3480156106f757600080fd5b5061043a6117af565b34801561070c57600080fd5b5061043a61071b3660046148ea565b6118bd565b34801561072c57600080fd5b5061043a61073b36600461492b565b6118d8565b34801561074c57600080fd5b5061043a61075b3660046149d8565b6119a2565b34801561076c57600080fd5b506103d261077b3660046147c5565b60009081527f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9485602052604090205460ff1690565b3480156107bb57600080fd5b506104c06107ca3660046147c5565b611a35565b3480156107db57600080fd5b5061043a6107ea366004614a1a565b611a43565b61043a6107fd366004614a94565b611b31565b34801561080e57600080fd5b5061043a61081d3660046148cd565b611d12565b34801561082e57600080fd5b5061053a61083d3660046147c5565b611dee565b34801561084e57600080fd5b5061086261085d3660046148cd565b611e09565b6040516103de9190614aff565b34801561087b57600080fd5b5061053a61088a3660046148cd565b611e4d565b34801561089b57600080fd5b5061043a6108aa3660046147c5565b611e9c565b3480156108bb57600080fd5b5061053a6108ca3660046148cd565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b81602052604090205490565b34801561091d57600080fd5b5061043a611f48565b34801561093257600080fd5b506104c06109413660046147c5565b611ff8565b34801561095257600080fd5b506104c0612009565b34801561096757600080fd5b50610451612049565b34801561097c57600080fd5b5061053a6201518081565b34801561099357600080fd5b506104c061207a565b3480156109a857600080fd5b506108626109b73660046148cd565b6120a2565b3480156109c857600080fd5b5061053a6109d73660046148cd565b6120e6565b3480156109e857600080fd5b5061043a6109f73660046148cd565b612138565b348015610a0857600080fd5b5061043a610a17366004614b12565b6122e9565b348015610a2857600080fd5b5061053a610a373660046148cd565b61239f565b348015610a4857600080fd5b506002546103d29060ff1681565b348015610a6257600080fd5b5060025466038d7ea4c68000690100000000000000000090910460ff160261053a565b348015610a9157600080fd5b5061043a610aa03660046148cd565b6123f8565b348015610ab157600080fd5b5061053a610ac03660046147c5565b612466565b348015610ad157600080fd5b5061043a610ae0366004614b6f565b61247a565b348015610af157600080fd5b5061043a610b003660046149d8565b6125a5565b348015610b1157600080fd5b506104c07f000000000000000000000000000000000000000000000000000000000000000081565b348015610b4557600080fd5b5061043a610b543660046147c5565b612638565b348015610b6557600080fd5b50610451610b743660046147c5565b6126e4565b348015610b8557600080fd5b5061043a610b94366004614c6d565b6127be565b348015610ba557600080fd5b5061043a610bb4366004614c93565b612884565b348015610bc557600080fd5b50600254610407906301000000900461ffff1681565b348015610be757600080fd5b5061053a600581565b348015610bfc57600080fd5b506104c07f000000000000000000000000000000000000000000000000000000000000000081565b61043a610c32366004614cf2565b612a0e565b348015610c4357600080fd5b506103d2610c52366004614d5c565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832093909416825291909152205460ff1690565b348015610cb857600080fd5b50610862610cc73660046148cd565b612c21565b348015610cd857600080fd5b5061043a610ce73660046148cd565b612c65565b348015610cf857600080fd5b5060025466038d7ea4c680006a010000000000000000000090910460ff160261053a565b348015610d2857600080fd5b5061043a612d8b565b348015610d3d57600080fd5b5061043a610d4c3660046149d8565b612e3e565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161480610de457507f80ac58cd000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b80610e3057507f5b5e139f000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ebc576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025460ff1615610ef9576040517fde7c738300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805461ffff9092166301000000027fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffff909216919091179055565b60607facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955998054610f6390614d8a565b80601f0160208091040260200160405190810160405280929190818152602001828054610f8f90614d8a565b8015610fdc5780601f10610fb157610100808354040283529160200191610fdc565b820191906000526020600020905b815481529060010190602001808311610fbf57829003601f168201915b5050505050905090565b6000610ff9610ff483612ed1565b612f7a565b90503373ffffffffffffffffffffffffffffffffffffffff821614801590611071575073ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff16155b156110a8576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff87811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b60066020908152600782901c60009081529081205460fe600184901b161c600316610e30565b60148111156111a9576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80158015906111dc57507f000000000000000000000000000000000000000000000000000000000000000062093a800142105b80156111ff5750600254611c2063ffffffff650100000000009092048216011642105b1561124e5760405173ffffffffffffffffffffffffffffffffffffffff841681527f7bf893b96a69dce258515c3db09a3b05e60d03e920a4ccc4c36bd5698772b35a9060200160405180910390a15b611259838383612f9c565b505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604081205460141c620fffff16610e30565b3073ffffffffffffffffffffffffffffffffffffffff8316036112ff576040517fdf5507ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821661134c576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061135782612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff861614806113ce575073ffffffffffffffffffffffffffffffffffffffff851660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b8061141b575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080611454576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff1661147483612f7a565b73ffffffffffffffffffffffffffffffffffffffff16146114c1576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e6020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556115216001840183612fdd565b61157a7fffffffffffffffffffffffff0000000000000000000000000000000000000000831673ffffffffffffffffffffffffffffffffffffffff8616175b740400000000000000000000000000000000000000001790565b60008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff8781168084527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c90925283832080546001019055881680835283832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190559251869391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a45050505050565b60008060008061166361306a565b9050600060015b828110156116cf5760008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d6020526040902054945084156116ac578491505b6116bb8260a11c600116151590565b156116c7578360010193505b60010161166a565b5091949350505050565b6014811115611714576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117417f000000000000000000000000000000000000000000000000000000000000000062093a80614e0c565b4210801561176d575060025461176a90620151809065010000000000900463ffffffff16614e0c565b42105b156117a4576040517f7d857b6700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611259838383613084565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611835576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040514790600090339083908381818185875af1925050503d8060008114611879576040519150601f19603f3d011682016040523d82523d6000602084013e61187e565b606091505b50509050806118b9576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b6112598383836040518060200160405280600081525061247a565b6118e886866001878787876130c6565b73ffffffffffffffffffffffffffffffffffffffff86811660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f60209081526040808320948a168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117909155825190815291517f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319281900390910190a3505050505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611a28576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003611259828483614e6a565b6000610e30610ff483612ed1565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611ac9576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b83811015611b2a57611b1a858583818110611ae957611ae9614f84565b90506020020135848484818110611b0257611b02614f84565b9050602002013560066133f19092919063ffffffff16565b611b2381614fb3565b9050611acc565b5050505050565b323314611b6a576040517f9453980400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254829061ffff6301000000820481166101009092041682011115611bbc576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260146005821115611bfa576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80611c043361125e565b83011115611c3e576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84611c6260025460ff69010000000000000000009091041666038d7ea4c680000290565b023414611c9b576040517fd2ade55600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254611c2063ffffffff6501000000000090920482160116421080611cd0575060025465010000000000900463ffffffff16155b15611d07576040517fc7d08f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b2a338686613401565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611d98576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805473ffffffffffffffffffffffffffffffffffffffff9092166b010000000000000000000000027fff0000000000000000000000000000000000000000ffffffffffffffffffffff909216919091179055565b6000610e30611dfc83612ed1565b60a81c64ffffffffff1690565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a95599600401836001611e4860025461ffff6101009091041690565b6134fd565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040812054620fffff16610e30565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611f22576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611f2b816135c6565b6002600a6101000a81548160ff021916908360ff16021790555050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611fce576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff169055565b6000610e3061200683612ed1565b90565b60007f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335b5473ffffffffffffffffffffffffffffffffffffffff16919050565b60607facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955996001018054610f6390614d8a565b60007f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd948461202d565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955996004018360016120e160025461ffff6101009091041690565b613650565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604081205460281c620fffff16610e30565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146121be576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa15801561222b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224f9190614feb565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526024810182905290915073ffffffffffffffffffffffffffffffffffffffff83169063a9059cbb906044016020604051808303816000875af11580156122c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112599190615004565b3360008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040812054603c1c64ffffffffff16610e30565b905090565b6124006136de565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd948480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000610e3061247483612ed1565b60d01c90565b6124858484846112b0565b73ffffffffffffffffffffffffffffffffffffffff83163b1580159061256857506040517f150b7a02000000000000000000000000000000000000000000000000000000008082529073ffffffffffffffffffffffffffffffffffffffff85169063150b7a0290612500903390899088908890600401615021565b6020604051808303816000875af115801561251f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612543919061506a565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b1561259f576040517ff5cd1f5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461262b576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004611259828483614e6a565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146126be576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6126c7816135c6565b600260096101000a81548160ff021916908360ff16021790555050565b6060600380546126f390614d8a565b15905061272d57600361270583613766565b600460405160200161271993929190615118565b604051602081830303815290604052610e30565b6005805461273a90614d8a565b80601f016020809104026020016040519081016040528092919081815260200182805461276690614d8a565b80156127b35780601f10612788576101008083540402835291602001916127b3565b820191906000526020600020905b81548152906001019060200180831161279657829003601f168201915b505050505092915050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612844576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805463ffffffff90921665010000000000027fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff909216919091179055565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461290a576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612914838361514b565b60025461ffff6301000000820481166101009092041682011115612964576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81156129bb5760005b848110156129b5576129a586868381811061298a5761298a614f84565b905060200201602081019061299f91906148cd565b856137c8565b6129ae81614fb3565b905061296d565b50611b2a565b60005b84811015612a06576129f68686838181106129db576129db614f84565b90506020020160208101906129f091906148cd565b856137d4565b6129ff81614fb3565b90506129be565b505050505050565b323314612a47576040517f9453980400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254859061ffff6301000000820481166101009092041682011115612a99576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85846005821115612ad6576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80612ae03361125e565b83011115612b1a576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b258585886137e1565b612b5b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254426501000000000090910463ffffffff908116611c2001161015612bae576040517f04cc9ce200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b87612bd360025460ff6a01000000000000000000009091041666038d7ea4c680000290565b023414612c0c576040517fd2ade55600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c17338989613401565b5050505050505050565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a95599600401836001612c6060025461ffff6101009091041690565b6138b3565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612ceb576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea3380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560408051338152602081019290925280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612e11576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612ec4576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005611259828483614e6a565b6000612edc8261392a565b612f12576040517fb1d04f0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000825b60008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602052604090205491508115612f53575092915050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01612f16565b6000612f8b8260a11c600116151590565b612f955781610e30565b3092915050565b60005b81811015612fd157612fc984848484818110612fbd57612fbd614f84565b90506020020135613948565b600101612f9f565b50611259838383613cad565b612fec8160a21c600116151590565b158015613024575060008281527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d6020526040902054155b801561303457506130348261392a565b156118b95760009182527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602052604090912055565b600254600090610100900461ffff166123f3906001614e0c565b60005b818110156130b9576130b1848484848181106130a5576130a5614f84565b90506020020135613d68565b600101613087565b5061125960008383613cad565b42841015613100576040517f1ab7da6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff871660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b81602052604081208054600180820190925591906131f8604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b604080517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9602082015273ffffffffffffffffffffffffffffffffffffffff808e1692820192909252908b166060820152608081018a905260a0810185905260c0810189905260e001604051602081830303815290604052805190602001206040516020016132b99291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff881690820152606081018690526080810185905260a0016020604051602081039080840390855afa158015613335573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615806133af57508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b156133e6576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050565b6020928352600091825291902055565b60028211156134705760405173ffffffffffffffffffffffffffffffffffffffff841681527f9065519f7be06a0e88a7d6493f56b85b3714d9efd3de44c904169be8d4589b649060200160405180910390a160025461041f61010090910461ffff161015613470578160010191505b8080156134945750600254611c2063ffffffff650100000000009092048216011642105b156134e35760405173ffffffffffffffffffffffffffffffffffffffff841681527f7bf893b96a69dce258515c3db09a3b05e60d03e920a4ccc4c36bd5698772b35a9060200160405180910390a15b80156134f35761125983836137c8565b61125983836137d4565b60405160208101600080848601865b8181101561358d57600081815260208b905260409020549350831561352f578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614801561357657506135748360a11c600116151590565b155b15613585578085526020850194505b60010161350c565b505050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08282030160051c8252604052949350505050565b60008066038d7ea4c6800083061561360a576040517f4823b65a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5066038d7ea4c68000820460ff811115610e30576040517f4823b65a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405160208101600080848601865b8181101561358d57600081815260208b9052604090205493508315613682578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161480156136c757506136c78360a11c600116151590565b156136d6578085526020850194505b60010161365f565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614613764576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b606060a06040510180604052602081039150506000815280825b600183039250600a81066030018353600a90048061378057508190037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909101908152919050565b6118b9828260006140cb565b6118b98282600080614169565b60408051306020820152339181019190915260608101829052600090819060800160405160208183030381529060405280519060200120905060006138578686613850856020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b919061449d565b905073ffffffffffffffffffffffffffffffffffffffff8116158015906138a7575060025473ffffffffffffffffffffffffffffffffffffffff8281166b01000000000000000000000090920416145b925050505b9392505050565b60405160208101600080848601865b8181101561358d57600081815260208b90526040902054935083156138e5578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1603613922578085526020850194505b6001016138c2565b600081600111158015610e30575061394061306a565b821092915050565b600061395382612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff851614806139ca575073ffffffffffffffffffffffffffffffffffffffff841660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b80613a17575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080613a50576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff16613a7083612f7a565b73ffffffffffffffffffffffffffffffffffffffff1614613abd576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e6020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055613b1d6001840183612fdd565b613b867ffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffff4260a81b7fffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff851617740200000000000000000000000000000000000000001716611560565b60008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff871682527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c90522054613c3390429065010000000000015b7ffffffffffffffffffffffffffffffffffffffff0000000000fffffffffffffff16603c9190911b1790565b73ffffffffffffffffffffffffffffffffffffffff851660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604080822093909355915185923092917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9190a450505050565b6112597f4d26d408b9c238c45bbfb91e45a4a735db7b67a74ca6c269254d8aa9b124208a848484604051602401613ce693929190615188565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261450c565b6000613d7382612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff85161480613dea575073ffffffffffffffffffffffffffffffffffffffff841660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b80613e37575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080613e70576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613e7f8260a11c600116151590565b613eb5576040517f30c8d84f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84168273ffffffffffffffffffffffffffffffffffffffff1614613f1a576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613f298260a01c600116151590565b15613f6957613f3b8360010183612fdd565b613f667ffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffff8316611560565b91505b7fffffffffffff0000000000fdffffffffffffffffffffffffffffffffffffffff82167ffffffffffffffffffffffffdffffffffffffffffffffffffffffffffffffffff4260a81b161760008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff871682527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c905220546140529042907fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000001613c07565b73ffffffffffffffffffffffffffffffffffffffff851660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040808220939093559151859230917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9190a450505050565b60006140d561306a565b90506140e48484600185614169565b60008367ffffffffffffffff8111156140ff576140ff614b40565b604051908082528060200260200182016040528015614128578160200160208202803683370190505b50905060005b8481101561415e5780830182828151811061414b5761414b614f84565b602090810291909101015260010161412e565b50611b2a8582614657565b826000036141a3576040517fb562e8dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166141f0576040517f2e07630000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006141fa61306a565b73ffffffffffffffffffffffffffffffffffffffff861660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604090205491925060d084901b7fffffffffffff0000000000000000000000000000000000000000000000000000161790600186900361428f5774040000000000000000000000000000000000000000821791505b84156143a657740200000000000000000000000000000000000000007fffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff740100000000000000000000000000000000000000008417164260a81b171791506142fd42613c07838960281b0190565b905060005b868110156143a0576040518482019073ffffffffffffffffffffffffffffffffffffffff8a16906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a460405184820190309073ffffffffffffffffffffffffffffffffffffffff8b16907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90600090a4600101614302565b50614402565b60005b86811015614400576040518482019073ffffffffffffffffffffffffffffffffffffffff8a16906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46001016143a9565b505b61441686614412838260141b0190565b0190565b73ffffffffffffffffffffffffffffffffffffffff881660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c60209081526040808320939093558582527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d9052208290556144948661468e565b50505050505050565b6000604182036138ac576040516040846040377f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0606051116145025784600052604084013560001a602052602060406080600060015afa5060006060523d6060035191505b6040529392505050565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd94845473ffffffffffffffffffffffffffffffffffffffff1661457a576040517fafd0683400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663b47204777f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9484546040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526146299173ffffffffffffffffffffffffffffffffffffffff169085906004016151fa565b600060405180830381600087803b15801561464357600080fd5b505af1158015611b2a573d6000803e3d6000fd5b6118b97f4d26d408b9c238c45bbfb91e45a4a735db7b67a74ca6c269254d8aa9b124208a8383604051602401613ce6929190615231565b80600260018282829054906101000a900461ffff166146ad9190615260565b92506101000a81548161ffff021916908361ffff16021790555050565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146146f857600080fd5b50565b60006020828403121561470d57600080fd5b81356138ac816146ca565b60006020828403121561472a57600080fd5b813561ffff811681146138ac57600080fd5b60005b8381101561475757818101518382015260200161473f565b8381111561259f5750506000910152565b6000815180845261478081602086016020860161473c565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006138ac6020830184614768565b6000602082840312156147d757600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146146f857600080fd5b6000806040838503121561481357600080fd5b823561481e816147de565b946020939093013593505050565b60008083601f84011261483e57600080fd5b50813567ffffffffffffffff81111561485657600080fd5b6020830191508360208260051b850101111561487157600080fd5b9250929050565b60008060006040848603121561488d57600080fd5b8335614898816147de565b9250602084013567ffffffffffffffff8111156148b457600080fd5b6148c08682870161482c565b9497909650939450505050565b6000602082840312156148df57600080fd5b81356138ac816147de565b6000806000606084860312156148ff57600080fd5b833561490a816147de565b9250602084013561491a816147de565b929592945050506040919091013590565b60008060008060008060c0878903121561494457600080fd5b863561494f816147de565b9550602087013561495f816147de565b945060408701359350606087013560ff8116811461497c57600080fd5b9598949750929560808101359460a0909101359350915050565b60008083601f8401126149a857600080fd5b50813567ffffffffffffffff8111156149c057600080fd5b60208301915083602082850101111561487157600080fd5b600080602083850312156149eb57600080fd5b823567ffffffffffffffff811115614a0257600080fd5b614a0e85828601614996565b90969095509350505050565b60008060008060408587031215614a3057600080fd5b843567ffffffffffffffff80821115614a4857600080fd5b614a548883890161482c565b90965094506020870135915080821115614a6d57600080fd5b50614a7a8782880161482c565b95989497509550505050565b80151581146146f857600080fd5b60008060408385031215614aa757600080fd5b823591506020830135614ab981614a86565b809150509250929050565b600081518084526020808501945080840160005b83811015614af457815187529582019590820190600101614ad8565b509495945050505050565b6020815260006138ac6020830184614ac4565b60008060408385031215614b2557600080fd5b8235614b30816147de565b91506020830135614ab981614a86565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060008060808587031215614b8557600080fd5b8435614b90816147de565b93506020850135614ba0816147de565b925060408501359150606085013567ffffffffffffffff80821115614bc457600080fd5b818701915087601f830112614bd857600080fd5b813581811115614bea57614bea614b40565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715614c3057614c30614b40565b816040528281528a6020848701011115614c4957600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b600060208284031215614c7f57600080fd5b813563ffffffff811681146138ac57600080fd5b60008060008060608587031215614ca957600080fd5b843567ffffffffffffffff811115614cc057600080fd5b614ccc8782880161482c565b909550935050602085013591506040850135614ce781614a86565b939692955090935050565b600080600080600060808688031215614d0a57600080fd5b853594506020860135614d1c81614a86565b935060408601359250606086013567ffffffffffffffff811115614d3f57600080fd5b614d4b88828901614996565b969995985093965092949392505050565b60008060408385031215614d6f57600080fd5b8235614d7a816147de565b91506020830135614ab9816147de565b600181811c90821680614d9e57607f821691505b602082108103614dd7577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115614e1f57614e1f614ddd565b500190565b601f82111561125957600081815260208120601f850160051c81016020861015614e4b5750805b601f850160051c820191505b81811015612a0657828155600101614e57565b67ffffffffffffffff831115614e8257614e82614b40565b614e9683614e908354614d8a565b83614e24565b6000601f841160018114614ee85760008515614eb25750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611b2a565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015614f375786850135825560209485019460019092019101614f17565b5086821015614f72577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614fe457614fe4614ddd565b5060010190565b600060208284031215614ffd57600080fd5b5051919050565b60006020828403121561501657600080fd5b81516138ac81614a86565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015250836040830152608060608301526150606080830184614768565b9695505050505050565b60006020828403121561507c57600080fd5b81516138ac816146ca565b6000815461509481614d8a565b600182811680156150ac57600181146150df5761510e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008416875282151583028701945061510e565b8560005260208060002060005b858110156151055781548a8201529084019082016150ec565b50505082870194505b5050505092915050565b60006151248286615087565b845161513481836020890161473c565b61514081830186615087565b979650505050505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561518357615183614ddd565b500290565b73ffffffffffffffffffffffffffffffffffffffff841681526040602082015281604082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156151dd57600080fd5b8260051b8085606085013760009201606001918252509392505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006152296040830184614768565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006152296040830184614ac4565b600061ffff80831681851680830382111561527d5761527d614ddd565b0194935050505056fea164736f6c634300080f000a697066733a2f2f516d547639566f58676b5a7846636f6d5457336b4e364352727955504d666765556b56656b46737a63643739674b2f00000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c287000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a2

Deployed Bytecode

0x6080604052600436106103ad5760003560e01c806388486f86116101e7578063bb58131f1161010d578063de9b771f116100a0578063f2fde38b1161006f578063f2fde38b14610ccc578063fc1a1c3614610cec578063fca76c2614610d1c578063fe2c7fee14610d3157600080fd5b8063de9b771f14610bf0578063e7f371e614610c24578063e985e9c514610c37578063eec1cbb714610cac57600080fd5b8063c90af357116100dc578063c90af35714610b79578063ca9228a114610b99578063d5abeb0114610bb9578063d75e611014610bdb57600080fd5b8063bb58131f14610ae5578063c0857ba014610b05578063c627525514610b39578063c87b56dd14610b5957600080fd5b80639be65a6011610185578063a945bf8011610154578063a945bf8014610a56578063aea4e49e14610a85578063af436af314610aa5578063b88d4fde14610ac557600080fd5b80639be65a60146109dc578063a22cb465146109fc578063a742530814610a1c578063a8d0466c14610a3c57600080fd5b806395e1a8a5116101c157806395e1a8a514610970578063972c4928146109875780639871e2221461099c5780639888eb1b146109bc57600080fd5b806388486f86146109265780638da5cb5b1461094657806395d89b411461095b57600080fd5b80633644e515116102d757806367f68fac1161026a57806370a082311161023957806370a082311461086f578063717d57d31461088f5780637ecebe00146108af5780638456cb591461091157600080fd5b806367f68fac146107ef5780636c19e783146108025780636e8ba803146108225780636e9010381461084257600080fd5b806355f804b3116102a657806355f804b314610740578063607f2d42146107605780636352211e146107af57806364c6cbbd146107cf57600080fd5b80633644e515146106365780633ccfd60b146106eb57806342842e0e1461070057806348613c281461072057600080fd5b806311836f321161034f57806323b872dd1161031e57806323b872dd146105a6578063255e4685146105c6578063257f84ae14610601578063306c76791461061657600080fd5b806311836f321461051a57806318160ddd146105485780631fb161b81461056657806320fc7eb21461058657600080fd5b806306fdde031161038b57806306fdde031461043c578063081812fc1461045e578063095ea7b3146104e55780630f2cdd6c1461050557600080fd5b806301ffc9a7146103b2578063047fc9aa146103e757806306421c2f1461041a575b600080fd5b3480156103be57600080fd5b506103d26103cd3660046146fb565b610d51565b60405190151581526020015b60405180910390f35b3480156103f357600080fd5b5060025461040790610100900461ffff1681565b60405161ffff90911681526020016103de565b34801561042657600080fd5b5061043a610435366004614718565b610e36565b005b34801561044857600080fd5b50610451610f35565b6040516103de91906147b2565b34801561046a57600080fd5b506104c06104793660046147c5565b60009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103de565b3480156104f157600080fd5b5061043a610500366004614800565b610fe6565b34801561051157600080fd5b50610407601481565b34801561052657600080fd5b5061053a6105353660046147c5565b611148565b6040519081526020016103de565b34801561055457600080fd5b50600254610100900461ffff1661053a565b34801561057257600080fd5b5061043a610581366004614878565b61116e565b34801561059257600080fd5b5061053a6105a13660046148cd565b61125e565b3480156105b257600080fd5b5061043a6105c13660046148ea565b6112b0565b3480156105d257600080fd5b506002546105ec9065010000000000900463ffffffff1681565b60405163ffffffff90911681526020016103de565b34801561060d57600080fd5b5061053a611655565b34801561062257600080fd5b5061043a610631366004614878565b6116d9565b34801561064257600080fd5b5061053a604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b3480156106f757600080fd5b5061043a6117af565b34801561070c57600080fd5b5061043a61071b3660046148ea565b6118bd565b34801561072c57600080fd5b5061043a61073b36600461492b565b6118d8565b34801561074c57600080fd5b5061043a61075b3660046149d8565b6119a2565b34801561076c57600080fd5b506103d261077b3660046147c5565b60009081527f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9485602052604090205460ff1690565b3480156107bb57600080fd5b506104c06107ca3660046147c5565b611a35565b3480156107db57600080fd5b5061043a6107ea366004614a1a565b611a43565b61043a6107fd366004614a94565b611b31565b34801561080e57600080fd5b5061043a61081d3660046148cd565b611d12565b34801561082e57600080fd5b5061053a61083d3660046147c5565b611dee565b34801561084e57600080fd5b5061086261085d3660046148cd565b611e09565b6040516103de9190614aff565b34801561087b57600080fd5b5061053a61088a3660046148cd565b611e4d565b34801561089b57600080fd5b5061043a6108aa3660046147c5565b611e9c565b3480156108bb57600080fd5b5061053a6108ca3660046148cd565b73ffffffffffffffffffffffffffffffffffffffff1660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b81602052604090205490565b34801561091d57600080fd5b5061043a611f48565b34801561093257600080fd5b506104c06109413660046147c5565b611ff8565b34801561095257600080fd5b506104c0612009565b34801561096757600080fd5b50610451612049565b34801561097c57600080fd5b5061053a6201518081565b34801561099357600080fd5b506104c061207a565b3480156109a857600080fd5b506108626109b73660046148cd565b6120a2565b3480156109c857600080fd5b5061053a6109d73660046148cd565b6120e6565b3480156109e857600080fd5b5061043a6109f73660046148cd565b612138565b348015610a0857600080fd5b5061043a610a17366004614b12565b6122e9565b348015610a2857600080fd5b5061053a610a373660046148cd565b61239f565b348015610a4857600080fd5b506002546103d29060ff1681565b348015610a6257600080fd5b5060025466038d7ea4c68000690100000000000000000090910460ff160261053a565b348015610a9157600080fd5b5061043a610aa03660046148cd565b6123f8565b348015610ab157600080fd5b5061053a610ac03660046147c5565b612466565b348015610ad157600080fd5b5061043a610ae0366004614b6f565b61247a565b348015610af157600080fd5b5061043a610b003660046149d8565b6125a5565b348015610b1157600080fd5b506104c07f00000000000000000000000086e4dc95c7fbdbf52e33d563bbdb00823894c28781565b348015610b4557600080fd5b5061043a610b543660046147c5565b612638565b348015610b6557600080fd5b50610451610b743660046147c5565b6126e4565b348015610b8557600080fd5b5061043a610b94366004614c6d565b6127be565b348015610ba557600080fd5b5061043a610bb4366004614c93565b612884565b348015610bc557600080fd5b50600254610407906301000000900461ffff1681565b348015610be757600080fd5b5061053a600581565b348015610bfc57600080fd5b506104c07f000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a281565b61043a610c32366004614cf2565b612a0e565b348015610c4357600080fd5b506103d2610c52366004614d5c565b73ffffffffffffffffffffffffffffffffffffffff91821660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832093909416825291909152205460ff1690565b348015610cb857600080fd5b50610862610cc73660046148cd565b612c21565b348015610cd857600080fd5b5061043a610ce73660046148cd565b612c65565b348015610cf857600080fd5b5060025466038d7ea4c680006a010000000000000000000090910460ff160261053a565b348015610d2857600080fd5b5061043a612d8b565b348015610d3d57600080fd5b5061043a610d4c3660046149d8565b612e3e565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161480610de457507f80ac58cd000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b80610e3057507f5b5e139f000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ebc576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025460ff1615610ef9576040517fde7c738300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805461ffff9092166301000000027fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffff909216919091179055565b60607facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955998054610f6390614d8a565b80601f0160208091040260200160405190810160405280929190818152602001828054610f8f90614d8a565b8015610fdc5780601f10610fb157610100808354040283529160200191610fdc565b820191906000526020600020905b815481529060010190602001808311610fbf57829003601f168201915b5050505050905090565b6000610ff9610ff483612ed1565b612f7a565b90503373ffffffffffffffffffffffffffffffffffffffff821614801590611071575073ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff16155b156110a8576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff87811691821790925591518593918516917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591a4505050565b60066020908152600782901c60009081529081205460fe600184901b161c600316610e30565b60148111156111a9576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80158015906111dc57507f00000000000000000000000000000000000000000000000000000000633bf57362093a800142105b80156111ff5750600254611c2063ffffffff650100000000009092048216011642105b1561124e5760405173ffffffffffffffffffffffffffffffffffffffff841681527f7bf893b96a69dce258515c3db09a3b05e60d03e920a4ccc4c36bd5698772b35a9060200160405180910390a15b611259838383612f9c565b505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604081205460141c620fffff16610e30565b3073ffffffffffffffffffffffffffffffffffffffff8316036112ff576040517fdf5507ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821661134c576040517fea553b3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061135782612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff861614806113ce575073ffffffffffffffffffffffffffffffffffffffff851660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b8061141b575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080611454576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff1661147483612f7a565b73ffffffffffffffffffffffffffffffffffffffff16146114c1576040517fa114810000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e6020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556115216001840183612fdd565b61157a7fffffffffffffffffffffffff0000000000000000000000000000000000000000831673ffffffffffffffffffffffffffffffffffffffff8616175b740400000000000000000000000000000000000000001790565b60008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff8781168084527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c90925283832080546001019055881680835283832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190559251869391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a45050505050565b60008060008061166361306a565b9050600060015b828110156116cf5760008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d6020526040902054945084156116ac578491505b6116bb8260a11c600116151590565b156116c7578360010193505b60010161166a565b5091949350505050565b6014811115611714576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6117417f00000000000000000000000000000000000000000000000000000000633bf57362093a80614e0c565b4210801561176d575060025461176a90620151809065010000000000900463ffffffff16614e0c565b42105b156117a4576040517f7d857b6700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611259838383613084565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611835576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040514790600090339083908381818185875af1925050503d8060008114611879576040519150601f19603f3d011682016040523d82523d6000602084013e61187e565b606091505b50509050806118b9576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b6112598383836040518060200160405280600081525061247a565b6118e886866001878787876130c6565b73ffffffffffffffffffffffffffffffffffffffff86811660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f60209081526040808320948a168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001908117909155825190815291517f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319281900390910190a3505050505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611a28576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003611259828483614e6a565b6000610e30610ff483612ed1565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611ac9576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b83811015611b2a57611b1a858583818110611ae957611ae9614f84565b90506020020135848484818110611b0257611b02614f84565b9050602002013560066133f19092919063ffffffff16565b611b2381614fb3565b9050611acc565b5050505050565b323314611b6a576040517f9453980400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254829061ffff6301000000820481166101009092041682011115611bbc576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260146005821115611bfa576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80611c043361125e565b83011115611c3e576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84611c6260025460ff69010000000000000000009091041666038d7ea4c680000290565b023414611c9b576040517fd2ade55600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254611c2063ffffffff6501000000000090920482160116421080611cd0575060025465010000000000900463ffffffff16155b15611d07576040517fc7d08f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b2a338686613401565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611d98576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805473ffffffffffffffffffffffffffffffffffffffff9092166b010000000000000000000000027fff0000000000000000000000000000000000000000ffffffffffffffffffffff909216919091179055565b6000610e30611dfc83612ed1565b60a81c64ffffffffff1690565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a95599600401836001611e4860025461ffff6101009091041690565b6134fd565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040812054620fffff16610e30565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611f22576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611f2b816135c6565b6002600a6101000a81548160ff021916908360ff16021790555050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611fce576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff169055565b6000610e3061200683612ed1565b90565b60007f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335b5473ffffffffffffffffffffffffffffffffffffffff16919050565b60607facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955996001018054610f6390614d8a565b60007f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd948461202d565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a955996004018360016120e160025461ffff6101009091041690565b613650565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604081205460281c620fffff16610e30565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146121be576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa15801561222b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061224f9190614feb565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081523360048201526024810182905290915073ffffffffffffffffffffffffffffffffffffffff83169063a9059cbb906044016020604051808303816000875af11580156122c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112599190615004565b3360008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040812054603c1c64ffffffffff16610e30565b905090565b6124006136de565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd948480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000610e3061247483612ed1565b60d01c90565b6124858484846112b0565b73ffffffffffffffffffffffffffffffffffffffff83163b1580159061256857506040517f150b7a02000000000000000000000000000000000000000000000000000000008082529073ffffffffffffffffffffffffffffffffffffffff85169063150b7a0290612500903390899088908890600401615021565b6020604051808303816000875af115801561251f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612543919061506a565b7fffffffff000000000000000000000000000000000000000000000000000000001614155b1561259f576040517ff5cd1f5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461262b576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004611259828483614e6a565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146126be576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6126c7816135c6565b600260096101000a81548160ff021916908360ff16021790555050565b6060600380546126f390614d8a565b15905061272d57600361270583613766565b600460405160200161271993929190615118565b604051602081830303815290604052610e30565b6005805461273a90614d8a565b80601f016020809104026020016040519081016040528092919081815260200182805461276690614d8a565b80156127b35780601f10612788576101008083540402835291602001916127b3565b820191906000526020600020905b81548152906001019060200180831161279657829003601f168201915b505050505092915050565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612844576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002805463ffffffff90921665010000000000027fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff909216919091179055565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461290a576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612914838361514b565b60025461ffff6301000000820481166101009092041682011115612964576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81156129bb5760005b848110156129b5576129a586868381811061298a5761298a614f84565b905060200201602081019061299f91906148cd565b856137c8565b6129ae81614fb3565b905061296d565b50611b2a565b60005b84811015612a06576129f68686838181106129db576129db614f84565b90506020020160208101906129f091906148cd565b856137d4565b6129ff81614fb3565b90506129be565b505050505050565b323314612a47576040517f9453980400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254859061ffff6301000000820481166101009092041682011115612a99576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85846005821115612ad6576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80612ae03361125e565b83011115612b1a576040517f4f2a111200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b258585886137e1565b612b5b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600254426501000000000090910463ffffffff908116611c2001161015612bae576040517f04cc9ce200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b87612bd360025460ff6a01000000000000000000009091041666038d7ea4c680000290565b023414612c0c576040517fd2ade55600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c17338989613401565b5050505050505050565b6060610e307facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a95599600401836001612c6060025461ffff6101009091041690565b6138b3565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612ceb576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea3380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560408051338152602081019290925280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612e11576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612ec4576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005611259828483614e6a565b6000612edc8261392a565b612f12576040517fb1d04f0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000825b60008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602052604090205491508115612f53575092915050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01612f16565b6000612f8b8260a11c600116151590565b612f955781610e30565b3092915050565b60005b81811015612fd157612fc984848484818110612fbd57612fbd614f84565b90506020020135613948565b600101612f9f565b50611259838383613cad565b612fec8160a21c600116151590565b158015613024575060008281527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d6020526040902054155b801561303457506130348261392a565b156118b95760009182527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602052604090912055565b600254600090610100900461ffff166123f3906001614e0c565b60005b818110156130b9576130b1848484848181106130a5576130a5614f84565b90506020020135613d68565b600101613087565b5061125960008383613cad565b42841015613100576040517f1ab7da6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff871660009081527f24034dbc71162a0a127c76a8ce123f10641be888cbac564cd2e6e6f5e2c19b81602052604081208054600180820190925591906131f8604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f421979463954f2ac93264f1ce0c11a780b4a7686122abe11bac8c8244f44a3de918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b604080517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9602082015273ffffffffffffffffffffffffffffffffffffffff808e1692820192909252908b166060820152608081018a905260a0810185905260c0810189905260e001604051602081830303815290604052805190602001206040516020016132b99291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff881690820152606081018690526080810185905260a0016020604051602081039080840390855afa158015613335573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615806133af57508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b156133e6576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050565b6020928352600091825291902055565b60028211156134705760405173ffffffffffffffffffffffffffffffffffffffff841681527f9065519f7be06a0e88a7d6493f56b85b3714d9efd3de44c904169be8d4589b649060200160405180910390a160025461041f61010090910461ffff161015613470578160010191505b8080156134945750600254611c2063ffffffff650100000000009092048216011642105b156134e35760405173ffffffffffffffffffffffffffffffffffffffff841681527f7bf893b96a69dce258515c3db09a3b05e60d03e920a4ccc4c36bd5698772b35a9060200160405180910390a15b80156134f35761125983836137c8565b61125983836137d4565b60405160208101600080848601865b8181101561358d57600081815260208b905260409020549350831561352f578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614801561357657506135748360a11c600116151590565b155b15613585578085526020850194505b60010161350c565b505050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08282030160051c8252604052949350505050565b60008066038d7ea4c6800083061561360a576040517f4823b65a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5066038d7ea4c68000820460ff811115610e30576040517f4823b65a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405160208101600080848601865b8181101561358d57600081815260208b9052604090205493508315613682578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161480156136c757506136c78360a11c600116151590565b156136d6578085526020850194505b60010161365f565b7f87917b04fc43108fc3d291ac961b425fe1ddcf80087b2cb7e3c48f3e9233ea335473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614613764576040517f5cd8319200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b606060a06040510180604052602081039150506000815280825b600183039250600a81066030018353600a90048061378057508190037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909101908152919050565b6118b9828260006140cb565b6118b98282600080614169565b60408051306020820152339181019190915260608101829052600090819060800160405160208183030381529060405280519060200120905060006138578686613850856020527b19457468657265756d205369676e6564204d6573736167653a0a3332600052603c60042090565b919061449d565b905073ffffffffffffffffffffffffffffffffffffffff8116158015906138a7575060025473ffffffffffffffffffffffffffffffffffffffff8281166b01000000000000000000000090920416145b925050505b9392505050565b60405160208101600080848601865b8181101561358d57600081815260208b90526040902054935083156138e5578392505b8273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1603613922578085526020850194505b6001016138c2565b600081600111158015610e30575061394061306a565b821092915050565b600061395382612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff851614806139ca575073ffffffffffffffffffffffffffffffffffffffff841660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b80613a17575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080613a50576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff16613a7083612f7a565b73ffffffffffffffffffffffffffffffffffffffff1614613abd576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e6020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055613b1d6001840183612fdd565b613b867ffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffff4260a81b7fffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff851617740200000000000000000000000000000000000000001716611560565b60008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff871682527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c90522054613c3390429065010000000000015b7ffffffffffffffffffffffffffffffffffffffff0000000000fffffffffffffff16603c9190911b1790565b73ffffffffffffffffffffffffffffffffffffffff851660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604080822093909355915185923092917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9190a450505050565b6112597f4d26d408b9c238c45bbfb91e45a4a735db7b67a74ca6c269254d8aa9b124208a848484604051602401613ce693929190615188565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261450c565b6000613d7382612ed1565b905060003373ffffffffffffffffffffffffffffffffffffffff85161480613dea575073ffffffffffffffffffffffffffffffffffffffff841660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559f6020908152604080832033845290915290205460ff165b80613e37575060008381527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559e602052604090205473ffffffffffffffffffffffffffffffffffffffff1633145b905080613e70576040517f4fb505aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613e7f8260a11c600116151590565b613eb5576040517f30c8d84f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84168273ffffffffffffffffffffffffffffffffffffffff1614613f1a576040517f3a6bbed300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613f298260a01c600116151590565b15613f6957613f3b8360010183612fdd565b613f667ffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffff8316611560565b91505b7fffffffffffff0000000000fdffffffffffffffffffffffffffffffffffffffff82167ffffffffffffffffffffffffdffffffffffffffffffffffffffffffffffffffff4260a81b161760008481527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d602090815260408083209390935573ffffffffffffffffffffffffffffffffffffffff871682527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c905220546140529042907fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000001613c07565b73ffffffffffffffffffffffffffffffffffffffff851660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c6020526040808220939093559151859230917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9190a450505050565b60006140d561306a565b90506140e48484600185614169565b60008367ffffffffffffffff8111156140ff576140ff614b40565b604051908082528060200260200182016040528015614128578160200160208202803683370190505b50905060005b8481101561415e5780830182828151811061414b5761414b614f84565b602090810291909101015260010161412e565b50611b2a8582614657565b826000036141a3576040517fb562e8dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166141f0576040517f2e07630000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006141fa61306a565b73ffffffffffffffffffffffffffffffffffffffff861660008181527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c602052604090205491925060d084901b7fffffffffffff0000000000000000000000000000000000000000000000000000161790600186900361428f5774040000000000000000000000000000000000000000821791505b84156143a657740200000000000000000000000000000000000000007fffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff740100000000000000000000000000000000000000008417164260a81b171791506142fd42613c07838960281b0190565b905060005b868110156143a0576040518482019073ffffffffffffffffffffffffffffffffffffffff8a16906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a460405184820190309073ffffffffffffffffffffffffffffffffffffffff8b16907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90600090a4600101614302565b50614402565b60005b86811015614400576040518482019073ffffffffffffffffffffffffffffffffffffffff8a16906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46001016143a9565b505b61441686614412838260141b0190565b0190565b73ffffffffffffffffffffffffffffffffffffffff881660009081527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559c60209081526040808320939093558582527facef0a52ec0e8b948b85810f48a276692a03896348e0958ead290f1909a9559d9052208290556144948661468e565b50505050505050565b6000604182036138ac576040516040846040377f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0606051116145025784600052604084013560001a602052602060406080600060015afa5060006060523d6060035191505b6040529392505050565b7f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd94845473ffffffffffffffffffffffffffffffffffffffff1661457a576040517fafd0683400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a21663b47204777f3849b0d9a476107bbeb9ff6ae9ec519d63a65bac06efa495b84a43dbacfd9484546040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526146299173ffffffffffffffffffffffffffffffffffffffff169085906004016151fa565b600060405180830381600087803b15801561464357600080fd5b505af1158015611b2a573d6000803e3d6000fd5b6118b97f4d26d408b9c238c45bbfb91e45a4a735db7b67a74ca6c269254d8aa9b124208a8383604051602401613ce6929190615231565b80600260018282829054906101000a900461ffff166146ad9190615260565b92506101000a81548161ffff021916908361ffff16021790555050565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146146f857600080fd5b50565b60006020828403121561470d57600080fd5b81356138ac816146ca565b60006020828403121561472a57600080fd5b813561ffff811681146138ac57600080fd5b60005b8381101561475757818101518382015260200161473f565b8381111561259f5750506000910152565b6000815180845261478081602086016020860161473c565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006138ac6020830184614768565b6000602082840312156147d757600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146146f857600080fd5b6000806040838503121561481357600080fd5b823561481e816147de565b946020939093013593505050565b60008083601f84011261483e57600080fd5b50813567ffffffffffffffff81111561485657600080fd5b6020830191508360208260051b850101111561487157600080fd5b9250929050565b60008060006040848603121561488d57600080fd5b8335614898816147de565b9250602084013567ffffffffffffffff8111156148b457600080fd5b6148c08682870161482c565b9497909650939450505050565b6000602082840312156148df57600080fd5b81356138ac816147de565b6000806000606084860312156148ff57600080fd5b833561490a816147de565b9250602084013561491a816147de565b929592945050506040919091013590565b60008060008060008060c0878903121561494457600080fd5b863561494f816147de565b9550602087013561495f816147de565b945060408701359350606087013560ff8116811461497c57600080fd5b9598949750929560808101359460a0909101359350915050565b60008083601f8401126149a857600080fd5b50813567ffffffffffffffff8111156149c057600080fd5b60208301915083602082850101111561487157600080fd5b600080602083850312156149eb57600080fd5b823567ffffffffffffffff811115614a0257600080fd5b614a0e85828601614996565b90969095509350505050565b60008060008060408587031215614a3057600080fd5b843567ffffffffffffffff80821115614a4857600080fd5b614a548883890161482c565b90965094506020870135915080821115614a6d57600080fd5b50614a7a8782880161482c565b95989497509550505050565b80151581146146f857600080fd5b60008060408385031215614aa757600080fd5b823591506020830135614ab981614a86565b809150509250929050565b600081518084526020808501945080840160005b83811015614af457815187529582019590820190600101614ad8565b509495945050505050565b6020815260006138ac6020830184614ac4565b60008060408385031215614b2557600080fd5b8235614b30816147de565b91506020830135614ab981614a86565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060008060808587031215614b8557600080fd5b8435614b90816147de565b93506020850135614ba0816147de565b925060408501359150606085013567ffffffffffffffff80821115614bc457600080fd5b818701915087601f830112614bd857600080fd5b813581811115614bea57614bea614b40565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715614c3057614c30614b40565b816040528281528a6020848701011115614c4957600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b600060208284031215614c7f57600080fd5b813563ffffffff811681146138ac57600080fd5b60008060008060608587031215614ca957600080fd5b843567ffffffffffffffff811115614cc057600080fd5b614ccc8782880161482c565b909550935050602085013591506040850135614ce781614a86565b939692955090935050565b600080600080600060808688031215614d0a57600080fd5b853594506020860135614d1c81614a86565b935060408601359250606086013567ffffffffffffffff811115614d3f57600080fd5b614d4b88828901614996565b969995985093965092949392505050565b60008060408385031215614d6f57600080fd5b8235614d7a816147de565b91506020830135614ab9816147de565b600181811c90821680614d9e57607f821691505b602082108103614dd7577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115614e1f57614e1f614ddd565b500190565b601f82111561125957600081815260208120601f850160051c81016020861015614e4b5750805b601f850160051c820191505b81811015612a0657828155600101614e57565b67ffffffffffffffff831115614e8257614e82614b40565b614e9683614e908354614d8a565b83614e24565b6000601f841160018114614ee85760008515614eb25750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611b2a565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015614f375786850135825560209485019460019092019101614f17565b5086821015614f72577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614fe457614fe4614ddd565b5060010190565b600060208284031215614ffd57600080fd5b5051919050565b60006020828403121561501657600080fd5b81516138ac81614a86565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015250836040830152608060608301526150606080830184614768565b9695505050505050565b60006020828403121561507c57600080fd5b81516138ac816146ca565b6000815461509481614d8a565b600182811680156150ac57600181146150df5761510e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008416875282151583028701945061510e565b8560005260208060002060005b858110156151055781548a8201529084019082016150ec565b50505082870194505b5050505092915050565b60006151248286615087565b845161513481836020890161473c565b61514081830186615087565b979650505050505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561518357615183614ddd565b500290565b73ffffffffffffffffffffffffffffffffffffffff841681526040602082015281604082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156151dd57600080fd5b8260051b8085606085013760009201606001918252509392505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006152296040830184614768565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006152296040830184614ac4565b600061ffff80831681851680830382111561527d5761527d614ddd565b0194935050505056fea164736f6c634300080f000a

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


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ 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.