{
  "language": "Solidity",
  "sources": {
    "@openzeppelin/contracts/access/AccessControl.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```solidity\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```solidity\n * function foo() public {\n *     require(hasRole(MY_ROLE, msg.sender));\n *     ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}\n * to enforce additional security measures for this role.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n    struct RoleData {\n        mapping(address => bool) members;\n        bytes32 adminRole;\n    }\n\n    mapping(bytes32 => RoleData) private _roles;\n\n    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n    /**\n     * @dev Modifier that checks that an account has a specific role. Reverts\n     * with a standardized message including the required role.\n     *\n     * The format of the revert reason is given by the following regular expression:\n     *\n     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n     *\n     * _Available since v4.1._\n     */\n    modifier onlyRole(bytes32 role) {\n        _checkRole(role);\n        _;\n    }\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {\n        return _roles[role].members[account];\n    }\n\n    /**\n     * @dev Revert with a standard message if `_msgSender()` is missing `role`.\n     * Overriding this function changes the behavior of the {onlyRole} modifier.\n     *\n     * Format of the revert message is described in {_checkRole}.\n     *\n     * _Available since v4.6._\n     */\n    function _checkRole(bytes32 role) internal view virtual {\n        _checkRole(role, _msgSender());\n    }\n\n    /**\n     * @dev Revert with a standard message if `account` is missing `role`.\n     *\n     * The format of the revert reason is given by the following regular expression:\n     *\n     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n     */\n    function _checkRole(bytes32 role, address account) internal view virtual {\n        if (!hasRole(role, account)) {\n            revert(\n                string(\n                    abi.encodePacked(\n                        \"AccessControl: account \",\n                        Strings.toHexString(account),\n                        \" is missing role \",\n                        Strings.toHexString(uint256(role), 32)\n                    )\n                )\n            );\n        }\n    }\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {\n        return _roles[role].adminRole;\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been revoked `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `account`.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function renounceRole(bytes32 role, address account) public virtual override {\n        require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event. Note that unlike {grantRole}, this function doesn't perform any\n     * checks on the calling account.\n     *\n     * May emit a {RoleGranted} event.\n     *\n     * [WARNING]\n     * ====\n     * This function should only be called from the constructor when setting\n     * up the initial roles for the system.\n     *\n     * Using this function in any other way is effectively circumventing the admin\n     * system imposed by {AccessControl}.\n     * ====\n     *\n     * NOTE: This function is deprecated in favor of {_grantRole}.\n     */\n    function _setupRole(bytes32 role, address account) internal virtual {\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Sets `adminRole` as ``role``'s admin role.\n     *\n     * Emits a {RoleAdminChanged} event.\n     */\n    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n        bytes32 previousAdminRole = getRoleAdmin(role);\n        _roles[role].adminRole = adminRole;\n        emit RoleAdminChanged(role, previousAdminRole, adminRole);\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function _grantRole(bytes32 role, address account) internal virtual {\n        if (!hasRole(role, account)) {\n            _roles[role].members[account] = true;\n            emit RoleGranted(role, account, _msgSender());\n        }\n    }\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function _revokeRole(bytes32 role, address account) internal virtual {\n        if (hasRole(role, account)) {\n            _roles[role].members[account] = false;\n            emit RoleRevoked(role, account, _msgSender());\n        }\n    }\n}\n"
    },
    "@openzeppelin/contracts/access/IAccessControl.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted signaling this.\n     *\n     * _Available since v3.1._\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call, an admin role\n     * bearer except when using {AccessControl-_setupRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) external view returns (bool);\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function grantRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function revokeRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `account`.\n     */\n    function renounceRole(bytes32 role, address account) external;\n}\n"
    },
    "@openzeppelin/contracts/access/Ownable.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor() {\n        _transferOwnership(_msgSender());\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
    },
    "@openzeppelin/contracts/token/ERC1155/ERC1155.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/ERC1155.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC1155.sol\";\nimport \"./IERC1155Receiver.sol\";\nimport \"./extensions/IERC1155MetadataURI.sol\";\nimport \"../../utils/Address.sol\";\nimport \"../../utils/Context.sol\";\nimport \"../../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Implementation of the basic standard multi-token.\n * See https://eips.ethereum.org/EIPS/eip-1155\n * Originally based on code by Enjin: https://github.com/enjin/erc-1155\n *\n * _Available since v3.1._\n */\ncontract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {\n    using Address for address;\n\n    // Mapping from token ID to account balances\n    mapping(uint256 => mapping(address => uint256)) private _balances;\n\n    // Mapping from account to operator approvals\n    mapping(address => mapping(address => bool)) private _operatorApprovals;\n\n    // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json\n    string private _uri;\n\n    /**\n     * @dev See {_setURI}.\n     */\n    constructor(string memory uri_) {\n        _setURI(uri_);\n    }\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {\n        return\n            interfaceId == type(IERC1155).interfaceId ||\n            interfaceId == type(IERC1155MetadataURI).interfaceId ||\n            super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev See {IERC1155MetadataURI-uri}.\n     *\n     * This implementation returns the same URI for *all* token types. It relies\n     * on the token type ID substitution mechanism\n     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].\n     *\n     * Clients calling this function must replace the `\\{id\\}` substring with the\n     * actual token type ID.\n     */\n    function uri(uint256) public view virtual override returns (string memory) {\n        return _uri;\n    }\n\n    /**\n     * @dev See {IERC1155-balanceOf}.\n     *\n     * Requirements:\n     *\n     * - `account` cannot be the zero address.\n     */\n    function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {\n        require(account != address(0), \"ERC1155: address zero is not a valid owner\");\n        return _balances[id][account];\n    }\n\n    /**\n     * @dev See {IERC1155-balanceOfBatch}.\n     *\n     * Requirements:\n     *\n     * - `accounts` and `ids` must have the same length.\n     */\n    function balanceOfBatch(\n        address[] memory accounts,\n        uint256[] memory ids\n    ) public view virtual override returns (uint256[] memory) {\n        require(accounts.length == ids.length, \"ERC1155: accounts and ids length mismatch\");\n\n        uint256[] memory batchBalances = new uint256[](accounts.length);\n\n        for (uint256 i = 0; i < accounts.length; ++i) {\n            batchBalances[i] = balanceOf(accounts[i], ids[i]);\n        }\n\n        return batchBalances;\n    }\n\n    /**\n     * @dev See {IERC1155-setApprovalForAll}.\n     */\n    function setApprovalForAll(address operator, bool approved) public virtual override {\n        _setApprovalForAll(_msgSender(), operator, approved);\n    }\n\n    /**\n     * @dev See {IERC1155-isApprovedForAll}.\n     */\n    function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {\n        return _operatorApprovals[account][operator];\n    }\n\n    /**\n     * @dev See {IERC1155-safeTransferFrom}.\n     */\n    function safeTransferFrom(\n        address from,\n        address to,\n        uint256 id,\n        uint256 amount,\n        bytes memory data\n    ) public virtual override {\n        require(\n            from == _msgSender() || isApprovedForAll(from, _msgSender()),\n            \"ERC1155: caller is not token owner or approved\"\n        );\n        _safeTransferFrom(from, to, id, amount, data);\n    }\n\n    /**\n     * @dev See {IERC1155-safeBatchTransferFrom}.\n     */\n    function safeBatchTransferFrom(\n        address from,\n        address to,\n        uint256[] memory ids,\n        uint256[] memory amounts,\n        bytes memory data\n    ) public virtual override {\n        require(\n            from == _msgSender() || isApprovedForAll(from, _msgSender()),\n            \"ERC1155: caller is not token owner or approved\"\n        );\n        _safeBatchTransferFrom(from, to, ids, amounts, data);\n    }\n\n    /**\n     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.\n     *\n     * Emits a {TransferSingle} event.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     * - `from` must have a balance of tokens of type `id` of at least `amount`.\n     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the\n     * acceptance magic value.\n     */\n    function _safeTransferFrom(\n        address from,\n        address to,\n        uint256 id,\n        uint256 amount,\n        bytes memory data\n    ) internal virtual {\n        require(to != address(0), \"ERC1155: transfer to the zero address\");\n\n        address operator = _msgSender();\n        uint256[] memory ids = _asSingletonArray(id);\n        uint256[] memory amounts = _asSingletonArray(amount);\n\n        _beforeTokenTransfer(operator, from, to, ids, amounts, data);\n\n        uint256 fromBalance = _balances[id][from];\n        require(fromBalance >= amount, \"ERC1155: insufficient balance for transfer\");\n        unchecked {\n            _balances[id][from] = fromBalance - amount;\n        }\n        _balances[id][to] += amount;\n\n        emit TransferSingle(operator, from, to, id, amount);\n\n        _afterTokenTransfer(operator, from, to, ids, amounts, data);\n\n        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);\n    }\n\n    /**\n     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.\n     *\n     * Emits a {TransferBatch} event.\n     *\n     * Requirements:\n     *\n     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the\n     * acceptance magic value.\n     */\n    function _safeBatchTransferFrom(\n        address from,\n        address to,\n        uint256[] memory ids,\n        uint256[] memory amounts,\n        bytes memory data\n    ) internal virtual {\n        require(ids.length == amounts.length, \"ERC1155: ids and amounts length mismatch\");\n        require(to != address(0), \"ERC1155: transfer to the zero address\");\n\n        address operator = _msgSender();\n\n        _beforeTokenTransfer(operator, from, to, ids, amounts, data);\n\n        for (uint256 i = 0; i < ids.length; ++i) {\n            uint256 id = ids[i];\n            uint256 amount = amounts[i];\n\n            uint256 fromBalance = _balances[id][from];\n            require(fromBalance >= amount, \"ERC1155: insufficient balance for transfer\");\n            unchecked {\n                _balances[id][from] = fromBalance - amount;\n            }\n            _balances[id][to] += amount;\n        }\n\n        emit TransferBatch(operator, from, to, ids, amounts);\n\n        _afterTokenTransfer(operator, from, to, ids, amounts, data);\n\n        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);\n    }\n\n    /**\n     * @dev Sets a new URI for all token types, by relying on the token type ID\n     * substitution mechanism\n     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].\n     *\n     * By this mechanism, any occurrence of the `\\{id\\}` substring in either the\n     * URI or any of the amounts in the JSON file at said URI will be replaced by\n     * clients with the token type ID.\n     *\n     * For example, the `https://token-cdn-domain/\\{id\\}.json` URI would be\n     * interpreted by clients as\n     * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`\n     * for token type ID 0x4cce0.\n     *\n     * See {uri}.\n     *\n     * Because these URIs cannot be meaningfully represented by the {URI} event,\n     * this function emits no events.\n     */\n    function _setURI(string memory newuri) internal virtual {\n        _uri = newuri;\n    }\n\n    /**\n     * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.\n     *\n     * Emits a {TransferSingle} event.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the\n     * acceptance magic value.\n     */\n    function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {\n        require(to != address(0), \"ERC1155: mint to the zero address\");\n\n        address operator = _msgSender();\n        uint256[] memory ids = _asSingletonArray(id);\n        uint256[] memory amounts = _asSingletonArray(amount);\n\n        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);\n\n        _balances[id][to] += amount;\n        emit TransferSingle(operator, address(0), to, id, amount);\n\n        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);\n\n        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);\n    }\n\n    /**\n     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.\n     *\n     * Emits a {TransferBatch} event.\n     *\n     * Requirements:\n     *\n     * - `ids` and `amounts` must have the same length.\n     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the\n     * acceptance magic value.\n     */\n    function _mintBatch(\n        address to,\n        uint256[] memory ids,\n        uint256[] memory amounts,\n        bytes memory data\n    ) internal virtual {\n        require(to != address(0), \"ERC1155: mint to the zero address\");\n        require(ids.length == amounts.length, \"ERC1155: ids and amounts length mismatch\");\n\n        address operator = _msgSender();\n\n        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);\n\n        for (uint256 i = 0; i < ids.length; i++) {\n            _balances[ids[i]][to] += amounts[i];\n        }\n\n        emit TransferBatch(operator, address(0), to, ids, amounts);\n\n        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);\n\n        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens of token type `id` from `from`\n     *\n     * Emits a {TransferSingle} event.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `from` must have at least `amount` tokens of token type `id`.\n     */\n    function _burn(address from, uint256 id, uint256 amount) internal virtual {\n        require(from != address(0), \"ERC1155: burn from the zero address\");\n\n        address operator = _msgSender();\n        uint256[] memory ids = _asSingletonArray(id);\n        uint256[] memory amounts = _asSingletonArray(amount);\n\n        _beforeTokenTransfer(operator, from, address(0), ids, amounts, \"\");\n\n        uint256 fromBalance = _balances[id][from];\n        require(fromBalance >= amount, \"ERC1155: burn amount exceeds balance\");\n        unchecked {\n            _balances[id][from] = fromBalance - amount;\n        }\n\n        emit TransferSingle(operator, from, address(0), id, amount);\n\n        _afterTokenTransfer(operator, from, address(0), ids, amounts, \"\");\n    }\n\n    /**\n     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.\n     *\n     * Emits a {TransferBatch} event.\n     *\n     * Requirements:\n     *\n     * - `ids` and `amounts` must have the same length.\n     */\n    function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) internal virtual {\n        require(from != address(0), \"ERC1155: burn from the zero address\");\n        require(ids.length == amounts.length, \"ERC1155: ids and amounts length mismatch\");\n\n        address operator = _msgSender();\n\n        _beforeTokenTransfer(operator, from, address(0), ids, amounts, \"\");\n\n        for (uint256 i = 0; i < ids.length; i++) {\n            uint256 id = ids[i];\n            uint256 amount = amounts[i];\n\n            uint256 fromBalance = _balances[id][from];\n            require(fromBalance >= amount, \"ERC1155: burn amount exceeds balance\");\n            unchecked {\n                _balances[id][from] = fromBalance - amount;\n            }\n        }\n\n        emit TransferBatch(operator, from, address(0), ids, amounts);\n\n        _afterTokenTransfer(operator, from, address(0), ids, amounts, \"\");\n    }\n\n    /**\n     * @dev Approve `operator` to operate on all of `owner` tokens\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {\n        require(owner != operator, \"ERC1155: setting approval status for self\");\n        _operatorApprovals[owner][operator] = approved;\n        emit ApprovalForAll(owner, operator, approved);\n    }\n\n    /**\n     * @dev Hook that is called before any token transfer. This includes minting\n     * and burning, as well as batched variants.\n     *\n     * The same hook is called on both single and batched variants. For single\n     * transfers, the length of the `ids` and `amounts` arrays will be 1.\n     *\n     * Calling conditions (for each `id` and `amount` pair):\n     *\n     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n     * of token type `id` will be  transferred to `to`.\n     * - When `from` is zero, `amount` tokens of token type `id` will be minted\n     * for `to`.\n     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`\n     * will be burned.\n     * - `from` and `to` are never both zero.\n     * - `ids` and `amounts` have the same, non-zero length.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _beforeTokenTransfer(\n        address operator,\n        address from,\n        address to,\n        uint256[] memory ids,\n        uint256[] memory amounts,\n        bytes memory data\n    ) internal virtual {}\n\n    /**\n     * @dev Hook that is called after any token transfer. This includes minting\n     * and burning, as well as batched variants.\n     *\n     * The same hook is called on both single and batched variants. For single\n     * transfers, the length of the `id` and `amount` arrays will be 1.\n     *\n     * Calling conditions (for each `id` and `amount` pair):\n     *\n     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n     * of token type `id` will be  transferred to `to`.\n     * - When `from` is zero, `amount` tokens of token type `id` will be minted\n     * for `to`.\n     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`\n     * will be burned.\n     * - `from` and `to` are never both zero.\n     * - `ids` and `amounts` have the same, non-zero length.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _afterTokenTransfer(\n        address operator,\n        address from,\n        address to,\n        uint256[] memory ids,\n        uint256[] memory amounts,\n        bytes memory data\n    ) internal virtual {}\n\n    function _doSafeTransferAcceptanceCheck(\n        address operator,\n        address from,\n        address to,\n        uint256 id,\n        uint256 amount,\n        bytes memory data\n    ) private {\n        if (to.isContract()) {\n            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {\n                if (response != IERC1155Receiver.onERC1155Received.selector) {\n                    revert(\"ERC1155: ERC1155Receiver rejected tokens\");\n                }\n            } catch Error(string memory reason) {\n                revert(reason);\n            } catch {\n                revert(\"ERC1155: transfer to non-ERC1155Receiver implementer\");\n            }\n        }\n    }\n\n    function _doSafeBatchTransferAcceptanceCheck(\n        address operator,\n        address from,\n        address to,\n        uint256[] memory ids,\n        uint256[] memory amounts,\n        bytes memory data\n    ) private {\n        if (to.isContract()) {\n            try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (\n                bytes4 response\n            ) {\n                if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {\n                    revert(\"ERC1155: ERC1155Receiver rejected tokens\");\n                }\n            } catch Error(string memory reason) {\n                revert(reason);\n            } catch {\n                revert(\"ERC1155: transfer to non-ERC1155Receiver implementer\");\n            }\n        }\n    }\n\n    function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {\n        uint256[] memory array = new uint256[](1);\n        array[0] = element;\n\n        return array;\n    }\n}\n"
    },
    "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC1155.sol\";\n\n/**\n * @dev Interface of the optional ERC1155MetadataExtension interface, as defined\n * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].\n *\n * _Available since v3.1._\n */\ninterface IERC1155MetadataURI is IERC1155 {\n    /**\n     * @dev Returns the URI for token type `id`.\n     *\n     * If the `\\{id\\}` substring is present in the URI, it must be replaced by\n     * clients with the actual token type ID.\n     */\n    function uri(uint256 id) external view returns (string memory);\n}\n"
    },
    "@openzeppelin/contracts/token/ERC1155/IERC1155.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/IERC1155.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC1155 compliant contract, as defined in the\n * https://eips.ethereum.org/EIPS/eip-1155[EIP].\n *\n * _Available since v3.1._\n */\ninterface IERC1155 is IERC165 {\n    /**\n     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.\n     */\n    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);\n\n    /**\n     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all\n     * transfers.\n     */\n    event TransferBatch(\n        address indexed operator,\n        address indexed from,\n        address indexed to,\n        uint256[] ids,\n        uint256[] values\n    );\n\n    /**\n     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to\n     * `approved`.\n     */\n    event ApprovalForAll(address indexed account, address indexed operator, bool approved);\n\n    /**\n     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.\n     *\n     * If an {URI} event was emitted for `id`, the standard\n     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value\n     * returned by {IERC1155MetadataURI-uri}.\n     */\n    event URI(string value, uint256 indexed id);\n\n    /**\n     * @dev Returns the amount of tokens of token type `id` owned by `account`.\n     *\n     * Requirements:\n     *\n     * - `account` cannot be the zero address.\n     */\n    function balanceOf(address account, uint256 id) external view returns (uint256);\n\n    /**\n     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.\n     *\n     * Requirements:\n     *\n     * - `accounts` and `ids` must have the same length.\n     */\n    function balanceOfBatch(\n        address[] calldata accounts,\n        uint256[] calldata ids\n    ) external view returns (uint256[] memory);\n\n    /**\n     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,\n     *\n     * Emits an {ApprovalForAll} event.\n     *\n     * Requirements:\n     *\n     * - `operator` cannot be the caller.\n     */\n    function setApprovalForAll(address operator, bool approved) external;\n\n    /**\n     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.\n     *\n     * See {setApprovalForAll}.\n     */\n    function isApprovedForAll(address account, address operator) external view returns (bool);\n\n    /**\n     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.\n     *\n     * Emits a {TransferSingle} event.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.\n     * - `from` must have a balance of tokens of type `id` of at least `amount`.\n     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the\n     * acceptance magic value.\n     */\n    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;\n\n    /**\n     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.\n     *\n     * Emits a {TransferBatch} event.\n     *\n     * Requirements:\n     *\n     * - `ids` and `amounts` must have the same length.\n     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the\n     * acceptance magic value.\n     */\n    function safeBatchTransferFrom(\n        address from,\n        address to,\n        uint256[] calldata ids,\n        uint256[] calldata amounts,\n        bytes calldata data\n    ) external;\n}\n"
    },
    "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev _Available since v3.1._\n */\ninterface IERC1155Receiver is IERC165 {\n    /**\n     * @dev Handles the receipt of a single ERC1155 token type. This function is\n     * called at the end of a `safeTransferFrom` after the balance has been updated.\n     *\n     * NOTE: To accept the transfer, this must return\n     * `bytes4(keccak256(\"onERC1155Received(address,address,uint256,uint256,bytes)\"))`\n     * (i.e. 0xf23a6e61, or its own function selector).\n     *\n     * @param operator The address which initiated the transfer (i.e. msg.sender)\n     * @param from The address which previously owned the token\n     * @param id The ID of the token being transferred\n     * @param value The amount of tokens being transferred\n     * @param data Additional data with no specified format\n     * @return `bytes4(keccak256(\"onERC1155Received(address,address,uint256,uint256,bytes)\"))` if transfer is allowed\n     */\n    function onERC1155Received(\n        address operator,\n        address from,\n        uint256 id,\n        uint256 value,\n        bytes calldata data\n    ) external returns (bytes4);\n\n    /**\n     * @dev Handles the receipt of a multiple ERC1155 token types. This function\n     * is called at the end of a `safeBatchTransferFrom` after the balances have\n     * been updated.\n     *\n     * NOTE: To accept the transfer(s), this must return\n     * `bytes4(keccak256(\"onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)\"))`\n     * (i.e. 0xbc197c81, or its own function selector).\n     *\n     * @param operator The address which initiated the batch transfer (i.e. msg.sender)\n     * @param from The address which previously owned the token\n     * @param ids An array containing ids of each token being transferred (order and length must match values array)\n     * @param values An array containing amounts of each token being transferred (order and length must match ids array)\n     * @param data Additional data with no specified format\n     * @return `bytes4(keccak256(\"onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)\"))` if transfer is allowed\n     */\n    function onERC1155BatchReceived(\n        address operator,\n        address from,\n        uint256[] calldata ids,\n        uint256[] calldata values,\n        bytes calldata data\n    ) external returns (bytes4);\n}\n"
    },
    "@openzeppelin/contracts/token/ERC20/ERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./extensions/IERC20Metadata.sol\";\nimport \"../../utils/Context.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * The default value of {decimals} is 18. To change this, you should override\n * this function so it returns a different value.\n *\n * We have followed general OpenZeppelin Contracts guidelines: functions revert\n * instead returning `false` on failure. This behavior is nonetheless\n * conventional and does not conflict with the expectations of ERC20\n * applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20, IERC20Metadata {\n    mapping(address => uint256) private _balances;\n\n    mapping(address => mapping(address => uint256)) private _allowances;\n\n    uint256 private _totalSupply;\n\n    string private _name;\n    string private _symbol;\n\n    /**\n     * @dev Sets the values for {name} and {symbol}.\n     *\n     * All two of these values are immutable: they can only be set once during\n     * construction.\n     */\n    constructor(string memory name_, string memory symbol_) {\n        _name = name_;\n        _symbol = symbol_;\n    }\n\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() public view virtual override returns (string memory) {\n        return _name;\n    }\n\n    /**\n     * @dev Returns the symbol of the token, usually a shorter version of the\n     * name.\n     */\n    function symbol() public view virtual override returns (string memory) {\n        return _symbol;\n    }\n\n    /**\n     * @dev Returns the number of decimals used to get its user representation.\n     * For example, if `decimals` equals `2`, a balance of `505` tokens should\n     * be displayed to a user as `5.05` (`505 / 10 ** 2`).\n     *\n     * Tokens usually opt for a value of 18, imitating the relationship between\n     * Ether and Wei. This is the default value returned by this function, unless\n     * it's overridden.\n     *\n     * NOTE: This information is only used for _display_ purposes: it in\n     * no way affects any of the arithmetic of the contract, including\n     * {IERC20-balanceOf} and {IERC20-transfer}.\n     */\n    function decimals() public view virtual override returns (uint8) {\n        return 18;\n    }\n\n    /**\n     * @dev See {IERC20-totalSupply}.\n     */\n    function totalSupply() public view virtual override returns (uint256) {\n        return _totalSupply;\n    }\n\n    /**\n     * @dev See {IERC20-balanceOf}.\n     */\n    function balanceOf(address account) public view virtual override returns (uint256) {\n        return _balances[account];\n    }\n\n    /**\n     * @dev See {IERC20-transfer}.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     * - the caller must have a balance of at least `amount`.\n     */\n    function transfer(address to, uint256 amount) public virtual override returns (bool) {\n        address owner = _msgSender();\n        _transfer(owner, to, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-allowance}.\n     */\n    function allowance(address owner, address spender) public view virtual override returns (uint256) {\n        return _allowances[owner][spender];\n    }\n\n    /**\n     * @dev See {IERC20-approve}.\n     *\n     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on\n     * `transferFrom`. This is semantically equivalent to an infinite approval.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function approve(address spender, uint256 amount) public virtual override returns (bool) {\n        address owner = _msgSender();\n        _approve(owner, spender, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-transferFrom}.\n     *\n     * Emits an {Approval} event indicating the updated allowance. This is not\n     * required by the EIP. See the note at the beginning of {ERC20}.\n     *\n     * NOTE: Does not update the allowance if the current allowance\n     * is the maximum `uint256`.\n     *\n     * Requirements:\n     *\n     * - `from` and `to` cannot be the zero address.\n     * - `from` must have a balance of at least `amount`.\n     * - the caller must have allowance for ``from``'s tokens of at least\n     * `amount`.\n     */\n    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {\n        address spender = _msgSender();\n        _spendAllowance(from, spender, amount);\n        _transfer(from, to, amount);\n        return true;\n    }\n\n    /**\n     * @dev Atomically increases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n        address owner = _msgSender();\n        _approve(owner, spender, allowance(owner, spender) + addedValue);\n        return true;\n    }\n\n    /**\n     * @dev Atomically decreases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `spender` must have allowance for the caller of at least\n     * `subtractedValue`.\n     */\n    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n        address owner = _msgSender();\n        uint256 currentAllowance = allowance(owner, spender);\n        require(currentAllowance >= subtractedValue, \"ERC20: decreased allowance below zero\");\n        unchecked {\n            _approve(owner, spender, currentAllowance - subtractedValue);\n        }\n\n        return true;\n    }\n\n    /**\n     * @dev Moves `amount` of tokens from `from` to `to`.\n     *\n     * This internal function is equivalent to {transfer}, and can be used to\n     * e.g. implement automatic token fees, slashing mechanisms, etc.\n     *\n     * Emits a {Transfer} event.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `from` must have a balance of at least `amount`.\n     */\n    function _transfer(address from, address to, uint256 amount) internal virtual {\n        require(from != address(0), \"ERC20: transfer from the zero address\");\n        require(to != address(0), \"ERC20: transfer to the zero address\");\n\n        _beforeTokenTransfer(from, to, amount);\n\n        uint256 fromBalance = _balances[from];\n        require(fromBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n        unchecked {\n            _balances[from] = fromBalance - amount;\n            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by\n            // decrementing then incrementing.\n            _balances[to] += amount;\n        }\n\n        emit Transfer(from, to, amount);\n\n        _afterTokenTransfer(from, to, amount);\n    }\n\n    /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n     * the total supply.\n     *\n     * Emits a {Transfer} event with `from` set to the zero address.\n     *\n     * Requirements:\n     *\n     * - `account` cannot be the zero address.\n     */\n    function _mint(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: mint to the zero address\");\n\n        _beforeTokenTransfer(address(0), account, amount);\n\n        _totalSupply += amount;\n        unchecked {\n            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.\n            _balances[account] += amount;\n        }\n        emit Transfer(address(0), account, amount);\n\n        _afterTokenTransfer(address(0), account, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, reducing the\n     * total supply.\n     *\n     * Emits a {Transfer} event with `to` set to the zero address.\n     *\n     * Requirements:\n     *\n     * - `account` cannot be the zero address.\n     * - `account` must have at least `amount` tokens.\n     */\n    function _burn(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: burn from the zero address\");\n\n        _beforeTokenTransfer(account, address(0), amount);\n\n        uint256 accountBalance = _balances[account];\n        require(accountBalance >= amount, \"ERC20: burn amount exceeds balance\");\n        unchecked {\n            _balances[account] = accountBalance - amount;\n            // Overflow not possible: amount <= accountBalance <= totalSupply.\n            _totalSupply -= amount;\n        }\n\n        emit Transfer(account, address(0), amount);\n\n        _afterTokenTransfer(account, address(0), amount);\n    }\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n     *\n     * This internal function is equivalent to `approve`, and can be used to\n     * e.g. set automatic allowances for certain subsystems, etc.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `owner` cannot be the zero address.\n     * - `spender` cannot be the zero address.\n     */\n    function _approve(address owner, address spender, uint256 amount) internal virtual {\n        require(owner != address(0), \"ERC20: approve from the zero address\");\n        require(spender != address(0), \"ERC20: approve to the zero address\");\n\n        _allowances[owner][spender] = amount;\n        emit Approval(owner, spender, amount);\n    }\n\n    /**\n     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.\n     *\n     * Does not update the allowance amount in case of infinite allowance.\n     * Revert if not enough allowance is available.\n     *\n     * Might emit an {Approval} event.\n     */\n    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {\n        uint256 currentAllowance = allowance(owner, spender);\n        if (currentAllowance != type(uint256).max) {\n            require(currentAllowance >= amount, \"ERC20: insufficient allowance\");\n            unchecked {\n                _approve(owner, spender, currentAllowance - amount);\n            }\n        }\n    }\n\n    /**\n     * @dev Hook that is called before any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n     * will be transferred to `to`.\n     * - when `from` is zero, `amount` tokens will be minted for `to`.\n     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}\n\n    /**\n     * @dev Hook that is called after any transfer of tokens. This includes\n     * minting and burning.\n     *\n     * Calling conditions:\n     *\n     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n     * has been transferred to `to`.\n     * - when `from` is zero, `amount` tokens have been minted for `to`.\n     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.\n     * - `from` and `to` are never both zero.\n     *\n     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n     */\n    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}\n}\n"
    },
    "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n"
    },
    "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.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(address from, address to, uint256 amount) external returns (bool);\n}\n"
    },
    "@openzeppelin/contracts/utils/Address.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     *\n     * Furthermore, `isContract` will also return true if the target contract within\n     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n     * which only has an effect at the end of a transaction.\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"
    },
    "@openzeppelin/contracts/utils/Context.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (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    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"
    },
    "@openzeppelin/contracts/utils/introspection/ERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IERC165).interfaceId;\n    }\n}\n"
    },
    "@openzeppelin/contracts/utils/introspection/IERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
    },
    "@openzeppelin/contracts/utils/math/Math.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    enum Rounding {\n        Down, // Toward negative infinity\n        Up, // Toward infinity\n        Zero // Toward zero\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds up instead\n     * of rounding down.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n     * with further edits by Uniswap Labs also under MIT license.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod0 := mul(x, y)\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n                // The surrounding unchecked block does not change this fact.\n                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            require(denominator > prod1, \"Math: mulDiv overflow\");\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n            // See https://cs.stackexchange.com/q/138556/92363.\n\n            // Does not overflow because the denominator cannot be zero at this stage in the function.\n            uint256 twos = denominator & (~denominator + 1);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n            // in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10 ** 64) {\n                value /= 10 ** 64;\n                result += 64;\n            }\n            if (value >= 10 ** 32) {\n                value /= 10 ** 32;\n                result += 32;\n            }\n            if (value >= 10 ** 16) {\n                value /= 10 ** 16;\n                result += 16;\n            }\n            if (value >= 10 ** 8) {\n                value /= 10 ** 8;\n                result += 8;\n            }\n            if (value >= 10 ** 4) {\n                value /= 10 ** 4;\n                result += 4;\n            }\n            if (value >= 10 ** 2) {\n                value /= 10 ** 2;\n                result += 2;\n            }\n            if (value >= 10 ** 1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);\n        }\n    }\n}\n"
    },
    "@openzeppelin/contracts/utils/math/SafeMath.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/SafeMath.sol)\n\npragma solidity ^0.8.0;\n\n// CAUTION\n// This version of SafeMath should only be used with Solidity 0.8 or later,\n// because it relies on the compiler's built in overflow checks.\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations.\n *\n * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler\n * now has built in overflow checking.\n */\nlibrary SafeMath {\n    /**\n     * @dev Returns the addition of two unsigned integers, with an overflow flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            uint256 c = a + b;\n            if (c < a) return (false, 0);\n            return (true, c);\n        }\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.\n     *\n     * _Available since v3.4._\n     */\n    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b > a) return (false, 0);\n            return (true, a - b);\n        }\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n            // benefit is lost if 'b' is also tested.\n            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n            if (a == 0) return (true, 0);\n            uint256 c = a * b;\n            if (c / a != b) return (false, 0);\n            return (true, c);\n        }\n    }\n\n    /**\n     * @dev Returns the division of two unsigned integers, with a division by zero flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a / b);\n        }\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a % b);\n        }\n    }\n\n    /**\n     * @dev Returns the addition of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity's `+` operator.\n     *\n     * Requirements:\n     *\n     * - Addition cannot overflow.\n     */\n    function add(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a + b;\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting on\n     * overflow (when the result is negative).\n     *\n     * Counterpart to Solidity's `-` operator.\n     *\n     * Requirements:\n     *\n     * - Subtraction cannot overflow.\n     */\n    function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a - b;\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity's `*` operator.\n     *\n     * Requirements:\n     *\n     * - Multiplication cannot overflow.\n     */\n    function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a * b;\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers, reverting on\n     * division by zero. The result is rounded towards zero.\n     *\n     * Counterpart to Solidity's `/` operator.\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a / b;\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * reverting when dividing by zero.\n     *\n     * Counterpart to Solidity's `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a % b;\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n     * overflow (when the result is negative).\n     *\n     * CAUTION: This function is deprecated because it requires allocating memory for the error\n     * message unnecessarily. For custom revert reasons use {trySub}.\n     *\n     * Counterpart to Solidity's `-` operator.\n     *\n     * Requirements:\n     *\n     * - Subtraction cannot overflow.\n     */\n    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        unchecked {\n            require(b <= a, errorMessage);\n            return a - b;\n        }\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n     * division by zero. The result is rounded towards zero.\n     *\n     * Counterpart to Solidity's `/` operator. Note: this function uses a\n     * `revert` opcode (which leaves remaining gas untouched) while Solidity\n     * uses an invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        unchecked {\n            require(b > 0, errorMessage);\n            return a / b;\n        }\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * reverting with custom message when dividing by zero.\n     *\n     * CAUTION: This function is deprecated because it requires allocating memory for the error\n     * message unnecessarily. For custom revert reasons use {tryMod}.\n     *\n     * Counterpart to Solidity's `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        unchecked {\n            require(b > 0, errorMessage);\n            return a % b;\n        }\n    }\n}\n"
    },
    "@openzeppelin/contracts/utils/math/SignedMath.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard signed math utilities missing in the Solidity language.\n */\nlibrary SignedMath {\n    /**\n     * @dev Returns the largest of two signed numbers.\n     */\n    function max(int256 a, int256 b) internal pure returns (int256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two signed numbers.\n     */\n    function min(int256 a, int256 b) internal pure returns (int256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two signed numbers without overflow.\n     * The result is rounded towards zero.\n     */\n    function average(int256 a, int256 b) internal pure returns (int256) {\n        // Formula from the book \"Hacker's Delight\"\n        int256 x = (a & b) + ((a ^ b) >> 1);\n        return x + (int256(uint256(x) >> 255) & (a ^ b));\n    }\n\n    /**\n     * @dev Returns the absolute unsigned value of a signed value.\n     */\n    function abs(int256 n) internal pure returns (uint256) {\n        unchecked {\n            // must be unchecked in order to support `n = type(int256).min`\n            return uint256(n >= 0 ? n : -n);\n        }\n    }\n}\n"
    },
    "@openzeppelin/contracts/utils/Strings.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\nimport \"./math/SignedMath.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n    bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n    uint8 private constant _ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            uint256 length = Math.log10(value) + 1;\n            string memory buffer = new string(length);\n            uint256 ptr;\n            /// @solidity memory-safe-assembly\n            assembly {\n                ptr := add(buffer, add(32, length))\n            }\n            while (true) {\n                ptr--;\n                /// @solidity memory-safe-assembly\n                assembly {\n                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n                }\n                value /= 10;\n                if (value == 0) break;\n            }\n            return buffer;\n        }\n    }\n\n    /**\n     * @dev Converts a `int256` to its ASCII `string` decimal representation.\n     */\n    function toString(int256 value) internal pure returns (string memory) {\n        return string(abi.encodePacked(value < 0 ? \"-\" : \"\", toString(SignedMath.abs(value))));\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            return toHexString(value, Math.log256(value) + 1);\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = _SYMBOLS[value & 0xf];\n            value >>= 4;\n        }\n        require(value == 0, \"Strings: hex length insufficient\");\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n    }\n\n    /**\n     * @dev Returns true if the two strings are equal.\n     */\n    function equal(string memory a, string memory b) internal pure returns (bool) {\n        return keccak256(bytes(a)) == keccak256(bytes(b));\n    }\n}\n"
    },
    "contracts/DataPoints.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/access/AccessControl.sol\";\n\n/**\n * @title DataPoints\n * @dev This contract implements a non-transferable ERC20 token, named DataPoints, intended to represent accumulated data points\n * within the Masa Network. It features minting and burning capabilities, with distinct roles for each action, as well as an admin role\n * for role management. The token is non-transferable, meaning it cannot be transferred between addresses once minted, making it\n * purely a representation of earned points rather than a tradable asset.\n *\n * The contract utilizes OpenZeppelin's ERC20, Ownable, and AccessControl contracts for standard token functionality, ownership management,\n * and role-based access control, respectively.\n */\ncontract DataPoints is ERC20, Ownable, AccessControl {\n    // Role definitions for minting, burning, and administration\n    bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n    bytes32 public constant BURNER_ROLE = keccak256(\"BURNER_ROLE\");\n    bytes32 public constant ADMIN_ROLE = keccak256(\"ADMIN_ROLE\");\n\n    // Events for logging minting and burning activities\n    event Minted(address indexed to, uint256 amount);\n    event Burned(address indexed from, uint256 amount);\n    event BatchMinted(address[] to, uint256[] amounts);\n    event BatchBurned(address[] from, uint256[] amounts);\n\n    constructor() ERC20(\"DataPoints\", \"DPT\") {\n        // Setup roles for the contract deployer\n        _setupRole(ADMIN_ROLE, msg.sender);\n        _setRoleAdmin(MINTER_ROLE, ADMIN_ROLE);\n        _setRoleAdmin(BURNER_ROLE, ADMIN_ROLE);\n\n        // Grant minting, and burning roles to the deployer\n        _setupRole(MINTER_ROLE, msg.sender);\n        _setupRole(BURNER_ROLE, msg.sender);\n    }\n\n    /**\n     * @dev Mints `amount` tokens to address `to`. Only callable by addresses with the MINTER_ROLE.\n     * Emits a {Minted} event.\n     */\n    function mint(address to, uint256 amount) public {\n        require(\n            hasRole(MINTER_ROLE, msg.sender),\n            \"DataPoints: must have minter role to mint\"\n        );\n        _mint(to, amount);\n        emit Minted(to, amount);\n    }\n\n    /**\n     * @dev Burns `amount` tokens from address `from`. Only callable by addresses with the BURNER_ROLE.\n     * Emits a {Burned} event.\n     */\n    function burn(address from, uint256 amount) public {\n        require(\n            hasRole(BURNER_ROLE, msg.sender),\n            \"DataPoints: must have burner role to burn\"\n        );\n        _burn(from, amount);\n        emit Burned(from, amount);\n    }\n\n    /**\n     * @dev Mints tokens to multiple addresses as specified in `to` and `amounts`. The lengths of\n     * the `to` and `amounts` arrays must match. Only callable by addresses with the MINTER_ROLE.\n     * Emits a {BatchMinted} event.\n     */\n    function batchMint(address[] memory to, uint256[] memory amounts) public {\n        require(\n            hasRole(MINTER_ROLE, msg.sender),\n            \"DataPoints: must have minter role to batch mint\"\n        );\n        require(\n            to.length == amounts.length,\n            \"DataPoints: to and amounts array length mismatch\"\n        );\n\n        for (uint256 i = 0; i < to.length; ++i) {\n            _mint(to[i], amounts[i]);\n        }\n        emit BatchMinted(to, amounts);\n    }\n\n    /**\n     * @dev Burns tokens from multiple addresses as specified in `from` and `amounts`. The lengths of\n     * the `from` and `amounts` arrays must match. Only callable by addresses with the BURNER_ROLE.\n     * Emits a {BatchBurned} event.\n     */\n    function batchBurn(address[] memory from, uint256[] memory amounts) public {\n        require(\n            hasRole(BURNER_ROLE, msg.sender),\n            \"DataPoints: must have burner role to batch burn\"\n        );\n        require(\n            from.length == amounts.length,\n            \"DataPoints: from and amounts array length mismatch\"\n        );\n\n        for (uint256 i = 0; i < from.length; ++i) {\n            _burn(from[i], amounts[i]);\n        }\n        emit BatchBurned(from, amounts);\n    }\n\n    /**\n     * @dev Overrides ERC20 transfer function to prevent token transfers, making the tokens non-transferable.\n     * This ensures that DataPoints can only be minted or burned, not transferred between addresses.\n     */\n    function transfer(address, uint256) public pure override returns (bool) {\n        revert(\"DataPoints: transfer not allowed\");\n    }\n\n    /**\n     * @dev Overrides ERC20 transferFrom function to also prevent token transfers.\n     */\n    function transferFrom(\n        address,\n        address,\n        uint256\n    ) public pure override returns (bool) {\n        revert(\"DataPoints: transfer not allowed\");\n    }\n}\n"
    },
    "contracts/DataPointsMulti.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC1155/ERC1155.sol\";\nimport \"@openzeppelin/contracts/access/AccessControl.sol\";\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\nimport \"./lib/base64.sol\";\n\n/**\n * @title DataPointsMulti\n * @dev This contract extends ERC1155 to create a multi-token contract, named DataPointsMulti, intended to represent accumulated data points\n * within the Masa Network. Each token ID represents a different data type, and it features minting and burning capabilities, with distinct roles\n * for each action, as well as an admin role for role management. The tokens are non-transferable, meaning they cannot be transferred between\n * addresses once minted, making them purely a representation of earned points rather than a tradable asset.\n *\n * The contract utilizes OpenZeppelin's ERC1155 for the multi-token standard and AccessControl for role-based access control, respectively.\n * It also implements the ERC1155 URI mechanism to provide on-chain metadata for each token type.\n */\ncontract DataPointsMulti is ERC1155, AccessControl {\n    using Strings for uint256;\n\n    // Role definitions for role-based access control\n    bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n    bytes32 public constant ADMIN_ROLE = keccak256(\"ADMIN_ROLE\");\n    bytes32 public constant UPDATER_ROLE = keccak256(\"UPDATER_ROLE\");\n\n    // Struct for storing token metadata.\n    struct TokenMetadata {\n        string name;\n        string image;\n        string description;\n    }\n\n    // Define a new struct that includes the token ID and its metadata\n    struct TokenIdMetadata {\n        uint256 tokenId;\n        TokenMetadata metadata;\n    }\n\n    // Struct to hold token balance information.\n    struct TokenBalance {\n        string tokenName;\n        uint256 balance;\n    }\n\n    // Mapping from token ID to its metadata.\n    mapping(uint256 => TokenMetadata) public tokenMetadata;\n\n    // Mapping from address and token ID to an array of UUIDs.\n    mapping(address => mapping(uint256 => uint256[])) public accountTokenUUIDs;\n\n    // Counter for the total number of token types.\n    uint256 public tokenTypeCount;\n\n    // Array of valid token IDs.\n    uint256[] public validTokenIds;\n\n    // Base URI for constructing token URIs.\n    string public baseURI;\n\n    // Events for logging actions on the contract.\n    event BaseURISet(string newBaseURI);\n    event TokenMinted(\n        address account,\n        uint256 id,\n        uint256 amount,\n        uint256 uuid\n    );\n    event TokenBatchMinted(\n        address account,\n        uint256[] ids,\n        uint256[] amounts,\n        uint256[] uuid\n    );\n    event TokenBurned(address account, uint256 id, uint256 amount);\n    event TokenBatchBurned(address account, uint256[] ids, uint256[] amounts);\n    event TokenNameSet(uint256 id, string name);\n    event RoleSetup(bytes32 role, address account);\n\n    /**\n     * @dev Initializes the contract by setting a base URI and configuring roles.\n     * @param baseUriInput The initial base URI for token metadata.\n     */\n    constructor(string memory baseUriInput) ERC1155(\"\") {\n        baseURI = baseUriInput;\n\n        // Grant roles to the contract deployer\n        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);\n        _setupRole(ADMIN_ROLE, msg.sender);\n        _setupRole(MINTER_ROLE, msg.sender);\n        _setupRole(UPDATER_ROLE, msg.sender);\n\n        // Configure role hierarchies\n        _setRoleAdmin(MINTER_ROLE, ADMIN_ROLE);\n        _setRoleAdmin(UPDATER_ROLE, ADMIN_ROLE);\n    }\n\n    /**\n     * @dev Updates the base URI used for token metadata. Can only be called by an admin.\n     * @param newBaseURI The new base URI to set.\n     */\n    function setBaseURI(string memory newBaseURI) public onlyRole(ADMIN_ROLE) {\n        require(\n            bytes(newBaseURI).length > 0,\n            \"DataPointsMulti: Base URI cannot be empty\"\n        );\n        baseURI = newBaseURI;\n        emit BaseURISet(newBaseURI);\n    }\n\n    /**\n     * @dev Sets the metadata for a token ID. Can only be called by an admin.\n     * @param tokenId The token ID to set metadata for.\n     * @param name The name for the token.\n     * @param image The image URL for the token.\n     * @param description The description for the token.\n     */\n    function setTokenMetadata(\n        uint256 tokenId,\n        string memory name,\n        string memory image,\n        string memory description\n    ) public onlyRole(ADMIN_ROLE) {\n        require(\n            bytes(name).length > 0,\n            \"DataPointsMulti: Token name cannot be empty\"\n        );\n        require(\n            bytes(image).length > 0,\n            \"DataPointsMulti: Token image cannot be empty\"\n        );\n        require(\n            bytes(description).length > 0,\n            \"DataPointsMulti: Token description cannot be empty\"\n        );\n\n        tokenMetadata[tokenId] = TokenMetadata(name, image, description);\n    }\n\n    /**\n     * @dev Generates a URI for a given token ID. The URI is a data URI with a JSON-formatted metadata blob.\n     * This includes the name, description, and image for the token.\n     * @param tokenId The token ID to generate a URI for.\n     * @return The URI string for the given token ID's metadata.\n     */\n    function uri(uint256 tokenId) public view override returns (string memory) {\n        require(\n            bytes(tokenMetadata[tokenId].name).length > 0,\n            \"DataPointsMulti: Token ID does not exist\"\n        );\n\n        // Create a JSON blob with the metadata\n        string memory json = string(\n            abi.encodePacked(\n                '{\"name\": \"',\n                tokenMetadata[tokenId].name,\n                '\", \"description\": \"',\n                tokenMetadata[tokenId].description,\n                '\", \"image\": \"',\n                tokenMetadata[tokenId].image,\n                '\"}'\n            )\n        );\n\n        // Encode the JSON blob as Base64\n        string memory encodedJson = Base64.encode(bytes(json));\n\n        // Return the encoded data as the URI\n        return\n            string(\n                abi.encodePacked(\"data:application/json;base64,\", encodedJson)\n            );\n    }\n\n    /**\n     * @dev Mint batch of tokens of different types to specified addresses, each with a unique numeric UUID.\n     * @param toAddresses An array of addresses to mint tokens to.\n     * @param ids An array of token types to mint.\n     * @param uuids An array of numeric UUIDs for each token being minted.\n     */\n\n    function batchMint(\n        address[] memory toAddresses,\n        uint256[] memory ids,\n        uint256[] memory uuids\n    ) public onlyRole(MINTER_ROLE) {\n        require(\n            toAddresses.length == ids.length && ids.length == uuids.length,\n            \"Must provide equal numbers of addresses, ids, and UUIDs\"\n        );\n\n        for (uint256 i = 0; i < toAddresses.length; i++) {\n            require(isTokenIdValid(ids[i]), \"Invalid token ID\");\n            require(uuids[i] != 0, \"UUID cannot be zero\");\n            require(\n                toAddresses[i] != address(0),\n                \"Cannot mint to the zero address\"\n            );\n\n            // Update the state variable before the external call\n            accountTokenUUIDs[toAddresses[i]][ids[i]].push(uuids[i]);\n\n            // External call to mint the token\n            _mint(toAddresses[i], ids[i], 1, \"0x00\");\n\n            emit TokenMinted(toAddresses[i], ids[i], 1, uuids[i]);\n        }\n    }\n\n    function isTokenIdValid(uint256 tokenId) public view returns (bool) {\n        // Example validation logic to check if the token ID has been initialized\n        // This could be checking against a list of validTokenIds or other criteria\n        // indicating the token ID has been properly set up.\n        for (uint256 i = 0; i < validTokenIds.length; i++) {\n            if (validTokenIds[i] == tokenId) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * @dev Sets the name for a given token ID.\n     * @param id The token ID.\n     * @param name The name to assign to the token ID.\n     * Requires that the caller has the UPDATER_ROLE.\n     */\n    function setTokenName(\n        uint256 id,\n        string memory name\n    ) public onlyRole(UPDATER_ROLE) {\n        require(\n            bytes(name).length > 0,\n            \"DataPointsMulti: Token name cannot be empty\"\n        );\n\n        // Check if the token already has metadata set\n        bool isNewToken = bytes(tokenMetadata[id].name).length == 0;\n\n        // Update the token name within the token metadata\n        tokenMetadata[id].name = name;\n\n        // If this is a new token type, add the token ID to the list and increment the token type counter\n        if (isNewToken) {\n            validTokenIds.push(id);\n            tokenTypeCount++;\n        }\n\n        emit TokenNameSet(id, name);\n    }\n\n    /**\n     * @dev Retrieves the name of a given token ID.\n     * @param id The token ID.\n     * @return The name of the token ID.\n     */\n    function getTokenName(uint256 id) public view returns (string memory) {\n        require(\n            bytes(tokenMetadata[id].name).length != 0,\n            \"DataPointsMulti: Token ID does not exist\"\n        );\n        return tokenMetadata[id].name;\n    }\n\n    /**\n     * @dev Grant or revoke role permissions dynamically.\n     * @param role The role identifier.\n     * @param account The address to grant or revoke the role from.\n     */\n    function setupRole(bytes32 role, address account) public {\n        require(\n            hasRole(DEFAULT_ADMIN_ROLE, msg.sender),\n            \"Caller is not an admin\"\n        );\n        _setupRole(role, account);\n        emit RoleSetup(role, account);\n    }\n\n    /**\n     * @dev Prevents token transfers by overriding `safeTransferFrom` and `safeBatchTransferFrom`.\n     */\n    function safeTransferFrom(\n        address,\n        address,\n        uint256,\n        uint256,\n        bytes memory\n    ) public pure override {\n        require(false, \"DataPointsMulti: transfer not allowed\");\n    }\n\n    function safeBatchTransferFrom(\n        address,\n        address,\n        uint256[] memory,\n        uint256[] memory,\n        bytes memory\n    ) public pure override {\n        require(false, \"DataPointsMulti: batch transfer not allowed\");\n    }\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(\n        bytes4 interfaceId\n    ) public view virtual override(ERC1155, AccessControl) returns (bool) {\n        return super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev Retrieves user balances for all token types.\n     * @param user The address of the user to query balances for.\n     * @return An array of TokenBalance structs containing token names and balances.\n     */\n    function getUserBalances(\n        address user\n    ) public view returns (TokenBalance[] memory) {\n        uint256 length = validTokenIds.length; // Cache the length of the array\n        TokenBalance[] memory balances = new TokenBalance[](length);\n\n        for (uint256 i = 0; i < validTokenIds.length; i++) {\n            uint256 tokenId = validTokenIds[i];\n            balances[i].tokenName = tokenMetadata[tokenId].name;\n            balances[i].balance = balanceOf(user, tokenId);\n        }\n\n        return balances;\n    }\n\n    /**\n     * @dev Retrieves the UUIDs for a specific token ID associated with an account.\n     * @param account The address of the user to query UUIDs for.\n     * @param id The token ID to query UUIDs for.\n     * @return An array of UUIDs for the specified token ID associated with the account.\n     */\n    function getAccountTokenUUIDs(\n        address account,\n        uint256 id\n    ) public view returns (uint256[] memory) {\n        return accountTokenUUIDs[account][id];\n    }\n\n    /**\n     * @dev Public getter to access the array of valid token IDs.\n     * @return An array of valid token IDs.\n     */\n    function getValidTokenIds() public view returns (uint256[] memory) {\n        return validTokenIds;\n    }\n\n    /**\n     * @dev Returns an array of structs containing token IDs and their corresponding metadata.\n     * @return An array of TokenIdMetadata structs.\n     */\n    function getAllTokenIdMetadata()\n        public\n        view\n        returns (TokenIdMetadata[] memory)\n    {\n        TokenIdMetadata[] memory allTokenIdMetadata = new TokenIdMetadata[](\n            validTokenIds.length\n        );\n        for (uint256 i = 0; i < validTokenIds.length; i++) {\n            allTokenIdMetadata[i].tokenId = validTokenIds[i];\n            allTokenIdMetadata[i].metadata = tokenMetadata[validTokenIds[i]];\n        }\n        return allTokenIdMetadata;\n    }\n}\n"
    },
    "contracts/DataStaking.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./DataPointsMulti.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\n\n/**\n * @title DataStaking\n * @dev This contract enables users to stake their non-transferable tokens, represented by DataPointsMulti.sol,\n * and earn rewards based on the staked amount for a specific duration. Each token ID is associated with a unique reward rate, and rewards\n * are distributed in the form of an ERC20 token (e.g., MASA or USDC) after the staking period ends.\n *\n * Inherits from DataPointsMulti for token balance interactions and uses OpenZeppelin's IERC20 for reward management.\n *\n * @notice Ensure that the DataPointsMulti and the ERC20 token used for rewards are deployed and their addresses are known\n * before deploying this contract.\n */\ncontract DataStaking {\n    using SafeMath for uint256;\n\n    // Name of the Data Staking Pool\n    string public name;\n\n    // Reference to the DataPointsMulti contract for staking operations.\n    DataPointsMulti public dataPointsMulti;\n    // Reference to the ERC20 token used for distributing rewards.\n    IERC20 public rewardsToken;\n\n    // Struct to hold staking information for a particular token ID.\n    struct StakeInfo {\n        uint256 amountStaked; // Amount of tokens staked.\n        uint256 rewardRate; // Reward rate for the staked tokens.\n        uint256 startTime; // When the staking started.\n        uint256 duration; // Duration of the staking period in seconds.\n    }\n\n    // Mapping from token ID to its staking information.\n    mapping(uint256 => StakeInfo) public stakeInfos;\n    // Nested mapping to track the staked amount by user and token ID.\n    mapping(address => mapping(uint256 => uint256)) public userStakes;\n\n    // State variable to track the total rewards allocated.\n    uint256 public totalRewardsAllocated;\n\n    // Event emitted when tokens are staked.\n    event Staked(\n        address indexed user,\n        uint256 tokenId,\n        uint256 amount,\n        uint256 startTime,\n        uint256 duration\n    );\n    // Event emitted when rewards are claimed.\n    event RewardsClaimed(address indexed user, uint256 tokenId, uint256 reward);\n\n    /**\n     * @dev Constructor to initialize the DataStaking contract.\n     * @param _name Name of the Data Staking Pool.\n     * @param _dataPointsMulti Address of the DataPointsMulti contract.\n     * @param _rewardsToken Address of the ERC20 token used for rewards.\n     */\n    constructor(\n        string memory _name,\n        DataPointsMulti _dataPointsMulti,\n        IERC20 _rewardsToken\n    ) {\n        name = _name;\n        dataPointsMulti = _dataPointsMulti;\n        rewardsToken = _rewardsToken;\n    }\n\n    /**\n     * @dev Sets the reward rate and duration for a specific token ID. This function should be restricted to authorized roles.\n     * @param tokenId The token ID for which to set the reward rate and duration.\n     * @param rewardRate The reward rate to set.\n     * @param duration The duration of the staking period in seconds.\n     */\n    function setRewardRateAndDuration(\n        uint256 tokenId,\n        uint256 rewardRate,\n        uint256 duration\n    ) public {\n        // Access control checks should be implemented here.\n        stakeInfos[tokenId].rewardRate = rewardRate;\n        stakeInfos[tokenId].duration = duration;\n    }\n\n    /**\n     * @dev Allows users to stake their tokens. Users must have a sufficient balance of the token they wish to stake.\n     * @param tokenId The ID of the token to stake.\n     * @param amount The amount of tokens to stake.\n     */\n    function stake(uint256 tokenId, uint256 amount) public {\n        require(\n            dataPointsMulti.balanceOf(msg.sender, tokenId) >= amount,\n            \"Insufficient token balance\"\n        );\n        require(amount > 0, \"Amount must be greater than 0\");\n\n        StakeInfo storage stakeInfo = stakeInfos[tokenId];\n        require(stakeInfo.duration > 0, \"Staking duration not set\");\n\n        userStakes[msg.sender][tokenId] = userStakes[msg.sender][tokenId].add(\n            amount\n        );\n        if (stakeInfo.startTime == 0) {\n            // If staking for the first time\n            stakeInfo.startTime = block.timestamp;\n        }\n\n        // Calculate potential reward for this stake based on the reward rate and duration\n        uint256 potentialReward = amount.mul(stakeInfo.rewardRate).mul(\n            stakeInfo.duration\n        );\n        totalRewardsAllocated = totalRewardsAllocated.add(potentialReward);\n\n        emit Staked(\n            msg.sender,\n            tokenId,\n            amount,\n            stakeInfo.startTime,\n            stakeInfo.duration\n        );\n    }\n\n    /**\n     * @dev Allows users to claim rewards for their staked tokens after the staking period ends.\n     * @param tokenId The ID of the token for which to claim rewards.\n     */\n    function claimRewards(uint256 tokenId) public {\n        StakeInfo storage stakeInfo = stakeInfos[tokenId];\n        require(\n            block.timestamp >= stakeInfo.startTime.add(stakeInfo.duration),\n            \"Staking period not yet ended\"\n        );\n\n        uint256 stakedAmount = userStakes[msg.sender][tokenId];\n        require(stakedAmount > 0, \"No staked amount found\");\n\n        uint256 reward = stakedAmount.mul(stakeInfo.rewardRate);\n        require(\n            rewardsToken.transfer(msg.sender, reward),\n            \"Failed to transfer rewards\"\n        );\n\n        emit RewardsClaimed(msg.sender, tokenId, reward);\n    }\n}\n"
    },
    "contracts/DataStakingDynamic.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./DataPointsMulti.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/**\n * @title DataStakingDynamic\n * @dev This contract enables users to stake their non-transferable tokens, represented by DataPointsMulti.sol,\n * and earn rewards based on the staked amount for a specific duration. Each token ID is associated with a unique reward rate, and rewards\n * are distributed in the form of an ERC20 token (e.g., MASA or USDC) after the staking period ends.\n *\n * Inherits from DataPointsMulti for token balance interactions and uses OpenZeppelin's IERC20 for reward management.\n *\n * @notice Ensure that the DataPointsMulti and the ERC20 token used for rewards are deployed and their addresses are known\n * before deploying this contract.\n */\ncontract DataStakingDynamic is Ownable {\n    using SafeMath for uint256;\n\n    // Name of the Data Staking Pool\n    string public name;\n\n    // Reference to the DataPointsMulti contract for staking operations.\n    DataPointsMulti public dataPointsMulti;\n    // Reference to the ERC20 token used for distributing rewards.\n    IERC20 public rewardsToken;\n\n    // Struct to hold staking information for a particular token ID.\n    struct StakeInfo {\n        uint256 amountStaked; // Total amount of tokens staked for this ID.\n        uint256 rewardPerToken; // Reward per token for this ID.\n        uint256 duration; // Duration of the staking period in seconds.\n        uint256 startTime; // Start time of staking.\n        uint256 maxStakeLimit; // Maximum limit of tokens that can be staked.\n    }\n\n    // Assume this mapping exists to keep track of all stakers for each tokenId\n    mapping(uint256 => address[]) private stakersPerTokenId;\n\n    // Mapping from token ID to its staking information.\n    mapping(uint256 => StakeInfo) public stakeInfos;\n    // Nested mapping to track the staked amount by user and token ID.\n    mapping(address => mapping(uint256 => uint256)) public userStakes;\n\n    // State variable to track the total rewards available for distribution.\n    uint256 public totalRewardsPool;\n\n    // Array to keep track of all tokenIds with staking parameters\n    uint256[] private stakingTokenIds;\n\n    // Event emitted when tokens are staked.\n    event Staked(\n        address indexed user,\n        uint256 tokenId,\n        uint256 amount,\n        uint256 startTime,\n        uint256 duration\n    );\n    // Event emitted when rewards are claimed.\n    event RewardsClaimed(address indexed user, uint256 tokenId, uint256 reward);\n\n    // Event emitted when rewards are deposited into the contract\n    event RewardsDeposited(\n        address indexed depositor,\n        uint256 amount,\n        uint256 newTotalRewardsPool\n    );\n\n    /**\n     * @dev Constructor to initialize the DataStaking contract.\n     * @param _name Name of the Data Staking Pool.\n     * @param _dataPointsMulti Address of the DataPointsMulti contract.\n     * @param _rewardsToken Address of the ERC20 token used for rewards.\n     */\n    constructor(\n        string memory _name,\n        DataPointsMulti _dataPointsMulti,\n        IERC20 _rewardsToken\n    ) Ownable() {\n        name = _name;\n        dataPointsMulti = _dataPointsMulti;\n        rewardsToken = _rewardsToken;\n    }\n\n    // Function to deposit rewards into the contract and update the rewards pool\n    function depositRewards(uint256 amount) public onlyOwner {\n        // Transfer the rewards tokens from the sender to this contract\n        require(\n            rewardsToken.transferFrom(msg.sender, address(this), amount),\n            \"Transfer failed\"\n        );\n\n        // Update the total rewards pool\n        totalRewardsPool = totalRewardsPool.add(amount);\n\n        // Emit an event for the deposit\n        emit RewardsDeposited(msg.sender, amount, totalRewardsPool);\n    }\n\n    /**\n     * @dev Sets the reward per token, duration, and max stake limit for a specific token ID.\n     * @param tokenId The token ID for which to set the staking parameters.\n     * @param rewardPerToken The reward per token to set for the token ID.\n     * @param duration The duration of the staking period in seconds.\n     * @param maxStakeLimit The maximum amount of tokens that can be staked for the token ID.\n     */\n    function setStakingParameters(\n        uint256 tokenId,\n        uint256 rewardPerToken,\n        uint256 duration,\n        uint256 maxStakeLimit\n    ) public onlyOwner {\n        // Set the staking parameters\n        stakeInfos[tokenId] = StakeInfo({\n            amountStaked: 0, // Initialize to zero\n            rewardPerToken: rewardPerToken,\n            duration: duration,\n            startTime: 0, // Initialize to zero or current block timestamp if staking starts immediately\n            maxStakeLimit: maxStakeLimit\n        });\n\n        // Check if tokenId is already in stakingTokenIds array to avoid duplicates\n        bool exists = false;\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            if (stakingTokenIds[i] == tokenId) {\n                exists = true;\n                break;\n            }\n        }\n        if (!exists) {\n            stakingTokenIds.push(tokenId);\n        }\n    }\n\n    /**\n     * @dev Allows users to stake all their tokens for which staking parameters have been set.\n     * This function iterates over the array of token IDs that have staking parameters defined.\n     * For each token ID, it checks the user's balance and stakes up to the maximum stake limit,\n     * or the user's balance, whichever is lower. It calculates the potential reward and ensures\n     * that staking does not exceed the total available reward pool. The function updates the user's\n     * staked amount and the total staked amount for each token ID. It emits a Staked event for each\n     * staking action performed.\n     */\n    function stakeAll() public {\n        uint256 totalPotentialReward;\n        bool hasStakableTokens = false;\n\n        // First pass: Check for stakable tokens and calculate the total potential reward\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            uint256 userBalance = dataPointsMulti.balanceOf(\n                msg.sender,\n                tokenId\n            );\n\n            // Skip tokens that the user does not have or for which staking is not set up\n            if (userBalance == 0 || stakeInfos[tokenId].duration == 0) {\n                continue;\n            }\n\n            hasStakableTokens = true;\n\n            uint256 stakeableAmount = (userBalance >\n                stakeInfos[tokenId].maxStakeLimit)\n                ? stakeInfos[tokenId].maxStakeLimit\n                : userBalance;\n\n            uint256 potentialReward = stakeableAmount.mul(\n                stakeInfos[tokenId].rewardPerToken\n            );\n\n            // Ensure the potential reward does not exceed the remaining reward pool\n            require(\n                totalPotentialReward.add(potentialReward) <= totalRewardsPool,\n                \"Staking exceeds available reward pool\"\n            );\n\n            totalPotentialReward = totalPotentialReward.add(potentialReward);\n        }\n\n        // Require that the user has at least one stakable token\n        require(\n            hasStakableTokens,\n            \"No stakable tokens found or no duration has been set.\"\n        );\n\n        // Second pass: Perform the actual staking\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            uint256 userBalance = dataPointsMulti.balanceOf(\n                msg.sender,\n                tokenId\n            );\n\n            // Skip tokens that the user does not have or for which staking is not set up\n            if (userBalance == 0 || stakeInfos[tokenId].duration == 0) {\n                continue;\n            }\n\n            uint256 stakeableAmount = (userBalance >\n                stakeInfos[tokenId].maxStakeLimit)\n                ? stakeInfos[tokenId].maxStakeLimit\n                : userBalance;\n\n            // Calculate the potential reward for the stakable amount\n            uint256 potentialReward = stakeableAmount.mul(\n                stakeInfos[tokenId].rewardPerToken\n            );\n\n            // Subtract the potential reward from the total reward pool\n            totalRewardsPool = totalRewardsPool.sub(potentialReward);\n\n            // Subtract the user's previous stake from the total staked amount for the tokenId\n            uint256 currentStake = userStakes[msg.sender][tokenId];\n            stakeInfos[tokenId].amountStaked = stakeInfos[tokenId]\n                .amountStaked\n                .sub(currentStake);\n\n            // Update the user's staked amount with the new stakable amount\n            userStakes[msg.sender][tokenId] = stakeableAmount;\n\n            // Add the new stakable amount to the total staked amount for the tokenId\n            stakeInfos[tokenId].amountStaked = stakeInfos[tokenId]\n                .amountStaked\n                .add(stakeableAmount);\n\n            // Emit an event that tokens have been staked\n            emit Staked(\n                msg.sender,\n                tokenId,\n                stakeableAmount,\n                stakeInfos[tokenId].startTime,\n                stakeInfos[tokenId].duration\n            );\n        }\n    }\n\n    /**\n     * @dev Allows users to claim rewards for all their staked tokens after their respective staking periods end.\n     */\n    function claimAllRewards() public {\n        uint256 totalReward = 0;\n\n        // First pass: Calculate the total reward without making state changes\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            StakeInfo storage stakeInfo = stakeInfos[tokenId];\n\n            if (\n                block.timestamp >= stakeInfo.startTime.add(stakeInfo.duration)\n            ) {\n                uint256 stakedAmount = userStakes[msg.sender][tokenId];\n                if (stakedAmount > 0) {\n                    uint256 reward = stakedAmount.mul(stakeInfo.rewardPerToken);\n                    totalReward = totalReward.add(reward);\n                }\n            }\n        }\n        // Check if there are any rewards to claim\n        require(totalReward > 0, \"No rewards to claim\");\n\n        // Ensure there are enough tokens in the reward pool to cover the claim\n        require(totalReward <= totalRewardsPool, \"Insufficient reward pool\");\n\n        // Second pass: Reset stakes and emit events\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            StakeInfo storage stakeInfo = stakeInfos[tokenId];\n\n            if (\n                block.timestamp >= stakeInfo.startTime.add(stakeInfo.duration)\n            ) {\n                uint256 stakedAmount = userStakes[msg.sender][tokenId];\n                if (stakedAmount > 0) {\n                    // Reset the user's staked amount for the tokenId to zero\n                    userStakes[msg.sender][tokenId] = 0;\n\n                    // Emit an event for the reward claim\n                    emit RewardsClaimed(\n                        msg.sender,\n                        tokenId,\n                        stakedAmount.mul(stakeInfo.rewardPerToken)\n                    );\n                }\n            }\n        }\n\n        // Subtract the total reward from the total reward pool\n        totalRewardsPool = totalRewardsPool.sub(totalReward);\n\n        // Transfer the total calculated reward to the user, reverting on failure\n        require(\n            rewardsToken.transfer(msg.sender, totalReward),\n            \"Failed to transfer rewards\"\n        );\n    }\n\n    /**\n     * @dev Returns the remaining amount of ERC20 tokens in the contract.\n     * @return The amount of tokens left for rewards.\n     */\n    function getRemainingRewards() public view returns (uint256) {\n        return rewardsToken.balanceOf(address(this));\n    }\n}\n"
    },
    "contracts/DataStakingDynamicNative.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./DataPointsMulti.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/**\n * @title DataStakingDynamicNative\n * @dev This contract enables users to stake their non-transferable tokens, represented by DataPointsMulti.sol,\n * and earn rewards based on the staked amount for a specific duration. Each token ID is associated with a unique reward rate, and rewards\n * are distributed in the form of the native currency (e.g., ETH) after the staking period ends.\n *\n * Inherits from DataPointsMulti for token balance interactions. Rewards are managed directly with the native currency of the blockchain,\n * allowing users to claim their earnings in ETH or the native currency of the network where the contract is deployed.\n *\n * @notice Ensure that the DataPointsMulti contract is deployed and its address is known before deploying this contract.\n * The contract should be funded with enough native currency to cover the reward payouts.\n */\ncontract DataStakingDynamicNative is Ownable {\n    using SafeMath for uint256;\n\n    // Name of the Data Staking Pool\n    string public name;\n\n    // Reference to the DataPointsMulti contract for staking operations.\n    DataPointsMulti public dataPointsMulti;\n\n    // Struct to hold staking information for a particular token ID.\n    struct StakeInfo {\n        uint256 amountStaked; // Total amount of tokens staked for this ID.\n        uint256 rewardPerToken; // Reward per token for this ID.\n        uint256 maxStakeLimit; // Maximum limit of tokens that can be staked.\n    }\n\n    // Struct to hold details of a staking transaction\n    struct StakingTransaction {\n        uint256 tokenId;\n        uint256 amount;\n        uint256 rewardPerToken;\n        uint256 timestamp;\n    }\n\n    // Struct to hold details of a claim transaction\n    struct ClaimTransaction {\n        uint256 totalRewardsClaimed;\n        uint256 timestamp;\n    }\n\n    // Struct to hold eligible staking amount details\n    struct EligibleStakeAmount {\n        uint256 tokenId;\n        uint256 amount;\n    }\n\n    // Mapping from user address to an array of their staking transactions\n    mapping(address => StakingTransaction[]) public userStakingTransactions;\n\n    // Mapping from user address to an array of their claimed transactions\n    mapping(address => ClaimTransaction) public userClaimTransactions;\n\n    // Mapping from token ID to its staking information.\n    mapping(uint256 => StakeInfo) public stakeInfos;\n\n    // Nested mapping to track the staked amount by user and token ID.\n    mapping(address => mapping(uint256 => uint256)) public userStakes;\n\n    // Mapping from address to its staked status.\n    mapping(address => bool) private hasStaked;\n\n    // State variable to track number of stakers in pool.\n    address[] private stakers;\n\n    // State variable to track the total rewards available for distribution.\n    uint256 public totalRewardsPool;\n\n    // Array to keep track of all tokenIds with staking parameters\n    uint256[] public stakingTokenIds;\n\n    // State variable to track the timestamp when the total rewards pool becomes full\n    uint256 public rewardsPoolFullTimestamp;\n\n    // State variable to track the amount of rewards that have been reserved but not yet claimed.\n    uint256 public totalReservedRewards;\n\n    // Duration of the staking period in seconds.\n    uint256 public duration;\n\n    // Start time of staking.\n    uint256 public startTime;\n\n    // Royalty fee info\n    uint256 public royaltyFeePercentage; // Represented as a whole number, e.g., 1 for 1%\n\n    // Royalty fee info\n    address public royaltyRecipient;\n\n    // Staking period initialized tracking\n    bool public stakingPeriodInitialized = false;\n\n    // Event emitted when tokens are staked.\n    event Staked(\n        address indexed user,\n        uint256 tokenId,\n        uint256 amount,\n        uint256 startTime,\n        uint256 duration\n    );\n\n    // Event emitted when rewards are claimed.\n    event RewardsClaimed(address indexed user, uint256 tokenId, uint256 reward);\n\n    // Event emitted when rewards are deposited into the contract\n    event RewardsDeposited(\n        address indexed depositor,\n        uint256 amount,\n        uint256 newTotalRewardsPool\n    );\n\n    // Event emitted when unclaimed rewards are withdrawn from the contract\n    event UnclaimedRewardsWithdrawn(address indexed owner, uint256 amount);\n\n    /**\n     * @dev Constructor to initialize the DataStaking contract.\n     * @param _name Name of the Data Staking Pool.\n     * @param _dataPointsMulti Address of the DataPointsMulti contract.\n     */\n    constructor(\n        string memory _name,\n        DataPointsMulti _dataPointsMulti\n    ) Ownable() {\n        name = _name;\n        dataPointsMulti = _dataPointsMulti;\n    }\n\n    // Function to receive Ether. msg.data must be empty\n    receive() external payable {\n        // Emit an event or handle received Ether\n        emit RewardsDeposited(\n            msg.sender,\n            msg.value,\n            totalRewardsPool.add(msg.value)\n        );\n        totalRewardsPool = totalRewardsPool.add(msg.value);\n    }\n\n    /**\n     * @dev Sets the staking duration and start time for the contract.\n     * @param _duration The duration of the staking period in seconds.\n     * @param _startTime The start time for staking to begin.\n     */\n    function setStakingTimeframe(\n        uint256 _duration,\n        uint256 _startTime\n    ) public onlyOwner {\n        require(\n            !stakingPeriodInitialized,\n            \"Staking timeframe cannot be changed after initialization.\"\n        );\n        duration = _duration;\n        startTime = _startTime;\n        stakingPeriodInitialized = true;\n    }\n\n    /**\n     * @dev Sets the reward per token, duration, and max stake limit for a specific token ID.\n     * @param tokenId The token ID for which to set the staking parameters.\n     * @param rewardPerToken The reward per token to set for the token ID.\n     * @param maxStakeLimit The maximum amount of tokens that can be staked for the token ID.\n     */\n    function setStakingParameters(\n        uint256 tokenId,\n        uint256 rewardPerToken,\n        uint256 maxStakeLimit\n    ) public onlyOwner {\n        require(\n            stakeInfos[tokenId].amountStaked == 0,\n            \"Token already initialized with staked amounts\"\n        );\n        require(\n            dataPointsMulti.isTokenIdValid(tokenId),\n            \"Token ID is not valid\"\n        );\n        require(\n            !stakingPeriodInitialized,\n            \"Staking parameters cannot be changed after initialization.\"\n        );\n\n        // Set the staking parameters\n        stakeInfos[tokenId] = StakeInfo({\n            amountStaked: 0, // Initialize to zero\n            rewardPerToken: rewardPerToken,\n            maxStakeLimit: maxStakeLimit\n        });\n\n        // Check if tokenId is already in stakingTokenIds array to avoid duplicates\n        bool exists = false;\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            if (stakingTokenIds[i] == tokenId) {\n                exists = true;\n                break;\n            }\n        }\n        if (!exists) {\n            stakingTokenIds.push(tokenId);\n        }\n    }\n\n    /**\n     * @dev Allows users to stake all their tokens for which staking parameters have been set.\n     * This function iterates over the array of token IDs that have staking parameters defined.\n     * For each token ID, it checks the user's balance and stakes up to the maximum stake limit,\n     * or the user's balance, whichever is lower. It calculates the potential reward and ensures\n     * that staking does not exceed the total available reward pool. The function updates the user's\n     * staked amount and the total staked amount for each token ID. It emits a Staked event for each\n     * staking action performed.\n     */\n    function stakeAll() public {\n        bool isStakingPeriodOver = isStakingPoolFilledOrExpired();\n        require(!isStakingPeriodOver, \"Staking period has not ended yet\");\n\n        uint256 remainingRewardsPool = totalRewardsPool.sub(\n            totalReservedRewards\n        );\n        require(\n            remainingRewardsPool > 0,\n            \"Insufficient rewards available in the pool.\"\n        );\n\n        uint256 minRewardPerToken = getMinRewardPerToken(); // Get the smallest rewardPerToken\n\n        bool hasStakableTokens = false;\n\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            uint256 userBalance = dataPointsMulti.balanceOf(\n                msg.sender,\n                tokenId\n            );\n            uint256 previousTotalStaked = userStakes[msg.sender][tokenId];\n            StakeInfo memory stakeInfo = stakeInfos[tokenId];\n\n            if (userBalance == 0 || duration == 0) {\n                continue;\n            }\n            if (userBalance <= previousTotalStaked) {\n                continue;\n            }\n\n            hasStakableTokens = true;\n\n            uint256 remainingUserStakeCapacity = stakeInfo.maxStakeLimit.sub(\n                previousTotalStaked\n            );\n            uint256 maxStakeableBasedOnRewards = remainingRewardsPool.div(\n                stakeInfo.rewardPerToken\n            );\n            uint256 actualMaxStakeable = Math.min(\n                remainingUserStakeCapacity,\n                maxStakeableBasedOnRewards\n            );\n\n            uint256 actualStakeableAmount = Math.min(\n                userBalance.sub(previousTotalStaked),\n                actualMaxStakeable\n            );\n\n            uint256 actualPotentialReward = actualStakeableAmount.mul(\n                stakeInfo.rewardPerToken\n            );\n            if (actualPotentialReward > remainingRewardsPool) {\n                actualStakeableAmount = remainingRewardsPool.div(\n                    stakeInfo.rewardPerToken\n                );\n                actualPotentialReward = actualStakeableAmount.mul(\n                    stakeInfo.rewardPerToken\n                );\n            }\n\n            remainingRewardsPool = remainingRewardsPool.sub(\n                actualPotentialReward\n            );\n\n            // Calculate the stake difference for the transaction record\n            uint256 stakeDifference = actualStakeableAmount;\n\n            // Update the user's total staked amount for the tokenId\n            userStakes[msg.sender][tokenId] = previousTotalStaked.add(\n                actualStakeableAmount\n            );\n\n            // Update the stake info and total reserved rewards\n            stakeInfo.amountStaked = stakeInfo.amountStaked.add(\n                actualStakeableAmount\n            );\n            totalReservedRewards = totalReservedRewards.add(\n                actualPotentialReward\n            );\n\n            if (stakeDifference > 0) {\n                userStakingTransactions[msg.sender].push(\n                    StakingTransaction({\n                        tokenId: tokenId,\n                        amount: stakeDifference,\n                        rewardPerToken: stakeInfo.rewardPerToken,\n                        timestamp: block.timestamp\n                    })\n                );\n                // Emit an event that tokens have been staked\n                emit Staked(\n                    msg.sender,\n                    tokenId,\n                    actualStakeableAmount,\n                    startTime,\n                    duration\n                );\n            }\n        }\n\n        // Track unique staker\n        if (hasStakableTokens && !hasStaked[msg.sender]) {\n            stakers.push(msg.sender);\n            hasStaked[msg.sender] = true;\n        }\n\n        require(\n            hasStakableTokens,\n            \"No stakable tokens found or staking period has ended.\"\n        );\n\n        // Adjust the condition to check if the remaining rewards pool is less than the smallest rewardPerToken\n        if (\n            remainingRewardsPool > 0 &&\n            remainingRewardsPool < minRewardPerToken &&\n            rewardsPoolFullTimestamp == 0\n        ) {\n            rewardsPoolFullTimestamp = block.timestamp;\n        }\n    }\n\n    /**\n     * @dev Allows users to claim rewards for all their staked tokens after their respective staking periods end.\n     */\n    function claimAllRewards() public {\n        bool isStakingPeriodOver = isStakingPoolFilledOrExpired();\n        require(isStakingPeriodOver, \"Staking period has not ended yet\");\n        uint256 totalReward = 0;\n\n        // First pass: Calculate the total reward without making state changes\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            StakeInfo memory stakeInfo = stakeInfos[tokenId];\n\n            uint256 stakedAmount = userStakes[msg.sender][tokenId];\n            if (stakedAmount > 0) {\n                uint256 reward = stakedAmount.mul(stakeInfo.rewardPerToken);\n                totalReward = totalReward.add(reward);\n            }\n        }\n\n        // Check if there are any rewards to claim\n        require(totalReward > 0, \"No rewards to claim\");\n\n        // Ensure there are enough reserved rewards to cover the claim\n        require(\n            totalReward <= totalReservedRewards,\n            \"Insufficient reserved rewards\"\n        );\n\n        // Subtract the claimed rewards from the total rewards pool\n        totalRewardsPool = totalRewardsPool.sub(totalReward); // Update totalRewardsPool to reflect the claimed rewards\n\n        // Second pass: Reset stakes and emit events\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            StakeInfo storage stakeInfo = stakeInfos[tokenId];\n\n            uint256 stakedAmount = userStakes[msg.sender][tokenId];\n            if (stakedAmount > 0) {\n                // Reset the user's staked amount for the tokenId to zero\n                userStakes[msg.sender][tokenId] = 0;\n\n                // Emit an event for the reward claim\n                emit RewardsClaimed(\n                    msg.sender,\n                    tokenId,\n                    stakedAmount.mul(stakeInfo.rewardPerToken)\n                );\n            }\n        }\n\n        // Initialize rewardAfterRoyalty with the totalReward\n        uint256 rewardAfterRoyalty = totalReward;\n        uint256 royaltyFee = 0;\n\n        // Calculate the royalty fee only if the royaltyFeePercentage is greater than 0\n        if (royaltyFeePercentage > 0) {\n            royaltyFee = totalReward.mul(royaltyFeePercentage).div(100);\n            rewardAfterRoyalty = totalReward.sub(royaltyFee);\n        }\n\n        // Update the claim transaction with the reward after potential royalty\n        ClaimTransaction memory newClaimTransaction = ClaimTransaction({\n            totalRewardsClaimed: rewardAfterRoyalty,\n            timestamp: block.timestamp\n        });\n\n        userClaimTransactions[msg.sender] = newClaimTransaction;\n\n        // Subtract the total reward from the total reserved rewards\n        totalReservedRewards = totalReservedRewards.sub(totalReward);\n\n        // Transfer the royalty fee to the royalty recipient if there is a fee\n        if (royaltyFee > 0) {\n            (bool royaltySuccess, ) = royaltyRecipient.call{value: royaltyFee}(\n                \"\"\n            );\n            require(royaltySuccess, \"Failed to transfer royalty fee\");\n        }\n\n        // Transfer the reward after royalty to the user, reverting on failure\n        (bool rewardSuccess, ) = msg.sender.call{value: rewardAfterRoyalty}(\"\");\n        require(rewardSuccess, \"Failed to transfer rewards\");\n    }\n\n    /**\n     * @dev Allows the contract owner to withdraw unclaimed rewards after the staking period has ended.\n     * This function is designed to recover funds that were not distributed because users did not claim their rewards.\n     * It can only be called by the owner of the contract and only after the staking period has concluded,\n     * as determined by the `startTime` and `duration` parameters. The amount withdrawn is the difference\n     * between the total rewards pool and the total reserved rewards, which represents the unclaimed rewards.\n     * It is crucial to ensure that all users have had a sufficient opportunity to claim their rewards\n     * before this function is called to maintain trust and integrity in the staking process.\n     * @notice This function will revert if the staking period has not ended or if there are no unclaimed rewards.\n     */\n    function withdrawUnclaimedRewards() public onlyOwner {\n        bool isStakingPeriodOver = isStakingPoolFilledOrExpired();\n        require(isStakingPeriodOver, \"Staking period has not ended yet\");\n        require(\n            totalReservedRewards < totalRewardsPool,\n            \"No unclaimed rewards available\"\n        );\n\n        uint256 unclaimedRewards = totalRewardsPool.sub(totalReservedRewards);\n        // Ensure there are unclaimed rewards to withdraw\n        require(unclaimedRewards > 0, \"No unclaimed rewards to withdraw\");\n\n        // Update the total rewards pool\n        totalRewardsPool = totalRewardsPool.sub(unclaimedRewards);\n\n        // Transfer the unclaimed rewards to the owner\n        (bool success, ) = owner().call{value: unclaimedRewards}(\"\");\n        require(success, \"Failed to withdraw unclaimed rewards\");\n\n        // Emit the event after a successful withdrawal\n        emit UnclaimedRewardsWithdrawn(owner(), unclaimedRewards);\n    }\n\n    /**\n     * @dev Sets the royalty fee and royalty recipient.\n     * Can only be called by the contract owner.\n     * @param _royaltyFeePercentage The new royalty fee percentage.\n     *  @param _royaltyRecipient The new royalty recipient address.\n     */\n    function setRoyaltyFeePercentageAndRecipient(\n        uint256 _royaltyFeePercentage,\n        address _royaltyRecipient\n    ) public onlyOwner {\n        require(\n            _royaltyRecipient != address(0),\n            \"Royalty recipient cannot be the zero address\"\n        );\n        royaltyFeePercentage = _royaltyFeePercentage;\n        royaltyRecipient = _royaltyRecipient;\n    }\n\n    /**\n     * @dev Returns the remaining amount of ETH in the contract.\n     * @return The amount of ETH left for rewards.\n     */\n    function getRemainingRewards() public view returns (uint256) {\n        return address(this).balance;\n    }\n\n    /**\n     * @dev Calculates the maximum amount a user can stake for each tokenId based on the current staking parameters.\n     * It takes into account the user's balance, the maxStakeLimit for each tokenId, and the remaining rewards in the pool.\n     * The function ensures that the user does not exceed the maximum staking capacity for any tokenId and that the\n     * total potential rewards do not surpass the available rewards in the pool. The function returns an array of structs,\n     * each containing a tokenId and the corresponding amount that the user is eligible to stake.\n     * @param user The address of the user for whom to calculate the available staking amounts.\n     * @return eligibleStakes An array of structs, each containing a tokenId and the corresponding eligible staking amount.\n     */\n    function getEligibleStakingAmounts(\n        address user\n    ) public view returns (EligibleStakeAmount[] memory eligibleStakes) {\n        eligibleStakes = new EligibleStakeAmount[](stakingTokenIds.length);\n\n        // Check if the staking pool is expired or full\n        bool poolIsExpiredOrFull = block.timestamp > startTime.add(duration) ||\n            totalReservedRewards >= totalRewardsPool;\n\n        if (poolIsExpiredOrFull) {\n            for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n                eligibleStakes[i] = EligibleStakeAmount({\n                    tokenId: stakingTokenIds[i],\n                    amount: 0\n                });\n            }\n            return eligibleStakes;\n        }\n\n        uint256 remainingUnreservedRewards = totalRewardsPool.sub(\n            totalReservedRewards\n        );\n\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            StakeInfo memory stakeInfo = stakeInfos[tokenId];\n\n            uint256 userBalance = dataPointsMulti.balanceOf(user, tokenId);\n            uint256 userStakedAmount = userStakes[user][tokenId];\n\n            // Calculate the remaining staking capacity for this user for this tokenId\n            uint256 remainingUserStakeCapacity = stakeInfo.maxStakeLimit >\n                userStakedAmount\n                ? stakeInfo.maxStakeLimit.sub(userStakedAmount)\n                : 0;\n\n            // Calculate the maximum stakeable amount based on the remaining rewards in the pool\n            uint256 maxStakeableBasedOnRemainingRewards = remainingUnreservedRewards\n                    .div(stakeInfo.rewardPerToken);\n\n            // The actual maximum stakeable amount is the minimum of the remainingUserStakeCapacity and maxStakeableBasedOnRemainingRewards\n            uint256 actualMaxStakeable = Math.min(\n                remainingUserStakeCapacity,\n                maxStakeableBasedOnRemainingRewards\n            );\n\n            // Determine the maximum additional amount the user can stake\n            uint256 maxAdditionalStake = Math.min(\n                userBalance.sub(userStakedAmount),\n                actualMaxStakeable\n            );\n\n            // Calculate the potential reward for the maxAdditionalStake\n            uint256 potentialReward = maxAdditionalStake.mul(\n                stakeInfo.rewardPerToken\n            );\n\n            // If the potential reward exceeds the remaining unreserved rewards, adjust the maxAdditionalStake\n            if (potentialReward > remainingUnreservedRewards) {\n                maxAdditionalStake = remainingUnreservedRewards.div(\n                    stakeInfo.rewardPerToken\n                );\n                potentialReward = maxAdditionalStake.mul(\n                    stakeInfo.rewardPerToken\n                ); // Recalculate potential reward after adjustment\n            }\n\n            // Set the available staking amount for this tokenId\n            eligibleStakes[i] = EligibleStakeAmount({\n                tokenId: tokenId,\n                amount: maxAdditionalStake\n            });\n\n            // Subtract the potential reward from the remaining unreserved rewards for subsequent iterations\n            remainingUnreservedRewards = remainingUnreservedRewards.sub(\n                potentialReward\n            );\n        }\n\n        return eligibleStakes;\n    }\n\n    /**\n     * @dev Checks if the staking pool is full based on the total reserved rewards or if the staking period has expired.\n     * The staking pool is considered full if the total reserved rewards equal the total rewards pool initially set.\n     * The staking period is considered expired if the current block timestamp is greater than or equal to\n     * the sum of the `startTime` and `duration`.\n     * @return True if the staking pool is full based on the total reserved rewards or the staking period has expired, false otherwise.\n     */\n    function isStakingPoolFilledOrExpired() public view returns (bool) {\n        bool isPoolFull = totalReservedRewards >= totalRewardsPool;\n        bool isStakingPeriodOver = block.timestamp >= startTime.add(duration);\n\n        // Calculate the remaining rewards pool\n        uint256 remainingRewardsPool = totalRewardsPool.sub(\n            totalReservedRewards\n        );\n        uint256 minRewardPerToken = getMinRewardPerToken(); // Assume this function exists\n\n        // Check if the remaining rewards pool is less than the smallest rewardPerToken\n        bool isPoolEffectivelyFull = remainingRewardsPool > 0 &&\n            remainingRewardsPool < minRewardPerToken;\n\n        return isPoolFull || isStakingPeriodOver || isPoolEffectivelyFull;\n    }\n\n    /**\n     * @dev Checks if the staking pool is full based on the total reserved rewards or if the staking period has expired.\n     * The staking pool is considered full if the total reserved rewards equal the total rewards pool initially set.\n     * The staking period is considered expired if the current block timestamp is greater than or equal to\n     * the sum of the `startTime` and `duration`.\n     * @return True if the staking pool is full based on the total reserved rewards or the staking period has expired, false otherwise.\n     */\n    function getUserStakingTransactionsCount(\n        address user\n    ) public view returns (uint256) {\n        return userStakingTransactions[user].length;\n    }\n\n    function getStakingTokenIds() public view returns (uint256[] memory) {\n        return stakingTokenIds;\n    }\n\n    /**\n     * @dev Checks if the user has claimed rewards and returns the details of the last claim transaction.\n     * @param user The address of the user to check.\n     * @return hasClaimed A boolean indicating whether the user has claimed rewards.\n     * @return lastClaimTransaction The details of the last claim transaction if one exists.\n     */\n    function hasUserClaimed(\n        address user\n    )\n        public\n        view\n        returns (bool hasClaimed, ClaimTransaction memory lastClaimTransaction)\n    {\n        // Check if the user's claim transaction has a non-zero timestamp\n        hasClaimed = userClaimTransactions[user].timestamp > 0;\n        if (hasClaimed) {\n            // If the user has claimed, return the claim transaction details\n            lastClaimTransaction = userClaimTransactions[user];\n        }\n    }\n\n    function getContractData()\n        public\n        view\n        returns (\n            string memory contractName,\n            uint256 totalRewards,\n            uint256 rewardsFullTimestamp,\n            uint256 reservedRewards,\n            uint256 remainingRewards,\n            uint256 stakingDuration,\n            uint256 stakingStartTime,\n            uint256 feePercentage,\n            address feeRecipient,\n            bool isStakingPoolFilledOrExpiredBool\n        )\n    {\n        contractName = name;\n        totalRewards = totalRewardsPool;\n        rewardsFullTimestamp = rewardsPoolFullTimestamp;\n        reservedRewards = totalReservedRewards;\n        remainingRewards = totalRewardsPool.sub(totalReservedRewards);\n        stakingDuration = duration;\n        stakingStartTime = startTime;\n        feePercentage = royaltyFeePercentage;\n        feeRecipient = royaltyRecipient;\n        isStakingPoolFilledOrExpiredBool = isStakingPoolFilledOrExpired();\n    }\n\n    function getUserData(\n        address user\n    )\n        public\n        view\n        returns (\n            StakingTransaction[] memory stakingTransactions,\n            ClaimTransaction memory claimTransaction,\n            StakeInfo[] memory stakeInfosArray,\n            uint256[] memory stakedAmounts,\n            EligibleStakeAmount[] memory eligibleStakes\n        )\n    {\n        stakingTransactions = userStakingTransactions[user];\n        claimTransaction = userClaimTransactions[user];\n\n        // Use stakingTokenIds specific to this contract\n        stakeInfosArray = new StakeInfo[](stakingTokenIds.length);\n        stakedAmounts = new uint256[](stakingTokenIds.length);\n\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            stakeInfosArray[i] = stakeInfos[tokenId];\n            stakedAmounts[i] = userStakes[user][tokenId];\n        }\n\n        // Reuse the getEligibleStakingAmounts function to avoid redundancy\n        eligibleStakes = getEligibleStakingAmounts(user);\n    }\n\n    function getNumberOfStakers() public view returns (uint256) {\n        return stakers.length;\n    }\n\n    function getMinRewardPerToken() public view returns (uint256) {\n        uint256 minReward = type(uint256).max;\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            uint256 rewardPerToken = stakeInfos[tokenId].rewardPerToken;\n            if (rewardPerToken < minReward) {\n                minReward = rewardPerToken;\n            }\n        }\n        return minReward == type(uint256).max ? 0 : minReward;\n    }\n}\n"
    },
    "contracts/DataStakingFixed.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./DataPointsMulti.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/math/SafeMath.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/**\n * @title DataStakingFixed\n * @dev This contract enables users to stake their non-transferable tokens, represented by DataPointsMulti.sol,\n * and earn rewards based on the staked amount for a specific duration. Each token ID is associated with a unique reward rate, and rewards\n * are distributed in the form of an ERC20 token (e.g., MASA or USDC) after the staking period ends.\n *\n * Inherits from DataPointsMulti for token balance interactions and uses OpenZeppelin's IERC20 for reward management.\n *\n * @notice Ensure that the DataPointsMulti and the ERC20 token used for rewards are deployed and their addresses are known\n * before deploying this contract.\n */\ncontract DataStakingFixed is Ownable {\n    using SafeMath for uint256;\n\n    // Name of the Data Staking Pool\n    string public name;\n\n    // Reference to the DataPointsMulti contract for staking operations.\n    DataPointsMulti public dataPointsMulti;\n    // Reference to the ERC20 token used for distributing rewards.\n    IERC20 public rewardsToken;\n\n    // Struct to hold staking information for a particular token ID.\n    struct StakeInfo {\n        uint256 amountStaked; // Total amount of tokens staked for this ID.\n        uint256 rewardWeight; // Reward weight per token for this ID.\n        uint256 duration; // Duration of the staking period in seconds.\n        uint256 startTime; // Start time of staking.\n        uint256 maxStakeLimit; // Maximum limit of tokens that can be staked.\n    }\n\n    // Assume this mapping exists to keep track of all stakers for each tokenId\n    mapping(uint256 => address[]) private stakersPerTokenId;\n\n    // Mapping from token ID to its staking information.\n    mapping(uint256 => StakeInfo) public stakeInfos;\n    // Nested mapping to track the staked amount by user and token ID.\n    mapping(address => mapping(uint256 => uint256)) public userStakes;\n\n    // State variable to track the total rewards available for distribution.\n    uint256 public totalRewardsPool;\n\n    // Array to keep track of all tokenIds with staking parameters\n    uint256[] private stakingTokenIds;\n\n    // Event emitted when tokens are staked.\n    event Staked(\n        address indexed user,\n        uint256 tokenId,\n        uint256 amount,\n        uint256 startTime,\n        uint256 duration\n    );\n    // Event emitted when rewards are claimed.\n    event RewardsClaimed(address indexed user, uint256 tokenId, uint256 reward);\n\n    // Event emitted when rewards are deposited into the contract\n    event RewardsDeposited(\n        address indexed depositor,\n        uint256 amount,\n        uint256 newTotalRewardsPool\n    );\n\n    /**\n     * @dev Constructor to initialize the DataStaking contract.\n     * @param _name Name of the Data Staking Pool.\n     * @param _dataPointsMulti Address of the DataPointsMulti contract.\n     * @param _rewardsToken Address of the ERC20 token used for rewards.\n     */\n    constructor(\n        string memory _name,\n        DataPointsMulti _dataPointsMulti,\n        IERC20 _rewardsToken\n    ) Ownable() {\n        name = _name;\n        dataPointsMulti = _dataPointsMulti;\n        rewardsToken = _rewardsToken;\n    }\n\n    // Function to deposit rewards into the contract and update the rewards pool\n    function depositRewards(uint256 amount) public onlyOwner {\n        // Transfer the rewards tokens from the sender to this contract\n        require(\n            rewardsToken.transferFrom(msg.sender, address(this), amount),\n            \"Transfer failed\"\n        );\n\n        // Update the total rewards pool\n        totalRewardsPool = totalRewardsPool.add(amount);\n\n        // Emit an event for the deposit\n        emit RewardsDeposited(msg.sender, amount, totalRewardsPool);\n    }\n\n    /**\n     * @dev Sets the reward per token, duration, and max stake limit for a specific token ID.\n     * @param tokenId The token ID for which to set the staking parameters.\n     * @param rewardWeight The reward weight per token to set for the token ID.\n     * @param duration The duration of the staking period in seconds.\n     * @param maxStakeLimit The maximum amount of tokens that can be staked for the token ID.\n     */\n    function setStakingParameters(\n        uint256 tokenId,\n        uint256 rewardWeight,\n        uint256 duration,\n        uint256 maxStakeLimit\n    ) public onlyOwner {\n        // Set the staking parameters\n        stakeInfos[tokenId] = StakeInfo({\n            amountStaked: 0, // Initialize to zero\n            rewardWeight: rewardWeight,\n            duration: duration,\n            startTime: 0, // Initialize to zero or current block timestamp if staking starts immediately\n            maxStakeLimit: maxStakeLimit\n        });\n\n        // Check if tokenId is already in stakingTokenIds array to avoid duplicates\n        bool exists = false;\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            if (stakingTokenIds[i] == tokenId) {\n                exists = true;\n                break;\n            }\n        }\n        if (!exists) {\n            stakingTokenIds.push(tokenId);\n        }\n    }\n\n    /**\n     * @dev Allows users to stake all their tokens for which staking parameters have been set.\n     * This function iterates over the array of token IDs that have staking parameters defined.\n     * For each token ID, it checks the user's balance and stakes up to the maximum stake limit,\n     * or the user's balance, whichever is lower. It calculates the potential reward and ensures\n     * that staking does not exceed the total available reward pool. The function updates the user's\n     * staked amount and the total staked amount for each token ID. It emits a Staked event for each\n     * staking action performed.\n     */\n    function stakeAll() public {\n        uint256 totalPotentialReward;\n        bool hasStakableTokens = false;\n\n        // First pass: Check for stakable tokens and calculate the total potential reward\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            uint256 userBalance = dataPointsMulti.balanceOf(\n                msg.sender,\n                tokenId\n            );\n\n            // Skip tokens that the user does not have or for which staking is not set up\n            if (userBalance == 0 || stakeInfos[tokenId].duration == 0) {\n                continue;\n            }\n\n            hasStakableTokens = true;\n\n            // Calculate the potential reward based on the reward weight\n            uint256 potentialReward = totalRewardsPool\n                .mul(stakeInfos[tokenId].rewardWeight)\n                .div(1e18);\n\n            // Ensure the potential reward does not exceed the remaining reward pool\n            require(\n                totalPotentialReward.add(potentialReward) <= totalRewardsPool,\n                \"Staking exceeds available reward pool\"\n            );\n\n            totalPotentialReward = totalPotentialReward.add(potentialReward);\n        }\n\n        // Require that the user has at least one stakable token\n        require(\n            hasStakableTokens,\n            \"No stakable tokens found or no duration has been set.\"\n        );\n\n        // Second pass: Perform the actual staking\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            uint256 userBalance = dataPointsMulti.balanceOf(\n                msg.sender,\n                tokenId\n            );\n\n            // Skip tokens that the user does not have or for which staking is not set up\n            if (userBalance == 0 || stakeInfos[tokenId].duration == 0) {\n                continue;\n            }\n\n            uint256 stakeableAmount = (userBalance >\n                stakeInfos[tokenId].maxStakeLimit)\n                ? stakeInfos[tokenId].maxStakeLimit\n                : userBalance;\n\n            // Subtract the user's previous stake from the total staked amount for the tokenId\n            uint256 currentStake = userStakes[msg.sender][tokenId];\n            stakeInfos[tokenId].amountStaked = stakeInfos[tokenId]\n                .amountStaked\n                .sub(currentStake);\n\n            // Update the user's staked amount with the new stakable amount\n            userStakes[msg.sender][tokenId] = stakeableAmount;\n\n            // Add the new stakable amount to the total staked amount for the tokenId\n            stakeInfos[tokenId].amountStaked = stakeInfos[tokenId]\n                .amountStaked\n                .add(stakeableAmount);\n\n            // Emit an event that tokens have been staked\n            emit Staked(\n                msg.sender,\n                tokenId,\n                stakeableAmount,\n                stakeInfos[tokenId].startTime,\n                stakeInfos[tokenId].duration\n            );\n        }\n    }\n\n    /**\n     * @dev Allows users to claim rewards for all their staked tokens after their respective staking periods end.\n     */\n    function claimAllRewards() public {\n        uint256 totalReward = 0;\n\n        // First pass: Calculate the total reward without making state changes\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            StakeInfo storage stakeInfo = stakeInfos[tokenId];\n\n            if (\n                block.timestamp >= stakeInfo.startTime.add(stakeInfo.duration)\n            ) {\n                uint256 stakedAmount = userStakes[msg.sender][tokenId];\n                if (stakedAmount > 0) {\n                    // Calculate the user's share of the staked tokens as a proportion of the total staked amount\n                    uint256 userShare = stakedAmount.mul(1e18).div(\n                        stakeInfo.amountStaked\n                    );\n\n                    // Calculate the user's potential reward based on their share and the reward weight for the tokenId\n                    uint256 reward = totalRewardsPool\n                        .mul(stakeInfo.rewardWeight)\n                        .div(1e18)\n                        .mul(userShare)\n                        .div(1e18); // Normalize the reward weight back to a fraction\n\n                    totalReward = totalReward.add(reward);\n                }\n            }\n        }\n\n        // Check if there are any rewards to claim\n        require(totalReward > 0, \"No rewards to claim\");\n\n        // Ensure there are enough tokens in the reward pool to cover the claim\n        require(totalReward <= totalRewardsPool, \"Insufficient reward pool\");\n\n        // Second pass: Reset stakes and emit events\n        for (uint256 i = 0; i < stakingTokenIds.length; i++) {\n            uint256 tokenId = stakingTokenIds[i];\n            StakeInfo storage stakeInfo = stakeInfos[tokenId];\n\n            if (\n                block.timestamp >= stakeInfo.startTime.add(stakeInfo.duration)\n            ) {\n                uint256 stakedAmount = userStakes[msg.sender][tokenId];\n                if (stakedAmount > 0) {\n                    // Calculate the user's share of the staked tokens as a proportion of the total staked amount\n                    uint256 userShare = stakedAmount.mul(1e18).div(\n                        stakeInfo.amountStaked\n                    );\n\n                    // Calculate the user's reward based on their share and the reward weight for the tokenId\n                    uint256 reward = totalRewardsPool\n                        .mul(stakeInfo.rewardWeight)\n                        .div(1e18)\n                        .mul(userShare)\n                        .div(1e18); // Normalize the reward weight back to a fraction\n\n                    // Reset the user's staked amount for the tokenId to zero\n                    userStakes[msg.sender][tokenId] = 0;\n\n                    // Update the total staked amount for the tokenId by subtracting the user's staked amount\n                    stakeInfo.amountStaked = stakeInfo.amountStaked.sub(\n                        stakedAmount\n                    );\n\n                    // Emit an event for the reward claim\n                    emit RewardsClaimed(msg.sender, tokenId, reward);\n                }\n            }\n        }\n\n        // Subtract the total reward from the total reward pool\n        totalRewardsPool = totalRewardsPool.sub(totalReward);\n\n        // Transfer the total calculated reward to the user, reverting on failure\n        require(\n            rewardsToken.transfer(msg.sender, totalReward),\n            \"Failed to transfer rewards\"\n        );\n    }\n}\n"
    },
    "contracts/lib/base64.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Base64.sol)\n\npragma solidity ^0.8.19;\n\n/**\n * @dev Provides a set of functions to operate with Base64 strings.\n */\nlibrary Base64 {\n    /**\n     * @dev Base64 Encoding/Decoding Table\n     * See sections 4 and 5 of https://datatracker.ietf.org/doc/html/rfc4648\n     */\n    string internal constant _TABLE =\n        \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n    string internal constant _TABLE_URL =\n        \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\n    /**\n     * @dev Converts a `bytes` to its Bytes64 `string` representation.\n     */\n    function encode(bytes memory data) internal pure returns (string memory) {\n        return _encode(data, _TABLE, true);\n    }\n\n    /**\n     * @dev Converts a `bytes` to its Bytes64Url `string` representation.\n     */\n    function encodeURL(\n        bytes memory data\n    ) internal pure returns (string memory) {\n        return _encode(data, _TABLE_URL, false);\n    }\n\n    /**\n     * @dev Internal table-agnostic conversion\n     */\n    function _encode(\n        bytes memory data,\n        string memory table,\n        bool withPadding\n    ) private pure returns (string memory) {\n        /**\n         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence\n         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol\n         */\n        if (data.length == 0) return \"\";\n\n        // If padding is enabled, the final length should be `bytes` data length divided by 3 rounded up and then\n        // multiplied by 4 so that it leaves room for padding the last chunk\n        // - `data.length + 2`  -> Round up\n        // - `/ 3`              -> Number of 3-bytes chunks\n        // - `4 *`              -> 4 characters for each chunk\n        // If padding is disabled, the final length should be `bytes` data length multiplied by 4/3 rounded up as\n        // opposed to when padding is required to fill the last chunk.\n        // - `4 *`              -> 4 characters for each chunk\n        // - `data.length + 2`  -> Round up\n        // - `/ 3`              -> Number of 3-bytes chunks\n        uint256 resultLength = withPadding\n            ? 4 * ((data.length + 2) / 3)\n            : (4 * data.length + 2) / 3;\n\n        string memory result = new string(resultLength);\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Prepare the lookup table (skip the first \"length\" byte)\n            let tablePtr := add(table, 1)\n\n            // Prepare result pointer, jump over length\n            let resultPtr := add(result, 32)\n\n            // Run over the input, 3 bytes at a time\n            for {\n                let dataPtr := data\n                let endPtr := add(data, mload(data))\n            } lt(dataPtr, endPtr) {\n\n            } {\n                // Advance 3 bytes\n                dataPtr := add(dataPtr, 3)\n                let input := mload(dataPtr)\n\n                // To write each character, shift the 3 bytes (18 bits) chunk\n                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)\n                // and apply logical AND with 0x3F which is the number of\n                // the previous character in the ASCII table prior to the Base64 Table\n                // The result is then added to the table to get the character to write,\n                // and finally write it in the result pointer but with a left shift\n                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits\n\n                mstore8(\n                    resultPtr,\n                    mload(add(tablePtr, and(shr(18, input), 0x3F)))\n                )\n                resultPtr := add(resultPtr, 1) // Advance\n\n                mstore8(\n                    resultPtr,\n                    mload(add(tablePtr, and(shr(12, input), 0x3F)))\n                )\n                resultPtr := add(resultPtr, 1) // Advance\n\n                mstore8(\n                    resultPtr,\n                    mload(add(tablePtr, and(shr(6, input), 0x3F)))\n                )\n                resultPtr := add(resultPtr, 1) // Advance\n\n                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))\n                resultPtr := add(resultPtr, 1) // Advance\n            }\n\n            if withPadding {\n                // When data `bytes` is not exactly 3 bytes long\n                // it is padded with `=` characters at the end\n                switch mod(mload(data), 3)\n                case 1 {\n                    mstore8(sub(resultPtr, 1), 0x3d)\n                    mstore8(sub(resultPtr, 2), 0x3d)\n                }\n                case 2 {\n                    mstore8(sub(resultPtr, 1), 0x3d)\n                }\n            }\n        }\n\n        return result;\n    }\n}\n"
    },
    "contracts/mock/MockERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract MockERC20 is ERC20 {\n    constructor(string memory name, string memory symbol) ERC20(name, symbol) {}\n\n    function mint(address to, uint256 amount) public {\n        _mint(to, amount);\n    }\n}\n"
    },
    "contracts/ProxyViewAggregator.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"./DataStakingDynamicNative.sol\";\n\ncontract ProxyViewAggregator is Ownable {\n    DataStakingDynamicNative[] public stakingContracts;\n\n    /**\n     * @dev Sets the array of DataStakingDynamicNative contract addresses.\n     * @param _stakingContracts An array of DataStakingDynamicNative contract addresses.\n     */\n    function setStakingContracts(\n        DataStakingDynamicNative[] calldata _stakingContracts\n    ) external onlyOwner {\n        // Here you might want to include access control, like onlyOwner modifier\n        stakingContracts = _stakingContracts;\n    }\n\n    /**\n     * @dev Aggregates the eligible stake amounts for a user across multiple DataStakingDynamicNative contracts.\n     * @param user The address of the user.\n     * @return totalEligibleAmount The total sum of eligible stake amounts for the user across all pools.\n     */\n    function aggregateEligibleStakes(\n        address user\n    ) external view returns (uint256 totalEligibleAmount) {\n        totalEligibleAmount = 0;\n        for (uint256 i = 0; i < stakingContracts.length; i++) {\n            DataStakingDynamicNative.EligibleStakeAmount[]\n                memory eligibleAmounts = stakingContracts[i]\n                    .getEligibleStakingAmounts(user);\n            for (uint256 j = 0; j < eligibleAmounts.length; j++) {\n                totalEligibleAmount += eligibleAmounts[j].amount;\n            }\n        }\n        return totalEligibleAmount;\n    }\n\n    function stakingContractsLength() public view returns (uint256) {\n        uint256 count = 0;\n        for (uint256 i = 0; i < stakingContracts.length; i++) {\n            // Directly use the isStakingPoolFilledOrExpired method to check the contract's status\n            if (!stakingContracts[i].isStakingPoolFilledOrExpired()) {\n                count++;\n            }\n        }\n        return count;\n    }\n}\n"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 1,
      "details": {
        "yul": false
      }
    },
    "viaIR": true,
    "evmVersion": "paris",
    "outputSelection": {
      "*": {
        "*": [
          "abi",
          "evm.bytecode",
          "evm.deployedBytecode",
          "evm.methodIdentifiers",
          "metadata",
          "devdoc",
          "userdoc",
          "storageLayout",
          "evm.gasEstimates",
          "devdoc",
          "userdoc"
        ],
        "": [
          "ast"
        ]
      }
    },
    "metadata": {
      "useLiteralContent": true
    }
  }
}