ETH Price: $2,349.80 (+0.29%)

Contract

0x7B64DDD5F92b59C2572B6335ed0ae0aa4ACDd4c8
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Transfer Ownersh...187374312023-12-07 22:38:59280 days ago1701988739IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0015976554.71037262
Set Signer187374312023-12-07 22:38:59280 days ago1701988739IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0013291854.71037262
Pause180865122023-09-07 19:03:35371 days ago1694113415IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.001067237.94518016
Main Burn180865092023-09-07 19:02:59371 days ago1694113379IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0084265934.17857489
Burn Normal Comi...180864902023-09-07 18:59:11371 days ago1694113151IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0067565526.50459631
Burn Normal Comi...180864902023-09-07 18:59:11371 days ago1694113151IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0089417226.50459631
Burn Normal Comi...180864832023-09-07 18:57:47371 days ago1694113067IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0049709626.39930132
Burn Normal Comi...180864792023-09-07 18:56:59371 days ago1694113019IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0056666826.66511197
Burn Normal Comi...180864682023-09-07 18:54:47371 days ago1694112887IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0057422929.10942271
Burn Normal Comi...180864602023-09-07 18:53:11371 days ago1694112791IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0048988928.17365841
Burn Normal Comi...180864522023-09-07 18:51:35371 days ago1694112695IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0078959928.38599763
Burn Normal Comi...180864472023-09-07 18:50:35371 days ago1694112635IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0074920829.11084186
Main Burn180864362023-09-07 18:48:23371 days ago1694112503IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0100053634.95251084
Main Burn180864302023-09-07 18:47:11371 days ago1694112431IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0115597736.72730006
Burn Normal Comi...180864202023-09-07 18:45:11371 days ago1694112311IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0075841328.80700087
Burn Normal Comi...180864042023-09-07 18:41:59371 days ago1694112119IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0052363929.12573749
Burn Normal Comi...180864022023-09-07 18:41:35371 days ago1694112095IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0207963429.25745917
Burn Normal Comi...180863762023-09-07 18:36:23371 days ago1694111783IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0081735435.12058558
Burn Normal Comi...180863512023-09-07 18:31:23371 days ago1694111483IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0086560332.8771917
Burn Normal Comi...180863462023-09-07 18:30:23371 days ago1694111423IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.008246633.03877259
Main Burn180863452023-09-07 18:30:11371 days ago1694111411IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0080137830.44902426
Burn Normal Comi...180863342023-09-07 18:27:59371 days ago1694111279IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.004770330.04502855
Burn Normal Comi...180863232023-09-07 18:25:47371 days ago1694111147IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0064361532.06004872
Burn Normal Comi...180863142023-09-07 18:23:59371 days ago1694111039IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.018594232
Main Burn180863122023-09-07 18:23:35371 days ago1694111015IN
0x7B64DDD5...a4ACDd4c8
0 ETH0.0130223533.44914155
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AITokenMinter

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion
File 1 of 16 : AITokenMinter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.19;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";

import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";

import "./interfaces/IBurnable.sol";
import "./interfaces/IGenesisToken.sol";
import "./interfaces/IAIToken.sol";
import "./interfaces/IERC721.sol";

/**
 * @dev Minter of AI Token
 *
 *   Huxley Token Id details:
 * - token id until 10110, Issue 1.
 * - token id from 10111 until 20220, Issue 2
 * - token id from 20221 until 30330, Issue 3
 * - token id from 30331 until 38775, Issue 4
 * - token id from 40441 until 49414, Issue 5+6 - If tokenId is even, it is Issue 6. If it is an odd tokenId, it is Issue 5
 *
 */
