ETH Price: $2,957.47 (+0.02%)

Contract

0x3261E96A63f24c480e0F8dfC7416126BBB2da441

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Set Approval For...341035742026-01-08 16:40:4016 days ago1767890440IN
0x3261E96A...BBB2da441
0 ETH0.000005620.04525
Set Approval For...338946012026-01-07 7:00:1017 days ago1767769210IN
0x3261E96A...BBB2da441
0 ETH0.000004090.04525
Set Approval For...338942102026-01-07 6:56:3117 days ago1767768991IN
0x3261E96A...BBB2da441
0 ETH0.000004090.04525
Set Approval For...338938422026-01-07 6:52:3817 days ago1767768758IN
0x3261E96A...BBB2da441
0 ETH0.000004090.04525
Set Approval For...338938412026-01-07 6:52:3817 days ago1767768758IN
0x3261E96A...BBB2da441
0 ETH0.000004090.04525
Set Approval For...338938392026-01-07 6:52:3717 days ago1767768757IN
0x3261E96A...BBB2da441
0 ETH0.000004090.04525
Set Approval For...334334472026-01-04 6:32:1220 days ago1767508332IN
0x3261E96A...BBB2da441
0 ETH0.000006740.06309
Set Approval For...333413182026-01-03 14:01:1421 days ago1767448874IN
0x3261E96A...BBB2da441
0 ETH0.000004090.04525
Set Approval For...333379182026-01-03 13:30:1321 days ago1767447013IN
0x3261E96A...BBB2da441
0 ETH0.000004090.04525
Set Approval For...333379122026-01-03 13:30:1021 days ago1767447010IN
0x3261E96A...BBB2da441
0 ETH0.000004090.04525
Set Approval For...333315432026-01-03 12:34:3121 days ago1767443671IN
0x3261E96A...BBB2da441
0 ETH0.000004730.04525
Safe Transfer Fr...330711542026-01-01 13:59:4223 days ago1767275982IN
0x3261E96A...BBB2da441
0 ETH0.000006910.04525
Set Approval For...325055992025-12-28 12:30:1727 days ago1766925017IN
0x3261E96A...BBB2da441
0 ETH0.00000430.04525
Set Approval For...324286002025-12-27 20:31:3728 days ago1766867497IN
0x3261E96A...BBB2da441
0 ETH0.000004380.04525
Safe Transfer Fr...317740852025-12-22 19:17:3833 days ago1766431058IN
0x3261E96A...BBB2da441
0 ETH0.000008620.06309
Safe Transfer Fr...317089362025-12-22 8:34:5633 days ago1766392496IN
0x3261E96A...BBB2da441
0 ETH0.000007830.06309
Set Approval For...312561772025-12-18 16:08:1837 days ago1766074098IN
0x3261E96A...BBB2da441
0 ETH0.000004860.04525
Set Approval For...311612692025-12-18 1:01:3237 days ago1766019692IN
0x3261E96A...BBB2da441
0 ETH0.000004090.04525
Set Approval For...310948452025-12-17 14:15:0038 days ago1765980900IN
0x3261E96A...BBB2da441
0 ETH0.000005590.04525
Set Approval For...308316622025-12-15 16:10:4540 days ago1765815045IN
0x3261E96A...BBB2da441
0 ETH0.00000440.04525
Set Approval For...308014292025-12-15 12:16:0440 days ago1765800964IN
0x3261E96A...BBB2da441
0 ETH0.000005590.04525
Set Approval For...305670012025-12-13 22:27:3942 days ago1765664859IN
0x3261E96A...BBB2da441
0 ETH0.000005590.04525
Set Approval For...303656332025-12-12 17:23:3043 days ago1765560210IN
0x3261E96A...BBB2da441
0 ETH0.000004380.04525
Set Approval For...295595212025-12-08 18:36:1947 days ago1765218979IN
0x3261E96A...BBB2da441
0 ETH0.000005610.04525
Set Approval For...289483722025-12-06 4:22:3049 days ago1764994950IN
0x3261E96A...BBB2da441
0 ETH0.000005590.04525
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
208332262025-10-08 7:28:15108 days ago1759908495  Contract Creation0 ETH
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PenguBoard

Compiler Version
v0.8.28+commit.7893614a

ZkSolc Version
v1.5.15

Optimization Enabled:
Yes with Mode 3

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {ERC721} from "@animoca/ethereum-contracts/contracts/token/ERC721/ERC721.sol";
import {ERC721Base} from "@animoca/ethereum-contracts/contracts/token/ERC721/base/ERC721Base.sol";
import {ERC721Metadata} from "@animoca/ethereum-contracts/contracts/token/ERC721/ERC721Metadata.sol";
import {ERC2981} from "@animoca/ethereum-contracts/contracts/token/royalty/ERC2981.sol";
import {AccessControl} from "@animoca/ethereum-contracts/contracts/access/AccessControl.sol";
import {TokenRecovery} from "@animoca/ethereum-contracts/contracts/security/TokenRecovery.sol";
import {ContractOwnership} from "@animoca/ethereum-contracts/contracts/access/ContractOwnership.sol";

import {ERC721Storage} from "@animoca/ethereum-contracts/contracts/token/ERC721/libraries/ERC721Storage.sol";
import {AccessControlStorage} from "@animoca/ethereum-contracts/contracts/access/libraries/AccessControlStorage.sol";
import {ContractOwnershipStorage} from "@animoca/ethereum-contracts/contracts/access/libraries/ContractOwnershipStorage.sol";

import {ITokenMetadataResolver} from "@animoca/ethereum-contracts/contracts/token/metadata/interfaces/ITokenMetadataResolver.sol";
import {IPenguBoard} from "./../interfaces/IPenguBoard.sol";
import {IScratching} from "./../../game/interfaces/IScratching.sol";

import {ERC721NonExistingToken} from "@animoca/ethereum-contracts/contracts/token/ERC721/errors/ERC721Errors.sol";
import {Transfer} from "@animoca/ethereum-contracts/contracts/token/ERC721/events/ERC721Events.sol";

