ETH Price: $2,509.11 (-0.54%)

Transaction Decoder

Block:
19356656 at Mar-03-2024 07:14:35 PM +UTC
Transaction Fee:
0.005919387311625432 ETH $14.85
Gas Used:
91,038 Gas / 65.021060564 Gwei

Emitted Events:

261 Ethrunes.TransferSingle( operator=[Receiver] LaunchSwap, from=[Receiver] LaunchSwap, to=[Sender] 0xfdb3971db79826766dcaf35149db8368543ef633, id=1110403331949107032233257649694731726496094606034, value=5000000000000000000 )
262 LaunchSwap.Buy( id=1110403331949107032233257649694731726496094606034, amountInEth=529125009136017794, amountOut=5000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x326490A8...5b0834D9B
0x49CaEB81...ddDEE2738 172.373836001708163338 Eth172.902961010844181132 Eth0.529125009136017794
(beaverbuild)
8.698757471777621463 Eth8.698759484321185479 Eth0.000002012543564016
0xFdB3971d...8543eF633
0.701940829335189101 Eth
Nonce: 1925
0.166896432887545875 Eth
Nonce: 1926
0.535044396447643226

Execution Trace

ETH 0.651717288538641748 LaunchSwap.buy( id=1110403331949107032233257649694731726496094606034, amountOut=5000000000000000000, deadline=1709493865, recipient=0xFdB3971dB79826766dCAF35149DB8368543eF633 ) => ( 529125009136017794 )
  • Ethrunes.transfer( to=0xFdB3971dB79826766dCAF35149DB8368543eF633, id=1110403331949107032233257649694731726496094606034, amount=5000000000000000000, data=0x )
  • ETH 0.122592279402623954 0xfdb3971db79826766dcaf35149db8368543ef633.CALL( )
    File 1 of 2: LaunchSwap
    // SPDX-License-Identifier: MIT
    
    // File @openzeppelin/contracts/utils/introspection/[email protected]
    
    
    // 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/[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/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/utils/[email protected]
    
    
    // OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)
    
    pragma solidity ^0.8.0;
    
    
    /**
     * @dev _Available since v3.1._
     */
    abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
            return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
        }
    }
    
    
    // File contracts/IEthrunes.sol
    
    
    pragma solidity ^0.8.17;
    
    interface IEthrunes is IERC1155 {
    
      function transfer(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
      ) external payable;
    
      function batchTransfer(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
      ) external payable;
    
      function deploy2(
        string calldata tick,
        uint8 decimals,
        uint256 supply,
        address to
      ) external payable;
    
      function tokens(uint160 _id) external view returns(
        uint160 id,
        uint8 decimals,
        uint256 supply,
        uint256 limit,
        string memory tick
      );
    
      function totalSupply(uint256 id) external view returns (uint256);
    }
    
    
    // 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/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/LaunchSwap.sol
    
    
    pragma solidity ^0.8.17;
    
    
    
    
    contract LaunchSwap is ReentrancyGuard, ERC1155Receiver {
      error InvalidSender();
      error InvalidCall();
      error NotSupportBatchReceivedCallback();
      error PoolAlreadyCreated();
      error ExceedLimitPerBuy();
      error ExceedLimitPerSell();
      error InsufficientAmountOut();
      error InsufficientAmountIn();
      error LessThanAmountOutMin();
      error Expired();
      error InitialPriceTooLow();
      error InvalidTotalSupply();
      error TotalSupplyMustBeEven();
      error ZeroAmountIn();
      error InvalidRecipient();
      error InvalidFeeRate();
    
      struct Pool {
        uint256 reserve0;
        uint256 reserve1;
        uint256 halfReserve;
        uint256 price;
        uint256 limitPerBuy;
        uint256 limitPerSell;
        uint256 fee;
        address creator;
        uint256 creatorFees;
      }
    
      uint256 public accProtocolFees;
      uint16 public protocolFee = 20; // 20% for creator and service
      address public ethrunes;
      address feeRecipient;
      address dev;
    
      mapping (uint160 => Pool) public pools;
      mapping (uint256 => bool) public feeTiers;
      
    
      event CreatePool(address indexed creator, uint160 indexed id, uint256 amount, uint256 price, uint256 fee);
      event Buy(uint160 indexed id, uint256 amountInEth, uint256 amountOut);
      event Sell(uint160 indexed id, uint256 amountIn, uint256 amountOutEth);
    
      constructor(address _ethrunes, address _feeRecipient) {
        ethrunes = _ethrunes;
        feeRecipient = _feeRecipient;
        dev = msg.sender;
        feeTiers[300] = true;
        feeTiers[200] = true;
        feeTiers[100] = true;
        feeTiers[50] = true;
        feeTiers[30] = true;
      }
    
      modifier checkDeadline(uint256 deadline) {
        if(deadline < block.timestamp) revert Expired();
        _;
      }
    
      function _createPool(
        uint160 id,
        uint256 amount,
        uint256 price,
        uint256 limitPerBuy,
        uint256 limitPerSell,
        uint256 fee,
        address creator
      ) internal {
        Pool storage pool = pools[id];
        if(pool.reserve1 != 0) revert PoolAlreadyCreated();
        uint256 supply = IEthrunes(ethrunes).totalSupply(id);
        if(amount != supply) revert InvalidTotalSupply();
        if(!feeTiers[fee]) revert InvalidFeeRate();
        if(amount / 2 * 2 != amount) revert TotalSupplyMustBeEven();
    
        pool.reserve0 = 0;
        pool.reserve1 = amount;
        pool.halfReserve = amount / 2;
        pool.price = price;
        pool.limitPerBuy = limitPerBuy;
        pool.limitPerSell = limitPerSell;
        pool.fee = fee;
        pool.creator = creator;
    
        if(pool.halfReserve * pool.price / 1e18 == 0) revert InitialPriceTooLow();
    
        emit CreatePool(creator, id, amount, price, fee);
      }
    
    
      function buy(
        uint160 id, 
        uint256 amountOut, 
        uint256 deadline,
        address recipient
      ) external payable checkDeadline(deadline) nonReentrant returns(uint256) {
        if(msg.value == 0) revert ZeroAmountIn();
        Pool storage pool = pools[id];
        if(pool.limitPerBuy > 0 && amountOut > pool.limitPerBuy) revert ExceedLimitPerBuy();
        if(amountOut >= pool.reserve1) revert InsufficientAmountOut();
    
        uint256 buyAmount;
        uint256 swapAmount;
    
        if(pool.reserve1 > pool.halfReserve) {
          uint256 maxBuyAmount = pool.reserve1 - pool.halfReserve;
          if(maxBuyAmount >= amountOut) {
            buyAmount = amountOut;
          } else {
            swapAmount = amountOut - maxBuyAmount;
            buyAmount = maxBuyAmount;
          }
        } else {
          swapAmount = amountOut;
        }
    
        uint256 amountIn;
    
        if(buyAmount > 0) {
          amountIn = pool.price * buyAmount / 1e18;
          pool.reserve0 += amountIn;
          pool.reserve1 -= buyAmount;
        }
    
        if(swapAmount > 0) {
          if(swapAmount >= pool.reserve1) revert InsufficientAmountOut();
    
          uint256 numerator =  swapAmount * pool.reserve0;
          uint256 denominator = pool.reserve1 - swapAmount;
    
          uint256 swapAmountIn = numerator / denominator + 1;
    
          uint256 feeAmount = swapAmountIn * pool.fee / 10000;
    
          uint256 _protocolFees = feeAmount * protocolFee / 100;
          uint256 _creatorFee = _protocolFees * 75 / 100;
          uint256 providerFees = feeAmount - _protocolFees;
    
          accProtocolFees += (_protocolFees - _creatorFee);
    
          pool.creatorFees += _creatorFee;
          pool.reserve0 += swapAmountIn;
          pool.reserve1 -= swapAmount;
          pool.price += (providerFees * 1e18 / pool.halfReserve);
    
          amountIn += (swapAmountIn + feeAmount);
        }
    
        if(msg.value < amountIn) revert InsufficientAmountIn();
    
        bytes memory data;
        if(recipient == address(0x0) || recipient == msg.sender) {
          recipient = msg.sender;
        } else {
          data = abi.encode(msg.sender);
        }
        IEthrunes(ethrunes).transfer(recipient, id, amountOut, data);
    
        uint256 refund = msg.value - amountIn;
    
        // refund
        if(refund > 0) {
          TransferHelper.safeTransferETH(msg.sender, refund);
        }
    
        emit Buy(id, amountIn, amountOut);
    
        return amountIn;
      }
    
      function _sell(
        uint160 id, 
        address to,
        uint256 amountIn, 
        uint256 amountOutMin, 
        uint256 deadline
      ) internal checkDeadline(deadline) nonReentrant {
        Pool storage pool = pools[id];
        if(to == address(0x0)) revert InvalidRecipient();
        if(amountIn == 0) revert ZeroAmountIn();
    
        if(pool.limitPerSell > 0 && amountIn > pool.limitPerSell) revert ExceedLimitPerSell();
    
        uint256 sellAmount;
        uint256 swapAmount;
    
        if(pool.reserve1 >= pool.halfReserve) {
          sellAmount = amountIn;
        } else {
          uint256 maxSwapAmount = pool.halfReserve - pool.reserve1;
          if(maxSwapAmount >= amountIn) {
            swapAmount = amountIn;
          } else {
            swapAmount = maxSwapAmount;
            sellAmount = amountIn - maxSwapAmount;
          }
        }
    
        uint256 amountOut;
        if(swapAmount > 0) {
          uint256 numerator =  swapAmount * pool.reserve0;
          uint256 denominator = pool.reserve1 + swapAmount;
          amountOut = numerator / denominator - 1;
    
          uint256 feeAmount = amountOut * pool.fee / 10000;
    
          uint256 _protocolFees = feeAmount * protocolFee / 100;
          uint256 _creatorFee = _protocolFees * 75 / 100;
    
          uint256 providerFees = feeAmount - _protocolFees;
    
          accProtocolFees += (_protocolFees - _creatorFee);
    
          pool.creatorFees += _creatorFee;
          pool.reserve0 -= amountOut;
          pool.reserve1 += swapAmount;
    
          pool.price += providerFees * 1e18 / pool.halfReserve;
    
          amountOut -= feeAmount;
        }
    
        if(pool.reserve1 == pool.halfReserve) {
          pool.reserve0 = pool.price * pool.halfReserve / 1e18;
        }
    
        if(sellAmount > 0) {
          uint256 sellValue = sellAmount * pool.price / 1e18;
          pool.reserve0 -= sellValue;
          pool.reserve1 += sellAmount;
          amountOut += sellValue;
        }
    
        if(amountOut < amountOutMin) revert LessThanAmountOutMin();
    
        TransferHelper.safeTransferETH(to, amountOut);
    
        emit Sell(id, amountIn, amountOut);
      }
    
    
      function setFeeRecipient(address _feeRecipient) external {
        require(msg.sender == dev);
        feeRecipient = _feeRecipient;
      }
    
      function setFeeTier(uint256 tier, bool b) external {
        require(tier <= 1000);
        require(msg.sender == dev);
        feeTiers[tier] = b;
      }
    
      function withdrawProtocolFees() external nonReentrant {
        uint256 fees = accProtocolFees;
        accProtocolFees = 0;
        TransferHelper.safeTransferETH(feeRecipient, fees);
      }
    
      function withdrawCreatorFees(uint160 id) external nonReentrant {
        Pool storage pool = pools[id];
        uint256 fees = pool.creatorFees;
        pool.creatorFees = 0;
        TransferHelper.safeTransferETH(pool.creator, fees);
      }
    
      function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 amount,
        bytes calldata data
      ) public override returns (bytes4) {
        if(msg.sender != ethrunes) revert InvalidSender();
        uint8 command = uint8(data[0]);
        // sell
        if(command == 1) {
          address recipient;
          uint256 amountOutMin;
          uint256 deadline;
          assembly {
            recipient := calldataload(add(data.offset, 0x1))
            amountOutMin := calldataload(add(data.offset, 0x21))
            deadline := calldataload(add(data.offset, 0x41))
          }
          _sell(uint160(id), recipient, amount, amountOutMin, deadline);
    
        } else if(command == 2) { // createPool
          uint256 price;
          uint256 limitPerBuy;
          uint256 limitPerSell;
          uint256 fee;
          address creator;
          assembly {
            price := calldataload(add(data.offset, 0x1))
            limitPerBuy := calldataload(add(data.offset, 0x21))
            limitPerSell := calldataload(add(data.offset, 0x41))
            fee := calldataload(add(data.offset, 0x61))
            creator := calldataload(add(data.offset, 0x81))
          }
    
          _createPool(
            uint160(id), 
            amount,
            price,
            limitPerBuy,
            limitPerSell,
            fee,
            creator
          );
        } else {
          revert InvalidCall();
        }
    
        return this.onERC1155Received.selector;
      }
    
      function onERC1155BatchReceived(
        address,
        address,
        uint256[] calldata,
        uint256[] calldata,
        bytes calldata
      ) public override returns (bytes4) {
        revert NotSupportBatchReceivedCallback();
      }
    }

    File 2 of 2: Ethrunes
    // 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);
    	}
    }