ETH Price: $2,521.29 (+0.87%)

Transaction Decoder

Block:
18868021 at Dec-26-2023 06:16:35 AM +UTC
Transaction Fee:
0.001197762668477744 ETH $3.02
Gas Used:
78,662 Gas / 15.226699912 Gwei

Emitted Events:

68 Ethrunes.TransferSingle( operator=[Receiver] 0x21ce7af14deb142da6863969ecdf7a4d8f19f78b, from=[Receiver] 0x21ce7af14deb142da6863969ecdf7a4d8f19f78b, to=[Sender] 0xe151f4f044b651ed9399a47a2e4ab35ae1fcb128, id=1154176857723061535322065504370124660086513746726, value=5000000000000000000000 )
69 0x21ce7af14deb142da6863969ecdf7a4d8f19f78b.0xbe5b06f9a175718836fcaa568526bf167ec1cb4559290a57a7c449425d988e3e( 0xbe5b06f9a175718836fcaa568526bf167ec1cb4559290a57a7c449425d988e3e, 0000000000000000000000009c297a8cbb8cfe19686a8a7a31a842a475a800fe, 000000000000000000000000e151f4f044b651ed9399a47a2e4ab35ae1fcb128, 000000000000000000000000ca2b148c42190d936f4329ff5e771675d8da4326, 00000000000000000000000000000000000000000000010f0cf064dd59200000, 00000000000000000000000000000000000000000000000000005738756f1800 )

Account State Difference:

  Address   Before After State Difference Code
0x21Ce7aF1...D8f19f78b
0x326490A8...5b0834D9B
(Titan Builder)
33.491828180129088154 Eth33.491867511129088154 Eth0.000039331
0x9C297A8C...475a800fE 0.007762869573005534 Eth0.472877869573005534 Eth0.465115
0xB27f8feA...0451Bd3C6 1.89712183311108 Eth1.91150683311108 Eth0.014385
0xE151F4f0...Ae1fCb128
1.92660561793340076 Eth
Nonce: 34
1.445907855264923016 Eth
Nonce: 35
0.480697762668477744

Execution Trace