contract PenguBoard is ERC721, ERC721Metadata, ERC2981, IPenguBoard, AccessControl, TokenRecovery {
    using ERC721Storage for ERC721Storage.Layout;
    using AccessControlStorage for AccessControlStorage.Layout;
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    bytes32 public constant MINTER_ROLE = "minter";

    uint256 public nextTokenId;
    IScratching public scratchingContract;

    event ScratchingContractSet(IScratching scratchingContract);

    error PendingScratchRequest(uint256 tokenId, uint256 requestId);

    constructor(
        string memory tokenName,
        string memory tokenSymbol,
        ITokenMetadataResolver metadataResolver
    ) ContractOwnership(msg.sender) ERC721Metadata(tokenName, tokenSymbol, metadataResolver) {}

    /// @notice Sets the scratching contract.
    /// @dev Reverts with {NotContractOwner} if the caller is not the contract owner.
    /// @param scratching the address of the scratching contract.
    function setScratchingContract(IScratching scratching) external {
        ContractOwnershipStorage.layout().enforceIsContractOwner(msg.sender);
        scratchingContract = scratching;
        emit ScratchingContractSet(scratching);
    }

    /// @notice Mints a new token with incremental token ID to the specified address.
    /// @dev Reverts with {MissingRole} if the caller does not have the MINTER_ROLE.
    /// @dev Emits a {Transfer} event from address 0.
    /// @param to the address to mint the token to.
    /// @return tokenId the ID of the minted token.
    function mint(address to) external virtual returns (uint256 tokenId) {
        AccessControlStorage.layout().enforceHasRole(MINTER_ROLE, msg.sender);
        tokenId = nextTokenId++;
        ERC721Storage.layout().mint(to, tokenId);
    }

    /// @notice Burns a token.
    /// @dev Reverts with {MissingRole} if the caller does not have the MINTER_ROLE.
    /// @dev Reverts with {ERC721NonExistingToken} if the token does not exist.
    /// @dev Emits a {Transfer} event to address 0.
    /// @param tokenId the ID of the token to burn.
    function burn(uint256 tokenId) external {
        AccessControlStorage.layout().enforceHasRole(MINTER_ROLE, _msgSender());

        ERC721Storage.Layout storage s = ERC721Storage.layout();
        address owner = address(uint160(s.owners[tokenId]));
        if (owner == address(0)) revert ERC721NonExistingToken(tokenId);

        s.owners[tokenId] = ERC721Storage.BURNT_TOKEN_OWNER_VALUE;

        unchecked {
            // cannot underflow as balance is verified through TOKEN ownership
            --s.balances[owner];
        }

        emit Transfer(owner, address(0), tokenId);
    }

    /// @inheritdoc ERC721Base
    /// @dev Reverts with {PendingScratchRequest} if there is a scratching contract and the token has a pending scratch request
    function transferFrom(address from, address to, uint256 tokenId) external virtual override {
        _requireNoPendingScratchRequest(tokenId);
        ERC721Storage.layout().transferFrom(_msgSender(), from, to, tokenId);
    }

    /// @inheritdoc ERC721Base
    /// @dev Reverts with {PendingScratchRequest} if there is a scratching contract and the token has a pending scratch request
    function safeTransferFrom(address from, address to, uint256 tokenId) external virtual override {
        _requireNoPendingScratchRequest(tokenId);
        ERC721Storage.layout().safeTransferFrom(_msgSender(), from, to, tokenId);
    }

    /// @inheritdoc ERC721Base
    /// @dev Reverts with {PendingScratchRequest} if there is a scratching contract and the token has a pending scratch request
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external virtual override {
        _requireNoPendingScratchRequest(tokenId);
        ERC721Storage.layout().safeTransferFrom(_msgSender(), from, to, tokenId, data);
    }

    function _requireNoPendingScratchRequest(uint256 tokenId) internal view {
        IScratching scratchingContract_ = scratchingContract;
        if (scratchingContract_ != IScratching(address(0))) {
            uint256 pendingRequestId = scratchingContract_.pendingScratchRequest(tokenId);
            require(pendingRequestId == 0, PendingScratchRequest(tokenId, pendingRequestId));
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IPenguBoard {
    function mint(address to) external returns (uint256 tokenId);
    function burn(uint256 tokenId) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IScratching {
    /// @notice Returns the pending scratch request ID for a given token ID.
    /// @param tokenId the token ID.
    /// @return requestId the pending scratch request ID (0 if no pending request).
    function pendingScratchRequest(uint256 tokenId) external view returns (uint256 requestId);
}

File 4 of 62 : TokenRecovery.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {TokenRecoveryBase} from "./base/TokenRecoveryBase.sol";
import {ContractOwnership} from "./../access/ContractOwnership.sol";

/// @title Recovery mechanism for ETH/ERC20/ERC721 tokens accidentally sent to this contract (immutable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract TokenRecovery is TokenRecoveryBase, ContractOwnership {}

File 5 of 62 : AccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {AccessControlBase} from "./base/AccessControlBase.sol";
import {ContractOwnership} from "./ContractOwnership.sol";

/// @title Access control via roles management (immutable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract AccessControl is AccessControlBase, ContractOwnership {}

File 6 of 62 : ERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {ERC721Storage} from "./libraries/ERC721Storage.sol";
import {ERC721Base} from "./base/ERC721Base.sol";
import {InterfaceDetection} from "./../../introspection/InterfaceDetection.sol";

/// @title ERC721 Non-Fungible Token Standard (immutable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ERC721 is ERC721Base, InterfaceDetection {
    /// @notice Marks the following ERC165 interfaces as supported: ERC721.
    constructor() {
        ERC721Storage.init();
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {NotRoleHolder, NotTargetContractRoleHolder} from "./../errors/AccessControlErrors.sol";
import {TargetIsNotAContract} from "./../errors/Common.sol";
import {RoleGranted, RoleRevoked} from "./../events/AccessControlEvents.sol";
import {IAccessControl} from "./../interfaces/IAccessControl.sol";
import {Address} from "./../../utils/libraries/Address.sol";

library AccessControlStorage {
    using Address for address;
    using AccessControlStorage for AccessControlStorage.Layout;

    struct Layout {
        mapping(bytes32 => mapping(address => bool)) roles;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.core.access.AccessControl.storage")) - 1);

    /// @notice Grants a role to an account.
    /// @dev Note: Call to this function should be properly access controlled.
    /// @dev Emits a {RoleGranted} event if the account did not previously have the role.
    /// @param role The role to grant.
    /// @param account The account to grant the role to.
    /// @param operator The account requesting the role change.
    function grantRole(Layout storage s, bytes32 role, address account, address operator) internal {
        if (!s.hasRole(role, account)) {
            s.roles[role][account] = true;
            emit RoleGranted(role, account, operator);
        }
    }

    /// @notice Revokes a role from an account.
    /// @dev Note: Call to this function should be properly access controlled.
    /// @dev Emits a {RoleRevoked} event if the account previously had the role.
    /// @param role The role to revoke.
    /// @param account The account to revoke the role from.
    /// @param operator The account requesting the role change.
    function revokeRole(Layout storage s, bytes32 role, address account, address operator) internal {
        if (s.hasRole(role, account)) {
            s.roles[role][account] = false;
            emit RoleRevoked(role, account, operator);
        }
    }

    /// @notice Renounces a role by the sender.
    /// @dev Reverts with {NotRoleHolder} if `sender` does not have `role`.
    /// @dev Emits a {RoleRevoked} event.
    /// @param sender The message sender.
    /// @param role The role to renounce.
    function renounceRole(Layout storage s, address sender, bytes32 role) internal {
        s.enforceHasRole(role, sender);
        s.roles[role][sender] = false;
        emit RoleRevoked(role, sender, sender);
    }

    /// @notice Retrieves whether an account has a role.
    /// @param role The role.
    /// @param account The account.
    /// @return hasRole_ Whether `account` has `role`.
    function hasRole(Layout storage s, bytes32 role, address account) internal view returns (bool hasRole_) {
        return s.roles[role][account];
    }

    /// @notice Checks whether an account has a role in a target contract.
    /// @param targetContract The contract to check.
    /// @param role The role to check.
    /// @param account The account to check.
    /// @return hasTargetContractRole_ Whether `account` has `role` in `targetContract`.
    function hasTargetContractRole(address targetContract, bytes32 role, address account) internal view returns (bool hasTargetContractRole_) {
        if (!targetContract.hasBytecode()) revert TargetIsNotAContract(targetContract);
        return IAccessControl(targetContract).hasRole(role, account);
    }

    /// @notice Ensures that an account has a role.
    /// @dev Reverts with {NotRoleHolder} if `account` does not have `role`.
    /// @param role The role.
    /// @param account The account.
    function enforceHasRole(Layout storage s, bytes32 role, address account) internal view {
        if (!s.hasRole(role, account)) revert NotRoleHolder(role, account);
    }

    /// @notice Enforces that an account has a role in a target contract.
    /// @dev Reverts with {NotTargetContractRoleHolder} if the account does not have the role.
    /// @param targetContract The contract to check.
    /// @param role The role to check.
    /// @param account The account to check.
    function enforceHasTargetContractRole(address targetContract, bytes32 role, address account) internal view {
        if (!hasTargetContractRole(targetContract, role, account)) revert NotTargetContractRoleHolder(targetContract, role, account);
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

File 8 of 62 : ERC721Metadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {ITokenMetadataResolver} from "./../metadata/interfaces/ITokenMetadataResolver.sol";
import {TokenMetadataStorage} from "./../metadata/libraries/TokenMetadataStorage.sol";
import {ERC721Storage} from "./libraries/ERC721Storage.sol";
import {ERC721MetadataBase} from "./base/ERC721MetadataBase.sol";

/// @title ERC721 Non-Fungible Token Standard, optional extension: Metadata (immutable version).
/// @notice This contracts uses an external resolver for managing individual tokens metadata.
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ERC721Metadata is ERC721MetadataBase {
    using TokenMetadataStorage for TokenMetadataStorage.Layout;

    /// @notice Marks the following ERC165 interfaces as supported: ERC721Metadata.
    /// @param name The name of the token.
    /// @param symbol The symbol of the token.
    /// @param metadataResolver The address of the metadata resolver contract.
    constructor(string memory name, string memory symbol, ITokenMetadataResolver metadataResolver) {
        TokenMetadataStorage.layout().constructorInit(name, symbol, metadataResolver);
        ERC721Storage.initERC721Metadata();
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {NotContractOwner, NotTargetContractOwner} from "./../errors/ContractOwnershipErrors.sol";
import {TargetIsNotAContract} from "./../errors/Common.sol";
import {OwnershipTransferred} from "./../events/ERC173Events.sol";
import {IERC173} from "./../interfaces/IERC173.sol";
import {Address} from "./../../utils/libraries/Address.sol";
import {ProxyInitialization} from "./../../proxy/libraries/ProxyInitialization.sol";
import {InterfaceDetectionStorage} from "./../../introspection/libraries/InterfaceDetectionStorage.sol";

library ContractOwnershipStorage {
    using Address for address;
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;
    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;

    struct Layout {
        address contractOwner;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.core.access.ContractOwnership.storage")) - 1);
    bytes32 internal constant PROXY_INIT_PHASE_SLOT = bytes32(uint256(keccak256("animoca.core.access.ContractOwnership.phase")) - 1);

    /// @notice Initializes the storage with an initial contract owner (immutable version).
    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.
    /// @dev Note: This function should be called ONLY in the constructor of an immutable (non-proxied) contract.
    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.
    /// @param initialOwner The initial contract owner.
    function constructorInit(Layout storage s, address initialOwner) internal {
        if (initialOwner != address(0)) {
            s.contractOwner = initialOwner;
            emit OwnershipTransferred(address(0), initialOwner);
        }
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC173).interfaceId, true);
    }

    /// @notice Initializes the storage with an initial contract owner (proxied version).
    /// @notice Sets the proxy initialization phase to `1`.
    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.
    /// @dev Note: This function should be called ONLY in the init function of a proxied contract.
    /// @dev Reverts with {InitializationPhaseAlreadyReached} if the proxy initialization phase is set to `1` or above.
    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.
    /// @param initialOwner The initial contract owner.
    function proxyInit(Layout storage s, address initialOwner) internal {
        ProxyInitialization.setPhase(PROXY_INIT_PHASE_SLOT, 1);
        s.constructorInit(initialOwner);
    }

    /// @notice Sets the address of the new contract owner.
    /// @dev Reverts with {NotContractOwner} if `sender` is not the contract owner.
    /// @dev Emits an {OwnershipTransferred} event if `newOwner` is different from the current contract owner.
    /// @param newOwner The address of the new contract owner. Using the zero address means renouncing ownership.
    function transferOwnership(Layout storage s, address sender, address newOwner) internal {
        address previousOwner = s.contractOwner;
        if (sender != previousOwner) revert NotContractOwner(sender);
        if (previousOwner != newOwner) {
            s.contractOwner = newOwner;
            emit OwnershipTransferred(previousOwner, newOwner);
        }
    }

    /// @notice Gets the address of the contract owner.
    /// @return contractOwner The address of the contract owner.
    function owner(Layout storage s) internal view returns (address contractOwner) {
        return s.contractOwner;
    }

    /// @notice Checks whether an account is the owner of a target contract.
    /// @param targetContract The contract to check.
    /// @param account The account to check.
    /// @return isTargetContractOwner_ Whether `account` is the owner of `targetContract`.
    function isTargetContractOwner(address targetContract, address account) internal view returns (bool isTargetContractOwner_) {
        if (!targetContract.hasBytecode()) revert TargetIsNotAContract(targetContract);
        return IERC173(targetContract).owner() == account;
    }

    /// @notice Ensures that an account is the contract owner.
    /// @dev Reverts with {NotContractOwner} if `account` is not the contract owner.
    /// @param account The account.
    function enforceIsContractOwner(Layout storage s, address account) internal view {
        if (account != s.contractOwner) revert NotContractOwner(account);
    }

    /// @notice Enforces that an account is the owner of a target contract.
    /// @dev Reverts with {NotTheTargetContractOwner} if the account is not the owner.
    /// @param targetContract The contract to check.
    /// @param account The account to check.
    function enforceIsTargetContractOwner(address targetContract, address account) internal view {
        if (!isTargetContractOwner(targetContract, account)) revert NotTargetContractOwner(targetContract, account);
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IERC721} from "./../interfaces/IERC721.sol";
import {ERC721Storage} from "./../libraries/ERC721Storage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title ERC721 Non-Fungible Token Standard (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC165 (Interface Detection Standard).
abstract contract ERC721Base is IERC721, Context {
    using ERC721Storage for ERC721Storage.Layout;

    /// @inheritdoc IERC721
    function approve(address to, uint256 tokenId) external virtual {
        ERC721Storage.layout().approve(_msgSender(), to, tokenId);
    }

    /// @inheritdoc IERC721
    function setApprovalForAll(address operator, bool approved) external virtual {
        ERC721Storage.layout().setApprovalForAll(_msgSender(), operator, approved);
    }

    /// @inheritdoc IERC721
    function transferFrom(address from, address to, uint256 tokenId) external virtual {
        ERC721Storage.layout().transferFrom(_msgSender(), from, to, tokenId);
    }

    /// @inheritdoc IERC721
    function safeTransferFrom(address from, address to, uint256 tokenId) external virtual {
        ERC721Storage.layout().safeTransferFrom(_msgSender(), from, to, tokenId);
    }

    /// @inheritdoc IERC721
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external virtual {
        ERC721Storage.layout().safeTransferFrom(_msgSender(), from, to, tokenId, data);
    }

    /// @inheritdoc IERC721
    function balanceOf(address owner) external view virtual returns (uint256 balance) {
        return ERC721Storage.layout().balanceOf(owner);
    }

    /// @inheritdoc IERC721
    function ownerOf(uint256 tokenId) external view virtual returns (address tokenOwner) {
        return ERC721Storage.layout().ownerOf(tokenId);
    }

    /// @inheritdoc IERC721
    function getApproved(uint256 tokenId) external view virtual returns (address approved) {
        return ERC721Storage.layout().getApproved(tokenId);
    }

    /// @inheritdoc IERC721
    function isApprovedForAll(address owner, address operator) external view virtual returns (bool approvedForAll) {
        return ERC721Storage.layout().isApprovedForAll(owner, operator);
    }
}

File 11 of 62 : ERC721Storage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

// solhint-disable-next-line max-line-length
import {ERC721SelfApproval, ERC721SelfApprovalForAll, ERC721NonApprovedForApproval, ERC721TransferToAddressZero, ERC721NonExistingToken, ERC721NonApprovedForTransfer, ERC721NonOwnedToken, ERC721SafeTransferRejected, ERC721BalanceOfAddressZero} from "./../errors/ERC721Errors.sol";
import {ERC721MintToAddressZero, ERC721ExistingToken} from "./../errors/ERC721MintableErrors.sol";
import {ERC721BurntToken} from "./../errors/ERC721MintableOnceErrors.sol";
import {InconsistentArrayLengths} from "./../../../CommonErrors.sol";
import {Transfer, Approval, ApprovalForAll} from "./../events/ERC721Events.sol";
import {IERC721} from "./../interfaces/IERC721.sol";
import {IERC721BatchTransfer} from "./../interfaces/IERC721BatchTransfer.sol";
import {IERC721Metadata} from "./../interfaces/IERC721Metadata.sol";
import {IERC721Mintable} from "./../interfaces/IERC721Mintable.sol";
import {IERC721Deliverable} from "./../interfaces/IERC721Deliverable.sol";
import {IERC721Burnable} from "./../interfaces/IERC721Burnable.sol";
import {IERC721Receiver} from "./../interfaces/IERC721Receiver.sol";
import {Address} from "./../../../utils/libraries/Address.sol";
import {InterfaceDetectionStorage} from "./../../../introspection/libraries/InterfaceDetectionStorage.sol";

library ERC721Storage {
    using Address for address;
    using ERC721Storage for ERC721Storage.Layout;
    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;

    struct Layout {
        mapping(uint256 => uint256) owners;
        mapping(address => uint256) balances;
        mapping(uint256 => address) approvals;
        mapping(address => mapping(address => bool)) operators;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.token.ERC721.ERC721.storage")) - 1);

    bytes4 internal constant ERC721_RECEIVED = IERC721Receiver.onERC721Received.selector;

    // Single token approval flag
    // This bit is set in the owner's value to indicate that there is an approval set for this token
    uint256 internal constant TOKEN_APPROVAL_OWNER_FLAG = 1 << 160;

    // Burnt token magic value
    // This magic number is used as the owner's value to indicate that the token has been burnt
    uint256 internal constant BURNT_TOKEN_OWNER_VALUE = 0xdead000000000000000000000000000000000000000000000000000000000000;

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721.
    function init() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721).interfaceId, true);
    }

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721BatchTransfer.
    function initERC721BatchTransfer() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721BatchTransfer).interfaceId, true);
    }

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721Metadata.
    function initERC721Metadata() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721Metadata).interfaceId, true);
    }

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721Mintable.
    function initERC721Mintable() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721Mintable).interfaceId, true);
    }

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721Deliverable.
    function initERC721Deliverable() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721Deliverable).interfaceId, true);
    }

    /// @notice Marks the following ERC165 interface(s) as supported: ERC721Burnable.
    function initERC721Burnable() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC721Burnable).interfaceId, true);
    }

    /// @notice Sets or unsets an approval to transfer a single token on behalf of its owner.
    /// @dev Note: This function implements {ERC721-approve(address,uint256)}.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @dev Reverts with {ERC721SelfApproval} if `to` is the token owner.
    /// @dev Reverts with {ERC721NonApprovedForApproval} if `sender` is not the token owner and has not been approved by the token owner.
    /// @dev Emits an {Approval} event.
    /// @param sender The message sender.
    /// @param to The address to approve, or the zero address to remove any existing approval.
    /// @param tokenId The token identifier to give approval for.
    function approve(Layout storage s, address sender, address to, uint256 tokenId) internal {
        uint256 owner = s.owners[tokenId];
        if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
        address ownerAddress = _tokenOwner(owner);
        if (to == ownerAddress) revert ERC721SelfApproval(ownerAddress);
        if (!_isOperatable(s, ownerAddress, sender)) revert ERC721NonApprovedForApproval(sender, ownerAddress, tokenId);
        if (to == address(0)) {
            if (_tokenHasApproval(owner)) {
                // remove the approval bit if it is present
                s.owners[tokenId] = uint256(uint160(ownerAddress));
            }
        } else {
            uint256 ownerWithApprovalBit = owner | TOKEN_APPROVAL_OWNER_FLAG;
            if (owner != ownerWithApprovalBit) {
                // add the approval bit if it is not present
                s.owners[tokenId] = ownerWithApprovalBit;
            }
            s.approvals[tokenId] = to;
        }
        emit Approval(ownerAddress, to, tokenId);
    }

    /// @notice Sets or unsets an approval to transfer all tokens on behalf of their owner.
    /// @dev Note: This function implements {ERC721-setApprovalForAll(address,bool)}.
    /// @dev Reverts with {ERC721SelfApprovalForAll} if `sender` is the same as `operator`.
    /// @dev Emits an {ApprovalForAll} event.
    /// @param sender The message sender.
    /// @param operator The address to approve for all tokens.
    /// @param approved True to set an approval for all tokens, false to unset it.
    function setApprovalForAll(Layout storage s, address sender, address operator, bool approved) internal {
        if (operator == sender) revert ERC721SelfApprovalForAll(sender);
        s.operators[sender][operator] = approved;
        emit ApprovalForAll(sender, operator, approved);
    }

    /// @notice Unsafely transfers the ownership of a token to a recipient by a sender.
    /// @dev Note: This function implements {ERC721-transferFrom(address,address,uint256)}.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts with {ERC721TransferToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @dev Reverts with {ERC721NonOwnedToken} if `from` is not the owner of `tokenId`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if `sender` is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Emits a {Transfer} event.
    /// @param sender The message sender.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer.
    /// @param tokenId The identifier of the token to transfer.
    function transferFrom(Layout storage s, address sender, address from, address to, uint256 tokenId) internal {
        if (to == address(0)) revert ERC721TransferToAddressZero();

        uint256 owner = s.owners[tokenId];
        if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
        if (_tokenOwner(owner) != from) revert ERC721NonOwnedToken(from, tokenId);

        if (!_isOperatable(s, from, sender)) {
            if (!_tokenHasApproval(owner) || sender != s.approvals[tokenId]) revert ERC721NonApprovedForTransfer(sender, from, tokenId);
        }

        s.owners[tokenId] = uint256(uint160(to));
        if (from != to) {
            unchecked {
                // cannot underflow as balance is verified through ownership
                --s.balances[from];
                //  cannot overflow as supply cannot overflow
                ++s.balances[to];
            }
        }

        emit Transfer(from, to, tokenId);
    }

    /// @notice Safely transfers the ownership of a token to a recipient by a sender.
    /// @dev Note: This function implements {ERC721-safeTransferFrom(address,address,uint256)}.
    /// @dev Warning: Since a `to` contract can run arbitrary code, developers should be aware of potential re-entrancy attacks.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts with {ERC721TransferToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @dev Reverts with {ERC721NonOwnedToken} if `from` is not the owner of `tokenId`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if `sender` is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Reverts with {ERC721SafeTransferRejected} if `to` is a contract and the call to
    ///  {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event.
    /// @param sender The message sender.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer.
    /// @param tokenId The identifier of the token to transfer.
    function safeTransferFrom(Layout storage s, address sender, address from, address to, uint256 tokenId) internal {
        s.transferFrom(sender, from, to, tokenId);
        if (to.hasBytecode()) {
            _callOnERC721Received(sender, from, to, tokenId, "");
        }
    }

    /// @notice Safely transfers the ownership of a token to a recipient by a sender.
    /// @dev Note: This function implements {ERC721-safeTransferFrom(address,address,uint256,bytes)}.
    /// @dev Warning: Since a `to` contract can run arbitrary code, developers should be aware of potential re-entrancy attacks.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts with {ERC721TransferToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @dev Reverts with {ERC721NonOwnedToken} if `from` is not the owner of `tokenId`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if `sender` is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Reverts with {ERC721SafeTransferRejected} if `to` is a contract and the call to
    ///  {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event.
    /// @param sender The message sender.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer.
    /// @param tokenId The identifier of the token to transfer.
    /// @param data Optional data to send along to a receiver contract.
    function safeTransferFrom(Layout storage s, address sender, address from, address to, uint256 tokenId, bytes calldata data) internal {
        s.transferFrom(sender, from, to, tokenId);
        if (to.hasBytecode()) {
            _callOnERC721Received(sender, from, to, tokenId, data);
        }
    }

    /// @notice Unsafely transfers a batch of tokens to a recipient by a sender.
    /// @dev Note: This function implements {ERC721BatchTransfer-batchTransferFrom(address,address,uint256[])}.
    /// @dev Resets the token approval for each of `tokenIds`.
    /// @dev Reverts with {ERC721TransferToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721NonExistingToken} if one of `tokenIds` does not exist.
    /// @dev Reverts with {ERC721NonOwnedToken} if one of `tokenIds` is not owned by `from`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if the sender is not `from` and has not been approved by `from` for each of `tokenIds`.
    /// @dev Emits a {Transfer} event for each of `tokenIds`.
    /// @param sender The message sender.
    /// @param from Current tokens owner.
    /// @param to Address of the new token owner.
    /// @param tokenIds Identifiers of the tokens to transfer.
    function batchTransferFrom(Layout storage s, address sender, address from, address to, uint256[] calldata tokenIds) internal {
        if (to == address(0)) revert ERC721TransferToAddressZero();
        bool operatable = _isOperatable(s, from, sender);

        uint256 length = tokenIds.length;
        for (uint256 i; i < length; ++i) {
            uint256 tokenId = tokenIds[i];
            uint256 owner = s.owners[tokenId];
            if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
            if (_tokenOwner(owner) != from) revert ERC721NonOwnedToken(from, tokenId);
            if (!operatable) {
                if (!_tokenHasApproval(owner) || sender != s.approvals[tokenId]) revert ERC721NonApprovedForTransfer(sender, from, tokenId);
            }
            s.owners[tokenId] = uint256(uint160(to));
            emit Transfer(from, to, tokenId);
        }

        if (from != to && length != 0) {
            unchecked {
                // cannot underflow as balance is verified through ownership
                s.balances[from] -= length;
                // cannot overflow as supply cannot overflow
                s.balances[to] += length;
            }
        }
    }

    /// @notice Unsafely mints a token.
    /// @dev Note: This function implements {ERC721Mintable-mint(address,uint256)}.
    /// @dev Note: Either `mint` or `mintOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if `tokenId` already exists.
    /// @dev Emits a {Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    function mint(Layout storage s, address to, uint256 tokenId) internal {
        if (to == address(0)) revert ERC721MintToAddressZero();
        if (_tokenExists(s.owners[tokenId])) revert ERC721ExistingToken(tokenId);

        s.owners[tokenId] = uint256(uint160(to));

        unchecked {
            // cannot overflow due to the cost of minting individual tokens
            ++s.balances[to];
        }

        emit Transfer(address(0), to, tokenId);
    }

    /// @notice Safely mints a token.
    /// @dev Note: This function implements {ERC721Mintable-safeMint(address,uint256,bytes)}.
    /// @dev Note: Either `safeMint` or `safeMintOnce` should be used in a given contract, but not both.
    /// @dev Warning: Since a `to` contract can run arbitrary code, developers should be aware of potential re-entrancy attacks.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if `tokenId` already exists.
    /// @dev Reverts with {ERC721SafeTransferRejected} if `to` is a contract and the call to
    ///  {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    /// @param data Optional data to pass along to the receiver call.
    function safeMint(Layout storage s, address sender, address to, uint256 tokenId, bytes memory data) internal {
        s.mint(to, tokenId);
        if (to.hasBytecode()) {
            _callOnERC721Received(sender, address(0), to, tokenId, data);
        }
    }

    /// @notice Unsafely mints a batch of tokens.
    /// @dev Note: This function implements {ERC721Mintable-batchMint(address,uint256[])}.
    /// @dev Note: Either `batchMint` or `batchMintOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if one of `tokenIds` already exists.
    /// @dev Emits a {Transfer} event from the zero address for each of `tokenIds`.
    /// @param to Address of the new tokens owner.
    /// @param tokenIds Identifiers of the tokens to mint.
    function batchMint(Layout storage s, address to, uint256[] memory tokenIds) internal {
        if (to == address(0)) revert ERC721MintToAddressZero();

        uint256 length = tokenIds.length;
        for (uint256 i; i < length; ++i) {
            uint256 tokenId = tokenIds[i];
            if (_tokenExists(s.owners[tokenId])) revert ERC721ExistingToken(tokenId);

            s.owners[tokenId] = uint256(uint160(to));
            emit Transfer(address(0), to, tokenId);
        }

        unchecked {
            s.balances[to] += length;
        }
    }

    /// @notice Unsafely mints tokens to multiple recipients.
    /// @dev Note: This function implements {ERC721Deliverable-deliver(address[],uint256[])}.
    /// @dev Note: Either `deliver` or `deliverOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {InconsistentArrayLengths} if `recipients` and `tokenIds` have different lengths.
    /// @dev Reverts with {ERC721MintToAddressZero} if one of `recipients` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if one of `tokenIds` already exists.
    /// @dev Emits a {Transfer} event from the zero address for each of `recipients` and `tokenIds`.
    /// @param recipients Addresses of the new tokens owners.
    /// @param tokenIds Identifiers of the tokens to mint.
    function deliver(Layout storage s, address[] memory recipients, uint256[] memory tokenIds) internal {
        uint256 length = recipients.length;
        if (length != tokenIds.length) revert InconsistentArrayLengths();
        for (uint256 i; i < length; ++i) {
            s.mint(recipients[i], tokenIds[i]);
        }
    }

    /// @notice Unsafely mints a token once.
    /// @dev Note: This function implements {ERC721Mintable-mint(address,uint256)}.
    /// @dev Note: Either `mint` or `mintOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if `tokenId` already exists.
    /// @dev Reverts with {ERC721BurntToken} if `tokenId` has been previously burnt.
    /// @dev Emits a {Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    function mintOnce(Layout storage s, address to, uint256 tokenId) internal {
        if (to == address(0)) revert ERC721MintToAddressZero();

        uint256 owner = s.owners[tokenId];
        if (_tokenExists(owner)) revert ERC721ExistingToken(tokenId);
        if (_tokenWasBurnt(owner)) revert ERC721BurntToken(tokenId);

        s.owners[tokenId] = uint256(uint160(to));

        unchecked {
            // cannot overflow due to the cost of minting individual tokens
            ++s.balances[to];
        }

        emit Transfer(address(0), to, tokenId);
    }

    /// @notice Safely mints a token once.
    /// @dev Note: This function implements {ERC721Mintable-safeMint(address,uint256,bytes)}.
    /// @dev Note: Either `safeMint` or `safeMintOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if `tokenId` already exists.
    /// @dev Reverts with {ERC721BurntToken} if `tokenId` has been previously burnt.
    /// @dev Reverts with {ERC721SafeTransferRejected} if `to` is a contract and the call to
    ///  {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    /// @param data Optional data to pass along to the receiver call.
    function safeMintOnce(Layout storage s, address sender, address to, uint256 tokenId, bytes memory data) internal {
        s.mintOnce(to, tokenId);
        if (to.hasBytecode()) {
            _callOnERC721Received(sender, address(0), to, tokenId, data);
        }
    }

    /// @notice Unsafely mints a batch of tokens once.
    /// @dev Note: This function implements {ERC721Mintable-batchMint(address,uint256[])}.
    /// @dev Note: Either `batchMint` or `batchMintOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {ERC721MintToAddressZero} if `to` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if one of `tokenIds` already exists.
    /// @dev Reverts with {ERC721BurntToken} if one of `tokenIds` has been previously burnt.
    /// @dev Emits a {Transfer} event from the zero address for each of `tokenIds`.
    /// @param to Address of the new tokens owner.
    /// @param tokenIds Identifiers of the tokens to mint.
    function batchMintOnce(Layout storage s, address to, uint256[] memory tokenIds) internal {
        if (to == address(0)) revert ERC721MintToAddressZero();

        uint256 length = tokenIds.length;
        for (uint256 i; i < length; ++i) {
            uint256 tokenId = tokenIds[i];
            uint256 owner = s.owners[tokenId];
            if (_tokenExists(owner)) revert ERC721ExistingToken(tokenId);
            if (_tokenWasBurnt(owner)) revert ERC721BurntToken(tokenId);

            s.owners[tokenId] = uint256(uint160(to));

            emit Transfer(address(0), to, tokenId);
        }

        unchecked {
            s.balances[to] += length;
        }
    }

    /// @notice Unsafely mints tokens to multiple recipients once.
    /// @dev Note: This function implements {ERC721Deliverable-deliver(address[],uint256[])}.
    /// @dev Note: Either `deliver` or `deliverOnce` should be used in a given contract, but not both.
    /// @dev Reverts with {InconsistentArrayLengths} if `recipients` and `tokenIds` have different lengths.
    /// @dev Reverts with {ERC721MintToAddressZero} if one of `recipients` is the zero address.
    /// @dev Reverts with {ERC721ExistingToken} if one of `tokenIds` already exists.
    /// @dev Reverts with {ERC721BurntToken} if one of `tokenIds` has been previously burnt.
    /// @dev Emits a {Transfer} event from the zero address for each of `recipients` and `tokenIds`.
    /// @param recipients Addresses of the new tokens owners.
    /// @param tokenIds Identifiers of the tokens to mint.
    function deliverOnce(Layout storage s, address[] memory recipients, uint256[] memory tokenIds) internal {
        uint256 length = recipients.length;
        if (length != tokenIds.length) revert InconsistentArrayLengths();
        for (uint256 i; i < length; ++i) {
            address to = recipients[i];
            if (to == address(0)) revert ERC721MintToAddressZero();

            uint256 tokenId = tokenIds[i];
            uint256 owner = s.owners[tokenId];
            if (_tokenExists(owner)) revert ERC721ExistingToken(tokenId);
            if (_tokenWasBurnt(owner)) revert ERC721BurntToken(tokenId);

            s.owners[tokenId] = uint256(uint160(to));
            unchecked {
                ++s.balances[to];
            }

            emit Transfer(address(0), to, tokenId);
        }
    }

    /// @notice Burns a token by a sender.
    /// @dev Note: This function implements {ERC721Burnable-burnFrom(address,uint256)}.
    /// @dev Reverts with {ERC721NonOwnedToken} if `tokenId` is not owned by `from`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if `sender` is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Emits a {Transfer} event with `to` set to the zero address.
    /// @param sender The message sender.
    /// @param from The current token owner.
    /// @param tokenId The identifier of the token to burn.
    function burnFrom(Layout storage s, address sender, address from, uint256 tokenId) internal {
        uint256 owner = s.owners[tokenId];
        if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
        if (_tokenOwner(owner) != from) revert ERC721NonOwnedToken(from, tokenId);

        if (!_isOperatable(s, from, sender)) {
            if (!_tokenHasApproval(owner) || sender != s.approvals[tokenId]) revert ERC721NonApprovedForTransfer(sender, from, tokenId);
        }

        s.owners[tokenId] = BURNT_TOKEN_OWNER_VALUE;

        unchecked {
            // cannot underflow as balance is verified through TOKEN ownership
            --s.balances[from];
        }
        emit Transfer(from, address(0), tokenId);
    }

    /// @notice Burns a batch of tokens by a sender.
    /// @dev Note: This function implements {ERC721Burnable-batchBurnFrom(address,uint256[])}.
    /// @dev Reverts with {ERC721NonOwnedToken} if one of `tokenIds` is not owned by `from`.
    /// @dev Reverts with {ERC721NonApprovedForTransfer} if `sender` is not `from` and has not been approved by `from` for each of `tokenIds`.
    /// @dev Emits a {Transfer} event with `to` set to the zero address for each of `tokenIds`.
    /// @param sender The message sender.
    /// @param from The current tokens owner.
    /// @param tokenIds The identifiers of the tokens to burn.
    function batchBurnFrom(Layout storage s, address sender, address from, uint256[] calldata tokenIds) internal {
        bool operatable = _isOperatable(s, from, sender);

        uint256 length = tokenIds.length;
        for (uint256 i; i < length; ++i) {
            uint256 tokenId = tokenIds[i];
            uint256 owner = s.owners[tokenId];
            if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
            if (_tokenOwner(owner) != from) revert ERC721NonOwnedToken(from, tokenId);
            if (!operatable) {
                if (!_tokenHasApproval(owner) || sender != s.approvals[tokenId]) revert ERC721NonApprovedForTransfer(sender, from, tokenId);
            }
            s.owners[tokenId] = BURNT_TOKEN_OWNER_VALUE;
            emit Transfer(from, address(0), tokenId);
        }

        if (length != 0) {
            unchecked {
                s.balances[from] -= length;
            }
        }
    }

    /// @notice Gets the balance of an address.
    /// @dev Note: This function implements {ERC721-balanceOf(address)}.
    /// @dev Reverts with {ERC721BalanceOfAddressZero} if `owner` is the zero address.
    /// @param owner The address to query the balance of.
    /// @return balance The amount owned by the owner.
    function balanceOf(Layout storage s, address owner) internal view returns (uint256 balance) {
        if (owner == address(0)) revert ERC721BalanceOfAddressZero();
        return s.balances[owner];
    }

    /// @notice Gets the owner of a token.
    /// @dev Note: This function implements {ERC721-ownerOf(uint256)}.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @param tokenId The token identifier to query the owner of.
    /// @return tokenOwner The owner of the token.
    function ownerOf(Layout storage s, uint256 tokenId) internal view returns (address tokenOwner) {
        uint256 owner = s.owners[tokenId];
        if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
        return _tokenOwner(owner);
    }

    /// @notice Gets the approved address for a token.
    /// @dev Note: This function implements {ERC721-getApproved(uint256)}.
    /// @dev Reverts with {ERC721NonExistingToken} if `tokenId` does not exist.
    /// @param tokenId The token identifier to query the approval of.
    /// @return approved The approved address for the token identifier, or the zero address if no approval is set.
    function getApproved(Layout storage s, uint256 tokenId) internal view returns (address approved) {
        uint256 owner = s.owners[tokenId];
        if (!_tokenExists(owner)) revert ERC721NonExistingToken(tokenId);
        if (_tokenHasApproval(owner)) {
            return s.approvals[tokenId];
        } else {
            return address(0);
        }
    }

    /// @notice Gets whether an operator is approved for all tokens by an owner.
    /// @dev Note: This function implements {ERC721-isApprovedForAll(address,address)}.
    /// @param owner The address which gives the approval for all tokens.
    /// @param operator The address which receives the approval for all tokens.
    /// @return approvedForAll Whether the operator is approved for all tokens by the owner.
    function isApprovedForAll(Layout storage s, address owner, address operator) internal view returns (bool approvedForAll) {
        return s.operators[owner][operator];
    }

    /// @notice Gets whether a token was burnt.
    /// @param tokenId The token identifier.
    /// @return tokenWasBurnt Whether the token was burnt.
    function wasBurnt(Layout storage s, uint256 tokenId) internal view returns (bool tokenWasBurnt) {
        return _tokenWasBurnt(s.owners[tokenId]);
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }

    /// @notice Calls {IERC721Receiver-onERC721Received} on a target contract.
    /// @dev Reverts with {ERC721SafeTransferRejected} if the call to the target fails, reverts or is rejected.
    /// @param sender The message sender.
    /// @param from Previous token owner.
    /// @param to New token owner.
    /// @param tokenId Identifier of the token transferred.
    /// @param data Optional data to send along with the receiver contract call.
    function _callOnERC721Received(address sender, address from, address to, uint256 tokenId, bytes memory data) private {
        if (IERC721Receiver(to).onERC721Received(sender, from, tokenId, data) != ERC721_RECEIVED) revert ERC721SafeTransferRejected(to, tokenId);
    }

    /// @notice Returns whether an account is authorised to make a transfer on behalf of an owner.
    /// @param owner The token owner.
    /// @param account The account to check the operatability of.
    /// @return operatable True if `account` is `owner` or is an operator for `owner`, false otherwise.
    function _isOperatable(Layout storage s, address owner, address account) private view returns (bool operatable) {
        return (owner == account) || s.operators[owner][account];
    }

    function _tokenOwner(uint256 owner) private pure returns (address tokenOwner) {
        return address(uint160(owner));
    }

    function _tokenExists(uint256 owner) private pure returns (bool tokenExists) {
        return uint160(owner) != 0;
    }

    function _tokenWasBurnt(uint256 owner) private pure returns (bool tokenWasBurnt) {
        return owner == BURNT_TOKEN_OWNER_VALUE;
    }

    function _tokenHasApproval(uint256 owner) private pure returns (bool tokenHasApproval) {
        return owner & TOKEN_APPROVAL_OWNER_FLAG != 0;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ITokenMetadataResolver
/// @notice Interface for Token Metadata Resolvers.
interface ITokenMetadataResolver {
    /// @notice Gets the token metadata URI for a token.
    /// @param tokenContract The token contract for which to retrieve the token URI.
    /// @param tokenId The token identifier.
    /// @return tokenURI The token metadata URI.
    function tokenMetadataURI(address tokenContract, uint256 tokenId) external view returns (string memory tokenURI);
}

File 13 of 62 : ERC721Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when trying to approve oneself.
/// @param account The account trying to approve itself.
error ERC721SelfApproval(address account);

/// @notice Thrown when trying to approveForAll oneself.
/// @param account The account trying to approveForAll itself.
error ERC721SelfApprovalForAll(address account);

/// @notice Thrown when a sender tries to set a token approval but is neither the owner nor approvedForAll by the owner.
/// @param sender The message sender.
/// @param tokenId The identifier of the token.
error ERC721NonApprovedForApproval(address sender, address owner, uint256 tokenId);

/// @notice Thrown when transferring a token to the zero address.
error ERC721TransferToAddressZero();

/// @notice Thrown when a token does not exist but is required to.
/// @param tokenId The identifier of the token that was checked.
error ERC721NonExistingToken(uint256 tokenId);

/// @notice Thrown when a sender tries to transfer a token but is neither the owner nor approved by the owner.
/// @param sender The message sender.
/// @param tokenId The identifier of the token.
error ERC721NonApprovedForTransfer(address sender, address owner, uint256 tokenId);

/// @notice Thrown when a token is not owned by the expected account.
/// @param account The account that was expected to own the token.
/// @param tokenId The identifier of the token.
error ERC721NonOwnedToken(address account, uint256 tokenId);

/// @notice Thrown when a safe transfer is rejected by the recipient contract.
/// @param recipient The recipient contract.
/// @param tokenId The identifier of the token.
error ERC721SafeTransferRejected(address recipient, uint256 tokenId);

/// @notice Thrown when querying the balance of the zero address.
error ERC721BalanceOfAddressZero();

File 14 of 62 : ContractOwnership.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {ContractOwnershipStorage} from "./libraries/ContractOwnershipStorage.sol";
import {ContractOwnershipBase} from "./base/ContractOwnershipBase.sol";
import {InterfaceDetection} from "./../introspection/InterfaceDetection.sol";

/// @title ERC173 Contract Ownership Standard (immutable version).
/// @dev See https://eips.ethereum.org/EIPS/eip-173
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ContractOwnership is ContractOwnershipBase, InterfaceDetection {
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    /// @notice Initializes the storage with an initial contract owner.
    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.
    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.
    /// @param initialOwner the initial contract owner.
    constructor(address initialOwner) {
        ContractOwnershipStorage.layout().constructorInit(initialOwner);
    }
}

File 15 of 62 : ERC2981.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {ERC2981Storage} from "./libraries/ERC2981Storage.sol";
import {ERC2981Base} from "./base/ERC2981Base.sol";
import {ContractOwnership} from "./../../access/ContractOwnership.sol";

/// @title ERC2981 NFT Royalty Standard (immutable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ERC2981 is ERC2981Base, ContractOwnership {
    /// @notice Marks the following ERC165 interface(s) as supported: ERC2981.
    constructor() {
        ERC2981Storage.init();
    }
}

