ETH Price: $2,428.92 (+0.19%)

Transaction Decoder

Block:
20991571 at Oct-18-2024 09:32:11 AM +UTC
Transaction Fee:
0.002049825596033968 ETH $4.98
Gas Used:
71,384 Gas / 28.715476802 Gwei

Emitted Events:

474 Ethrunes.TransferSingle( operator=[Sender] 0x79a7100d6b06a7a9e7a15dfc25431401bdb41df4, from=[Sender] 0x79a7100d6b06a7a9e7a15dfc25431401bdb41df4, to=LaunchSwap, id=1110403331949107032233257649694731726496094606034, value=232536562703991070054 )
475 LaunchSwap.Sell( id=1110403331949107032233257649694731726496094606034, amountIn=232536562703991070054, amountOutEth=2999999999999999999 )

Account State Difference:

  Address   Before After State Difference Code
0x326490A8...5b0834D9B
(Titan Builder)
12.442358059209120282 Eth12.442523000095600138 Eth0.000164940886479856
0x49CaEB81...ddDEE2738 45.814726204272522128 Eth42.814726204272522129 Eth2.999999999999999999
0x79a7100D...1bDB41DF4
0.23757179373008899 Eth
Nonce: 775
3.235521968134055021 Eth
Nonce: 776
2.997950174403966031

Execution Trace

Ethrunes.transfer( to=0x49CaEB8175DC9Db709d4e6299CF8252ddDEE2738, id=1110403331949107032233257649694731726496094606034, amount=232536562703991070054, data=0x0100000000000000000000000079A7100D6B06A7A9E7A15DFC25431401BDB41DF400000000000000000000000000000000000000000000000029378F43A6E900000000000000000000000000000000000000000000000000000000000067122D68 )
  • LaunchSwap.onERC1155Received( operator=0x79a7100D6b06A7a9e7A15dfC25431401bDB41DF4, from=0x79a7100D6b06A7a9e7A15dfC25431401bDB41DF4, id=1110403331949107032233257649694731726496094606034, amount=232536562703991070054, data=0x0100000000000000000000000079A7100D6B06A7A9E7A15DFC25431401BDB41DF400000000000000000000000000000000000000000000000029378F43A6E900000000000000000000000000000000000000000000000000000000000067122D68 )
    • ETH 2.999999999999999999 0x79a7100d6b06a7a9e7a15dfc25431401bdb41df4.CALL( )
      File 1 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);
      	}
      }

      File 2 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();
        }
      }