ETH 0.4795 0x21ce7af14deb142da6863969ecdf7a4d8f19f78b.3fda5389( )
  • ETH 0.465115 0x9c297a8cbb8cfe19686a8a7a31a842a475a800fe.CALL( )
  • ETH 0.014385 0xb27f8fead12398ee590fd376ae7da510451bd3c6.CALL( )
  • Ethrunes.transfer( to=0xE151F4f044b651eD9399A47a2E4aB35Ae1fCb128, id=1154176857723061535322065504370124660086513746726, amount=5000000000000000000000, data=0x )
    // SPDX-License-Identifier: MIT
    
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * 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[EIP 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);
    }
    
    
    // File @openzeppelin/contracts/token/ERC1155/[email protected]
    
    // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Required interface of an ERC1155 compliant contract, as defined in the
     * https://eips.ethereum.org/EIPS/eip-1155[EIP].
     *
     * _Available since v3.1._
     */
    interface IERC1155 is IERC165 {
        /**
         * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
         */
        event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
    
        /**
         * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
         * transfers.
         */
        event TransferBatch(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256[] ids,
            uint256[] values
        );
    
        /**
         * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
         * `approved`.
         */
        event ApprovalForAll(address indexed account, address indexed operator, bool approved);
    
        /**
         * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
         *
         * If an {URI} event was emitted for `id`, the standard
         * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
         * returned by {IERC1155MetadataURI-uri}.
         */
        event URI(string value, uint256 indexed id);
    
        /**
         * @dev Returns the amount of tokens of token type `id` owned by `account`.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function balanceOf(address account, uint256 id) external view returns (uint256);
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
            external
            view
            returns (uint256[] memory);
    
        /**
         * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
         *
         * Emits an {ApprovalForAll} event.
         *
         * Requirements:
         *
         * - `operator` cannot be the caller.
         */
        function setApprovalForAll(address operator, bool approved) external;
    
        /**
         * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address account, address operator) external view returns (bool);
    
        /**
         * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
         * - `from` must have a balance of tokens of type `id` of at least `amount`.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes calldata data
        ) external;
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts,
            bytes calldata data
        ) external;
    }
    
    
    // File @openzeppelin/contracts/token/ERC1155/extensions/[email protected]
    
    // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
     * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
     *
     * _Available since v3.1._
     */
    interface IERC1155MetadataURI is IERC1155 {
        /**
         * @dev Returns the URI for token type `id`.
         *
         * If the `\{id\}` substring is present in the URI, it must be replaced by
         * clients with the actual token type ID.
         */
        function uri(uint256 id) external view returns (string memory);
    }
    
    
    // File @openzeppelin/contracts/token/ERC1155/[email protected]
    
    // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev _Available since v3.1._
     */
    interface IERC1155Receiver is IERC165 {
        /**
         * @dev Handles the receipt of a single ERC1155 token type. This function is
         * called at the end of a `safeTransferFrom` after the balance has been updated.
         *
         * NOTE: To accept the transfer, this must return
         * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
         * (i.e. 0xf23a6e61, or its own function selector).
         *
         * @param operator The address which initiated the transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param id The ID of the token being transferred
         * @param value The amount of tokens being transferred
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
         */
        function onERC1155Received(
            address operator,
            address from,
            uint256 id,
            uint256 value,
            bytes calldata data
        ) external returns (bytes4);
    
        /**
         * @dev Handles the receipt of a multiple ERC1155 token types. This function
         * is called at the end of a `safeBatchTransferFrom` after the balances have
         * been updated.
         *
         * NOTE: To accept the transfer(s), this must return
         * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
         * (i.e. 0xbc197c81, or its own function selector).
         *
         * @param operator The address which initiated the batch transfer (i.e. msg.sender)
         * @param from The address which previously owned the token
         * @param ids An array containing ids of each token being transferred (order and length must match values array)
         * @param values An array containing amounts of each token being transferred (order and length must match ids array)
         * @param data Additional data with no specified format
         * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
         */
        function onERC1155BatchReceived(
            address operator,
            address from,
            uint256[] calldata ids,
            uint256[] calldata values,
            bytes calldata data
        ) external returns (bytes4);
    }
    
    
    // File @openzeppelin/contracts/utils/[email protected]
    
    // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
    
    pragma solidity ^0.8.1;
    
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
    
            return account.code.length > 0;
        }
    
        /**
         * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
    
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
    
        /**
         * @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, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * 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.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, "Address: low-level call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
    
        /**
         * @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`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
    
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
         * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
         *
         * _Available since v4.8._
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            if (success) {
                if (returndata.length == 0) {
                    // only check isContract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    require(isContract(target), "Address: call to non-contract");
                }
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
    
        /**
         * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason or using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
    
        function _revert(bytes memory returndata, string memory errorMessage) 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
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
    
    
    // File @openzeppelin/contracts/utils/[email protected]
    
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
    
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    
    
    // File @openzeppelin/contracts/utils/introspection/[email protected]
    
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    
    
    // File @openzeppelin/contracts/token/ERC1155/[email protected]
    
    // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC1155/ERC1155.sol)
    
    pragma solidity ^0.8.0;
    
    
    
    
    
    
    /**
     * @dev Implementation of the basic standard multi-token.
     * See https://eips.ethereum.org/EIPS/eip-1155
     * Originally based on code by Enjin: https://github.com/enjin/erc-1155
     *
     * _Available since v3.1._
     */
    contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
        using Address for address;
    
        // Mapping from token ID to account balances
        mapping(uint256 => mapping(address => uint256)) private _balances;
    
        // Mapping from account to operator approvals
        mapping(address => mapping(address => bool)) private _operatorApprovals;
    
        // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
        string private _uri;
    
        /**
         * @dev See {_setURI}.
         */
        constructor(string memory uri_) {
            _setURI(uri_);
        }
    
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return
                interfaceId == type(IERC1155).interfaceId ||
                interfaceId == type(IERC1155MetadataURI).interfaceId ||
                super.supportsInterface(interfaceId);
        }
    
        /**
         * @dev See {IERC1155MetadataURI-uri}.
         *
         * This implementation returns the same URI for *all* token types. It relies
         * on the token type ID substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * Clients calling this function must replace the `\{id\}` substring with the
         * actual token type ID.
         */
        function uri(uint256) public view virtual override returns (string memory) {
            return _uri;
        }
    
        /**
         * @dev See {IERC1155-balanceOf}.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
            require(account != address(0), "ERC1155: address zero is not a valid owner");
            return _balances[id][account];
        }
    
        /**
         * @dev See {IERC1155-balanceOfBatch}.
         *
         * Requirements:
         *
         * - `accounts` and `ids` must have the same length.
         */
        function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
            public
            view
            virtual
            override
            returns (uint256[] memory)
        {
            require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
    
            uint256[] memory batchBalances = new uint256[](accounts.length);
    
            for (uint256 i = 0; i < accounts.length; ++i) {
                batchBalances[i] = balanceOf(accounts[i], ids[i]);
            }
    
            return batchBalances;
        }
    
        /**
         * @dev See {IERC1155-setApprovalForAll}.
         */
        function setApprovalForAll(address operator, bool approved) public virtual override {
            _setApprovalForAll(_msgSender(), operator, approved);
        }
    
        /**
         * @dev See {IERC1155-isApprovedForAll}.
         */
        function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
            return _operatorApprovals[account][operator];
        }
    
        /**
         * @dev See {IERC1155-safeTransferFrom}.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) public virtual override {
            require(
                from == _msgSender() || isApprovedForAll(from, _msgSender()),
                "ERC1155: caller is not token owner or approved"
            );
            _safeTransferFrom(from, to, id, amount, data);
        }
    
        /**
         * @dev See {IERC1155-safeBatchTransferFrom}.
         */
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) public virtual override {
            require(
                from == _msgSender() || isApprovedForAll(from, _msgSender()),
                "ERC1155: caller is not token owner or approved"
            );
            _safeBatchTransferFrom(from, to, ids, amounts, data);
        }
    
        /**
         * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - `from` must have a balance of tokens of type `id` of at least `amount`.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function _safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual {
            require(to != address(0), "ERC1155: transfer to the zero address");
    
            address operator = _msgSender();
            uint256[] memory ids = _asSingletonArray(id);
            uint256[] memory amounts = _asSingletonArray(amount);
    
            _beforeTokenTransfer(operator, from, to, ids, amounts, data);
    
            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
    
            emit TransferSingle(operator, from, to, id, amount);
    
            _afterTokenTransfer(operator, from, to, ids, amounts, data);
    
            _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
        }
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function _safeBatchTransferFrom(
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
            require(to != address(0), "ERC1155: transfer to the zero address");
    
            address operator = _msgSender();
    
            _beforeTokenTransfer(operator, from, to, ids, amounts, data);
    
            for (uint256 i = 0; i < ids.length; ++i) {
                uint256 id = ids[i];
                uint256 amount = amounts[i];
    
                uint256 fromBalance = _balances[id][from];
                require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
                unchecked {
                    _balances[id][from] = fromBalance - amount;
                }
                _balances[id][to] += amount;
            }
    
            emit TransferBatch(operator, from, to, ids, amounts);
    
            _afterTokenTransfer(operator, from, to, ids, amounts, data);
    
            _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
        }
    
        /**
         * @dev Sets a new URI for all token types, by relying on the token type ID
         * substitution mechanism
         * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
         *
         * By this mechanism, any occurrence of the `\{id\}` substring in either the
         * URI or any of the amounts in the JSON file at said URI will be replaced by
         * clients with the token type ID.
         *
         * For example, the `https://token-cdn-domain/\{id\}.json` URI would be
         * interpreted by clients as
         * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
         * for token type ID 0x4cce0.
         *
         * See {uri}.
         *
         * Because these URIs cannot be meaningfully represented by the {URI} event,
         * this function emits no events.
         */
        function _setURI(string memory newuri) internal virtual {
            _uri = newuri;
        }
    
        /**
         * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
         * acceptance magic value.
         */
        function _mint(
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual {
            require(to != address(0), "ERC1155: mint to the zero address");
    
            address operator = _msgSender();
            uint256[] memory ids = _asSingletonArray(id);
            uint256[] memory amounts = _asSingletonArray(amount);
    
            _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
    
            _balances[id][to] += amount;
            emit TransferSingle(operator, address(0), to, id, amount);
    
            _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
    
            _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
        }
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
         * acceptance magic value.
         */
        function _mintBatch(
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            require(to != address(0), "ERC1155: mint to the zero address");
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
    
            address operator = _msgSender();
    
            _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
    
            for (uint256 i = 0; i < ids.length; i++) {
                _balances[ids[i]][to] += amounts[i];
            }
    
            emit TransferBatch(operator, address(0), to, ids, amounts);
    
            _afterTokenTransfer(operator, address(0), to, ids, amounts, data);
    
            _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
        }
    
        /**
         * @dev Destroys `amount` tokens of token type `id` from `from`
         *
         * Emits a {TransferSingle} event.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `from` must have at least `amount` tokens of token type `id`.
         */
        function _burn(
            address from,
            uint256 id,
            uint256 amount
        ) internal virtual {
            require(from != address(0), "ERC1155: burn from the zero address");
    
            address operator = _msgSender();
            uint256[] memory ids = _asSingletonArray(id);
            uint256[] memory amounts = _asSingletonArray(amount);
    
            _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
    
            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
    
            emit TransferSingle(operator, from, address(0), id, amount);
    
            _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
        }
    
        /**
         * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
         *
         * Emits a {TransferBatch} event.
         *
         * Requirements:
         *
         * - `ids` and `amounts` must have the same length.
         */
        function _burnBatch(
            address from,
            uint256[] memory ids,
            uint256[] memory amounts
        ) internal virtual {
            require(from != address(0), "ERC1155: burn from the zero address");
            require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
    
            address operator = _msgSender();
    
            _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");
    
            for (uint256 i = 0; i < ids.length; i++) {
                uint256 id = ids[i];
                uint256 amount = amounts[i];
    
                uint256 fromBalance = _balances[id][from];
                require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
                unchecked {
                    _balances[id][from] = fromBalance - amount;
                }
            }
    
            emit TransferBatch(operator, from, address(0), ids, amounts);
    
            _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
        }
    
        /**
         * @dev Approve `operator` to operate on all of `owner` tokens
         *
         * Emits an {ApprovalForAll} event.
         */
        function _setApprovalForAll(
            address owner,
            address operator,
            bool approved
        ) internal virtual {
            require(owner != operator, "ERC1155: setting approval status for self");
            _operatorApprovals[owner][operator] = approved;
            emit ApprovalForAll(owner, operator, approved);
        }
    
        /**
         * @dev Hook that is called before any token transfer. This includes minting
         * and burning, as well as batched variants.
         *
         * The same hook is called on both single and batched variants. For single
         * transfers, the length of the `ids` and `amounts` arrays will be 1.
         *
         * Calling conditions (for each `id` and `amount` pair):
         *
         * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * of token type `id` will be  transferred to `to`.
         * - When `from` is zero, `amount` tokens of token type `id` will be minted
         * for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
         * will be burned.
         * - `from` and `to` are never both zero.
         * - `ids` and `amounts` have the same, non-zero length.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {}
    
        /**
         * @dev Hook that is called after any token transfer. This includes minting
         * and burning, as well as batched variants.
         *
         * The same hook is called on both single and batched variants. For single
         * transfers, the length of the `id` and `amount` arrays will be 1.
         *
         * Calling conditions (for each `id` and `amount` pair):
         *
         * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * of token type `id` will be  transferred to `to`.
         * - When `from` is zero, `amount` tokens of token type `id` will be minted
         * for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
         * will be burned.
         * - `from` and `to` are never both zero.
         * - `ids` and `amounts` have the same, non-zero length.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {}
    
        function _doSafeTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) private {
            if (to.isContract()) {
                try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                    if (response != IERC1155Receiver.onERC1155Received.selector) {
                        revert("ERC1155: ERC1155Receiver rejected tokens");
                    }
                } catch Error(string memory reason) {
                    revert(reason);
                } catch {
                    revert("ERC1155: transfer to non-ERC1155Receiver implementer");
                }
            }
        }
    
        function _doSafeBatchTransferAcceptanceCheck(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) private {
            if (to.isContract()) {
                try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                    bytes4 response
                ) {
                    if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                        revert("ERC1155: ERC1155Receiver rejected tokens");
                    }
                } catch Error(string memory reason) {
                    revert(reason);
                } catch {
                    revert("ERC1155: transfer to non-ERC1155Receiver implementer");
                }
            }
        }
    
        function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
            uint256[] memory array = new uint256[](1);
            array[0] = element;
    
            return array;
        }
    }
    
    
    // File @openzeppelin/contracts/token/ERC1155/extensions/[email protected]
    
    // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/extensions/ERC1155Supply.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Extension of ERC1155 that adds tracking of total supply per id.
     *
     * Useful for scenarios where Fungible and Non-fungible tokens have to be
     * clearly identified. Note: While a totalSupply of 1 might mean the
     * corresponding is an NFT, there is no guarantees that no other token with the
     * same id are not going to be minted.
     */
    abstract contract ERC1155Supply is ERC1155 {
        mapping(uint256 => uint256) private _totalSupply;
    
        /**
         * @dev Total amount of tokens in with a given id.
         */
        function totalSupply(uint256 id) public view virtual returns (uint256) {
            return _totalSupply[id];
        }
    
        /**
         * @dev Indicates whether any token exist with a given id, or not.
         */
        function exists(uint256 id) public view virtual returns (bool) {
            return ERC1155Supply.totalSupply(id) > 0;
        }
    
        /**
         * @dev See {ERC1155-_beforeTokenTransfer}.
         */
        function _beforeTokenTransfer(
            address operator,
            address from,
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual override {
            super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
    
            if (from == address(0)) {
                for (uint256 i = 0; i < ids.length; ++i) {
                    _totalSupply[ids[i]] += amounts[i];
                }
            }
    
            if (to == address(0)) {
                for (uint256 i = 0; i < ids.length; ++i) {
                    uint256 id = ids[i];
                    uint256 amount = amounts[i];
                    uint256 supply = _totalSupply[id];
                    require(supply >= amount, "ERC1155: burn amount exceeds totalSupply");
                    unchecked {
                        _totalSupply[id] = supply - amount;
                    }
                }
            }
        }
    }
    
    
    // File @openzeppelin/contracts/security/[email protected]
    
    // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
    
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
    
        uint256 private _status;
    
        constructor() {
            _status = _NOT_ENTERED;
        }
    
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            _nonReentrantBefore();
            _;
            _nonReentrantAfter();
        }
    
        function _nonReentrantBefore() private {
            // On the first call to nonReentrant, _status will be _NOT_ENTERED
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
    
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
        }
    
        function _nonReentrantAfter() private {
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = _NOT_ENTERED;
        }
    }
    
    
    // File @openzeppelin/contracts/token/ERC20/[email protected]
    
    // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
    
    pragma solidity ^0.8.0;
    
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    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 amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
    
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
    
        /**
         * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
    
        /**
         * @dev Moves `amount` tokens from `from` to `to` using the
         * allowance mechanism. `amount` 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 amount
        ) external returns (bool);
    }
    
    
    // File contracts/IEthrunesURI.sol
    
    pragma solidity ^0.8.17;
    
    interface IEthrunesURI {
    	function uri(uint256 id) external view returns(string memory);
    }
    
    
    // File contracts/StringUtils.sol
    
    pragma solidity ^0.8.17;
    
    library StringUtils {
      function strLen(string memory s) internal pure returns (uint256) {
        uint256 len;
        uint256 i = 0;
        uint256 bytelength = bytes(s).length;
        for (len = 0; i < bytelength; len++) {
          bytes1 b = bytes(s)[i];
          if (b < 0x80) {
              i += 1;
          } else if (b < 0xE0) {
              i += 2;
          } else if (b < 0xF0) {
              i += 3;
          } else if (b < 0xF8) {
              i += 4;
          } else if (b < 0xFC) {
              i += 5;
          } else {
              i += 6;
          }
        }
        return len;
      }
    
      function toLower(string memory s) internal pure returns (string memory) {
        bytes memory _bytes = bytes(s);
        for (uint i = 0; i < _bytes.length; i++) {
          if (uint8(_bytes[i]) >= 65 && uint8(_bytes[i]) <= 90) {
            _bytes[i] = bytes1(uint8(_bytes[i]) + 32);
          }
        }
        return string(_bytes);
      }
    }
    
    
    // File contracts/TransferHelper.sol
    
    pragma solidity >=0.6.0;
    
    library TransferHelper {
        /// @notice Transfers tokens from the targeted address to the given destination
        /// @notice Errors with 'STF' if transfer fails
        /// @param token The contract address of the token to be transferred
        /// @param from The originating address from which the tokens will be transferred
        /// @param to The destination address of the transfer
        /// @param value The amount to be transferred
        function safeTransferFrom(
            address token,
            address from,
            address to,
            uint256 value
        ) internal {
            (bool success, bytes memory data) =
                token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
        }
    
        /// @notice Transfers tokens from msg.sender to a recipient
        /// @dev Errors with ST if transfer fails
        /// @param token The contract address of the token which will be transferred
        /// @param to The recipient of the transfer
        /// @param value The value of the transfer
        function safeTransfer(
            address token,
            address to,
            uint256 value
        ) internal {
            (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
        }
    
        /// @notice Approves the stipulated contract to spend the given allowance in the given token
        /// @dev Errors with 'SA' if transfer fails
        /// @param token The contract address of the token to be approved
        /// @param to The target of the approval
        /// @param value The amount of the given token the target will be allowed to spend
        function safeApprove(
            address token,
            address to,
            uint256 value
        ) internal {
            (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
        }
    
        /// @notice Transfers ETH to the recipient address
        /// @dev Fails with `STE`
        /// @param to The destination of the transfer
        /// @param value The value to be transferred
        function safeTransferETH(address to, uint256 value) internal {
            (bool success, ) = to.call{value: value}(new bytes(0));
            require(success, 'STE');
        }
    }
    
    
    // File contracts/Ethrunes.sol
    
    pragma solidity ^0.8.17;
    
    
    
    
    
    
    contract Ethrunes is ERC1155Supply, ReentrancyGuard {
    	struct Token {
    		uint160 id;
    		uint8 decimals;
    		uint256 supply;
    		uint256 limit;
    		string tick;
    	}
    	
    	string public constant name = "Ethrunes";
    	string public constant symbol = "ERS";
    
    	address dev;
    	IEthrunesURI public ethrunesURI;
    	// No length limit if is 0
    	uint256 public tickMaxLength = 4;
    
    	uint160[] public allTokenIds;
    	mapping (uint160 => Token) public tokens;
    
    	event Deploy(uint8 typ, Token token);
    	event Wrap(address sender, address erc20, uint256 amount);
    	event Unwrap(address sender, address erc20, uint256 amount);
    
    	constructor() ERC1155("") {
    		dev = msg.sender;
    	}
    
    	modifier onlyEOA() { 
    		require(msg.sender == tx.origin, "Only EOA"); 
    		_; 
    	}
    
    	modifier checkTickLength(string memory tick) { 
    		uint tickLen = StringUtils.strLen(tick);
    		if(tickLen == 0) {
    			revert("Tick is empty");
    		}
    		if(tickMaxLength > 0 && tickLen > tickMaxLength) {
    			revert("Tick too long");
    		}
    		_;
    	}
    	
    	function setDev(address _dev) external {
    		require(msg.sender == dev);
    		dev = _dev;
    	}
    
    	function setEthrunesURI(address _uri) external {
    		require(msg.sender == dev);
    		ethrunesURI = IEthrunesURI(_uri);
    	}
    
    	function setTickMaxLength(uint256 _len) external {
    		require(msg.sender == dev);
    		tickMaxLength = _len;
    	}
    	
    	function _idOf(string memory tick) internal view returns(uint160) {
    		return uint160(uint256(keccak256(abi.encodePacked("ethrunes:", block.chainid, ":", StringUtils.toLower(tick)))));
    	}
    
    	function deploy(
    		string calldata tick,
    		uint8 decimals,
    		uint256 supply,
    		uint256 limit
    	) external checkTickLength(tick) {
    		require(limit > 0 && limit <= supply, "Invalid deploy args");
    		uint160 id = _idOf(tick);
    		require(tokens[id].supply == 0, "Deployed");
    		require(totalSupply(id) == 0, "Token id occupied");
    
    		Token memory token = Token({
    			id: id,
    			decimals: decimals,
    			supply: supply,
    			limit: limit,
    			tick: tick
    		});
    
    		tokens[id] = token;
    		allTokenIds.push(id);
    		emit Deploy(1, token);
    	}
    
    	function deploy2(
    		string calldata tick,
    		uint8 decimals,
    		uint256 supply,
    		address to
    	) external payable checkTickLength(tick) {
    		require(supply > 0, "Supply is 0");
    		uint160 id = _idOf(tick);
    		require(tokens[id].supply == 0, "Deployed");
    		require(totalSupply(id) == 0, "Token id occupied");
    
    		Token memory token = Token({
    			id: id,
    			decimals: decimals,
    			supply: supply,
    			limit: supply,
    			tick: tick
    		});
    		
    		tokens[id] = token;
    		allTokenIds.push(id);
    		_mint(to, id, supply, "");
    		emit Deploy(2, token);
    	}
    
    	function mint(string calldata tick) external onlyEOA {
    		uint160 id = _idOf(tick);
    		Token memory token = tokens[id];
    		require(token.supply != 0, "Token not exists");
    		require(totalSupply(token.id) + token.limit <= token.supply, "Exceeded maxSupply");
    		_mint(msg.sender, token.id, token.limit, "");
    	}
    
    	function wrapERC20(IERC20 erc20, uint256 amount) external nonReentrant {
    		uint160 id = uint160(address(erc20));
    		require(tokens[id].supply == 0, "Failed to wrap, token id deployed");
    		uint256 balanceBefore = erc20.balanceOf(address(this));
    		TransferHelper.safeTransferFrom(address(erc20), msg.sender, address(this), amount);
    		uint256 balanceAfter = erc20.balanceOf(address(this));
    		require(balanceAfter - balanceBefore == amount, "Invalid amount");
    		_mint(msg.sender, id, amount, "");
    
    		emit Wrap(msg.sender, address(erc20), amount);
    	}
    
    	function unwrapERC20(IERC20 erc20, uint256 amount) external {
    		uint256 id = uint160(address(erc20));
    		require(balanceOf(msg.sender, id) >= amount, "Insufficient balance");
    		_burn(msg.sender, id, amount);
    		TransferHelper.safeTransfer(address(erc20), msg.sender, amount);
    		emit Unwrap(msg.sender, address(erc20), amount);
    	}
    
    	function symbolOf(uint160 id) public view returns(string memory) {
    		if(tokens[id].supply > 0) {
    			return tokens[id].tick;	
    		} else {
    			bytes memory data = abi.encodeWithSelector(bytes4(keccak256("symbol()")));
    			(bool success, bytes memory returnData) = address(id).staticcall(data);
    			require(success, "call symbol() failed");
    			if(returnData.length == 32) {
    				return string(abi.encodePacked(returnData));
    			} else {
    				return abi.decode(returnData, (string));
    			}
    		}
    	}
    
    	function decimalsOf(uint160 id) public view returns(uint8) {
    		if(tokens[id].supply > 0) {
    			return tokens[id].decimals;
    		} else {
    			bytes memory data = abi.encodeWithSelector(bytes4(keccak256("decimals()")));
    			(bool success, bytes memory returnData) = address(id).staticcall(data);
    			require(success, "call decimals() failed");
    			return abi.decode(returnData, (uint8));
    		}
    	}
    
    	function allTokensCount() public view returns(uint256) {
    		return allTokenIds.length;
    	}
    
    	function getTokens(uint256 from, uint256 to) public view returns(Token[] memory tokenArray) {
    		if(allTokenIds.length == 0) {
    			return tokenArray;
    		}
    		if(to > allTokenIds.length) {
    			to = allTokenIds.length;
    		}
    		require(from < to, "Invalid range");
    		tokenArray = new Token[](to - from);
    		for(uint256 i = 0; i < tokenArray.length; i++) {
    			tokenArray[i] = tokens[allTokenIds[i + from]];
    		}
    	}
    
    	// disabled setApprovalForAll
    	function setApprovalForAll(address, bool) public override {
    		revert("setApprovalForAll is disabled");
    	}
    
    	function transfer(
    		address to,
    		uint256 id,
    		uint256 amount,
    		bytes memory data
    	) external payable nonReentrant {
    		if(msg.value > 0) {
      		TransferHelper.safeTransferETH(to, msg.value);	
      	}
    		safeTransferFrom(msg.sender, to, id, amount, data);
    	}
    
    	function batchTransfer(
    		address to,
    		uint256[] memory ids,
    		uint256[] memory amounts,
    		bytes memory data
    	) external payable nonReentrant {
    		if(msg.value > 0) {
    			TransferHelper.safeTransferETH(to, msg.value);	
    		}
    		safeBatchTransferFrom(msg.sender, to, ids, amounts, data);
    	}
    
    	function uri(uint256 id) public view override returns (string memory){
    		return ethrunesURI.uri(id);
    	}
    }