File 16 of 62 : ERC721Events.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Emitted when a token is transferred.
/// @param from The previous token owner.
/// @param to The new token owner.
/// @param tokenId The transferred token identifier.
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

/// @notice Emitted when a single token approval is set.
/// @param owner The token owner.
/// @param approved The approved address.
/// @param tokenId The approved token identifier.
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

/// @notice Emitted when an approval for all tokens is set or unset.
/// @param owner The tokens owner.
/// @param operator The approved address.
/// @param approved True when then approval is set, false when it is unset.
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 18 of 62 : CommonErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when trying to transfer tokens without calldata to the contract.
error EtherReceptionDisabled();

/// @notice Thrown when the multiple related arrays have different lengths.
error InconsistentArrayLengths();

/// @notice Thrown when an ETH transfer has failed.
error TransferFailed();

File 19 of 62 : InterfaceDetection.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IERC165} from "./interfaces/IERC165.sol";
import {InterfaceDetectionStorage} from "./libraries/InterfaceDetectionStorage.sol";

/// @title ERC165 Interface Detection Standard (immutable or proxiable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) or proxied implementation.
abstract contract InterfaceDetection is IERC165 {
    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;

    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) external view returns (bool) {
        return InterfaceDetectionStorage.layout().supportsInterface(interfaceId);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {IERC721} from "./../../token/ERC721/interfaces/IERC721.sol";
import {ITokenRecovery} from "./../interfaces/ITokenRecovery.sol";
import {ContractOwnershipStorage} from "./../../access/libraries/ContractOwnershipStorage.sol";
import {TokenRecoveryLibrary} from "./../libraries/TokenRecoveryLibrary.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title Recovery mechanism for ETH/ERC20/ERC721 tokens accidentally sent to this contract (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC173 (Contract Ownership standard).
abstract contract TokenRecoveryBase is ITokenRecovery, Context {
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    /// @inheritdoc ITokenRecovery
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Reverts with {InconsistentArrayLengths} `accounts` and `amounts` do not have the same length.
    /// @dev Reverts if one of the ETH transfers fails for any reason.
    function recoverETH(address payable[] calldata accounts, uint256[] calldata amounts) public virtual {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        TokenRecoveryLibrary.recoverETH(accounts, amounts);
    }

    /// @inheritdoc ITokenRecovery
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Reverts with {InconsistentArrayLengths} if `accounts`, `tokens` and `amounts` do not have the same length.
    /// @dev Reverts if one of the ERC20 transfers fails for any reason.
    function recoverERC20s(address[] calldata accounts, IERC20[] calldata tokens, uint256[] calldata amounts) public virtual {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        TokenRecoveryLibrary.recoverERC20s(accounts, tokens, amounts);
    }

    /// @inheritdoc ITokenRecovery
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Reverts with {InconsistentArrayLengths} if `accounts`, `contracts` and `amounts` do not have the same length.
    /// @dev Reverts if one of the ERC721 transfers fails for any reason.
    function recoverERC721s(address[] calldata accounts, IERC721[] calldata contracts, uint256[] calldata tokenIds) public virtual {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        TokenRecoveryLibrary.recoverERC721s(accounts, contracts, tokenIds);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IAccessControl} from "./../../access/interfaces/IAccessControl.sol";
import {AccessControlStorage} from "./../libraries/AccessControlStorage.sol";
import {ContractOwnershipStorage} from "./../libraries/ContractOwnershipStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title Access control via roles management (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC173 (Contract Ownership standard).
abstract contract AccessControlBase is IAccessControl, Context {
    using AccessControlStorage for AccessControlStorage.Layout;
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    /// @notice Grants a role to an account.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Emits a {RoleGranted} event if the account did not previously have the role.
    /// @param role The role to grant.
    /// @param account The account to grant the role to.
    function grantRole(bytes32 role, address account) external virtual {
        address operator = _msgSender();
        ContractOwnershipStorage.layout().enforceIsContractOwner(operator);
        AccessControlStorage.layout().grantRole(role, account, operator);
    }

    /// @notice Revokes a role from an account.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Emits a {RoleRevoked} event if the account previously had the role.
    /// @param role The role to revoke.
    /// @param account The account to revoke the role from.
    function revokeRole(bytes32 role, address account) external virtual {
        address operator = _msgSender();
        ContractOwnershipStorage.layout().enforceIsContractOwner(operator);
        AccessControlStorage.layout().revokeRole(role, account, operator);
    }

    /// @inheritdoc IAccessControl
    function renounceRole(bytes32 role) external virtual {
        AccessControlStorage.layout().renounceRole(_msgSender(), role);
    }

    /// @inheritdoc IAccessControl
    function hasRole(bytes32 role, address account) external view virtual returns (bool hasRole_) {
        return AccessControlStorage.layout().hasRole(role, account);
    }
}

File 22 of 62 : Common.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when the target contract is actually not a contract.
/// @param targetContract The contract that was checked
error TargetIsNotAContract(address targetContract);

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title Access control via roles management (functions)
interface IAccessControl {
    /// @notice Renounces a role by the sender.
    /// @dev Reverts if `sender` does not have `role`.
    /// @dev Emits a {RoleRevoked} event.
    /// @param role The role to renounce.
    function renounceRole(bytes32 role) external;

    /// @notice Retrieves whether an account has a role.
    /// @param role The role.
    /// @param account The account.
    /// @return hasRole_ Whether `account` has `role`.
    function hasRole(bytes32 role, address account) external view returns (bool hasRole_);
}

File 24 of 62 : AccessControlErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when an account does not have the required role.
/// @param role The role the caller is missing.
/// @param account The account that was checked.
error NotRoleHolder(bytes32 role, address account);

/// @notice Thrown when an account does not have the required role on a target contract.
/// @param targetContract The contract that was checked.
/// @param role The role that was checked.
/// @param account The account that was checked.
error NotTargetContractRoleHolder(address targetContract, bytes32 role, address account);

File 25 of 62 : ERC173Events.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Emitted when the contract ownership changes.
/// @param previousOwner the previous contract owner.
/// @param newOwner the new contract owner.
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

/// @notice Emitted when a new contract owner is pending.
/// @param pendingOwner the address of the new contract owner.
event OwnershipTransferPending(address indexed pendingOwner);

File 26 of 62 : ContractOwnershipErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when an account is not the contract owner but is required to.
/// @param account The account that was checked.
error NotContractOwner(address account);

/// @notice Thrown when an account is not the pending contract owner but is required to.
/// @param account The account that was checked.
error NotPendingContractOwner(address account);

/// @notice Thrown when an account is not the target contract owner but is required to.
/// @param targetContract The contract that was checked.
/// @param account The account that was checked.
error NotTargetContractOwner(address targetContract, address account);

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

library Address {
    /// @notice Checks if the address is a deployed smart contract.
    /// @param addr The address to check.
    /// @return hasBytecode True if `addr` is a deployed smart contract, false otherwise.
    function hasBytecode(address addr) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(addr)
        }
        return size != 0;
    }
}