contract AITokenMinter is Pausable, Ownable {
    using SignatureChecker for address;

    /**
     * @dev Burn methods to mint AI Tokens
     * GTS: burns Genesis Tokens
     * MainBurn: Burns 2x tokens from Issue 4, 5 and 6
     * NormalBurn: Burns token from any issue (1, 2, 3, 4, 5 or 6) - 2 of any Comic
     */
    enum MintMethods {
        GTSBurn,
        MainBurn,
        NormalBurn
    }

    /// @notice Interface to burn GenesisToken
    IGenesisToken public immutable genesisToken;

    /// @notice Interface to burn HuxleyComics Issues 1, 2 or 3
    IERC721 public immutable huxleyComics;

    /// @notice Interface to burn HuxleyComics Issue 4
    IBurnable public immutable huxleyComics4;

    /// @notice Interface to burn HuxleyComics Issue 5/6
    IBurnable public immutable huxleyComics56;

    /// @notice Interface to mint AI Token
    IAIToken public aiToken;

    /// @notice Address of the wallet that signs the holder type
    address public signer;

    constructor(
        address _huxley123,
        address _huxley4,
        address _huxley56,
        address _genesis
    ) {
        huxleyComics = IERC721(_huxley123);
        huxleyComics4 = IBurnable(_huxley4);
        huxleyComics56 = IBurnable(_huxley56);

        genesisToken = IGenesisToken(_genesis);

        _pause();
    }

    /**
     * User burns 1 or more GTS token and gets a certain amount of AI
     * burnBatch() from GTS is called.
     * It reverts if wallet that is trying to burn is not the owner of the token
     * It reverts if array size are different
     * It reverts if array is empty
     * It reverts if amount is over token balance
     *
     * If _categories = [1,2] it will burn categories 1 and 2
     * If _amounts = [10,5] it will burn 10 tokens from category 1 and 5 tokens from category 2.
     *
     * @param _categories Genesis token categories. It is from 1 to 10.
     * @param _amounts Genesis token amount to burn. It can't be over the wallet token balance
     * @param _type It is type of holder. If it has a complete set that is redeemed or unredeemed, if it has Avatar/Robots token
     * @param _typeSignature Signature created by the signer confirming the wallet type
     */
    function burnGenesis(
        uint256[] calldata _categories,
        uint256[] calldata _amounts,
        uint256 _type,
        bytes calldata _typeSignature
    ) external whenNotPaused {
        uint256 size = _amounts.length;

        // burn batch checks if msg.sender is the token owners
        // if arrays are empty or mismatched, it reverts
        genesisToken.burnBatch(msg.sender, _categories, _amounts);

        uint256 totalBurned;

        for (uint256 i; i < size; ) {
            totalBurned += _amounts[i];
            unchecked {
                i++;
            }
        }

        _mintAI(MintMethods.GTSBurn, totalBurned, _type, _typeSignature);
    }

    /**
     * Wallet burns 2 Comics 4, 2 Comics 5 and 2 Comics 6 token and gets a certain amount of AI
     * _tokenIds array should follow the correct order: token id from 4, 5 and 6
     *
     * @param _tokenIds4 List of token ids from Issue 4. It cannot be empty.
     * @param _tokenIds5 List of token ids from Issue 5. It cannot be empty.
     * @param _tokenIds6 List of token ids from Issue 5. It cannot be empty.
     * @param _type It is type of holder. If it has a complete set that is redeemed or unredeemed, if it has Avatar/Robots token
     * @param _typeSignature Signature created by the signer confirming the wallet type
     */
    function mainBurn(
        uint256[] calldata _tokenIds4,
        uint256[] calldata _tokenIds5,
        uint256[] calldata _tokenIds6,
        uint256 _type,
        bytes calldata _typeSignature
    ) external whenNotPaused {
        uint256 size4 = _tokenIds4.length;
        uint256 size5 = _tokenIds5.length;
        uint256 size6 = _tokenIds6.length;

        //check if they have same size
        require(size4 == size5, "AI: Different size 4 and 5");
        require(size4 == size6, "AI: Different size 4 and 6");

        // It must have at least 2 tokens ids
        require(size4 >= 2, "AI: Array 4 has less than 2 tokens");

        // it must be an even size. At least 2 tokens to get AI. User can send 2, 4, 6, etc tokens
        require(isEven(size4), "AI: Size must be even"); // since 4, 5 and 6 has same size, we just need to check one array

        // If token id doesn't exist, it reverts
        // It also checks ownership
        _burn4(_tokenIds4);
        _burn5(_tokenIds5);
        _burn6(_tokenIds6);

        // each 2 tokens gives a certain amount of AI. Divide by 2 to have the amount wallet is going
        // to receive when minting using this method
        _mintAI(MintMethods.MainBurn, size4 / 2, _type, _typeSignature);
    }

    /**
     * I burns at least 2 tokens from any comic and then mint AI token
     * Issue 1, 2 and 3 needs an approvalForAkk() to be able to burn because AI Minter needs to transfer
     * it first to itself and then bur.
     * @param _tokenIds123 List of token ids from Issue 1, 2 and 3. It can be empty.
     * @param _tokenIds4 List of token ids from Issue 4. It can be empty.
     * @param _tokenIds56 List of token ids from Issue 5/6. It can be empty.
     * @param _type It is type of holder. If it has a complete set that is redeemed or unredeemed, if it has Avatar/Robots token
     * @param _typeSignature Signature created by the signer confirming the wallet type
     */
    function burnNormalComics123456(
        uint256[] calldata _tokenIds123,
        uint256[] calldata _tokenIds4,
        uint256[] calldata _tokenIds56,
        uint256 _type,
        bytes calldata _typeSignature
    ) external whenNotPaused {
        // burns tokens and return the amount of tokens burned
        // It should burn in pairs
        uint256 totalBurned = _executeBurnNormal(
            _tokenIds123,
            _tokenIds4,
            _tokenIds56
        );

        // mint AI token
        _mintAI(
            MintMethods.NormalBurn,
            (totalBurned) / 2, // for each 2 tokens burned, 1 AI
            _type,
            _typeSignature
        );
    }

    /**
     * Burns tokens
     * @param _tokenIds123 List of token ids from Issue 1, 2 and 3. It can be empty.
     * @param _tokenIds4 List of token ids from Issue 4. It can be empty.
     * @param _tokenIds56 List of token ids from Issue 5/6. It can be empty.
     */
    function _executeBurnNormal(
        uint256[] calldata _tokenIds123,
        uint256[] calldata _tokenIds4,
        uint256[] calldata _tokenIds56
    ) internal returns (uint256 totalBurned) {
        totalBurned =
            _tokenIds123.length +
            _tokenIds4.length +
            _tokenIds56.length;

        require(isEven(totalBurned), "AI: Should be in pairs");

        _burn123(_tokenIds123);
        _burn4(_tokenIds4);
        // it doesn't need to check if tokens are from 5 or 6. It could be all from 5 or 6 or both
        _burn56(_tokenIds56, false, false);
    }

    /**
     * Burns tokens from Issue 1, 2 or 3.
     * It first transfer the token to AITokenMinter and then burns. Owner should approval
     * the transfer before.
     * @param _tokenIds123 List of token ids from Issue 1, 2 and 3. It can be empty.
     */
    function _burn123(uint256[] calldata _tokenIds123) internal {
        uint256 size = _tokenIds123.length;

        for (uint256 i; i < size; ) {
            // 1) transfer token so it can be burned - setApprovalForAll was called before
            // Since it is using msg.sender, we don't need to check ownerOf
            huxleyComics.transferFrom(
                msg.sender,
                address(this),
                _tokenIds123[i]
            );

            huxleyComics.burn(_tokenIds123[i]);
            unchecked {
                i++;
            }
        }
    }

    /**
     * Burn tokens from Issue 4
     * It reverts if msg.sender is not the token Owner
     * It won't call the burn function from Issue 4 it the token list is empty.
     * @param _tokenIds4 List of token ids from 4. It can be empty.
     */
    function _burn4(uint256[] calldata _tokenIds4) internal {
        uint256 size = _tokenIds4.length;

        for (uint256 i; i < size; ) {
            require(
                huxleyComics4.ownerOf(_tokenIds4[i]) == msg.sender,
                "AI: Not owner 4"
            );
            unchecked {
                i++;
            }
        }

        if (size > 0) {
            huxleyComics4.burnBatch(_tokenIds4);
        }
    }

    /**
     * Burn tokens from Issue 5.
     * @param _tokenIds5 List of token ids from Issue 5. It can be empty.
     */
    function _burn5(uint256[] calldata _tokenIds5) internal {
        // checkIssue and if it is Five
        _burn56(_tokenIds5, true, true);
    }

    /**
     * Burn tokens from Issue 6
     * @param _tokenIds6 List of token ids from Issue 6. It can be empty.
     */
    function _burn6(uint256[] calldata _tokenIds6) internal {
        // checkIssue and is not Five
        _burn56(_tokenIds6, true, false);
    }

    /**
     * Burn tokens from Issue 5/6.
     * It checks the token owner before burning it. It token ids list is empty, it doesn't
     * call the burn function from the Issue 5/6 contract and returns 0 tokens burned.
     * @param _tokenIds56 List of token ids from Issue 5 or Issue 6. It can be empty.
     * @param _isMainBurn If it is main burn, it is necessary to check if token is from Issue 5 or 6.
     * @param _isFive True if it is from Issue 5 (is odd)
     */
    function _burn56(
        uint256[] calldata _tokenIds56,
        bool _isMainBurn,
        bool _isFive
    ) internal {
        uint256 size = _tokenIds56.length;

        // Before burning Issues 56, it needs to check Ownership and if it is Issue 5 and 6 token ids range
        // If it is even, it is from Issue 6
        for (uint256 i; i < size; ) {
            require(
                huxleyComics56.ownerOf(_tokenIds56[i]) == msg.sender,
                "AI: Not owner 56"
            );

            if (_isMainBurn) {
                if (_isFive) {
                    require(!isEven(_tokenIds56[i]), "AI: Not Issue 5"); // odd tokenId is Issue 5
                } else {
                    require(isEven(_tokenIds56[i]), "AI: Not Issue 6"); // even tokenId is Issue 6
                }
            }

            unchecked {
                i++;
            }
        }

        if (size > 0) {
            huxleyComics56.burnBatch(_tokenIds56);
        }
    }

    /**
     * Before minting, it needs to get the Type
     * For GenesisToken, amount is the total GenesisToken burned * typeAmount
     * For MainBurn (2x Issue 4, 5 and6), is total burned / 2 (it is 2 tokens per Issue) * typeAmount
     * For NormalBurn (Burn of any Comic Issue), it is total burned / 2 (it is 2 tokens per Issue) * typeAmount
     *
     * @param _mintMethod Burn method. It can be GTSBurn, MainBurn or NormalBurn
     * @param _burnMethodQuantity Quantity of tokens for the burn method. I reverts if it is 0 (zero)
     * @param _type Wallet type to determine total amount of AI tokens to mint
     * @param _typeSignature Signature confirming the type
     */
    function _mintAI(
        MintMethods _mintMethod,
        uint256 _burnMethodQuantity,
        uint256 _type,
        bytes calldata _typeSignature
    ) internal {
        // is type correct signed?
        require(hasValidType(_type, _typeSignature));

        // Gets amount of AI tokens to be minted
        uint256 typeAmount = _getTypeAmount(_mintMethod, _type);

        // Mint AI ERC721A Tokens
        aiToken.mint(msg.sender, _burnMethodQuantity * typeAmount);
    }

    /**
     * Return amount of tokens to be minted depending of the type and Burn method.
     * @param _mintMethod It can be GTSBurn, MainBurn or NormalBurn
     * @param _type Type can be 1, 2 or 3
     */
    function _getTypeAmount(
        MintMethods _mintMethod,
        uint256 _type
    ) internal pure returns (uint256 typeAmount) {
        if (_mintMethod == MintMethods.GTSBurn) {
            if (_type == 3) {
                typeAmount = 11;
            } else if (_type == 2) {
                typeAmount = 9;
            } else {
                typeAmount = 5;
            }
        } else if (_mintMethod == MintMethods.MainBurn) {
            if (_type == 3) {
                typeAmount = 10;
            } else if (_type == 2) {
                typeAmount = 8;
            } else {
                typeAmount = 4;
            }
        } else {
            if (_type == 3) {
                typeAmount = 3;
            } else if (_type == 2) {
                typeAmount = 2;
            } else {
                typeAmount = 1;
            }
        }
    }

    /**
     * Verify type signature.
     * @param _type Type can be 1, 2 or 3
     * @param _typeSignature Signature to confirm wallet type
     */
    function hasValidType(
        uint256 _type,
        bytes calldata _typeSignature
    ) internal view returns (bool) {
        bytes32 result = keccak256(abi.encodePacked(_type, msg.sender));

        bytes32 hash = keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n32", result)
        );
        return signer.isValidSignatureNow(hash, _typeSignature);
    }

    /**
     * Set AI Token contract. OnlyOwner can call it
     * @param _addr  AI Token address
     */
    function setAIToken(address _addr) external onlyOwner {
        aiToken = IAIToken(_addr);
    }

    /**
     * @dev Updates address of 'signer'. OnlyOwner can call it
     * @param _signer  New address for 'signer'
     */
    function setSigner(address _signer) external onlyOwner {
        signer = _signer;
    }

    /// @dev check if a number is even - it is used to check if token id is from Issue 5 or Issue 6
    function isEven(uint256 _num) internal pure returns (bool) {
        return _num % 2 == 0;
    }

    /// @dev Pause burn functions
    function pause() external onlyOwner {
        _pause();
    }

    /// @dev Unpause burn functions
    function unpause() external onlyOwner {
        _unpause();
    }
}

