creation_sourcecode: {{
    "language": "Solidity",
    "settings": {
      "evmVersion": "london",
      "libraries": {},
      "metadata": {
        "bytecodeHash": "ipfs",
        "useLiteralContent": true
      },
      "optimizer": {
        "enabled": true,
        "runs": 99999
      },
      "remappings": [],
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      }
    },
    "sources": {
      "@animoca/ethereum-contracts/contracts/access/ContractOwnership.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport {ContractOwnershipStorage} from \"./libraries/ContractOwnershipStorage.sol\";\nimport {ContractOwnershipBase} from \"./base/ContractOwnershipBase.sol\";\nimport {InterfaceDetection} from \"./../introspection/InterfaceDetection.sol\";\n\n/// @title ERC173 Contract Ownership Standard (immutable version).\n/// @dev See https://eips.ethereum.org/EIPS/eip-173\n/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.\nabstract contract ContractOwnership is ContractOwnershipBase, InterfaceDetection {\n    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;\n\n    /// @notice Initializes the storage with an initial contract owner.\n    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.\n    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.\n    /// @param initialOwner the initial contract owner.\n    constructor(address initialOwner) {\n        ContractOwnershipStorage.layout().constructorInit(initialOwner);\n    }\n}\n"
      },
      "@animoca/ethereum-contracts/contracts/access/base/ContractOwnershipBase.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport {IERC173} from \"./../interfaces/IERC173.sol\";\nimport {ContractOwnershipStorage} from \"./../libraries/ContractOwnershipStorage.sol\";\nimport {Context} from \"@openzeppelin/contracts/utils/Context.sol\";\n\n/// @title ERC173 Contract Ownership Standard (proxiable version).\n/// @dev See https://eips.ethereum.org/EIPS/eip-173\n/// @dev This contract is to be used via inheritance in a proxied implementation.\n/// @dev Note: This contract requires ERC165 (Interface Detection Standard).\nabstract contract ContractOwnershipBase is Context, IERC173 {\n    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;\n\n    /// @inheritdoc IERC173\n    function owner() public view virtual override returns (address) {\n        return ContractOwnershipStorage.layout().owner();\n    }\n\n    /// @inheritdoc IERC173\n    function transferOwnership(address newOwner) public virtual override {\n        ContractOwnershipStorage.layout().transferOwnership(_msgSender(), newOwner);\n    }\n}\n"
      },
      "@animoca/ethereum-contracts/contracts/access/interfaces/IERC173.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\n/// @title ERC-173 Contract Ownership Standard\n/// @dev See https://eips.ethereum.org/EIPS/eip-173\n/// @dev Note: the ERC-165 identifier for this interface is 0x7f5828d0\ninterface IERC173 {\n    /// @notice Emitted when the contract ownership changes.\n    /// @param previousOwner the previous contract owner.\n    /// @param newOwner the new contract owner.\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /// @notice Sets the address of the new contract owner.\n    /// @dev Reverts if the sender is not the contract owner.\n    /// @dev Emits an {OwnershipTransferred} event if `newOwner` is different from the current contract owner.\n    /// @param newOwner The address of the new contract owner. Using the zero address means renouncing ownership.\n    function transferOwnership(address newOwner) external;\n\n    /// @notice Gets the address of the contract owner.\n    /// @return contractOwner The address of the contract owner.\n    function owner() external view returns (address contractOwner);\n}\n"
      },
      "@animoca/ethereum-contracts/contracts/access/libraries/ContractOwnershipStorage.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport {IERC173} from \"./../interfaces/IERC173.sol\";\nimport {ProxyInitialization} from \"./../../proxy/libraries/ProxyInitialization.sol\";\nimport {InterfaceDetectionStorage} from \"./../../introspection/libraries/InterfaceDetectionStorage.sol\";\n\nlibrary ContractOwnershipStorage {\n    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;\n    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;\n\n    struct Layout {\n        address contractOwner;\n    }\n\n    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256(\"animoca.core.access.ContractOwnership.storage\")) - 1);\n    bytes32 internal constant PROXY_INIT_PHASE_SLOT = bytes32(uint256(keccak256(\"animoca.core.access.ContractOwnership.phase\")) - 1);\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /// @notice Initializes the storage with an initial contract owner (immutable version).\n    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.\n    /// @dev Note: This function should be called ONLY in the constructor of an immutable (non-proxied) contract.\n    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.\n    /// @param initialOwner The initial contract owner.\n    function constructorInit(Layout storage s, address initialOwner) internal {\n        if (initialOwner != address(0)) {\n            s.contractOwner = initialOwner;\n            emit OwnershipTransferred(address(0), initialOwner);\n        }\n        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC173).interfaceId, true);\n    }\n\n    /// @notice Initializes the storage with an initial contract owner (proxied version).\n    /// @notice Sets the proxy initialization phase to `1`.\n    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.\n    /// @dev Note: This function should be called ONLY in the init function of a proxied contract.\n    /// @dev Reverts if the proxy initialization phase is set to `1` or above.\n    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.\n    /// @param initialOwner The initial contract owner.\n    function proxyInit(Layout storage s, address initialOwner) internal {\n        ProxyInitialization.setPhase(PROXY_INIT_PHASE_SLOT, 1);\n        s.constructorInit(initialOwner);\n    }\n\n    /// @notice Sets the address of the new contract owner.\n    /// @dev Reverts if `sender` is not the contract owner.\n    /// @dev Emits an {OwnershipTransferred} event if `newOwner` is different from the current contract owner.\n    /// @param newOwner The address of the new contract owner. Using the zero address means renouncing ownership.\n    function transferOwnership(Layout storage s, address sender, address newOwner) internal {\n        address previousOwner = s.contractOwner;\n        require(sender == previousOwner, \"Ownership: not the owner\");\n        if (previousOwner != newOwner) {\n            s.contractOwner = newOwner;\n            emit OwnershipTransferred(previousOwner, newOwner);\n        }\n    }\n\n    /// @notice Gets the address of the contract owner.\n    /// @return contractOwner The address of the contract owner.\n    function owner(Layout storage s) internal view returns (address contractOwner) {\n        return s.contractOwner;\n    }\n\n    /// @notice Ensures that an account is the contract owner.\n    /// @dev Reverts if `account` is not the contract owner.\n    /// @param account The account.\n    function enforceIsContractOwner(Layout storage s, address account) internal view {\n        require(account == s.contractOwner, \"Ownership: not the owner\");\n    }\n\n    function layout() internal pure returns (Layout storage s) {\n        bytes32 position = LAYOUT_STORAGE_SLOT;\n        assembly {\n            s.slot := position\n        }\n    }\n}\n"
      },
      "@animoca/ethereum-contracts/contracts/introspection/InterfaceDetection.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport {IERC165} from \"./interfaces/IERC165.sol\";\nimport {InterfaceDetectionStorage} from \"./libraries/InterfaceDetectionStorage.sol\";\n\n/// @title ERC165 Interface Detection Standard (immutable or proxiable version).\n/// @dev This contract is to be used via inheritance in an immutable (non-proxied) or proxied implementation.\nabstract contract InterfaceDetection is IERC165 {\n    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;\n\n    /// @inheritdoc IERC165\n    function supportsInterface(bytes4 interfaceId) external view override returns (bool) {\n        return InterfaceDetectionStorage.layout().supportsInterface(interfaceId);\n    }\n}\n"
      },
      "@animoca/ethereum-contracts/contracts/introspection/interfaces/IERC165.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\n/// @title ERC165 Interface Detection Standard.\n/// @dev See https://eips.ethereum.org/EIPS/eip-165.\n/// @dev Note: The ERC-165 identifier for this interface is 0x01ffc9a7.\ninterface IERC165 {\n    /// @notice Returns whether this contract implements a given interface.\n    /// @dev Note: This function call must use less than 30 000 gas.\n    /// @param interfaceId the interface identifier to test.\n    /// @return supported True if the interface is supported, false if `interfaceId` is `0xffffffff` or if the interface is not supported.\n    function supportsInterface(bytes4 interfaceId) external view returns (bool supported);\n}\n"
      },
      "@animoca/ethereum-contracts/contracts/introspection/libraries/InterfaceDetectionStorage.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport {IERC165} from \"./../interfaces/IERC165.sol\";\n\nlibrary InterfaceDetectionStorage {\n    struct Layout {\n        mapping(bytes4 => bool) supportedInterfaces;\n    }\n\n    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256(\"animoca.core.introspection.InterfaceDetection.storage\")) - 1);\n\n    bytes4 internal constant ILLEGAL_INTERFACE_ID = 0xffffffff;\n\n    /// @notice Sets or unsets an ERC165 interface.\n    /// @dev Reverts if `interfaceId` is `0xffffffff`.\n    /// @param interfaceId the interface identifier.\n    /// @param supported True to set the interface, false to unset it.\n    function setSupportedInterface(Layout storage s, bytes4 interfaceId, bool supported) internal {\n        require(interfaceId != ILLEGAL_INTERFACE_ID, \"InterfaceDetection: wrong value\");\n        s.supportedInterfaces[interfaceId] = supported;\n    }\n\n    /// @notice Returns whether this contract implements a given interface.\n    /// @dev Note: This function call must use less than 30 000 gas.\n    /// @param interfaceId The interface identifier to test.\n    /// @return supported True if the interface is supported, false if `interfaceId` is `0xffffffff` or if the interface is not supported.\n    function supportsInterface(Layout storage s, bytes4 interfaceId) internal view returns (bool supported) {\n        if (interfaceId == ILLEGAL_INTERFACE_ID) {\n            return false;\n        }\n        if (interfaceId == type(IERC165).interfaceId) {\n            return true;\n        }\n        return s.supportedInterfaces[interfaceId];\n    }\n\n    function layout() internal pure returns (Layout storage s) {\n        bytes32 position = LAYOUT_STORAGE_SLOT;\n        assembly {\n            s.slot := position\n        }\n    }\n}\n"
      },
      "@animoca/ethereum-contracts/contracts/lifecycle/Pause.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport {PauseStorage} from \"./libraries/PauseStorage.sol\";\nimport {PauseBase} from \"./base/PauseBase.sol\";\nimport {ContractOwnership} from \"../access/ContractOwnership.sol\";\n\n/// @title Pausing mechanism (immutable version).\n/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.\nabstract contract Pause is PauseBase, ContractOwnership {\n    using PauseStorage for PauseStorage.Layout;\n\n    /// @notice Initializes the storage with an initial pause state.\n    /// @dev Emits a {Paused} event if `isPaused` is true.\n    /// @param isPaused The initial pause state.\n    constructor(bool isPaused) {\n        PauseStorage.layout().constructorInit(isPaused);\n    }\n}\n"
      },
      "@animoca/ethereum-contracts/contracts/lifecycle/base/PauseBase.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport {PauseStorage} from \"./../libraries/PauseStorage.sol\";\nimport {ContractOwnershipStorage} from \"./../../access/libraries/ContractOwnershipStorage.sol\";\nimport {Context} from \"@openzeppelin/contracts/utils/Context.sol\";\n\n/// @title Pausing mechanism (proxiable version).\n/// @dev This contract is to be used via inheritance in a proxied implementation.\n/// @dev Note: This contract requires ERC173 (Contract Ownership standard).\nabstract contract PauseBase is Context {\n    using PauseStorage for PauseStorage.Layout;\n    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;\n\n    /// @notice Emitted when the pause is triggered.\n    event Paused();\n\n    /// @notice Emitted when the pause is lifted.\n    event Unpaused();\n\n    /// @notice Pauses the contract.\n    /// @dev Reverts if the sender is not the contract owner.\n    /// @dev Reverts if the contract is paused.\n    /// @dev Emits a {Paused} event.\n    function pause() external {\n        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());\n        PauseStorage.layout().pause();\n    }\n\n    /// @notice Unpauses the contract.\n    /// @dev Reverts if the sender is not the contract owner.\n    /// @dev Reverts if the contract is not paused.\n    /// @dev Emits an {Unpaused} event.\n    function unpause() external {\n        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());\n        PauseStorage.layout().unpause();\n    }\n\n    /// @notice Gets the paused state of the contract.\n    /// @return isPaused The paused state of the contract.\n    function paused() external view returns (bool) {\n        return PauseStorage.layout().paused();\n    }\n}\n"
      },
      "@animoca/ethereum-contracts/contracts/lifecycle/libraries/PauseStorage.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport {ProxyInitialization} from \"./../../proxy/libraries/ProxyInitialization.sol\";\n\nlibrary PauseStorage {\n    using PauseStorage for PauseStorage.Layout;\n\n    struct Layout {\n        bool isPaused;\n    }\n\n    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256(\"animoca.core.lifecycle.Pause.storage\")) - 1);\n    bytes32 internal constant PROXY_INIT_PHASE_SLOT = bytes32(uint256(keccak256(\"animoca.core.lifecycle.Pause.phase\")) - 1);\n\n    event Paused();\n    event Unpaused();\n\n    /// @notice Initializes the storage with an initial pause state (immutable version).\n    /// @dev Note: This function should be called ONLY in the constructor of an immutable (non-proxied) contract.\n    /// @dev Emits a {Paused} event if `isPaused` is true.\n    /// @param isPaused The initial pause state.\n    function constructorInit(Layout storage s, bool isPaused) internal {\n        if (isPaused) {\n            s.isPaused = true;\n            emit Paused();\n        }\n    }\n\n    /// @notice Initializes the storage with an initial pause state (proxied version).\n    /// @notice Sets the proxy initialization phase to `1`.\n    /// @dev Note: This function should be called ONLY in the init function of a proxied contract.\n    /// @dev Reverts if the proxy initialization phase is set to `1` or above.\n    /// @dev Emits a {Paused} event if `isPaused` is true.\n    /// @param isPaused The initial pause state.\n    function proxyInit(Layout storage s, bool isPaused) internal {\n        ProxyInitialization.setPhase(PROXY_INIT_PHASE_SLOT, 1);\n        s.constructorInit(isPaused);\n    }\n\n    /// @notice Pauses the contract.\n    /// @dev Reverts if the contract is paused.\n    /// @dev Emits a {Paused} event.\n    function pause(Layout storage s) internal {\n        s.enforceIsNotPaused();\n        s.isPaused = true;\n        emit Paused();\n    }\n\n    /// @notice Unpauses the contract.\n    /// @dev Reverts if the contract is not paused.\n    /// @dev Emits an {Unpaused} event.\n    function unpause(Layout storage s) internal {\n        s.enforceIsPaused();\n        s.isPaused = false;\n        emit Unpaused();\n    }\n\n    /// @notice Gets the paused state of the contract.\n    /// @return isPaused The paused state of the contract.\n    function paused(Layout storage s) internal view returns (bool isPaused) {\n        return s.isPaused;\n    }\n\n    /// @notice Ensures that the contract is paused.\n    /// @dev Reverts if the contract is not paused.\n    function enforceIsPaused(Layout storage s) internal view {\n        require(s.isPaused, \"Pause: not paused\");\n    }\n\n    /// @notice Ensures that the contract is not paused.\n    /// @dev Reverts if the contract is paused.\n    function enforceIsNotPaused(Layout storage s) internal view {\n        require(!s.isPaused, \"Pause: paused\");\n    }\n\n    function layout() internal pure returns (Layout storage s) {\n        bytes32 position = LAYOUT_STORAGE_SLOT;\n        assembly {\n            s.slot := position\n        }\n    }\n}\n"
      },
      "@animoca/ethereum-contracts/contracts/proxy/libraries/ProxyInitialization.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.8;\n\nimport {StorageSlot} from \"@openzeppelin/contracts/utils/StorageSlot.sol\";\n\n/// @notice Multiple calls protection for storage-modifying proxy initialization functions.\nlibrary ProxyInitialization {\n    /// @notice Sets the initialization phase during a storage-modifying proxy initialization function.\n    /// @dev Reverts if `phase` has been reached already.\n    /// @param storageSlot the storage slot where `phase` is stored.\n    /// @param phase the initialization phase.\n    function setPhase(bytes32 storageSlot, uint256 phase) internal {\n        StorageSlot.Uint256Slot storage currentVersion = StorageSlot.getUint256Slot(storageSlot);\n        require(currentVersion.value < phase, \"Storage: phase reached\");\n        currentVersion.value = phase;\n    }\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/StorageSlot.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for reading and writing primitive types to specific storage slots.\n *\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\n * This library helps with reading and writing to such slots without the need for inline assembly.\n *\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\n *\n * Example usage to set ERC1967 implementation slot:\n * ```\n * contract ERC1967 {\n *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n *\n *     function _getImplementation() internal view returns (address) {\n *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\n *     }\n *\n *     function _setImplementation(address newImplementation) internal {\n *         require(Address.isContract(newImplementation), \"ERC1967: new implementation is not a contract\");\n *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\n *     }\n * }\n * ```\n *\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\n */\nlibrary StorageSlot {\n    struct AddressSlot {\n        address value;\n    }\n\n    struct BooleanSlot {\n        bool value;\n    }\n\n    struct Bytes32Slot {\n        bytes32 value;\n    }\n\n    struct Uint256Slot {\n        uint256 value;\n    }\n\n    /**\n     * @dev Returns an `AddressSlot` with member `value` located at `slot`.\n     */\n    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\n     */\n    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\n     */\n    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := slot\n        }\n    }\n\n    /**\n     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\n     */\n    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            r.slot := slot\n        }\n    }\n}\n"
      },
      "contracts/staking/GMEEStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.17;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {PauseStorage} from \"@animoca/ethereum-contracts/contracts/lifecycle/libraries/PauseStorage.sol\";\nimport {Pause} from \"@animoca/ethereum-contracts/contracts/lifecycle/Pause.sol\";\nimport {ContractOwnership} from \"@animoca/ethereum-contracts/contracts/access/ContractOwnership.sol\";\nimport {ContractOwnershipStorage} from \"@animoca/ethereum-contracts/contracts/access/libraries/ContractOwnershipStorage.sol\";\n\n/**\n * @title GMEEStaking\n * @dev Contract for staking $GMEE tokens.\n */\ncontract GMEEStaking is ContractOwnership, Pause {\n    using PauseStorage for PauseStorage.Layout;\n    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;\n\n    struct Stake {\n        uint256 lockTimestamp;\n        uint248 amount;\n        uint8 period;\n    }\n\n    // Immutable variable representing the GMEE token contract\n    IERC20 public immutable gmee;\n\n    // Unstake tax percentage must be an integer value\n    // for example to set 3.5% we use 35 (3.5*10) and divide 35 by DECIMAL_FACTOR to get actual value of 3.5% i.e., 0.035\n    uint248 public tax;\n\n    // Decimal factor to support decimal percentages\n    uint248 public constant DECIMAL_FACTOR = 1000;\n\n    // Address to receive unstake tax\n    address public treasury;\n\n    // Mapping to store stake information for each user\n    mapping(address => Stake) public stakes;\n\n    // Total amount of $GMEE tokens staked in the contract\n    uint248 public totalStaked;\n\n    // Events to log staking and unstaking actions\n    event Staked(address indexed account, uint256 amount, uint256 totalAmount, uint256 period);\n    event Unstaked(address indexed account, uint256 amount, uint256 taxAmount, uint256 remainingStake);\n    event Restaked(address indexed account, uint256 totalAmount, uint256 period);\n    event UnstakeTaxTreasuryChanged(address indexed account);\n    event UnstakeTaxPercentageUpdated(uint256 newPercentage);\n\n    /**\n     * @dev Constructor function for the GMEEStaking contract.\n     * @param _gmee The address of the GMEE token contract.\n     * @param _treasury The address to send unstake tax to.\n     * @param _owner The address to set as the owner of the contract.\n     * @param _initialTaxPercentage The initial unstake tax percentage.\n     */\n    constructor(address _gmee, address _treasury, address _owner, uint248 _initialTaxPercentage) Pause(false) ContractOwnership(_owner) {\n        require(_initialTaxPercentage <= DECIMAL_FACTOR, \"GMEE:tax% must be 0-100\");\n        require(_treasury != address(0), \"GMEE:treasury != zero address\");\n        gmee = IERC20(_gmee);\n        treasury = _treasury;\n        tax = _initialTaxPercentage;\n        emit UnstakeTaxTreasuryChanged(_treasury);\n        emit UnstakeTaxPercentageUpdated(_initialTaxPercentage);\n    }\n\n    /**\n     * @dev Stake $GMEE tokens for a specified period.\n     * @param amount The amount of $GMEE tokens to stake.\n     * @param period The staking period (30, 60, or 90 days).\n     */\n    function stake(uint248 amount, uint8 period) external {\n        PauseStorage.layout().enforceIsNotPaused();\n        address account = msg.sender;\n        require(period == 30 || period == 60 || period == 90, \"GMEE:Invalid period\");\n        require(amount > 0, \"GMEE:Amount must be > 0\");\n\n        Stake storage userStake = stakes[account];\n        uint248 userStakeAmount = userStake.amount; // Read storage variable once\n\n        uint248 newStakeAmount;\n        if (userStakeAmount > 0) {\n            require(period == userStake.period, \"GMEE:staking period differed\");\n            newStakeAmount = userStakeAmount + amount;\n        } else {\n            userStake.period = period;\n            newStakeAmount = amount;\n        }\n\n        userStake.amount = newStakeAmount; // Update userStake.amount\n        userStake.lockTimestamp = block.timestamp;\n        gmee.transferFrom(account, address(this), amount);\n        totalStaked += amount;\n        emit Staked(account, amount, newStakeAmount, period);\n    }\n\n    /**\n     * @dev Unstake $GMEE tokens.\n     * @param amount The amount of $GMEE tokens to unstake.\n     */\n    function unstake(uint248 amount) external {\n        require(amount > 0, \"Unstake amount must be > zero\");\n        address account = msg.sender;\n        Stake storage userStake = stakes[account];\n        uint248 userStakeAmount = userStake.amount; // Read storage variable once\n\n        require(userStakeAmount >= amount, \"Insufficient staked amount\");\n\n        uint248 taxAmount = 0;\n        if (tax > 0 && block.timestamp < userStake.lockTimestamp + uint256(userStake.period) * 1 days) {\n            taxAmount = (amount * tax) / DECIMAL_FACTOR;\n        }\n\n        uint248 remainingStake = userStakeAmount - amount;\n\n        userStake.amount = remainingStake;\n        totalStaked -= amount;\n\n        if (taxAmount > 0) {\n            gmee.transfer(treasury, taxAmount);\n        }\n        gmee.transfer(account, amount - taxAmount);\n\n        emit Unstaked(account, amount, taxAmount, remainingStake);\n    }\n\n    /**\n     * @dev Restake $GMEE tokens (restarts staking period).\n     */\n    function restake() external {\n        PauseStorage.layout().enforceIsNotPaused();\n        address account = msg.sender;\n        Stake storage userStake = stakes[account];\n        uint248 userStakeAmount = userStake.amount; // Read storage variable once\n        require(userStakeAmount > 0, \"Insufficient staked amount\");\n\n        // Calculate remaining time in the current lock period\n        uint256 elapsedTime = block.timestamp - userStake.lockTimestamp;\n        require(elapsedTime >= uint256(userStake.period) * 1 days, \"Current lock period not expired\");\n\n        userStake.lockTimestamp = block.timestamp;\n\n        emit Restaked(account, userStakeAmount, userStake.period);\n    }\n\n    /**\n     * @dev Set the address to receive unstake tax.\n     * @param _treasury The address to set as the treasury.\n     */\n    function setUnstakeTaxTreasury(address _treasury) external {\n        require(_treasury != address(0), \"GMEE:treasury != zero address\");\n        ContractOwnershipStorage.layout().enforceIsContractOwner(msg.sender);\n        treasury = _treasury;\n        emit UnstakeTaxTreasuryChanged(_treasury);\n    }\n\n    /**\n     * @dev Set or update the unstake tax percentage.\n     * @param _percentage The new unstake tax percentage.\n     */\n    function setUnstakeTaxPercentage(uint248 _percentage) external {\n        ContractOwnershipStorage.layout().enforceIsContractOwner(msg.sender);\n        require(_percentage <= DECIMAL_FACTOR, \"GMEE:tax% must be 0-100\");\n        if (totalStaked > 0) {\n            require(_percentage <= tax, \"GMEE:New tax% <= current tax%\");\n        }\n        tax = _percentage;\n        emit UnstakeTaxPercentageUpdated(_percentage);\n    }\n}\n"
      }
    }
  }}