File 28 of 62 : AccessControlEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Emitted when `role` is granted to `account`.
/// @param role The role that has been granted.
/// @param account The account that has been granted the role.
/// @param operator The account that granted the role.
event RoleGranted(bytes32 role, address account, address operator);

/// @notice Emitted when `role` is revoked from `account`.
/// @param role The role that has been revoked.
/// @param account The account that has been revoked the role.
/// @param operator The account that revoked the role.
event RoleRevoked(bytes32 role, address account, address operator);

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {InitializationPhaseAlreadyReached} from "./../errors/ProxyInitializationErrors.sol";
import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol";

/// @notice Multiple calls protection for storage-modifying proxy initialization functions.
library ProxyInitialization {
    /// @notice Sets the initialization phase during a storage-modifying proxy initialization function.
    /// @dev Reverts with {InitializationPhaseAlreadyReached} if `phase` has been reached already.
    /// @param storageSlot the storage slot where `phase` is stored.
    /// @param phase the initialization phase.
    function setPhase(bytes32 storageSlot, uint256 phase) internal {
        StorageSlot.Uint256Slot storage currentVersion = StorageSlot.getUint256Slot(storageSlot);
        uint256 currentPhase = currentVersion.value;
        if (currentPhase >= phase) revert InitializationPhaseAlreadyReached(currentPhase, phase);
        currentVersion.value = phase;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC-173 Contract Ownership Standard (functions)
/// @dev See https://eips.ethereum.org/EIPS/eip-173
/// @dev Note: the ERC-165 identifier for this interface is 0x7f5828d0
interface IERC173 {
    /// @notice Sets the address of the new contract owner.
    /// @dev Reverts if the sender is not the contract owner.
    /// @dev Emits an {OwnershipTransferred} event if `newOwner` is different from the current contract owner.
    /// @param newOwner The address of the new contract owner. Using the zero address means renouncing ownership.
    function transferOwnership(address newOwner) external;

    /// @notice Gets the address of the contract owner.
    /// @return contractOwner The address of the contract owner.
    function owner() external view returns (address contractOwner);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IllegalInterfaceId} from "./../errors/InterfaceDetectionErrors.sol";
import {IERC165} from "./../interfaces/IERC165.sol";

library InterfaceDetectionStorage {
    struct Layout {
        mapping(bytes4 => bool) supportedInterfaces;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.core.introspection.InterfaceDetection.storage")) - 1);

    bytes4 internal constant ILLEGAL_INTERFACE_ID = 0xffffffff;

    /// @notice Sets or unsets an ERC165 interface.
    /// @dev Revertswith {IllegalInterfaceId} if `interfaceId` is `0xffffffff`.
    /// @param interfaceId the interface identifier.
    /// @param supported True to set the interface, false to unset it.
    function setSupportedInterface(Layout storage s, bytes4 interfaceId, bool supported) internal {
        if (interfaceId == ILLEGAL_INTERFACE_ID) revert IllegalInterfaceId();
        s.supportedInterfaces[interfaceId] = supported;
    }

    /// @notice Returns whether this contract implements a given interface.
    /// @dev Note: This function call must use less than 30 000 gas.
    /// @param interfaceId The interface identifier to test.
    /// @return supported True if the interface is supported, false if `interfaceId` is `0xffffffff` or if the interface is not supported.
    function supportsInterface(Layout storage s, bytes4 interfaceId) internal view returns (bool supported) {
        if (interfaceId == ILLEGAL_INTERFACE_ID) {
            return false;
        }
        if (interfaceId == type(IERC165).interfaceId) {
            return true;
        }
        return s.supportedInterfaces[interfaceId];
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {ITokenMetadataResolver} from "./../interfaces/ITokenMetadataResolver.sol";
import {ProxyInitialization} from "./../../../proxy/libraries/ProxyInitialization.sol";

library TokenMetadataStorage {
    struct Layout {
        string tokenName;
        string tokenSymbol;
        ITokenMetadataResolver tokenMetadataResolver;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.token.metadata.TokenMetadata.storage")) - 1);
    bytes32 internal constant PROXY_INIT_PHASE_SLOT = bytes32(uint256(keccak256("animoca.token.metadata.TokenMetadata.phase")) - 1);

    /// @notice Initializes the metadata storage (immutable version).
    /// @dev Note: This function should be called ONLY in the constructor of an immutable (non-proxied) contract.
    /// @param tokenName The token name.
    /// @param tokenSymbol The token symbol.
    /// @param tokenMetadataResolver The address of the metadata resolver contract.
    function constructorInit(
        Layout storage s,
        string memory tokenName,
        string memory tokenSymbol,
        ITokenMetadataResolver tokenMetadataResolver
    ) internal {
        s.tokenName = tokenName;
        s.tokenSymbol = tokenSymbol;
        s.tokenMetadataResolver = tokenMetadataResolver;
    }

    /// @notice Initializes the metadata storage (proxied version).
    /// @notice Sets the proxy initialization phase to `1`.
    /// @dev Note: This function should be called ONLY in the init function of a proxied contract.
    /// @dev Reverts with {InitializationPhaseAlreadyReached} if the proxy initialization phase is set to `1` or above.
    /// @param tokenName The token name.
    /// @param tokenSymbol The token symbol.
    /// @param tokenMetadataResolver The address of the metadata resolver contract.
    function proxyInit(
        Layout storage s,
        string calldata tokenName,
        string calldata tokenSymbol,
        ITokenMetadataResolver tokenMetadataResolver
    ) internal {
        ProxyInitialization.setPhase(PROXY_INIT_PHASE_SLOT, 1);
        s.tokenName = tokenName;
        s.tokenSymbol = tokenSymbol;
        s.tokenMetadataResolver = tokenMetadataResolver;
    }

    /// @notice Gets the name of the token.
    /// @return tokenName The name of the token contract.
    function name(Layout storage s) internal view returns (string memory tokenName) {
        return s.tokenName;
    }

    /// @notice Gets the symbol of the token.
    /// @return tokenSymbol The symbol of the token contract.
    function symbol(Layout storage s) internal view returns (string memory tokenSymbol) {
        return s.tokenSymbol;
    }

    /// @notice Gets the address of the token metadata resolver.
    /// @return tokenMetadataResolver The address of the token metadata resolver.
    function metadataResolver(Layout storage s) internal view returns (ITokenMetadataResolver tokenMetadataResolver) {
        return s.tokenMetadataResolver;
    }

    /// @notice Gets the token metadata URI retieved from the metadata resolver contract.
    /// @param tokenContract The address of the token contract.
    /// @param tokenId The ID of the token.
    function tokenMetadataURI(Layout storage s, address tokenContract, uint256 tokenId) internal view returns (string memory) {
        return s.tokenMetadataResolver.tokenMetadataURI(tokenContract, tokenId);
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

File 33 of 62 : ERC721MintableOnceErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when minting a token which has been burnt before (MintableOnce implementation).
/// @param tokenId The identifier of the token that has been burnt before.
error ERC721BurntToken(uint256 tokenId);

File 34 of 62 : ERC721MintableErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when minting a token to the zero address.
error ERC721MintToAddressZero();

/// @notice Thrown when minting a token that already exists.
/// @param tokenId The identifier of the token that already exists.
error ERC721ExistingToken(uint256 tokenId);

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC721 Non-Fungible Token Standard, basic interface (functions).
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev This interface only contains the standard functions. See IERC721Events for the events.
/// @dev Note: The ERC-165 identifier for this interface is 0x80ac58cd.
interface IERC721 {
    /// @notice Sets or unsets an approval to transfer a single token on behalf of its owner.
    /// @dev Note: There can only be one approved address per token at a given time.
    /// @dev Note: A token approval gets reset when this token is transferred, including a self-transfer.
    /// @dev Reverts if `tokenId` does not exist.
    /// @dev Reverts if `to` is the token owner.
    /// @dev Reverts if the sender is not the token owner and has not been approved by the token owner.
    /// @dev Emits an {Approval} event.
    /// @param to The address to approve, or the zero address to remove any existing approval.
    /// @param tokenId The token identifier to give approval for.
    function approve(address to, uint256 tokenId) external;

    /// @notice Sets or unsets an approval to transfer all tokens on behalf of their owner.
    /// @dev Reverts if the sender is the same as `operator`.
    /// @dev Emits an {ApprovalForAll} event.
    /// @param operator The address to approve for all tokens.
    /// @param approved True to set an approval for all tokens, false to unset it.
    function setApprovalForAll(address operator, bool approved) external;

    /// @notice Unsafely transfers the ownership of a token to a recipient.
    /// @dev Note: Usage of this method is discouraged, use `safeTransferFrom` whenever possible.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if `from` is not the owner of `tokenId`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Emits a {Transfer} event.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer. Self-transfers are possible.
    /// @param tokenId The identifier of the token to transfer.
    function transferFrom(address from, address to, uint256 tokenId) external;

    /// @notice Safely transfers the ownership of a token to a recipient.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if `from` is not the owner of `tokenId`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Reverts if `to` is a contract and the call to {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer.
    /// @param tokenId The identifier of the token to transfer.
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /// @notice Safely transfers the ownership of a token to a recipient.
    /// @dev Resets the token approval for `tokenId`.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if `from` is not the owner of `tokenId`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Reverts if `to` is a contract and the call to {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits a {Transfer} event.
    /// @param from The current token owner.
    /// @param to The recipient of the token transfer.
    /// @param tokenId The identifier of the token to transfer.
    /// @param data Optional data to send along to a receiver contract.
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /// @notice Gets the balance of an address.
    /// @dev Reverts if `owner` is the zero address.
    /// @param owner The address to query the balance of.
    /// @return balance The amount owned by the owner.
    function balanceOf(address owner) external view returns (uint256 balance);

    /// @notice Gets the owner of a token.
    /// @dev Reverts if `tokenId` does not exist.
    /// @param tokenId The token identifier to query the owner of.
    /// @return tokenOwner The owner of the token identifier.
    function ownerOf(uint256 tokenId) external view returns (address tokenOwner);

    /// @notice Gets the approved address for a token.
    /// @dev Reverts if `tokenId` does not exist.
    /// @param tokenId The token identifier to query the approval of.
    /// @return approved The approved address for the token identifier, or the zero address if no approval is set.
    function getApproved(uint256 tokenId) external view returns (address approved);

    /// @notice Gets whether an operator is approved for all tokens by an owner.
    /// @param owner The address which gives the approval for all tokens.
    /// @param operator The address which receives the approval for all tokens.
    /// @return approvedForAll Whether the operator is approved for all tokens by the owner.
    function isApprovedForAll(address owner, address operator) external view returns (bool approvedForAll);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC721 Non-Fungible Token Standard, optional extension: Batch Transfer.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0xf3993d11.
interface IERC721BatchTransfer {
    /// @notice Unsafely transfers a batch of tokens to a recipient.
    /// @dev Resets the token approval for each of `tokenIds`.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if one of `tokenIds` is not owned by `from`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for each of `tokenIds`.
    /// @dev Emits an {IERC721-Transfer} event for each of `tokenIds`.
    /// @param from Current tokens owner.
    /// @param to Address of the new token owner.
    /// @param tokenIds Identifiers of the tokens to transfer.
    function batchTransferFrom(address from, address to, uint256[] calldata tokenIds) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC721 Non-Fungible Token Standard, optional extension: Metadata.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0x5b5e139f.
interface IERC721Metadata {
    /// @notice Gets the name of the token. E.g. "My Token".
    /// @return tokenName The name of the token.
    function name() external view returns (string memory tokenName);

    /// @notice Gets the symbol of the token. E.g. "TOK".
    /// @return tokenSymbol The symbol of the token.
    function symbol() external view returns (string memory tokenSymbol);

    /// @notice Gets the metadata URI for a token identifier.
    /// @dev Reverts if `tokenId` does not exist.
    /// @param tokenId The token identifier.
    /// @return uri The metadata URI for the token identifier.
    function tokenURI(uint256 tokenId) external view returns (string memory uri);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IERC721Metadata} from "./../interfaces/IERC721Metadata.sol";
import {ERC721Storage} from "./../libraries/ERC721Storage.sol";
import {TokenMetadataStorage} from "./../../metadata/libraries/TokenMetadataStorage.sol";
import {TokenMetadataBase} from "./../../metadata/base/TokenMetadataBase.sol";

/// @title ERC721 Non-Fungible Token Standard, optional extension: Metadata (proxiable version).
/// @notice This contracts uses an external resolver for managing individual tokens metadata.
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC721 (Non-Fungible Token Standard).
abstract contract ERC721MetadataBase is TokenMetadataBase, IERC721Metadata {
    using ERC721Storage for ERC721Storage.Layout;
    using TokenMetadataStorage for TokenMetadataStorage.Layout;

    /// @inheritdoc IERC721Metadata
    function name() public view virtual override(IERC721Metadata, TokenMetadataBase) returns (string memory tokenName) {
        return TokenMetadataBase.name();
    }

    /// @inheritdoc IERC721Metadata
    function symbol() public view virtual override(IERC721Metadata, TokenMetadataBase) returns (string memory tokenSymbol) {
        return TokenMetadataBase.symbol();
    }

    /// @inheritdoc IERC721Metadata
    function tokenURI(uint256 tokenId) external view virtual returns (string memory uri) {
        ERC721Storage.layout().ownerOf(tokenId); // reverts if the token does not exist
        return TokenMetadataStorage.layout().tokenMetadataURI(address(this), tokenId);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC721 Non-Fungible Token Standard, optional extension: Deliverable.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0x9da5e832.
interface IERC721Deliverable {
    /// @notice Unsafely mints tokens to multiple recipients.
    /// @dev Reverts if `recipients` and `tokenIds` have different lengths.
    /// @dev Reverts if one of `recipients` is the zero address.
    /// @dev Reverts if one of `tokenIds` already exists.
    /// @dev Emits an {IERC721-Transfer} event from the zero address for each of `recipients` and `tokenIds`.
    /// @param recipients Addresses of the new tokens owners.
    /// @param tokenIds Identifiers of the tokens to mint.
    function deliver(address[] calldata recipients, uint256[] calldata tokenIds) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC721 Non-Fungible Token Standard, optional extension: Burnable.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0x8b8b4ef5.
interface IERC721Burnable {
    /// @notice Burns a token.
    /// @dev Reverts if `tokenId` is not owned by `from`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for `tokenId`.
    /// @dev Emits an {IERC721-Transfer} event with `to` set to the zero address.
    /// @param from The current token owner.
    /// @param tokenId The identifier of the token to burn.
    function burnFrom(address from, uint256 tokenId) external;

    /// @notice Burns a batch of tokens.
    /// @dev Reverts if one of `tokenIds` is not owned by `from`.
    /// @dev Reverts if the sender is not `from` and has not been approved by `from` for each of `tokenIds`.
    /// @dev Emits an {IERC721-Transfer} event with `to` set to the zero address for each of `tokenIds`.
    /// @param from The current tokens owner.
    /// @param tokenIds The identifiers of the tokens to burn.
    function batchBurnFrom(address from, uint256[] calldata tokenIds) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC721 Non-Fungible Token Standard, Tokens Receiver.
/// @notice Interface for supporting safe transfers from ERC721 contracts.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0x150b7a02.
interface IERC721Receiver {
    /// @notice Handles the receipt of an ERC721 token.
    /// @dev Note: This function is called by an ERC721 contract after a safe transfer.
    /// @dev Note: The ERC721 contract address is always the message sender.
    /// @param operator The initiator of the safe transfer.
    /// @param from The previous token owner.
    /// @param tokenId The token identifier.
    /// @param data Optional additional data with no specified format.
    /// @return magicValue `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` (`0x150b7a02`) to accept, any other value to refuse.
    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4 magicValue);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC721 Non-Fungible Token Standard, optional extension: Mintable.
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev Note: The ERC-165 identifier for this interface is 0x8e773e13.
interface IERC721Mintable {
    /// @notice Unsafely mints a token.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if `tokenId` already exists.
    /// @dev Emits an {IERC721-Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    function mint(address to, uint256 tokenId) external;

    /// @notice Safely mints a token.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if `tokenId` already exists.
    /// @dev Reverts if `to` is a contract and the call to {IERC721Receiver-onERC721Received} fails, reverts or is rejected.
    /// @dev Emits an {IERC721-Transfer} event from the zero address.
    /// @param to Address of the new token owner.
    /// @param tokenId Identifier of the token to mint.
    /// @param data Optional data to pass along to the receiver call.
    function safeMint(address to, uint256 tokenId, bytes calldata data) external;

    /// @notice Unsafely mints a batch of tokens.
    /// @dev Reverts if `to` is the zero address.
    /// @dev Reverts if one of `tokenIds` already exists.
    /// @dev Emits an {IERC721-Transfer} event from the zero address for each of `tokenIds`.
    /// @param to Address of the new tokens owner.
    /// @param tokenIds Identifiers of the tokens to mint.
    function batchMint(address to, uint256[] calldata tokenIds) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IERC173} from "./../interfaces/IERC173.sol";
import {ContractOwnershipStorage} from "./../libraries/ContractOwnershipStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title ERC173 Contract Ownership Standard (proxiable version).
/// @dev See https://eips.ethereum.org/EIPS/eip-173
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC165 (Interface Detection Standard).
abstract contract ContractOwnershipBase is IERC173, Context {
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    /// @inheritdoc IERC173
    function owner() public view virtual returns (address) {
        return ContractOwnershipStorage.layout().owner();
    }

    /// @inheritdoc IERC173
    function transferOwnership(address newOwner) public virtual {
        ContractOwnershipStorage.layout().transferOwnership(_msgSender(), newOwner);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IERC2981} from "./../interfaces/IERC2981.sol";
import {ERC2981Storage} from "./../libraries/ERC2981Storage.sol";
import {ContractOwnershipStorage} from "./../../../access/libraries/ContractOwnershipStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title ERC2981 NFT Royalty Standard (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC165 (Interface Detection Standard).
/// @dev Note: This contract requires ERC173 (Contract Ownership standard).
abstract contract ERC2981Base is Context, IERC2981 {
    using ERC2981Storage for ERC2981Storage.Layout;
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    uint256 public constant ROYALTY_FEE_DENOMINATOR = ERC2981Storage.ROYALTY_FEE_DENOMINATOR;

    /// @notice Sets the royalty percentage.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Reverts with {IncorrectRoyaltyPercentage} if `percentage` is above 100% (> FEE_DENOMINATOR).
    /// @param percentage The new percentage to set. For example 50000 sets 50% royalty.
    function setRoyaltyPercentage(uint256 percentage) external virtual {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        ERC2981Storage.layout().setRoyaltyPercentage(percentage);
    }

    /// @notice Sets the royalty receiver.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Reverts with {IncorrectRoyaltyReceiver} if `receiver` is the zero address.
    /// @param receiver The new receiver to set.
    function setRoyaltyReceiver(address receiver) external virtual {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        ERC2981Storage.layout().setRoyaltyReceiver(receiver);
    }

    /// @inheritdoc IERC2981
    function royaltyInfo(uint256 tokenId, uint256 salePrice) external view virtual returns (address receiver, uint256 royaltyAmount) {
        return ERC2981Storage.layout().royaltyInfo(tokenId, salePrice);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {ERC2981IncorrectRoyaltyReceiver, ERC2981IncorrectRoyaltyPercentage} from "./../errors/ERC2981Errors.sol";
import {IERC2981} from "./../interfaces/IERC2981.sol";
import {InterfaceDetectionStorage} from "./../../../introspection/libraries/InterfaceDetectionStorage.sol";

library ERC2981Storage {
    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;

    struct Layout {
        address royaltyReceiver;
        uint96 royaltyPercentage;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.token.royalty.ERC2981.storage")) - 1);

    uint256 internal constant ROYALTY_FEE_DENOMINATOR = 100000;

    /// @notice Marks the following ERC165 interface(s) as supported: ERC2981.
    function init() internal {
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC2981).interfaceId, true);
    }

    /// @notice Sets the royalty percentage.
    /// @dev Reverts with {ERC2981IncorrectRoyaltyPercentage} if `percentage` is above 100% (> FEE_DENOMINATOR).
    /// @param percentage The new percentage to set. For example 50000 sets 50% royalty.
    function setRoyaltyPercentage(Layout storage s, uint256 percentage) internal {
        if (percentage > ROYALTY_FEE_DENOMINATOR) {
            revert ERC2981IncorrectRoyaltyPercentage(percentage);
        }
        s.royaltyPercentage = uint96(percentage);
    }

    /// @notice Sets the royalty receiver.
    /// @dev Reverts with {ERC2981IncorrectRoyaltyReceiver} if `receiver` is the zero address.
    /// @param receiver The new receiver to set.
    function setRoyaltyReceiver(Layout storage s, address receiver) internal {
        if (receiver == address(0)) {
            revert ERC2981IncorrectRoyaltyReceiver();
        }
        s.royaltyReceiver = receiver;
    }

    /// @notice Called with the sale price to determine how much royalty is owed and to whom.
    // / @param tokenId The NFT asset queried for royalty information
    /// @param salePrice The sale price of the NFT asset specified by `tokenId`
    /// @return receiver Address of who should be sent the royalty payment
    /// @return royaltyAmount The royalty payment amount for `salePrice`
    function royaltyInfo(Layout storage s, uint256, uint256 salePrice) internal view returns (address receiver, uint256 royaltyAmount) {
        receiver = s.royaltyReceiver;
        uint256 royaltyPercentage = s.royaltyPercentage;
        if (salePrice == 0 || royaltyPercentage == 0) {
            royaltyAmount = 0;
        } else {
            if (salePrice < ROYALTY_FEE_DENOMINATOR) {
                royaltyAmount = (salePrice * royaltyPercentage) / ROYALTY_FEE_DENOMINATOR;
            } else {
                royaltyAmount = (salePrice / ROYALTY_FEE_DENOMINATOR) * royaltyPercentage;
            }
        }
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

File 46 of 62 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC165 Interface Detection Standard.
/// @dev See https://eips.ethereum.org/EIPS/eip-165.
/// @dev Note: The ERC-165 identifier for this interface is 0x01ffc9a7.
interface IERC165 {
    /// @notice Returns whether this contract implements a given interface.
    /// @dev Note: This function call must use less than 30 000 gas.
    /// @param interfaceId the interface identifier to test.
    /// @return supported True if the interface is supported, false if `interfaceId` is `0xffffffff` or if the interface is not supported.
    function supportsInterface(bytes4 interfaceId) external view returns (bool supported);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {IERC721} from "./../../token/ERC721/interfaces/IERC721.sol";

/// @title Uniquely identified seals management.
interface ITokenRecovery {
    /// @notice Extract ETH tokens which were accidentally sent to the contract to a list of accounts.
    /// @dev Note: While contracts can generally prevent accidental ETH transfer by implementating a reverting
    ///  `receive()` function, this can still be bypassed in a `selfdestruct(address)` scenario.
    /// @dev Warning: this function should be overriden for contracts which are supposed to hold ETH tokens
    ///  so that the extraction is limited to only amounts sent accidentally.
    /// @param accounts the list of accounts to transfer the tokens to.
    /// @param amounts the list of token amounts to transfer.
    function recoverETH(address payable[] calldata accounts, uint256[] calldata amounts) external;

    /// @notice Extract ERC20 tokens which were accidentally sent to the contract to a list of accounts.
    /// @dev Warning: this function should be overriden for contracts which are supposed to hold ERC20 tokens
    ///  so that the extraction is limited to only amounts sent accidentally.
    /// @param accounts the list of accounts to transfer the tokens to.
    /// @param tokens the list of ERC20 token addresses.
    /// @param amounts the list of token amounts to transfer.
    function recoverERC20s(address[] calldata accounts, IERC20[] calldata tokens, uint256[] calldata amounts) external;

    /// @notice Extract ERC721 tokens which were accidentally sent to the contract to a list of accounts.
    /// @dev Warning: this function should be overriden for contracts which are supposed to hold ERC721 tokens
    ///  so that the extraction is limited to only tokens sent accidentally.
    /// @param accounts the list of accounts to transfer the tokens to.
    /// @param contracts the list of ERC721 contract addresses.
    /// @param tokenIds the list of token ids to transfer.
    function recoverERC721s(address[] calldata accounts, IERC721[] calldata contracts, uint256[] calldata tokenIds) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {InconsistentArrayLengths} from "./../../CommonErrors.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {IERC721} from "./../../token/ERC721/interfaces/IERC721.sol";
import {IERC165} from "./../../introspection/interfaces/IERC165.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

library TokenRecoveryLibrary {
    using SafeERC20 for IERC20;
    using Address for address payable;

    /// @notice Thrown when trying to recover a token of the wrong contract type.
    /// @param tokenContract The token contract being recovered.
    error IncorrectTokenContractType(address tokenContract);

    /// @notice Extract ETH tokens which were accidentally sent to the contract to a list of accounts.
    /// @dev Note: While contracts can generally prevent accidental ETH transfer by implementating a reverting
    ///  `receive()` function, this can still be bypassed in a `selfdestruct(address)` scenario.
    /// @dev Warning: this function should be overriden for contracts which are supposed to hold ETH tokens
    ///  so that the extraction is limited to only amounts sent accidentally.
    /// @dev Reverts with {InconsistentArrayLengths} `accounts` and `amounts` do not have the same length.
    /// @dev Reverts if one of the ETH transfers fails for any reason.
    /// @param accounts the list of accounts to transfer the tokens to.
    /// @param amounts the list of token amounts to transfer.
    function recoverETH(address payable[] calldata accounts, uint256[] calldata amounts) internal {
        uint256 length = accounts.length;
        if (length != amounts.length) revert InconsistentArrayLengths();
        for (uint256 i; i < length; ++i) {
            accounts[i].sendValue(amounts[i]);
        }
    }

    /// @notice Extract ERC20 tokens which were accidentally sent to the contract to a list of accounts.
    /// @dev Warning: this function should be overriden for contracts which are supposed to hold ERC20 tokens
    ///  so that the extraction is limited to only amounts sent accidentally.
    /// @dev Reverts with {InconsistentArrayLengths} if `accounts`, `tokens` and `amounts` do not have the same length.
    /// @dev Reverts if one of the ERC20 transfers fails for any reason.
    /// @param accounts the list of accounts to transfer the tokens to.
    /// @param tokens the list of ERC20 token addresses.
    /// @param amounts the list of token amounts to transfer.
    function recoverERC20s(address[] calldata accounts, IERC20[] calldata tokens, uint256[] calldata amounts) internal {
        uint256 length = accounts.length;
        if (length != tokens.length || length != amounts.length) revert InconsistentArrayLengths();
        for (uint256 i; i < length; ++i) {
            tokens[i].safeTransfer(accounts[i], amounts[i]);
        }
    }

    /// @notice Extract ERC721 tokens which were accidentally sent to the contract to a list of accounts.
    /// @dev Warning: this function should be overriden for contracts which are supposed to hold ERC721 tokens
    ///  so that the extraction is limited to only tokens sent accidentally.
    /// @dev Reverts with {InconsistentArrayLengths} if `accounts`, `contracts` and `amounts` do not have the same length.
    /// @dev Reverts if one of the ERC721 transfers fails for any reason.
    /// @param accounts the list of accounts to transfer the tokens to.
    /// @param contracts the list of ERC721 contract addresses.
    /// @param tokenIds the list of token ids to transfer.
    function recoverERC721s(address[] calldata accounts, IERC721[] calldata contracts, uint256[] calldata tokenIds) internal {
        uint256 length = accounts.length;
        if (length != contracts.length || length != tokenIds.length) revert InconsistentArrayLengths();
        for (uint256 i; i < length; ++i) {
            IERC721 tokenContract = contracts[i];
            if (!IERC165(address(tokenContract)).supportsInterface(type(IERC721).interfaceId)) {
                revert IncorrectTokenContractType(address(tokenContract));
            }
            contracts[i].safeTransferFrom(address(this), accounts[i], tokenIds[i]);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC-1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(newImplementation.code.length > 0);
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {SlotDerivation}.
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct Int256Slot {
        int256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Int256Slot` with member `value` located at `slot`.
     */
    function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns a `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }
}

File 51 of 62 : ProxyInitializationErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Emitted when trying to set a phase value that has already been reached.
/// @param currentPhase The current phase.
/// @param newPhase The new phase trying to be set.
error InitializationPhaseAlreadyReached(uint256 currentPhase, uint256 newPhase);

File 52 of 62 : InterfaceDetectionErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when setting the illegal interfaceId 0xffffffff.
error IllegalInterfaceId();

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {ITokenMetadataResolver} from "./../interfaces/ITokenMetadataResolver.sol";
import {TokenMetadataStorage} from "./../libraries/TokenMetadataStorage.sol";

/// @title TokenMetadataBase (proxiable version).
/// @notice Provides metadata management for token contracts (ERC721/ERC1155) which uses an external resolver for managing individual tokens metadata.
/// @dev This contract is to be used via inheritance in a proxied implementation.
abstract contract TokenMetadataBase {
    using TokenMetadataStorage for TokenMetadataStorage.Layout;

    /// @notice Gets the token name. E.g. "My Token".
    /// @return tokenName The token name.
    function name() public view virtual returns (string memory tokenName) {
        return TokenMetadataStorage.layout().name();
    }

    /// @notice Gets the token symbol. E.g. "TOK".
    /// @return tokenSymbol The token symbol.
    function symbol() public view virtual returns (string memory tokenSymbol) {
        return TokenMetadataStorage.layout().symbol();
    }

    /// @notice Gets the token metadata resolver address.
    /// @return tokenMetadataResolver The token metadata resolver address.
    function metadataResolver() external view virtual returns (ITokenMetadataResolver tokenMetadataResolver) {
        return TokenMetadataStorage.layout().metadataResolver();
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC2981 NFT Royalty Standard.
/// @dev See https://eips.ethereum.org/EIPS/eip-2981
/// @dev Note: The ERC-165 identifier for this interface is 0x2a55205a.
interface IERC2981 {
    /// @notice Called with the sale price to determine how much royalty is owed and to whom.
    /// @param tokenId The NFT asset queried for royalty information
    /// @param salePrice The sale price of the NFT asset specified by `tokenId`
    /// @return receiver Address of who should be sent the royalty payment
    /// @return royaltyAmount The royalty payment amount for `salePrice`
    function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount);
}

File 55 of 62 : ERC2981Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when setting a royalty percentage that is above 100% (> FEE_DENOMINATOR).
/// @param percentage The royalty percentage that was attempted to be set.
error ERC2981IncorrectRoyaltyPercentage(uint256 percentage);

/// @notice Thrown when setting a royalty receiver that is the zero address.
error ERC2981IncorrectRoyaltyReceiver();

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)

pragma solidity ^0.8.20;

import {Errors} from "./Errors.sol";

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }

        (bool success, bytes memory returndata) = recipient.call{value: amount}("");
        if (!success) {
            _revert(returndata);
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {Errors.FailedCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

File 59 of 62 : Errors.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

File 61 of 62 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "mode": "3"
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "abi"
      ]
    }
  },
  "detectMissingLibraries": false,
  "forceEVMLA": false,
  "enableEraVMExtensions": false,
  "codegen": "yul",
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"string","name":"tokenName","type":"string"},{"internalType":"string","name":"tokenSymbol","type":"string"},{"internalType":"contract ITokenMetadataResolver","name":"metadataResolver","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"percentage","type":"uint256"}],"name":"ERC2981IncorrectRoyaltyPercentage","type":"error"},{"inputs":[],"name":"ERC2981IncorrectRoyaltyReceiver","type":"error"},{"inputs":[],"name":"ERC721BalanceOfAddressZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721ExistingToken","type":"error"},{"inputs":[],"name":"ERC721MintToAddressZero","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonApprovedForApproval","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonApprovedForTransfer","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonExistingToken","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonOwnedToken","type":"error"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721SafeTransferRejected","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ERC721SelfApproval","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ERC721SelfApprovalForAll","type":"error"},{"inputs":[],"name":"ERC721TransferToAddressZero","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[],"name":"IllegalInterfaceId","type":"error"},{"inputs":[],"name":"InconsistentArrayLengths","type":"error"},{"inputs":[{"internalType":"address","name":"tokenContract","type":"address"}],"name":"IncorrectTokenContractType","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"NotContractOwner","type":"error"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"NotRoleHolder","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"PendingScratchRequest","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IScratching","name":"scratchingContract","type":"address"}],"name":"ScratchingContractSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"MINTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROYALTY_FEE_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"approved","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"hasRole_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"approvedForAll","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadataResolver","outputs":[{"internalType":"contract ITokenMetadataResolver","name":"tokenMetadataResolver","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"tokenName","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"tokenOwner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"recoverERC20s","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"contract IERC721[]","name":"contracts","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"recoverERC721s","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"recoverETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"scratchingContract","outputs":[{"internalType":"contract IScratching","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"percentage","type":"uint256"}],"name":"setRoyaltyPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"setRoyaltyReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IScratching","name":"scratching","type":"address"}],"name":"setScratchingContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"tokenSymbol","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"uri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

9c4d535b00000000000000000000000000000000000000000000000000000000000000000100040b6abbfe684f22fd90a517251b77a10270bf1e49758d199e0e6c15f28e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000006aa75ad1fe433cf6eb83436e3fdfd02999638f2d0000000000000000000000000000000000000000000000000000000000000013416e6963686573732050656e6775426f61726400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a50454e4755424f41524400000000000000000000000000000000000000000000

Deployed Bytecode

0x00010000000000020009000000000002000000000301034f00000000000103550000008004000039000000400040043f000000600110027000000377011001970000000100200190000000ad0000c13d000000040010008c000000cc0000413d000000000203043b000000e002200270000003960020009c000001150000a13d000003970020009c000001950000a13d000003980020009c000001cc0000a13d000003990020009c0000031e0000a13d0000039a0020009c000007b30000613d0000039b0020009c000004c10000613d0000039c0020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d000000440010008c000000cc0000413d0000000402300370000000000202043b0000037a0020009c000000cc0000213d0000002304200039000000000014004b000000cc0000813d0000000404200039000000000443034f000000000404043b000600000004001d0000037a0040009c000000cc0000213d000500240020003d000000060200002900000005022002100000000502200029000000000012004b000000cc0000213d0000002402300370000000000402043b0000037a0040009c000000cc0000213d0000002302400039000000000012004b000000cc0000813d0000000402400039000000000223034f000000000202043b0000037a0020009c000000cc0000213d000400240040003d00000005032002100000000403300029000000000013004b000000cc0000213d00000000010004110000037f011001970000038e03000041000000000303041a0000037f03300197000000000031004b000008490000c13d000000060020006b00000ac20000c13d000000060000006b0000078b0000613d0000000003000019000000050130021000000005021000290000000002200367000000000202043b000800000002001d0000037f0020009c000000cc0000213d000700000003001d00000004011000290000000001100367000000000101043b000900000001001d000003c7010000410000000000100443000000000100041000000004001004430000000001000414000003770010009c0000037701008041000000c001100210000003c8011001c70000800a020000390dd70dd20000040f0000000100200190000009610000613d000000000101043b0000000903000029000000000031004b00000b420000413d00000008010000290000037f041001970000000001000414000003770010009c0000037701008041000000c001100210000000000003004b0000007a0000613d0000038f011001c7000080090200003900000000050000190000007b0000013d00000000020400190dd70dcd0000040f0000006003100270000003770530019800000080040000390000006003000039000000a60000613d0000001f0350003900000378033001970000003f03300039000003c904300197000000400300043d0000000004430019000000000034004b000000000600003900000001060040390000037a0040009c0000010f0000213d00000001006001900000010f0000c13d000000400040043f000000000453043600000379075001980000000006740019000000990000613d000000000801034f0000000009040019000000008a08043c0000000009a90436000000000069004b000000950000c13d0000001f05500190000000a60000613d000000000171034f0000000305500210000000000706043300000000075701cf000000000757022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000171019f0000000000160435000000010020019000000b4d0000613d00000007030000290000000103300039000000060030006c000000510000413d0000078b0000013d0000000002000416000000000002004b000000cc0000c13d0000001f0210003900000378022001970000008002200039000000400020043f0000001f0510018f00000379061001980000008002600039000000bd0000613d000000000703034f000000007807043c0000000004840436000000000024004b000000b90000c13d000000000005004b000000ca0000613d000000000363034f0000000304500210000000000502043300000000054501cf000000000545022f000000000303043b0000010004400089000000000343022f00000000034301cf000000000353019f0000000000320435000000600010008c000000ce0000813d000000000100001900000dd900010430000000800400043d0000037a0040009c000000cc0000213d00000080021000390000009f03400039000000000023004b000000cc0000813d000000800340003900000000030304330000037b0030009c0000010f0000813d0000001f053000390000037c055001970000003f055000390000037d05500197000000400700043d0000000005570019000000000075004b000000000600003900000001060040390000037a0050009c0000010f0000213d00000001006001900000010f0000c13d000000400050043f000900000007001d0000000005370436000800000005001d000000a0044000390000000005430019000000000025004b000000cc0000213d000000000003004b0000000808000029000000f90000613d000000000500001900000000065800190000000007450019000000000707043300000000007604350000002005500039000000000035004b000000f20000413d000000090330002900000020033000390000000000030435000000a00300043d0000037a0030009c000000cc0000213d0000001f04300039000000000014004b00000000010000190000037e010080410000037e04400197000000000004004b00000000050000190000037e050040410000037e0040009c000000000501c019000000000005004b000000cc0000c13d000000800130003900000000010104330000037a0010009c000007fb0000a13d0000039401000041000000000010043f0000004101000039000000040010043f000003950100004100000dd900010430000003af0020009c000001240000213d000003bb0020009c000002ab0000213d000003c10020009c000003290000213d000003c40020009c000004e20000613d000003c50020009c000000cc0000c13d0000000001000416000000000001004b000000cc0000c13d0000038301000041000003030000013d000003b00020009c000002c10000213d000003b60020009c000003500000213d000003b90020009c000004f00000613d000003ba0020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000101043b000900000001001d000003d401000041000000000010043f000003d101000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b00000000020004110000037f02200197000800000002001d000000000020043f000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a000000ff00100190000002f10000613d0000000901000029000000000010043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a0008037f0010019c000007640000613d0000000901000029000000000010043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000003f602000041000000000021041b0000000801000029000000000010043f000003ec01000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000201041a000000010220008a000000000021041b0000000001000414000003770010009c0000037701008041000000c0011002100000038f011001c70000800d020000390000000403000039000003ef040000410000000805000029000000000600001900000009070000290dd70dcd0000040f0000000100200190000000cc0000613d0000078b0000013d000003a40020009c000002f90000213d000003aa0020009c0000048c0000213d000003ad0020009c000007c40000613d000003ae0020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000101043b000900000001001d000000000010043f000003d101000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b00000000020004110000037f02200197000800000002001d000000000020043f000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a000000ff00100190000008860000c13d000003e701000041000000000010043f0000000901000029000000040010043f0000000001000411000000240010043f000003cd0100004100000dd9000104300000039f0020009c000003130000213d000003a20020009c0000058c0000613d000003a30020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d000000640010008c000000cc0000413d0000000402300370000000000202043b0000037a0020009c000000cc0000213d0000002304200039000000000014004b000000cc0000813d0000000404200039000000000443034f000000000404043b000700000004001d0000037a0040009c000000cc0000213d000300240020003d000000070200002900000005022002100000000302200029000000000012004b000000cc0000213d0000002402300370000000000402043b0000037a0040009c000000cc0000213d0000002302400039000000000012004b000000cc0000813d0000000402400039000000000223034f000000000202043b0000037a0020009c000000cc0000213d000200240040003d00000005042002100000000204400029000000000014004b000000cc0000213d0000004404300370000000000404043b0000037a0040009c000000cc0000213d0000002305400039000000000015004b000000cc0000813d0000000405400039000000000353034f000000000303043b0000037a0030009c000000cc0000213d000100240040003d00000005043002100000000104400029000000000014004b000000cc0000213d00000000010004110000037f011001970000038e04000041000000000404041a0000037f04400197000000000041004b000008490000c13d000000070020006b00000ac20000c13d000000070030006b00000ac20000c13d000000070000006b0000078b0000613d00000080030000390000008405000039000800000000001d0000000802000029000000070020006c00000bbd0000813d00000005042002100000000201400029000400000001001d0000000001100367000000000201043b0000037f0020009c000000cc0000213d000600000004001d000003d801000041000000000013043500000380010000410000000000150435000003770030009c0000037701000041000000000103401900000040011002100000000004000414000003770040009c0000037704008041000000c004400210000000000114019f00000395011001c7000500000002001d000900000003001d0dd70dd20000040f000000090a00002900000060031002700000037703300197000000200030008c00000020040000390000000004034019000000200640019000000000056a0019000002470000613d000000000701034f00000000080a0019000000007907043c0000000008980436000000000058004b000002430000c13d0000001f07400190000002540000613d000000000661034f0000000307700210000000000805043300000000087801cf000000000878022f000000000606043b0000010007700089000000000676022f00000000067601cf000000000686019f0000000000650435000000010020019000000bcf0000613d0000001f01400039000000600110018f0000000001a100190000037a0010009c0000010f0000213d000000400010043f000000200030008c00000006030000290000000502000029000000cc0000413d00000000010a0433000000010010008c000000cc0000213d000000000001004b00000bdb0000613d00000000010003670000000402100360000000000402043b0000037f0040009c000000cc0000213d0000000302300029000000000221034f000000000202043b000900000002001d0000037f0020009c000000cc0000213d0000000102300029000000000121034f000000000101043b000500000001001d000003d9010000410000000000100443000600000004001d00000004004004430000000001000414000003770010009c0000037701008041000000c001100210000003c8011001c700008002020000390dd70dd20000040f0000000100200190000009610000613d000000000101043b000000000001004b0000000602000029000000cc0000613d000000400400043d000000440140003900000005030000290000000000310435000000240140003900000009030000290000000000310435000003da01000041000000000014043500000004034000390000000001000410000500000003001d0000000000130435000003770040009c000900000004001d0000037701000041000000000104401900000040011002100000000003000414000003770030009c0000037703008041000000c003300210000000000113019f000003db011001c70dd70dcd0000040f000000010020019000000bdd0000613d00000009030000290000037a0030009c0000010f0000213d000000400030043f00000008020000290000000102200039000800000002001d000000070020006c00000005050000290000021c0000413d0000078b0000013d000003bc0020009c000003660000213d000003bf0020009c000006540000613d000003c00020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d0dd70c0b0000040f000900000001001d000800000002001d0000000001030019000700000003001d0dd70c1d0000040f00000000010004110000000902000029000000080300002900000007040000290dd70c910000040f000000000100001900000dd80001042e000003b10020009c000003d20000213d000003b40020009c000006590000613d000003b50020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000101043b000900000001001d0000037f0010009c000000cc0000213d000003d401000041000000000010043f000003d101000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b00000000020004110000037f02200197000800000002001d000000000020043f000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a000000ff00100190000009620000c13d000003e701000041000000000010043f000003d401000041000000040010043f0000000801000029000000240010043f000003cd0100004100000dd900010430000003a50020009c000004a60000213d000003a80020009c000007cb0000613d000003a90020009c000000cc0000c13d0000000001000416000000000001004b000000cc0000c13d00000388010000410dd70d5f0000040f0000002002000039000000400300043d000900000003001d00000000022304360dd70bf90000040f00000009020000290000000001210049000003770010009c00000377010080410000006001100210000003770020009c00000377020080410000004002200210000000000121019f00000dd80001042e000003a00020009c000006630000613d000003a10020009c000000cc0000c13d0000000001000416000000000001004b000000cc0000c13d000003d401000041000000800010043f000003cf0100004100000dd80001042e0000039d0020009c000006ed0000613d0000039e0020009c000000cc0000c13d0000000001000416000000000001004b000000cc0000c13d000003d001000041000000800010043f000003cf0100004100000dd80001042e000003c20020009c0000074c0000613d000003c30020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d000000440010008c000000cc0000413d0000000401300370000000000101043b000900000001001d0000037f0010009c000000cc0000213d0000002401300370000000000101043b000800000001001d000000000010043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a0000037f03100198000008b90000c13d000003d701000041000000000010043f0000000801000029000000040010043f000003950100004100000dd900010430000003b70020009c0000076a0000613d000003b80020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000101043b00000000020004110000037f022001970000038e03000041000000000303041a0000037f03300197000000000032004b000007f60000c13d000003f20010009c0000084e0000413d000003f3020000410000084a0000013d000003bd0020009c0000078d0000613d000003be0020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d000000440010008c000000cc0000413d0000000401300370000000000101043b000900000001001d0000002401300370000000000101043b000800000001001d0000037f0010009c000000cc0000213d00000000010004110000037f011001970000038e02000041000000000202041a0000037f02200197000000000021004b000008490000c13d000700000002001d0000000901000029000000000010043f000003d101000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b0000000802000029000000000020043f000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a000000ff001001900000078b0000c13d0000000901000029000000000010043f000003d101000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b0000000802000029000000000020043f000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000201041a000004010220019700000001022001bf000000000021041b000000400100043d00000040021000390000000703000029000000000032043500000020021000390000000803000029000000000032043500000009020000290000000000210435000003770010009c000003770100804100000040011002100000000002000414000003770020009c0000037702008041000000c002200210000000000112019f000003d2011001c70000800d020000390000000103000039000003f804000041000007880000013d000003b20020009c000007a40000613d000003b30020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d000000640010008c000000cc0000413d0000000402300370000000000202043b0000037a0020009c000000cc0000213d0000002304200039000000000014004b000000cc0000813d0000000404200039000000000443034f000000000404043b000800000004001d0000037a0040009c000000cc0000213d000600240020003d000000080200002900000005022002100000000602200029000000000012004b000000cc0000213d0000002402300370000000000402043b0000037a0040009c000000cc0000213d0000002302400039000000000012004b000000cc0000813d0000000402400039000000000223034f000000000202043b0000037a0020009c000000cc0000213d000500240040003d00000005042002100000000504400029000000000014004b000000cc0000213d0000004404300370000000000404043b0000037a0040009c000000cc0000213d0000002305400039000000000015004b000000cc0000813d0000000405400039000000000353034f000000000303043b0000037a0030009c000000cc0000213d000400240040003d00000005043002100000000404400029000000000014004b000000cc0000213d00000000010004110000037f011001970000038e04000041000000000404041a0000037f04400197000000000041004b000008490000c13d000000080020006b00000ac20000c13d000000080030006b00000ac20000c13d000000080000006b0000078b0000613d0000000004000019000004280000013d000000000100043d000000010010008c00000000010000390000000101006039000000000001004b0000048a0000613d0000000104400039000000080040006c0000078b0000813d000000080040006c00000bbd0000813d000000050140021000000005031000290000000005000367000000000335034f000000000203043b0000037f0020009c000000cc0000213d0000000603100029000000000335034f000000000303043b0000037f0030009c000000cc0000213d000900000004001d0000000401100029000000000115034f000000000501043b000000400100043d000000440410003900000000005404350000002005100039000003e80400004100000000004504350000002404100039000000000034043500000044030000390000000000310435000003e90010009c0000010f0000213d0000008003100039000000400030043f000003770050009c000003770500804100000040035002100000000001010433000003770010009c00000377010080410000006001100210000000000131019f0000000003000414000003770030009c0000037703008041000000c003300210000000000131019f000700000002001d0dd70dcd0000040f00000060031002700000037703300197000000200030008c000000200500003900000000050340190000002004500190000004640000613d000000000601034f0000000007000019000000006806043c0000000007870436000000000047004b000004600000c13d0000001f05500190000004710000613d000000000641034f0000000305500210000000000704043300000000075701cf000000000757022f000000000606043b0000010005500089000000000656022f00000000055601cf000000000575019f0000000000540435000000010020019000000bc30000613d00000007010000290000037f02100197000000000003004b00000009040000290000041f0000c13d000003d9010000410000000000100443000700000002001d00000004002004430000000001000414000003770010009c0000037701008041000000c001100210000003c8011001c700008002020000390dd70dd20000040f0000000100200190000009610000613d000000000101043b00000009040000290000000702000029000000000001004b000004250000c13d000003ea01000041000007f70000013d000003ab0020009c000007e80000613d000003ac0020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000101043b0000037f0010009c000000cc0000213d00000000020004110000037f022001970000038e03000041000000000303041a0000037f03300197000000000032004b000007f60000c13d000000000001004b000008be0000c13d000003e601000041000000000010043f000003cb0100004100000dd900010430000003a60020009c000007ed0000613d000003a70020009c000000cc0000c13d0000000002000416000000000002004b000000cc0000c13d000000440010008c000000cc0000413d0000000401300370000000000101043b000900000001001d0000037f0010009c000000cc0000213d0000002401300370000000000101043b000000000001004b0000000002000039000000010200c039000800000002001d000000010010008c000000cc0000213d0000000002000411000000090020006b000008c50000c13d000003e401000041000007f70000013d0000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000601043b0000037f0060009c000000cc0000213d0000038e01000041000000000101041a0000037f021001970000000005000411000000000025004b000008530000c13d000000000065004b0000078b0000613d0000038c01100197000000000161019f0000038e02000041000000000012041b0000000001000414000003770010009c0000037701008041000000c0011002100000038f011001c70000800d02000039000000030300003900000390040000410dd70dcd0000040f0000000100200190000000cc0000613d0000078b0000013d0000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000101043b000003e100100198000000cc0000c13d0000008002000039000004000010009c000008580000a13d00000000010000190000095a0000013d0000000002000416000000000002004b000000cc0000c13d000000640010008c000000cc0000413d0000000401300370000000000101043b000900000001001d0000037f0010009c000000cc0000213d0000002401300370000000000101043b000800000001001d0000037f0010009c000000cc0000213d0000004401300370000000000401043b0000000101000039000000000101041a0000037f02100198000700000004001d000008f90000c13d0000000001000411000000090200002900000008030000290dd70c910000040f000003d9010000410000000000100443000000080100002900000004001004430000000001000414000003770010009c0000037701008041000000c001100210000003c8011001c700008002020000390dd70dd20000040f0000000100200190000009610000613d000000000101043b000000000001004b0000078b0000613d000000400200043d000003f70020009c0000010f0000213d0000002001200039000000400010043f0000000000020435000000400500043d000000640350003900000080040000390000000000430435000000440350003900000007040000290000000000430435000000240350003900000009040000290000000000430435000003e003000041000000000035043500000000030004110000037f0330019700000004045000390000000000340435000000000202043300000084035000390000000000230435000600000005001d000000a403500039000000000002004b0000053f0000613d000000000400001900000000053400190000000006140019000000000606043300000000006504350000002004400039000000000024004b000005380000413d0000001f01200039000004020110019700000000023200190000000000020435000000a401100039000003770010009c000003770100804100000060011002100000000602000029000003770020009c00000377020080410000004002200210000000000121019f0000000002000414000003770020009c0000037702008041000000c002200210000000000112019f00000008020000290dd70dcd0000040f00000060031002700000037703300197000000200030008c000000200400003900000000040340190000001f0640018f00000020074001900000000605700029000005620000613d000000000801034f0000000609000029000000008a08043c0000000009a90436000000000059004b0000055e0000c13d000000000006004b0000056f0000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000010020019000000a830000613d0000001f01400039000000600210018f0000000601200029000000000021004b000000000200003900000001020040390000037a0010009c0000010f0000213d00000001002001900000010f0000c13d000000400010043f000000200030008c000000cc0000413d00000006010000290000000001010433000003e100100198000000cc0000c13d000003e00010009c0000078b0000613d000003e201000041000000000010043f0000000801000029000000040010043f0000000701000029000000240010043f000003cd0100004100000dd9000104300000000002000416000000000002004b000000cc0000c13d000000840010008c000000cc0000413d0000000402300370000000000202043b000900000002001d0000037f0020009c000000cc0000213d0000002402300370000000000202043b000800000002001d0000037f0020009c000000cc0000213d0000004402300370000000000202043b000700000002001d0000006402300370000000000202043b0000037a0020009c000000cc0000213d0000002304200039000000000014004b000000cc0000813d000500040020003d0000000503300360000000000303043b000600000003001d0000037a0030009c000000cc0000213d00000006022000290000002402200039000000000012004b000000cc0000213d0000000101000039000000000101041a0000037f0210019800000a070000c13d00000000010004110000000902000029000000080300002900000007040000290dd70c910000040f000003d9010000410000000000100443000000080100002900000004001004430000000001000414000003770010009c0000037701008041000000c001100210000003c8011001c700008002020000390dd70dd20000040f0000000100200190000009610000613d000000000101043b000000000001004b0000078b0000613d00000006010000290000001f011000390000037c011001970000003f011000390000037d01100197000000400300043d0000000001130019000000000031004b000000000200003900000001020040390000037a0010009c0000010f0000213d00000001002001900000010f0000c13d000000400010043f0000000604000029000000000243043600000402054001980000001f0640018f0000000004520019000000050700002900000020077000390000000007700367000005e60000613d000000000807034f0000000009020019000000008a08043c0000000009a90436000000000049004b000005e20000c13d000000000006004b000005f30000613d000000000557034f0000000306600210000000000704043300000000076701cf000000000767022f000000000505043b0000010006600089000000000565022f00000000056501cf000000000575019f000000000054043500000006042000290000000000040435000000400600043d000000640460003900000080050000390000000000540435000000440460003900000007050000290000000000540435000000240460003900000009050000290000000000540435000003e004000041000000000046043500000000040004110000037f0440019700000004056000390000000000450435000000000303043300000084046000390000000000340435000600000006001d000000a404600039000000000003004b000006140000613d000000000500001900000000064500190000000007250019000000000707043300000000007604350000002005500039000000000035004b0000060d0000413d0000001f02300039000004020120019700000000024300190000000000020435000000a401100039000003770010009c000003770100804100000060011002100000000602000029000003770020009c00000377020080410000004002200210000000000121019f0000000002000414000003770020009c0000037702008041000000c002200210000000000112019f00000008020000290dd70dcd0000040f00000060031002700000037703300197000000200030008c000000200400003900000000040340190000001f0640018f00000020074001900000000605700029000006370000613d000000000801034f0000000609000029000000008a08043c0000000009a90436000000000059004b000006330000c13d000000000006004b000006440000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000010020019000000ad50000613d0000001f01400039000000600210018f0000000601200029000000000021004b000000000200003900000001020040390000037a0010009c0000010f0000213d00000001002001900000010f0000c13d000000400010043f000000200030008c0000057e0000813d000000cc0000013d0000000001000416000000000001004b000000cc0000c13d0000000101000039000007f10000013d0000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000101043b0dd70d440000040f000000400200043d0000095b0000013d0000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000101043b000900000001001d000000000010043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a0000037f00100198000007640000613d0000038b01000041000000000201041a000000400400043d000000240140003900000009030000290000000000310435000003d601000041000000000014043500000000010004100000037f0110019700000004034000390000000000130435000003770040009c000900000004001d0000037701000041000000000104401900000040011002100000000003000414000003770030009c0000037703008041000000c003300210000000000113019f000003cd011001c70000037f022001970dd70dd20000040f00000060031002700000001f0430018f000003790530019700000377033001970000000100200190000009300000613d00000009090000290000000002590019000000000005004b000006a40000613d000000000601034f0000000007090019000000006806043c0000000007870436000000000027004b000006a00000c13d000000000004004b000006b10000613d000000000151034f0000000304400210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001204350000001f0130003900000378021001970000000001920019000000000021004b000000000200003900000001020040390000037a0010009c0000010f0000213d00000001002001900000010f0000c13d000000400010043f000000200030008c000000cc0000413d00000000020904330000037a0020009c000000cc0000213d000000000493001900000000029200190000001f03200039000000000043004b00000000050000190000037e050080410000037e033001970000037e06400197000000000763013f000000000063004b00000000030000190000037e030040410000037e0070009c000000000305c019000000000003004b000000cc0000c13d00000000320204340000037a0020009c0000010f0000213d0000001f052000390000037c055001970000003f055000390000037d0550019700000000051500190000037a0050009c0000010f0000213d000000400050043f00000000052104360000000006230019000000000046004b000000cc0000213d000000000002004b000006ea0000613d000000000400001900000000065400190000000007430019000000000707043300000000007604350000002004400039000000000024004b000006e30000413d00000000025200190000000000020435000003040000013d0000000002000416000000000002004b000000cc0000c13d000000440010008c000000cc0000413d0000000401300370000000000101043b000900000001001d0000002401300370000000000101043b000800000001001d0000037f0010009c000000cc0000213d00000000010004110000037f011001970000038e02000041000000000202041a0000037f02200197000000000021004b000008490000c13d000700000002001d0000000901000029000000000010043f000003d101000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b0000000802000029000000000020043f000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a000000ff001001900000078b0000613d0000000901000029000000000010043f000003d101000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b0000000802000029000000000020043f000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000201041a0000040102200197000000000021041b000000400100043d00000040021000390000000703000029000000000032043500000020021000390000000803000029000000000032043500000009020000290000000000210435000003770010009c000003770100804100000040011002100000000002000414000008b00000013d0000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000101043b000900000001001d000000000010043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a0000037f001001980000085c0000c13d000003d701000041000000000010043f0000000901000029000000040010043f000003950100004100000dd9000104300000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000101043b0000037f0010009c000000cc0000213d00000000020004110000037f022001970000038e03000041000000000303041a0000037f03300197000000000032004b000007f60000c13d0000037f021001970000000103000039000000000403041a0000038c04400197000000000114019f000000000013041b000000800020043f0000000001000414000003770010009c0000037701008041000000c001100210000003f4011001c70000800d02000039000003f5040000410dd70dcd0000040f0000000100200190000000cc0000613d000000000100001900000dd80001042e0000000002000416000000000002004b000000cc0000c13d000000440010008c000000cc0000413d000003e501000041000000000101041a0000002402300370000000000302043b000000000003004b0000086f0000613d0000037f0010009c0000000002000019000008780000a13d000000a004100270000003d00030009c000008710000413d000003d00330012a00000000023400a900000000033200d9000000000043004b000008780000613d000009650000013d0000000002000416000000000002004b000000cc0000c13d000000240010008c000000cc0000413d0000000401300370000000000101043b0000037f0010009c000000cc0000213d000000000001004b0000087d0000c13d000003ed01000041000000000010043f000003cb0100004100000dd9000104300000000002000416000000000002004b000000cc0000c13d000000440010008c000000cc0000413d0000000401300370000000000101043b0000037f0010009c000000cc0000213d0000002402300370000000000202043b000900000002001d0000037f0020009c000000cc0000213d000000000010043f000003ce01000041000007d90000013d0000000001000416000000000001004b000000cc0000c13d000000000100041a000000800010043f000003cf0100004100000dd80001042e0000000002000416000000000002004b000000cc0000c13d000000440010008c000000cc0000413d0000002401300370000000000101043b000900000001001d0000037f0010009c000000cc0000213d0000000401300370000000000101043b000000000010043f000003d101000041000000200010043f00000040010000390dd70dad0000040f0000000902000029000000000020043f000000200010043f00000040010000390dd70dad0000040f000000000101041a000000ff001001900000000001000039000000010100c039000000800010043f000003cf0100004100000dd80001042e0000000001000416000000000001004b000000cc0000c13d0000038e01000041000007f10000013d0000000001000416000000000001004b000000cc0000c13d0000038b01000041000000000101041a0000037f01100197000000800010043f000003cf0100004100000dd80001042e000003c601000041000000000010043f000000040020043f000003950100004100000dd9000104300000001f041000390000037c044001970000003f044000390000037d04400197000000400600043d0000000004460019000000000064004b000000000500003900000001050040390000037a0040009c0000010f0000213d00000001005001900000010f0000c13d000000400040043f000700000006001d0000000004160436000600000004001d000000a0033000390000000004310019000000000024004b000000cc0000213d000000000001004b00000006060000290000081b0000613d000000000200001900000000042600190000000005320019000000000505043300000000005404350000002002200039000000000012004b000008140000413d000000070110002900000020011000390000000000010435000000c00100043d000500000001001d0000037f0010009c000000cc0000213d0000038001000041000000000010043f0000038101000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000201041a000004010220019700000001022001bf000000000021041b00000009010000290000000001010433000400000001001d0000037a0010009c0000010f0000213d0000038301000041000000000101041a000000010210019000000001011002700000007f0110618f0000001f0010008c00000000030000390000000103002039000000000023004b00000a8f0000613d0000039401000041000000000010043f0000002201000039000000040010043f000003950100004100000dd900010430000003c602000041000000000020043f000000040010043f000003950100004100000dd900010430000000a001100210000003e502000041000000000302041a0000037f03300197000008c10000013d000003c601000041000000000010043f000000040050043f000003950100004100000dd900010430000003d80010009c000009480000c13d00000001010000390000095a0000013d000003fc0010019800000000010000190000086d0000613d000003fd01000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a0000037f01100197000000400200043d0000095b0000013d0000000002000019000008780000013d00000000023400a9000003f903300197000003f90220019700000000033200d9000000000034004b000009650000c13d000003d00220012a0000037f01100197000000800010043f000000a00020043f000003fa0100004100000dd80001042e000000000010043f000003ec01000041000000200010043f00000040010000390dd70dad0000040f000000000101041a000000800010043f000003cf0100004100000dd80001042e0000000901000029000000000010043f000003d101000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b0000000802000029000000000020043f000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000201041a0000040102200197000000000021041b000000400100043d0000004002100039000000080300002900000000003204350000002002100039000000000032043500000009020000290000000000210435000003770010009c000003770100804100000040011002100000000002000414000003770020009c0000037702008041000000c002200210000000000112019f000003d2011001c70000800d020000390000000103000039000003d304000041000007880000013d0000000902000029000000000032004b0000096b0000c13d000003ff01000041000007f70000013d000003e502000041000000000302041a0000038c03300197000000000113019f000000000012041b000000000100001900000dd80001042e0000037f01200197000000000010043f000003ce01000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b0000000902000029000000000020043f000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000201041a00000401022001970000000803000029000000000232019f000000000021041b000000400100043d0000000000310435000003770010009c000003770100804100000040011002100000000002000414000003770020009c0000037702008041000000c002200210000000000112019f00000386011001c70000800d020000390000000303000039000003e304000041000000000500041100000009060000290dd70dcd0000040f0000000100200190000000cc0000613d0000078b0000013d000003dd01000041000000800010043f000000840040043f0000000001000414000003770010009c0000037701008041000000c001100210000003de011001c70dd70dd20000040f00000060031002700000037703300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000080057001bf000009110000613d0000008008000039000000000901034f000000009a09043c0000000008a80436000000000058004b0000090d0000c13d000000000006004b0000091e0000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f00000000006504350000000100200190000009840000613d0000001f01400039000000600110018f00000080011001bf000000400010043f000000200030008c000000cc0000413d000000800100043d000000000001004b0000000704000029000005060000613d000003df02000041000000000020043f000000040040043f000000240010043f000003cd0100004100000dd900010430000000400200043d0000000006520019000000000005004b0000093a0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000068004b000009360000c13d000000000004004b0000099c0000613d000000000151034f0000000304400210000000000506043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001604350000099c0000013d000000000010043f0000038101000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000400200043d000000000101043b000000000101041a000000ff001001900000000001000039000000010100c039000000010110018f0000000000120435000003770020009c00000377020080410000004001200210000003f0011001c700000dd80001042e000000000001042f000000000200041a000000010120003a000009a20000c13d0000039401000041000000000010043f0000001101000039000000040010043f000003950100004100000dd90001043000000000040004110006037f0040019b000000060030006c000700000003001d000009a90000c13d000000000002004b000009d30000c13d000003fc00100198000009f80000613d0000000801000029000000000010043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b0000000702000029000009f70000013d0000001f0530018f0000037906300198000000400200043d00000000046200190000098f0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b0000098b0000c13d000000000005004b0000099c0000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000006001300210000003770020009c00000377020080410000004002200210000000000112019f00000dd900010430000000000010041b000000090000006b00000a3f0000c13d000003f101000041000000000010043f000003cb0100004100000dd900010430000500000001001d000000000030043f000003ce01000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b0000000602000029000000000020043f000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a000000ff00100190000000090200002900000007030000290000000501000029000009700000c13d000003fb01000041000000000010043f0000000001000411000000040010043f000000240030043f0000000801000029000000440010043f000003db0100004100000dd900010430000603fc001001cb000000060010006c000009e60000613d0000000801000029000000000010043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b0000000602000029000000000021041b0000000801000029000000000010043f000003fd01000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000201041a0000038c0220019700000009022001af000000000021041b0000000001000414000003770010009c0000037701008041000000c0011002100000038f011001c70000800d020000390000000403000039000003fe040000410000000705000029000000090600002900000008070000290dd70dcd0000040f0000000100200190000000cc0000613d0000078b0000013d000003dd01000041000000800010043f0000000701000029000000840010043f0000000001000414000003770010009c0000037701008041000000c001100210000003de011001c70dd70dd20000040f00000060031002700000037703300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000080057001bf00000a200000613d0000008008000039000000000901034f000000009a09043c0000000008a80436000000000058004b00000a1c0000c13d000000000006004b00000a2d0000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000010020019000000ab60000613d0000001f01400039000000600110018f00000080011001bf000000400010043f000000200030008c000000cc0000413d000000800100043d000000000001004b000005b30000613d000003df02000041000000000020043f0000000702000029000000040020043f000000240010043f000003cd0100004100000dd900010430000800000002001d000000000020043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000101041a0000037f0010019800000ad30000c13d0000000801000029000000000010043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b0000000902000029000000000021041b000000000020043f000003ec01000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000201041a0000000102200039000000000021041b000000400100043d000700000001001d0000000001000414000003770010009c0000037701008041000000c0011002100000038f011001c70000800d020000390000000403000039000003ef040000410000000005000019000000090600002900000008070000290dd70dcd0000040f0000000100200190000000cc0000613d000000080100002900000007020000290000095b0000013d0000001f0530018f0000037906300198000000400200043d00000000046200190000098f0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00000a8a0000c13d0000098f0000013d000000200010008c00000aa20000413d0000038302000041000000000020043f00000004030000290000001f023000390000000502200270000003840220009a000000200030008c00000385020040410000001f011000390000000501100270000003840110009a000000000012004b00000aa20000813d000000000002041b0000000102200039000000000012004b00000a9e0000413d00000004010000290000001f0010008c00000ac60000a13d0000038301000041000000000010043f0000000001000414000003770010009c0000037701008041000000c00110021000000386011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d00000004020000290000038702200198000000000101043b00000ae10000c13d000000200300003900000aee0000013d0000001f0530018f0000037906300198000000400200043d00000000046200190000098f0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00000abd0000c13d0000098f0000013d000003eb01000041000000000010043f000003cb0100004100000dd900010430000000040000006b000000000100001900000acb0000613d0000000801000029000000000101043300000004040000290000000302400210000004030220027f0000040302200167000000000121016f0000000102400210000000000121019f00000afc0000013d000003ee010000410000034b0000013d0000001f0530018f0000037906300198000000400200043d00000000046200190000098f0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00000adc0000c13d0000098f0000013d000000010320008a0000000503300270000000000431001900000020030000390000000104400039000000090600002900000000056300190000000005050433000000000051041b00000020033000390000000101100039000000000041004b00000ae70000c13d000000040020006c00000af90000613d00000004020000290000000302200210000000f80220018f000004030220027f000004030220016700000009033000290000000003030433000000000223016f000000000021041b0000000401000029000000010110021000000001011001bf0000038302000041000000000012041b00000007010000290000000001010433000900000001001d0000037a0010009c0000010f0000213d0000038801000041000000000201041a000000010020019000000001012002700000007f0110618f0000001f0010008c00000000030000390000000103002039000000000223013f0000000100200190000008430000c13d000000200010008c00000b210000413d0000038802000041000000000020043f00000009030000290000001f023000390000000502200270000003890220009a000000200030008c0000038a020040410000001f011000390000000501100270000003890110009a000000000012004b00000b210000813d000000000002041b0000000102200039000000000012004b00000b1d0000413d00000009010000290000001f0010008c00000b350000a13d0000038801000041000000000010043f0000000001000414000003770010009c0000037701008041000000c00110021000000386011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d00000009020000290000038702200198000000000101043b00000b540000c13d000000200300003900000b610000013d000000090000006b000000000100001900000b3a0000613d0000000601000029000000000101043300000009040000290000000302400210000004030220027f0000040302200167000000000121016f0000000102400210000000000121019f00000b6f0000013d0000800a01000039000003c70200004100000000030004100dd70dbe0000040f000003cc02000041000000000020043f000000040010043f0000000901000029000000240010043f000003cd0100004100000dd9000104300000000001030433000000000001004b00000bb50000c13d000003ca01000041000000000010043f000003cb0100004100000dd900010430000000010320008a0000000503300270000000000431001900000020030000390000000104400039000000070600002900000000056300190000000005050433000000000051041b00000020033000390000000101100039000000000041004b00000b5a0000c13d000000090020006c00000b6c0000613d00000009020000290000000302200210000000f80220018f000004030220027f000004030220016700000007033000290000000003030433000000000223016f000000000021041b0000000901000029000000010110021000000001011001bf0000038802000041000000000012041b0000038b01000041000000000201041a0000038c0220019700000005022001af000000000021041b0000038d01000041000000000010043f0000038101000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f0000000100200190000000cc0000613d000000000101043b000000000201041a000004010220019700000001022001bf000000000021041b0000000006000411000000000006004b00000b9c0000613d0000038e01000041000000000201041a0000038c02200197000000000262019f000000000021041b0000000001000414000003770010009c0000037701008041000000c0011002100000038f011001c70000800d020000390000000303000039000003900400004100000000050000190dd70dcd0000040f0000000100200190000000cc0000613d0000039101000041000000000010043f0000038101000041000000200010043f00000040010000390dd70dad0000040f000000000201041a000004010220019700000001022001bf000000000021041b0000039201000041000000000010043f0000038101000041000000200010043f00000040010000390dd70dad0000040f000000000201041a000004010220019700000001022001bf000000000021041b000000200100003900000100001004430000012000000443000003930100004100000dd80001042e000003770040009c00000377040080410000004002400210000003770010009c00000377010080410000006001100210000000000121019f00000dd9000104300000039401000041000000000010043f0000003201000039000000040010043f000003950100004100000dd9000104300000001f0530018f0000037906300198000000400200043d00000000046200190000098f0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00000bca0000c13d0000098f0000013d0000001f0530018f0000037906300198000000400200043d00000000046200190000098f0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00000bd60000c13d0000098f0000013d000003dc01000041000007f70000013d00000060061002700000001f0460018f0000037905600198000000400200043d000000000352001900000be90000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b00000be50000c13d0000037706600197000000000004004b00000bf70000613d000000000151034f0000000304400210000000000503043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f000000000013043500000060016002100000099d0000013d00000000430104340000000001320436000000000003004b00000c050000613d000000000200001900000000051200190000000006240019000000000606043300000000006504350000002002200039000000000032004b00000bfe0000413d000000000213001900000000000204350000001f0230003900000402022001970000000001210019000000000001042d000004040010009c00000c1b0000213d000000630010008c00000c1b0000a13d00000000030003670000000401300370000000000101043b0000037f0010009c00000c1b0000213d0000002402300370000000000202043b0000037f0020009c00000c1b0000213d0000004403300370000000000303043b000000000001042d000000000100001900000dd90001043000020000000000020000000102000039000000000202041a0000037f0220019800000c640000613d000000400400043d000200000004001d000003dd0300004100000000003404350000000403400039000100000001001d0000000000130435000003770040009c0000037701000041000000000104401900000040011002100000000003000414000003770030009c0000037703008041000000c003300210000000000113019f00000395011001c70dd70dd20000040f000000020b00002900000060031002700000037703300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000000057b001900000c440000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b00000c400000c13d000000000006004b00000c510000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000010020019000000c650000613d0000001f01400039000000600210018f0000000001b20019000000000021004b000000000200003900000001020040390000037a0010009c00000c830000213d000000010020019000000c830000c13d000000400010043f000000200030008c00000c890000413d00000000010b0433000000000001004b000000010300002900000c8b0000c13d000000000001042d0000001f0530018f0000037906300198000000400200043d000000000462001900000c700000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00000c6c0000c13d000000000005004b00000c7d0000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000006001300210000003770020009c00000377020080410000004002200210000000000112019f00000dd9000104300000039401000041000000000010043f0000004101000039000000040010043f000003950100004100000dd900010430000000000100001900000dd900010430000003df02000041000000000020043f000000040030043f000000240010043f000003cd0100004100000dd9000104300006000000000002000400000002001d000300000001001d0006037f0030019c00000d2a0000613d000500000004001d000000000040043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f000000010020019000000d280000613d000000000101043b000000000301041a0000037f0230019800000d2e0000613d00000004010000290000037f01100197000000000012004b00000d340000c13d00000003010000290002037f0010019b000000020020006c000400000002001d00000ce50000613d000100000003001d000000000020043f000003ce01000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f000000010020019000000d280000613d000000000101043b0000000202000029000000000020043f000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f000000010020019000000d280000613d000000000101043b000000000101041a000000ff001001900000000402000029000000010100002900000ce50000c13d000003fc0010019800000d3b0000613d0000000501000029000000000010043f000003fd01000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f000000010020019000000d280000613d000000000101043b000000000101041a000000030110014f0000037f00100198000000040200002900000d3b0000c13d0000000501000029000000000010043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f000000010020019000000d280000613d000000000101043b0000000602000029000000000021041b0000000405000029000000000025004b00000d1a0000613d000000000050043f000003ec01000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f000000010020019000000d280000613d000000000101043b000000000201041a000000010220008a000000000021041b0000000601000029000000000010043f000003ec01000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f000000010020019000000d280000613d000000000101043b000000000201041a0000000102200039000000000021041b00000004050000290000000001000414000003770010009c0000037701008041000000c0011002100000038f011001c70000800d020000390000000403000039000003ef04000041000000060600002900000005070000290dd70dcd0000040f000000010020019000000d280000613d000000000001042d000000000100001900000dd9000104300000040701000041000000000010043f000003cb0100004100000dd900010430000003d701000041000000000010043f0000000501000029000000040010043f000003950100004100000dd9000104300000040502000041000000000020043f000000040010043f0000000501000029000000240010043f000003cd0100004100000dd9000104300000040601000041000000000010043f0000000201000029000000040010043f000000240020043f0000000501000029000000440010043f000003db0100004100000dd9000104300001000000000002000100000001001d000000000010043f000003d501000041000000200010043f0000000001000414000003770010009c0000037701008041000000c00110021000000382011001c700008010020000390dd70dd20000040f000000010020019000000d570000613d000000000101043b000000000101041a0000037f0110019800000d590000613d000000000001042d000000000100001900000dd900010430000003d701000041000000000010043f0000000101000029000000040010043f000003950100004100000dd9000104300003000000000002000000000201041a000000010320019000000001062002700000007f0660618f0000001f0060008c00000000040000390000000104002039000000000034004b00000d9e0000c13d000000400500043d0000000004650436000000000003004b00000d890000613d000100000004001d000200000006001d000300000005001d000000000010043f0000000001000414000003770010009c0000037701008041000000c00110021000000386011001c700008010020000390dd70dd20000040f000000010020019000000daa0000613d0000000206000029000000000006004b00000d8f0000613d000000000201043b0000000001000019000000030500002900000001070000290000000003170019000000000402041a000000000043043500000001022000390000002001100039000000000061004b00000d810000413d00000d910000013d00000401012001970000000000140435000000000006004b0000002001000039000000000100603900000d910000013d000000000100001900000003050000290000003f0110003900000402021001970000000001520019000000000021004b000000000200003900000001020040390000037a0010009c00000da40000213d000000010020019000000da40000c13d000000400010043f0000000001050019000000000001042d0000039401000041000000000010043f0000002201000039000000040010043f000003950100004100000dd9000104300000039401000041000000000010043f0000004101000039000000040010043f000003950100004100000dd900010430000000000100001900000dd900010430000000000001042f000003770010009c000003770100804100000060011002100000000002000414000003770020009c0000037702008041000000c002200210000000000112019f0000038f011001c700008010020000390dd70dd20000040f000000010020019000000dbc0000613d000000000101043b000000000001042d000000000100001900000dd9000104300000000004010019000000000020044300000004003004430000000001000414000003770010009c0000037701008041000000c001100210000003c8011001c700000000020400190dd70dd20000040f000000010020019000000dcc0000613d000000000101043b000000000001042d000000000001042f00000dd0002104210000000102000039000000000001042d0000000002000019000000000001042d00000dd5002104230000000102000039000000000001042d0000000002000019000000000001042d00000dd70000043200000dd80001042e00000dd9000104300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000001ffffffe000000000000000000000000000000000000000000000000000000000ffffffe0000000000000000000000000000000000000000000000000ffffffffffffffff0000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffe0000000000000000000000000000000000000000000000003ffffffffffffffe08000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffff80ac58cd00000000000000000000000000000000000000000000000000000000ca9d3e17f264b0f3984e2634e94adb37fa3e6a8103f06aeae6fa59e21c769f5d02000000000000000000000000000000000000400000000000000000000000007ea68fd2dcf1c056b94db6a0a537aa80d26fa9ab1eacd85da1ba0b61b7e7a8a4a9a21f304475c08f36c724e503aa9d96f0083aa88c38dcb59b6cff0a71c4cf44565de0cfbb8a3f70c938db1afc5562690ff7c55773c7234a649300f58e3b30bc0200000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffe07ea68fd2dcf1c056b94db6a0a537aa80d26fa9ab1eacd85da1ba0b61b7e7a8a53c6452c24a2a923307c64375868a64ce6d4688a8266c0899c366670b0410375fc39bad3db5d56dccf839bc8a79759b3192b97757d993f7663c9998f4fbefc8a17ea68fd2dcf1c056b94db6a0a537aa80d26fa9ab1eacd85da1ba0b61b7e7a8a6ffffffffffffffffffffffff00000000000000000000000000000000000000005b5e139f00000000000000000000000000000000000000000000000000000000c9ed16f33ab3a66c84bfd83099ccb2a8845871e2e1c1928f63797152f0fd54cc02000000000000000000000000000000000000000000000000000000000000008be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e07f5828d0000000000000000000000000000000000000000000000000000000002a55205a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000400000010000000000000000004e487b710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000000000000000000000075794a3b00000000000000000000000000000000000000000000000000000000b88d4fdd00000000000000000000000000000000000000000000000000000000d547741e00000000000000000000000000000000000000000000000000000000e985e9c400000000000000000000000000000000000000000000000000000000e985e9c500000000000000000000000000000000000000000000000000000000f2fde38b00000000000000000000000000000000000000000000000000000000f7ba94bd00000000000000000000000000000000000000000000000000000000d547741f00000000000000000000000000000000000000000000000000000000e1a8bf2c00000000000000000000000000000000000000000000000000000000c87b56dc00000000000000000000000000000000000000000000000000000000c87b56dd00000000000000000000000000000000000000000000000000000000d539139300000000000000000000000000000000000000000000000000000000b88d4fde00000000000000000000000000000000000000000000000000000000c3666c360000000000000000000000000000000000000000000000000000000091d1485300000000000000000000000000000000000000000000000000000000a0c76f6100000000000000000000000000000000000000000000000000000000a0c76f6200000000000000000000000000000000000000000000000000000000a22cb4650000000000000000000000000000000000000000000000000000000091d148540000000000000000000000000000000000000000000000000000000095d89b41000000000000000000000000000000000000000000000000000000008da5cb5a000000000000000000000000000000000000000000000000000000008da5cb5b000000000000000000000000000000000000000000000000000000008dc251e30000000000000000000000000000000000000000000000000000000075794a3c000000000000000000000000000000000000000000000000000000008bb9c5bf0000000000000000000000000000000000000000000000000000000042842e0d000000000000000000000000000000000000000000000000000000006352211d0000000000000000000000000000000000000000000000000000000070a082300000000000000000000000000000000000000000000000000000000070a082310000000000000000000000000000000000000000000000000000000073c8a958000000000000000000000000000000000000000000000000000000006352211e000000000000000000000000000000000000000000000000000000006a6278420000000000000000000000000000000000000000000000000000000053f92e300000000000000000000000000000000000000000000000000000000053f92e310000000000000000000000000000000000000000000000000000000061ba27da0000000000000000000000000000000000000000000000000000000042842e0e0000000000000000000000000000000000000000000000000000000042966c68000000000000000000000000000000000000000000000000000000001548ed18000000000000000000000000000000000000000000000000000000002a552059000000000000000000000000000000000000000000000000000000002a55205a000000000000000000000000000000000000000000000000000000002f2ff15d000000000000000000000000000000000000000000000000000000001548ed190000000000000000000000000000000000000000000000000000000023b872dd00000000000000000000000000000000000000000000000000000000081812fb00000000000000000000000000000000000000000000000000000000081812fc00000000000000000000000000000000000000000000000000000000095ea7b30000000000000000000000000000000000000000000000000000000001ffc9a70000000000000000000000000000000000000000000000000000000006fdde032ef4875e000000000000000000000000000000000000000000000000000000009cc7f708afc65944829bd487b90b72536b1951864fbfc14e125fc972a6507f39020000020000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffe0d6bda275000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cf479181000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044000000000000000000000000ddf3ee18ae6d688373c219468f8be446e7ae82215f4779d821c7ea5e8c13c0c3000000000000000000000000000000000000002000000080000000000000000000000000000000000000000000000000000000000000000000000000000186a0c8827d3282af6f37b64c3e9e6f3ac9df286ab0bb0fccd6f8661bf19adb368b220200000000000000000000000000000000000060000000000000000000000000f6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b6d696e7465720000000000000000000000000000000000000000000000000000ddf3ee18ae6d688373c219468f8be446e7ae82215f4779d821c7ea5e8c13c0c0f724dad700000000000000000000000000000000000000000000000000000000dd5604750000000000000000000000000000000000000000000000000000000001ffc9a7000000000000000000000000000000000000000000000000000000001806aa1896bbf26568e884a7374b41e002500962caba6a15023a8d90e8508b8342842e0e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000986b9f1f000000000000000000000000000000000000000000000000000000000ac7843c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000800000000000000000f11294ae00000000000000000000000000000000000000000000000000000000150b7a020000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffc215a7a10000000000000000000000000000000000000000000000000000000017307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31e4215c6a000000000000000000000000000000000000000000000000000000002c0cf10337caabbd02dcf226f05f5fd19a0919a41a8df8958c39b8000782685716de0c80000000000000000000000000000000000000000000000000000000007aa7288200000000000000000000000000000000000000000000000000000000a9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff7f5274afe7000000000000000000000000000000000000000000000000000000006582533600000000000000000000000000000000000000000000000000000000ddf3ee18ae6d688373c219468f8be446e7ae82215f4779d821c7ea5e8c13c0c1aefbce4e00000000000000000000000000000000000000000000000000000000753eceac00000000000000000000000000000000000000000000000000000000ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef00000000000000000000000000000000000000200000000000000000000000007851b9cd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a1ac04040800000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000002000000080000000000000000033e85773eda69c91a470cca572dc936a94f4c242cbcbe59f4c9d8b77c16a7ddadead000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffdf2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d00000000000000000000000000000000ffffffffffffffffffffffffffffffff0000000000000000000000000000000000000040000000800000000000000000c7563a18000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000ddf3ee18ae6d688373c219468f8be446e7ae82215f4779d821c7ea5e8c13c0c28c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925b7fdc01000000000000000000000000000000000000000000000000000000000fffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14dbb53600000000000000000000000000000000000000000000000000000000a4a3018b0000000000000000000000000000000000000000000000000000000086c8706700000000000000000000000000000000000000000000000000000000000000000000000000a26469706673582212203ee34e29b4bb3c1c20c112fda5140a1b2a943ac2e0e1f9984d0b0a6de12184e064736f6c6378247a6b736f6c633a312e352e31353b736f6c633a302e382e32383b6c6c766d3a312e302e320055

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

000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000006aa75ad1fe433cf6eb83436e3fdfd02999638f2d0000000000000000000000000000000000000000000000000000000000000013416e6963686573732050656e6775426f61726400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a50454e4755424f41524400000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : tokenName (string): Anichess PenguBoard
Arg [1] : tokenSymbol (string): PENGUBOARD
Arg [2] : metadataResolver (address): 0x6AA75ad1fe433CF6Eb83436e3fDFD02999638f2D

-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [2] : 0000000000000000000000006aa75ad1fe433cf6eb83436e3fdfd02999638f2d
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000013
Arg [4] : 416e6963686573732050656e6775426f61726400000000000000000000000000
Arg [5] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [6] : 50454e4755424f41524400000000000000000000000000000000000000000000


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.