File 2 of 16 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 3 of 16 : IERC1271.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 *
 * _Available since v4.1._
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

File 4 of 16 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 5 of 16 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like 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 6 of 16 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

File 7 of 16 : SignatureChecker.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/SignatureChecker.sol)

pragma solidity ^0.8.0;

import "./ECDSA.sol";
import "../../interfaces/IERC1271.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
 * Argent and Gnosis Safe.
 *
 * _Available since v4.1._
 */
library SignatureChecker {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
        (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);
        return
            (error == ECDSA.RecoverError.NoError && recovered == signer) ||
            isValidERC1271SignatureNow(signer, hash, signature);
    }

    /**
     * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
     * against the signer smart contract using ERC1271.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidERC1271SignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
        );
        return (success &&
            result.length >= 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
    }
}

File 8 of 16 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

File 9 of 16 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

File 10 of 16 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 11 of 16 : IAIToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

import "erc721a/contracts/IERC721A.sol";

interface IAIToken is IERC721A {
    function mint(address _account, uint256 _quantity) external;

    function burn(uint256[] memory _tokenIds) external;
}

File 12 of 16 : IBurnable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

import "erc721a/contracts/interfaces/IERC721A.sol";

interface IBurnable is IERC721A{
    function burnBatch(uint256[] memory _tokenIds) external;
}

File 13 of 16 : IERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

interface IERC721 {
    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    function burn(uint256 _tokenId) external;
}

File 14 of 16 : IGenesisToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

interface IGenesisToken {
    function mint(
        address account,
        uint256 category,
        bytes memory data
    ) external;

    function mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) external;

    function burnBatch(
        address account,
        uint256[] memory ids,
        uint256[] memory amounts
    ) external;

    function redeem(address _account, uint256 _category) external;

    function privateMintBatch(
        address _account,
        uint256 _amountToMint,
        uint256 _category,
        bytes memory _data
    ) external;
}

File 15 of 16 : IERC721A.sol
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

/**
 * @dev Interface of ERC721A.
 */
interface IERC721A {
    /**
     * The caller must own the token or be an approved operator.
     */
    error ApprovalCallerNotOwnerNorApproved();

    /**
     * The token does not exist.
     */
    error ApprovalQueryForNonexistentToken();

    /**
     * Cannot query the balance for the zero address.
     */
    error BalanceQueryForZeroAddress();

    /**
     * Cannot mint to the zero address.
     */
    error MintToZeroAddress();

    /**
     * The quantity of tokens minted must be more than zero.
     */
    error MintZeroQuantity();

    /**
     * The token does not exist.
     */
    error OwnerQueryForNonexistentToken();

    /**
     * The caller must own the token or be an approved operator.
     */
    error TransferCallerNotOwnerNorApproved();

    /**
     * The token must be owned by `from`.
     */
    error TransferFromIncorrectOwner();

    /**
     * Cannot safely transfer to a contract that does not implement the
     * ERC721Receiver interface.
     */
    error TransferToNonERC721ReceiverImplementer();

    /**
     * Cannot transfer to the zero address.
     */
    error TransferToZeroAddress();

    /**
     * The token does not exist.
     */
    error URIQueryForNonexistentToken();

    /**
     * The `quantity` minted with ERC2309 exceeds the safety limit.
     */
    error MintERC2309QuantityExceedsLimit();

    /**
     * The `extraData` cannot be set on an unintialized ownership slot.
     */
    error OwnershipNotInitializedForExtraData();

    // =============================================================
    //                            STRUCTS
    // =============================================================

    struct TokenOwnership {
        // The address of the owner.
        address addr;
        // Stores the start time of ownership with minimal overhead for tokenomics.
        uint64 startTimestamp;
        // Whether the token has been burned.
        bool burned;
        // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
        uint24 extraData;
    }

    // =============================================================
    //                         TOKEN COUNTERS
    // =============================================================

    /**
     * @dev Returns the total number of tokens in existence.
     * Burned tokens will reduce the count.
     * To get the total number of tokens minted, please see {_totalMinted}.
     */
    function totalSupply() external view returns (uint256);

    // =============================================================
    //                            IERC165
    // =============================================================

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);

    // =============================================================
    //                            IERC721
    // =============================================================

    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables
     * (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in `owner`'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`,
     * checking first that contract recipients are aware of the ERC721 protocol
     * to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move
     * this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement
     * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external payable;

    /**
     * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom}
     * whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token
     * by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the
     * zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external payable;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom}
     * for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    // =============================================================
    //                        IERC721Metadata
    // =============================================================

    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);

    // =============================================================
    //                           IERC2309
    // =============================================================

    /**
     * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
     * (inclusive) is transferred from `from` to `to`, as defined in the
     * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
     *
     * See {_mintERC2309} for more details.
     */
    event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}

File 16 of 16 : IERC721A.sol
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import '../IERC721A.sol';

Settings
{
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_huxley123","type":"address"},{"internalType":"address","name":"_huxley4","type":"address"},{"internalType":"address","name":"_huxley56","type":"address"},{"internalType":"address","name":"_genesis","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"aiToken","outputs":[{"internalType":"contract IAIToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_categories","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256","name":"_type","type":"uint256"},{"internalType":"bytes","name":"_typeSignature","type":"bytes"}],"name":"burnGenesis","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds123","type":"uint256[]"},{"internalType":"uint256[]","name":"_tokenIds4","type":"uint256[]"},{"internalType":"uint256[]","name":"_tokenIds56","type":"uint256[]"},{"internalType":"uint256","name":"_type","type":"uint256"},{"internalType":"bytes","name":"_typeSignature","type":"bytes"}],"name":"burnNormalComics123456","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"genesisToken","outputs":[{"internalType":"contract IGenesisToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"huxleyComics","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"huxleyComics4","outputs":[{"internalType":"contract IBurnable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"huxleyComics56","outputs":[{"internalType":"contract IBurnable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds4","type":"uint256[]"},{"internalType":"uint256[]","name":"_tokenIds5","type":"uint256[]"},{"internalType":"uint256[]","name":"_tokenIds6","type":"uint256[]"},{"internalType":"uint256","name":"_type","type":"uint256"},{"internalType":"bytes","name":"_typeSignature","type":"bytes"}],"name":"mainBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"setAIToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101006040523480156200001257600080fd5b5060405162002d4938038062002d49833981810160405281019062000038919062000372565b60008060006101000a81548160ff02191690831515021790555062000072620000666200015c60201b60201c565b6200016460201b60201c565b8373ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff16815250508273ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff16815250508173ffffffffffffffffffffffffffffffffffffffff1660e08173ffffffffffffffffffffffffffffffffffffffff16815250508073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1681525050620001526200022960201b60201c565b5050505062000495565b600033905090565b60008060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600060016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b620002396200029d60201b60201c565b60016000806101000a81548160ff0219169083151502179055507f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258620002846200015c60201b60201c565b604051620002939190620003f5565b60405180910390a1565b620002ad620002f260201b60201c565b15620002f0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620002e79062000473565b60405180910390fd5b565b60008060009054906101000a900460ff16905090565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200033a826200030d565b9050919050565b6200034c816200032d565b81146200035857600080fd5b50565b6000815190506200036c8162000341565b92915050565b600080600080608085870312156200038f576200038e62000308565b5b60006200039f878288016200035b565b9450506020620003b2878288016200035b565b9350506040620003c5878288016200035b565b9250506060620003d8878288016200035b565b91505092959194509250565b620003ef816200032d565b82525050565b60006020820190506200040c6000830184620003e4565b92915050565b600082825260208201905092915050565b7f5061757361626c653a2070617573656400000000000000000000000000000000600082015250565b60006200045b60108362000412565b9150620004688262000423565b602082019050919050565b600060208201905081810360008301526200048e816200044c565b9050919050565b60805160a05160c05160e05161284962000500600039600081816105d601528181610dd60152610fcc0152600081816105b20152818161087401526109940152600081816106200152818161125401526112fc01526000818161065401526107c401526128496000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c80638456cb59116100a2578063c820352111610071578063c820352114610234578063cc7afff814610252578063e6e1acaa14610270578063f2fde38b1461028c578063fda237f8146102a85761010b565b80638456cb59146101d05780638da5cb5b146101da578063a4f40394146101f8578063c7c078cc146102165761010b565b80633f4ba83a116100de5780633f4ba83a146101825780635c975abb1461018c5780636c19e783146101aa578063715018a6146101c65761010b565b80631237252f14610110578063238ac9331461012c5780632985055f1461014a5780632dafc6b814610166575b600080fd5b61012a600480360381019061012591906117fb565b6102c6565b005b61013461043c565b6040516101419190611938565b60405180910390f35b610164600480360381019061015f919061197f565b610462565b005b610180600480360381019061017b91906117fb565b6104ae565b005b61018a6104ed565b005b6101946104ff565b6040516101a191906119c7565b60405180910390f35b6101c460048036038101906101bf919061197f565b610515565b005b6101ce610561565b005b6101d8610575565b005b6101e2610587565b6040516101ef9190611938565b60405180910390f35b6102006105b0565b60405161020d9190611a41565b60405180910390f35b61021e6105d4565b60405161022b9190611a41565b60405180910390f35b61023c6105f8565b6040516102499190611a7d565b60405180910390f35b61025a61061e565b6040516102679190611ab9565b60405180910390f35b61028a60048036038101906102859190611ad4565b610642565b005b6102a660048036038101906102a1919061197f565b61073f565b005b6102b06107c2565b6040516102bd9190611bbe565b60405180910390f35b6102ce6107e6565b600089899050905060008888905090506000878790509050818314610328576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161031f90611c36565b60405180910390fd5b80831461036a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161036190611ca2565b60405180910390fd5b60028310156103ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103a590611d34565b60405180910390fd5b6103b783610830565b6103f6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103ed90611da0565b60405180910390fd5b6104008c8c610848565b61040a8a8a610a25565b6104148888610a36565b61042e60016002856104269190611e1e565b888888610a48565b505050505050505050505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61046a610b0c565b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6104b66107e6565b60006104c68a8a8a8a8a8a610b8a565b90506104e1600280836104d99190611e1e565b868686610a48565b50505050505050505050565b6104f5610b0c565b6104fd610c21565b565b60008060009054906101000a900460ff16905090565b61051d610b0c565b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610569610b0c565b6105736000610c83565b565b61057d610b0c565b610585610d48565b565b60008060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b7f000000000000000000000000000000000000000000000000000000000000000081565b61064a6107e6565b60008585905090507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16636b20c454338a8a8a8a6040518663ffffffff1660e01b81526004016106b3959493929190611eca565b600060405180830381600087803b1580156106cd57600080fd5b505af11580156106e1573d6000803e3d6000fd5b505050506000805b828110156107255787878281811061070457610703611f13565b5b90506020020135826107169190611f42565b915080806001019150506106e9565b50610734600082878787610a48565b505050505050505050565b610747610b0c565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036107b6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107ad90611fe8565b60405180910390fd5b6107bf81610c83565b50565b7f000000000000000000000000000000000000000000000000000000000000000081565b6107ee6104ff565b1561082e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161082590612054565b60405180910390fd5b565b6000806002836108409190612074565b149050919050565b600082829050905060005b81811015610988573373ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16636352211e8686858181106108c1576108c0611f13565b5b905060200201356040518263ffffffff1660e01b81526004016108e491906120b4565b602060405180830381865afa158015610901573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061092591906120e4565b73ffffffffffffffffffffffffffffffffffffffff161461097b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109729061215d565b60405180910390fd5b8080600101915050610853565b506000811115610a20577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663e4623c1b84846040518363ffffffff1660e01b81526004016109ed92919061217d565b600060405180830381600087803b158015610a0757600080fd5b505af1158015610a1b573d6000803e3d6000fd5b505050505b505050565b610a328282600180610daa565b5050565b610a44828260016000610daa565b5050565b610a5383838361105f565b610a5c57600080fd5b6000610a688685611154565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166340c10f19338388610ab591906121a1565b6040518363ffffffff1660e01b8152600401610ad29291906121e3565b600060405180830381600087803b158015610aec57600080fd5b505af1158015610b00573d6000803e3d6000fd5b50505050505050505050565b610b14611237565b73ffffffffffffffffffffffffffffffffffffffff16610b32610587565b73ffffffffffffffffffffffffffffffffffffffff1614610b88576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7f90612258565b60405180910390fd5b565b6000828290508585905088889050610ba29190611f42565b610bac9190611f42565b9050610bb781610830565b610bf6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bed906122c4565b60405180910390fd5b610c00878761123f565b610c0a8585610848565b610c178383600080610daa565b9695505050505050565b610c296113b1565b60008060006101000a81548160ff0219169083151502179055507f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa610c6c611237565b604051610c799190611938565b60405180910390a1565b60008060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600060016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b610d506107e6565b60016000806101000a81548160ff0219169083151502179055507f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258610d93611237565b604051610da09190611938565b60405180910390a1565b600084849050905060005b81811015610fc0573373ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16636352211e888885818110610e2357610e22611f13565b5b905060200201356040518263ffffffff1660e01b8152600401610e4691906120b4565b602060405180830381865afa158015610e63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8791906120e4565b73ffffffffffffffffffffffffffffffffffffffff1614610edd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ed490612330565b60405180910390fd5b8315610fb3578215610f5057610f0b868683818110610eff57610efe611f13565b5b90506020020135610830565b15610f4b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f429061239c565b60405180910390fd5b610fb2565b610f72868683818110610f6657610f65611f13565b5b90506020020135610830565b610fb1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fa890612408565b60405180910390fd5b5b5b8080600101915050610db5565b506000811115611058577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663e4623c1b86866040518363ffffffff1660e01b815260040161102592919061217d565b600060405180830381600087803b15801561103f57600080fd5b505af1158015611053573d6000803e3d6000fd5b505050505b5050505050565b6000808433604051602001611075929190612491565b6040516020818303038152906040528051906020012090506000816040516020016110a0919061253f565b6040516020818303038152906040528051906020012090506111498186868080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166113fa9092919063ffffffff16565b925050509392505050565b600080600281111561116957611168612565565b5b83600281111561117c5761117b612565565b5b036111ae576003820361119257600b90506111a9565b600282036111a357600990506111a8565b600590505b5b611231565b600160028111156111c2576111c1612565565b5b8360028111156111d5576111d4612565565b5b0361120757600382036111eb57600a9050611202565b600282036111fc5760089050611201565b600490505b5b611230565b60038203611218576003905061122f565b60028203611229576002905061122e565b600190505b5b5b5b92915050565b600033905090565b600082829050905060005b818110156113ab577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166323b872dd33308787868181106112a3576112a2611f13565b5b905060200201356040518463ffffffff1660e01b81526004016112c893929190612594565b600060405180830381600087803b1580156112e257600080fd5b505af11580156112f6573d6000803e3d6000fd5b505050507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166342966c6885858481811061134957611348611f13565b5b905060200201356040518263ffffffff1660e01b815260040161136c91906120b4565b600060405180830381600087803b15801561138657600080fd5b505af115801561139a573d6000803e3d6000fd5b50505050808060010191505061124a565b50505050565b6113b96104ff565b6113f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113ef90612617565b60405180910390fd5b565b60008060006114098585611489565b915091506000600481111561142157611420612565565b5b81600481111561143457611433612565565b5b14801561146c57508573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b8061147e575061147d8686866114da565b5b925050509392505050565b60008060418351036114ca5760008060006020860151925060408601519150606086015160001a90506114be8782858561161e565b945094505050506114d3565b60006002915091505b9250929050565b60008060008573ffffffffffffffffffffffffffffffffffffffff16631626ba7e60e01b86866040516024016115119291906126d6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161157b9190612742565b600060405180830381855afa9150503d80600081146115b6576040519150601f19603f3d011682016040523d82523d6000602084013e6115bb565b606091505b50915091508180156115cf57506020815110155b80156116135750631626ba7e60e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916818060200190518101906116119190612785565b145b925050509392505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08360001c11156116595760006003915091506116f7565b60006001878787876040516000815260200160405260405161167e94939291906127ce565b6020604051602081039080840390855afa1580156116a0573d6000803e3d6000fd5b505050602060405103519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036116ee576000600192509250506116f7565b80600092509250505b94509492505050565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f84011261172f5761172e61170a565b5b8235905067ffffffffffffffff81111561174c5761174b61170f565b5b60208301915083602082028301111561176857611767611714565b5b9250929050565b6000819050919050565b6117828161176f565b811461178d57600080fd5b50565b60008135905061179f81611779565b92915050565b60008083601f8401126117bb576117ba61170a565b5b8235905067ffffffffffffffff8111156117d8576117d761170f565b5b6020830191508360018202830111156117f4576117f3611714565b5b9250929050565b600080600080600080600080600060a08a8c03121561181d5761181c611700565b5b60008a013567ffffffffffffffff81111561183b5761183a611705565b5b6118478c828d01611719565b995099505060208a013567ffffffffffffffff81111561186a57611869611705565b5b6118768c828d01611719565b975097505060408a013567ffffffffffffffff81111561189957611898611705565b5b6118a58c828d01611719565b955095505060606118b88c828d01611790565b93505060808a013567ffffffffffffffff8111156118d9576118d8611705565b5b6118e58c828d016117a5565b92509250509295985092959850929598565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611922826118f7565b9050919050565b61193281611917565b82525050565b600060208201905061194d6000830184611929565b92915050565b61195c81611917565b811461196757600080fd5b50565b60008135905061197981611953565b92915050565b60006020828403121561199557611994611700565b5b60006119a38482850161196a565b91505092915050565b60008115159050919050565b6119c1816119ac565b82525050565b60006020820190506119dc60008301846119b8565b92915050565b6000819050919050565b6000611a07611a026119fd846118f7565b6119e2565b6118f7565b9050919050565b6000611a19826119ec565b9050919050565b6000611a2b82611a0e565b9050919050565b611a3b81611a20565b82525050565b6000602082019050611a566000830184611a32565b92915050565b6000611a6782611a0e565b9050919050565b611a7781611a5c565b82525050565b6000602082019050611a926000830184611a6e565b92915050565b6000611aa382611a0e565b9050919050565b611ab381611a98565b82525050565b6000602082019050611ace6000830184611aaa565b92915050565b60008060008060008060006080888a031215611af357611af2611700565b5b600088013567ffffffffffffffff811115611b1157611b10611705565b5b611b1d8a828b01611719565b9750975050602088013567ffffffffffffffff811115611b4057611b3f611705565b5b611b4c8a828b01611719565b95509550506040611b5f8a828b01611790565b935050606088013567ffffffffffffffff811115611b8057611b7f611705565b5b611b8c8a828b016117a5565b925092505092959891949750929550565b6000611ba882611a0e565b9050919050565b611bb881611b9d565b82525050565b6000602082019050611bd36000830184611baf565b92915050565b600082825260208201905092915050565b7f41493a20446966666572656e742073697a65203420616e642035000000000000600082015250565b6000611c20601a83611bd9565b9150611c2b82611bea565b602082019050919050565b60006020820190508181036000830152611c4f81611c13565b9050919050565b7f41493a20446966666572656e742073697a65203420616e642036000000000000600082015250565b6000611c8c601a83611bd9565b9150611c9782611c56565b602082019050919050565b60006020820190508181036000830152611cbb81611c7f565b9050919050565b7f41493a204172726179203420686173206c657373207468616e203220746f6b6560008201527f6e73000000000000000000000000000000000000000000000000000000000000602082015250565b6000611d1e602283611bd9565b9150611d2982611cc2565b604082019050919050565b60006020820190508181036000830152611d4d81611d11565b9050919050565b7f41493a2053697a65206d757374206265206576656e0000000000000000000000600082015250565b6000611d8a601583611bd9565b9150611d9582611d54565b602082019050919050565b60006020820190508181036000830152611db981611d7d565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000611e298261176f565b9150611e348361176f565b925082611e4457611e43611dc0565b5b828204905092915050565b600082825260208201905092915050565b600080fd5b82818337505050565b6000611e7a8385611e4f565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115611ead57611eac611e60565b5b602083029250611ebe838584611e65565b82840190509392505050565b6000606082019050611edf6000830188611929565b8181036020830152611ef2818688611e6e565b90508181036040830152611f07818486611e6e565b90509695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000611f4d8261176f565b9150611f588361176f565b9250828201905080821115611f7057611f6f611def565b5b92915050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000611fd2602683611bd9565b9150611fdd82611f76565b604082019050919050565b6000602082019050818103600083015261200181611fc5565b9050919050565b7f5061757361626c653a2070617573656400000000000000000000000000000000600082015250565b600061203e601083611bd9565b915061204982612008565b602082019050919050565b6000602082019050818103600083015261206d81612031565b9050919050565b600061207f8261176f565b915061208a8361176f565b92508261209a57612099611dc0565b5b828206905092915050565b6120ae8161176f565b82525050565b60006020820190506120c960008301846120a5565b92915050565b6000815190506120de81611953565b92915050565b6000602082840312156120fa576120f9611700565b5b6000612108848285016120cf565b91505092915050565b7f41493a204e6f74206f776e657220340000000000000000000000000000000000600082015250565b6000612147600f83611bd9565b915061215282612111565b602082019050919050565b600060208201905081810360008301526121768161213a565b9050919050565b60006020820190508181036000830152612198818486611e6e565b90509392505050565b60006121ac8261176f565b91506121b78361176f565b92508282026121c58161176f565b915082820484148315176121dc576121db611def565b5b5092915050565b60006040820190506121f86000830185611929565b61220560208301846120a5565b9392505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000612242602083611bd9565b915061224d8261220c565b602082019050919050565b6000602082019050818103600083015261227181612235565b9050919050565b7f41493a2053686f756c6420626520696e20706169727300000000000000000000600082015250565b60006122ae601683611bd9565b91506122b982612278565b602082019050919050565b600060208201905081810360008301526122dd816122a1565b9050919050565b7f41493a204e6f74206f776e657220353600000000000000000000000000000000600082015250565b600061231a601083611bd9565b9150612325826122e4565b602082019050919050565b600060208201905081810360008301526123498161230d565b9050919050565b7f41493a204e6f7420497373756520350000000000000000000000000000000000600082015250565b6000612386600f83611bd9565b915061239182612350565b602082019050919050565b600060208201905081810360008301526123b581612379565b9050919050565b7f41493a204e6f7420497373756520360000000000000000000000000000000000600082015250565b60006123f2600f83611bd9565b91506123fd826123bc565b602082019050919050565b60006020820190508181036000830152612421816123e5565b9050919050565b6000819050919050565b61244361243e8261176f565b612428565b82525050565b60008160601b9050919050565b600061246182612449565b9050919050565b600061247382612456565b9050919050565b61248b61248682611917565b612468565b82525050565b600061249d8285612432565b6020820191506124ad828461247a565b6014820191508190509392505050565b600081905092915050565b7f19457468657265756d205369676e6564204d6573736167653a0a333200000000600082015250565b60006124fe601c836124bd565b9150612509826124c8565b601c82019050919050565b6000819050919050565b6000819050919050565b61253961253482612514565b61251e565b82525050565b600061254a826124f1565b91506125568284612528565b60208201915081905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60006060820190506125a96000830186611929565b6125b66020830185611929565b6125c360408301846120a5565b949350505050565b7f5061757361626c653a206e6f7420706175736564000000000000000000000000600082015250565b6000612601601483611bd9565b915061260c826125cb565b602082019050919050565b60006020820190508181036000830152612630816125f4565b9050919050565b61264081612514565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015612680578082015181840152602081019050612665565b60008484015250505050565b6000601f19601f8301169050919050565b60006126a882612646565b6126b28185612651565b93506126c2818560208601612662565b6126cb8161268c565b840191505092915050565b60006040820190506126eb6000830185612637565b81810360208301526126fd818461269d565b90509392505050565b600081905092915050565b600061271c82612646565b6127268185612706565b9350612736818560208601612662565b80840191505092915050565b600061274e8284612711565b915081905092915050565b61276281612514565b811461276d57600080fd5b50565b60008151905061277f81612759565b92915050565b60006020828403121561279b5761279a611700565b5b60006127a984828501612770565b91505092915050565b600060ff82169050919050565b6127c8816127b2565b82525050565b60006080820190506127e36000830187612637565b6127f060208301866127bf565b6127fd6040830185612637565b61280a6060830184612637565b9594505050505056fea2646970667358221220fef27ea96ac4fd31d541a8a1b4872253a93c3efa9d0384999b8a03e4547abbcb64736f6c634300081300330000000000000000000000009ca8887d13bc4591ae36972702fdf9de2c97957f000000000000000000000000c65ef668114a1d0446f960ac4caa8c080efab78600000000000000000000000042fe737749683595e4315b443eadcc9346a994d900000000000000000000000038221a026370360d8c0767c232f39f72f8f3ffde

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061010b5760003560e01c80638456cb59116100a2578063c820352111610071578063c820352114610234578063cc7afff814610252578063e6e1acaa14610270578063f2fde38b1461028c578063fda237f8146102a85761010b565b80638456cb59146101d05780638da5cb5b146101da578063a4f40394146101f8578063c7c078cc146102165761010b565b80633f4ba83a116100de5780633f4ba83a146101825780635c975abb1461018c5780636c19e783146101aa578063715018a6146101c65761010b565b80631237252f14610110578063238ac9331461012c5780632985055f1461014a5780632dafc6b814610166575b600080fd5b61012a600480360381019061012591906117fb565b6102c6565b005b61013461043c565b6040516101419190611938565b60405180910390f35b610164600480360381019061015f919061197f565b610462565b005b610180600480360381019061017b91906117fb565b6104ae565b005b61018a6104ed565b005b6101946104ff565b6040516101a191906119c7565b60405180910390f35b6101c460048036038101906101bf919061197f565b610515565b005b6101ce610561565b005b6101d8610575565b005b6101e2610587565b6040516101ef9190611938565b60405180910390f35b6102006105b0565b60405161020d9190611a41565b60405180910390f35b61021e6105d4565b60405161022b9190611a41565b60405180910390f35b61023c6105f8565b6040516102499190611a7d565b60405180910390f35b61025a61061e565b6040516102679190611ab9565b60405180910390f35b61028a60048036038101906102859190611ad4565b610642565b005b6102a660048036038101906102a1919061197f565b61073f565b005b6102b06107c2565b6040516102bd9190611bbe565b60405180910390f35b6102ce6107e6565b600089899050905060008888905090506000878790509050818314610328576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161031f90611c36565b60405180910390fd5b80831461036a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161036190611ca2565b60405180910390fd5b60028310156103ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103a590611d34565b60405180910390fd5b6103b783610830565b6103f6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103ed90611da0565b60405180910390fd5b6104008c8c610848565b61040a8a8a610a25565b6104148888610a36565b61042e60016002856104269190611e1e565b888888610a48565b505050505050505050505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61046a610b0c565b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6104b66107e6565b60006104c68a8a8a8a8a8a610b8a565b90506104e1600280836104d99190611e1e565b868686610a48565b50505050505050505050565b6104f5610b0c565b6104fd610c21565b565b60008060009054906101000a900460ff16905090565b61051d610b0c565b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610569610b0c565b6105736000610c83565b565b61057d610b0c565b610585610d48565b565b60008060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b7f000000000000000000000000c65ef668114a1d0446f960ac4caa8c080efab78681565b7f00000000000000000000000042fe737749683595e4315b443eadcc9346a994d981565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b7f0000000000000000000000009ca8887d13bc4591ae36972702fdf9de2c97957f81565b61064a6107e6565b60008585905090507f00000000000000000000000038221a026370360d8c0767c232f39f72f8f3ffde73ffffffffffffffffffffffffffffffffffffffff16636b20c454338a8a8a8a6040518663ffffffff1660e01b81526004016106b3959493929190611eca565b600060405180830381600087803b1580156106cd57600080fd5b505af11580156106e1573d6000803e3d6000fd5b505050506000805b828110156107255787878281811061070457610703611f13565b5b90506020020135826107169190611f42565b915080806001019150506106e9565b50610734600082878787610a48565b505050505050505050565b610747610b0c565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036107b6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107ad90611fe8565b60405180910390fd5b6107bf81610c83565b50565b7f00000000000000000000000038221a026370360d8c0767c232f39f72f8f3ffde81565b6107ee6104ff565b1561082e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161082590612054565b60405180910390fd5b565b6000806002836108409190612074565b149050919050565b600082829050905060005b81811015610988573373ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000c65ef668114a1d0446f960ac4caa8c080efab78673ffffffffffffffffffffffffffffffffffffffff16636352211e8686858181106108c1576108c0611f13565b5b905060200201356040518263ffffffff1660e01b81526004016108e491906120b4565b602060405180830381865afa158015610901573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061092591906120e4565b73ffffffffffffffffffffffffffffffffffffffff161461097b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109729061215d565b60405180910390fd5b8080600101915050610853565b506000811115610a20577f000000000000000000000000c65ef668114a1d0446f960ac4caa8c080efab78673ffffffffffffffffffffffffffffffffffffffff1663e4623c1b84846040518363ffffffff1660e01b81526004016109ed92919061217d565b600060405180830381600087803b158015610a0757600080fd5b505af1158015610a1b573d6000803e3d6000fd5b505050505b505050565b610a328282600180610daa565b5050565b610a44828260016000610daa565b5050565b610a5383838361105f565b610a5c57600080fd5b6000610a688685611154565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166340c10f19338388610ab591906121a1565b6040518363ffffffff1660e01b8152600401610ad29291906121e3565b600060405180830381600087803b158015610aec57600080fd5b505af1158015610b00573d6000803e3d6000fd5b50505050505050505050565b610b14611237565b73ffffffffffffffffffffffffffffffffffffffff16610b32610587565b73ffffffffffffffffffffffffffffffffffffffff1614610b88576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b7f90612258565b60405180910390fd5b565b6000828290508585905088889050610ba29190611f42565b610bac9190611f42565b9050610bb781610830565b610bf6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bed906122c4565b60405180910390fd5b610c00878761123f565b610c0a8585610848565b610c178383600080610daa565b9695505050505050565b610c296113b1565b60008060006101000a81548160ff0219169083151502179055507f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa610c6c611237565b604051610c799190611938565b60405180910390a1565b60008060019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600060016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b610d506107e6565b60016000806101000a81548160ff0219169083151502179055507f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258610d93611237565b604051610da09190611938565b60405180910390a1565b600084849050905060005b81811015610fc0573373ffffffffffffffffffffffffffffffffffffffff167f00000000000000000000000042fe737749683595e4315b443eadcc9346a994d973ffffffffffffffffffffffffffffffffffffffff16636352211e888885818110610e2357610e22611f13565b5b905060200201356040518263ffffffff1660e01b8152600401610e4691906120b4565b602060405180830381865afa158015610e63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8791906120e4565b73ffffffffffffffffffffffffffffffffffffffff1614610edd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ed490612330565b60405180910390fd5b8315610fb3578215610f5057610f0b868683818110610eff57610efe611f13565b5b90506020020135610830565b15610f4b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f429061239c565b60405180910390fd5b610fb2565b610f72868683818110610f6657610f65611f13565b5b90506020020135610830565b610fb1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fa890612408565b60405180910390fd5b5b5b8080600101915050610db5565b506000811115611058577f00000000000000000000000042fe737749683595e4315b443eadcc9346a994d973ffffffffffffffffffffffffffffffffffffffff1663e4623c1b86866040518363ffffffff1660e01b815260040161102592919061217d565b600060405180830381600087803b15801561103f57600080fd5b505af1158015611053573d6000803e3d6000fd5b505050505b5050505050565b6000808433604051602001611075929190612491565b6040516020818303038152906040528051906020012090506000816040516020016110a0919061253f565b6040516020818303038152906040528051906020012090506111498186868080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166113fa9092919063ffffffff16565b925050509392505050565b600080600281111561116957611168612565565b5b83600281111561117c5761117b612565565b5b036111ae576003820361119257600b90506111a9565b600282036111a357600990506111a8565b600590505b5b611231565b600160028111156111c2576111c1612565565b5b8360028111156111d5576111d4612565565b5b0361120757600382036111eb57600a9050611202565b600282036111fc5760089050611201565b600490505b5b611230565b60038203611218576003905061122f565b60028203611229576002905061122e565b600190505b5b5b5b92915050565b600033905090565b600082829050905060005b818110156113ab577f0000000000000000000000009ca8887d13bc4591ae36972702fdf9de2c97957f73ffffffffffffffffffffffffffffffffffffffff166323b872dd33308787868181106112a3576112a2611f13565b5b905060200201356040518463ffffffff1660e01b81526004016112c893929190612594565b600060405180830381600087803b1580156112e257600080fd5b505af11580156112f6573d6000803e3d6000fd5b505050507f0000000000000000000000009ca8887d13bc4591ae36972702fdf9de2c97957f73ffffffffffffffffffffffffffffffffffffffff166342966c6885858481811061134957611348611f13565b5b905060200201356040518263ffffffff1660e01b815260040161136c91906120b4565b600060405180830381600087803b15801561138657600080fd5b505af115801561139a573d6000803e3d6000fd5b50505050808060010191505061124a565b50505050565b6113b96104ff565b6113f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113ef90612617565b60405180910390fd5b565b60008060006114098585611489565b915091506000600481111561142157611420612565565b5b81600481111561143457611433612565565b5b14801561146c57508573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b8061147e575061147d8686866114da565b5b925050509392505050565b60008060418351036114ca5760008060006020860151925060408601519150606086015160001a90506114be8782858561161e565b945094505050506114d3565b60006002915091505b9250929050565b60008060008573ffffffffffffffffffffffffffffffffffffffff16631626ba7e60e01b86866040516024016115119291906126d6565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161157b9190612742565b600060405180830381855afa9150503d80600081146115b6576040519150601f19603f3d011682016040523d82523d6000602084013e6115bb565b606091505b50915091508180156115cf57506020815110155b80156116135750631626ba7e60e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916818060200190518101906116119190612785565b145b925050509392505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08360001c11156116595760006003915091506116f7565b60006001878787876040516000815260200160405260405161167e94939291906127ce565b6020604051602081039080840390855afa1580156116a0573d6000803e3d6000fd5b505050602060405103519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036116ee576000600192509250506116f7565b80600092509250505b94509492505050565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f84011261172f5761172e61170a565b5b8235905067ffffffffffffffff81111561174c5761174b61170f565b5b60208301915083602082028301111561176857611767611714565b5b9250929050565b6000819050919050565b6117828161176f565b811461178d57600080fd5b50565b60008135905061179f81611779565b92915050565b60008083601f8401126117bb576117ba61170a565b5b8235905067ffffffffffffffff8111156117d8576117d761170f565b5b6020830191508360018202830111156117f4576117f3611714565b5b9250929050565b600080600080600080600080600060a08a8c03121561181d5761181c611700565b5b60008a013567ffffffffffffffff81111561183b5761183a611705565b5b6118478c828d01611719565b995099505060208a013567ffffffffffffffff81111561186a57611869611705565b5b6118768c828d01611719565b975097505060408a013567ffffffffffffffff81111561189957611898611705565b5b6118a58c828d01611719565b955095505060606118b88c828d01611790565b93505060808a013567ffffffffffffffff8111156118d9576118d8611705565b5b6118e58c828d016117a5565b92509250509295985092959850929598565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611922826118f7565b9050919050565b61193281611917565b82525050565b600060208201905061194d6000830184611929565b92915050565b61195c81611917565b811461196757600080fd5b50565b60008135905061197981611953565b92915050565b60006020828403121561199557611994611700565b5b60006119a38482850161196a565b91505092915050565b60008115159050919050565b6119c1816119ac565b82525050565b60006020820190506119dc60008301846119b8565b92915050565b6000819050919050565b6000611a07611a026119fd846118f7565b6119e2565b6118f7565b9050919050565b6000611a19826119ec565b9050919050565b6000611a2b82611a0e565b9050919050565b611a3b81611a20565b82525050565b6000602082019050611a566000830184611a32565b92915050565b6000611a6782611a0e565b9050919050565b611a7781611a5c565b82525050565b6000602082019050611a926000830184611a6e565b92915050565b6000611aa382611a0e565b9050919050565b611ab381611a98565b82525050565b6000602082019050611ace6000830184611aaa565b92915050565b60008060008060008060006080888a031215611af357611af2611700565b5b600088013567ffffffffffffffff811115611b1157611b10611705565b5b611b1d8a828b01611719565b9750975050602088013567ffffffffffffffff811115611b4057611b3f611705565b5b611b4c8a828b01611719565b95509550506040611b5f8a828b01611790565b935050606088013567ffffffffffffffff811115611b8057611b7f611705565b5b611b8c8a828b016117a5565b925092505092959891949750929550565b6000611ba882611a0e565b9050919050565b611bb881611b9d565b82525050565b6000602082019050611bd36000830184611baf565b92915050565b600082825260208201905092915050565b7f41493a20446966666572656e742073697a65203420616e642035000000000000600082015250565b6000611c20601a83611bd9565b9150611c2b82611bea565b602082019050919050565b60006020820190508181036000830152611c4f81611c13565b9050919050565b7f41493a20446966666572656e742073697a65203420616e642036000000000000600082015250565b6000611c8c601a83611bd9565b9150611c9782611c56565b602082019050919050565b60006020820190508181036000830152611cbb81611c7f565b9050919050565b7f41493a204172726179203420686173206c657373207468616e203220746f6b6560008201527f6e73000000000000000000000000000000000000000000000000000000000000602082015250565b6000611d1e602283611bd9565b9150611d2982611cc2565b604082019050919050565b60006020820190508181036000830152611d4d81611d11565b9050919050565b7f41493a2053697a65206d757374206265206576656e0000000000000000000000600082015250565b6000611d8a601583611bd9565b9150611d9582611d54565b602082019050919050565b60006020820190508181036000830152611db981611d7d565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000611e298261176f565b9150611e348361176f565b925082611e4457611e43611dc0565b5b828204905092915050565b600082825260208201905092915050565b600080fd5b82818337505050565b6000611e7a8385611e4f565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115611ead57611eac611e60565b5b602083029250611ebe838584611e65565b82840190509392505050565b6000606082019050611edf6000830188611929565b8181036020830152611ef2818688611e6e565b90508181036040830152611f07818486611e6e565b90509695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000611f4d8261176f565b9150611f588361176f565b9250828201905080821115611f7057611f6f611def565b5b92915050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000611fd2602683611bd9565b9150611fdd82611f76565b604082019050919050565b6000602082019050818103600083015261200181611fc5565b9050919050565b7f5061757361626c653a2070617573656400000000000000000000000000000000600082015250565b600061203e601083611bd9565b915061204982612008565b602082019050919050565b6000602082019050818103600083015261206d81612031565b9050919050565b600061207f8261176f565b915061208a8361176f565b92508261209a57612099611dc0565b5b828206905092915050565b6120ae8161176f565b82525050565b60006020820190506120c960008301846120a5565b92915050565b6000815190506120de81611953565b92915050565b6000602082840312156120fa576120f9611700565b5b6000612108848285016120cf565b91505092915050565b7f41493a204e6f74206f776e657220340000000000000000000000000000000000600082015250565b6000612147600f83611bd9565b915061215282612111565b602082019050919050565b600060208201905081810360008301526121768161213a565b9050919050565b60006020820190508181036000830152612198818486611e6e565b90509392505050565b60006121ac8261176f565b91506121b78361176f565b92508282026121c58161176f565b915082820484148315176121dc576121db611def565b5b5092915050565b60006040820190506121f86000830185611929565b61220560208301846120a5565b9392505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000612242602083611bd9565b915061224d8261220c565b602082019050919050565b6000602082019050818103600083015261227181612235565b9050919050565b7f41493a2053686f756c6420626520696e20706169727300000000000000000000600082015250565b60006122ae601683611bd9565b91506122b982612278565b602082019050919050565b600060208201905081810360008301526122dd816122a1565b9050919050565b7f41493a204e6f74206f776e657220353600000000000000000000000000000000600082015250565b600061231a601083611bd9565b9150612325826122e4565b602082019050919050565b600060208201905081810360008301526123498161230d565b9050919050565b7f41493a204e6f7420497373756520350000000000000000000000000000000000600082015250565b6000612386600f83611bd9565b915061239182612350565b602082019050919050565b600060208201905081810360008301526123b581612379565b9050919050565b7f41493a204e6f7420497373756520360000000000000000000000000000000000600082015250565b60006123f2600f83611bd9565b91506123fd826123bc565b602082019050919050565b60006020820190508181036000830152612421816123e5565b9050919050565b6000819050919050565b61244361243e8261176f565b612428565b82525050565b60008160601b9050919050565b600061246182612449565b9050919050565b600061247382612456565b9050919050565b61248b61248682611917565b612468565b82525050565b600061249d8285612432565b6020820191506124ad828461247a565b6014820191508190509392505050565b600081905092915050565b7f19457468657265756d205369676e6564204d6573736167653a0a333200000000600082015250565b60006124fe601c836124bd565b9150612509826124c8565b601c82019050919050565b6000819050919050565b6000819050919050565b61253961253482612514565b61251e565b82525050565b600061254a826124f1565b91506125568284612528565b60208201915081905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60006060820190506125a96000830186611929565b6125b66020830185611929565b6125c360408301846120a5565b949350505050565b7f5061757361626c653a206e6f7420706175736564000000000000000000000000600082015250565b6000612601601483611bd9565b915061260c826125cb565b602082019050919050565b60006020820190508181036000830152612630816125f4565b9050919050565b61264081612514565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015612680578082015181840152602081019050612665565b60008484015250505050565b6000601f19601f8301169050919050565b60006126a882612646565b6126b28185612651565b93506126c2818560208601612662565b6126cb8161268c565b840191505092915050565b60006040820190506126eb6000830185612637565b81810360208301526126fd818461269d565b90509392505050565b600081905092915050565b600061271c82612646565b6127268185612706565b9350612736818560208601612662565b80840191505092915050565b600061274e8284612711565b915081905092915050565b61276281612514565b811461276d57600080fd5b50565b60008151905061277f81612759565b92915050565b60006020828403121561279b5761279a611700565b5b60006127a984828501612770565b91505092915050565b600060ff82169050919050565b6127c8816127b2565b82525050565b60006080820190506127e36000830187612637565b6127f060208301866127bf565b6127fd6040830185612637565b61280a6060830184612637565b9594505050505056fea2646970667358221220fef27ea96ac4fd31d541a8a1b4872253a93c3efa9d0384999b8a03e4547abbcb64736f6c63430008130033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000009ca8887d13bc4591ae36972702fdf9de2c97957f000000000000000000000000c65ef668114a1d0446f960ac4caa8c080efab78600000000000000000000000042fe737749683595e4315b443eadcc9346a994d900000000000000000000000038221a026370360d8c0767c232f39f72f8f3ffde

-----Decoded View---------------
Arg [0] : _huxley123 (address): 0x9Ca8887D13BC4591Ae36972702fDf9de2c97957f
Arg [1] : _huxley4 (address): 0xc65eF668114A1d0446F960Ac4cAa8c080eFAB786
Arg [2] : _huxley56 (address): 0x42fe737749683595e4315b443eadcC9346a994D9
Arg [3] : _genesis (address): 0x38221A026370360d8C0767C232F39F72F8f3fFde

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000009ca8887d13bc4591ae36972702fdf9de2c97957f
Arg [1] : 000000000000000000000000c65ef668114a1d0446f960ac4caa8c080efab786
Arg [2] : 00000000000000000000000042fe737749683595e4315b443eadcc9346a994d9
Arg [3] : 00000000000000000000000038221a026370360d8c0767c232f39f72f8f3ffde


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  ]

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.