Contract 0xc32eb36f886f638fffd836df44c124074cfe3584 3

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x34fbfa5e99a49df1d4698505cbd92e675eb9fa76c6ef1cc6356d3ed1e67b0fbdDo Multiple Inte...286522932022-10-06 8:09:321 min ago0x018511fb3754017b4befc357c35ee51481c711f7 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00002014
0x017f182a835a88fef96a14f4bd2436438b03ef0332f7c6fb2ddf7496a764cf66Do Multiple Inte...286521462022-10-06 8:08:263 mins ago0xf0b1dad0027b4ae38baaeb2878a64f9fd8a378e4 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00002014
0xc5e0638a483a9ff5442bb43a363614ef52d133bc70ff7789f6e43a9e3ab7353bDo Multiple Inte...286519302022-10-06 8:06:504 mins ago0x3b25846c80f06f207c64a7aaa355dc463f4100ca IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00002023
0x59076be3f9b8083da7297172b21fd4e1a91655155df9a39af8dcc2110713522cDo Multiple Inte...286517422022-10-06 8:05:216 mins ago0x9fd3d1ab21e53ada301f93f02d555b2b0a101b01 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00002002
0x7b49ef396b205ed49617c262e015ccb40d439d8a7dddd2f1fc2266fb43081facDo Multiple Inte...286515042022-10-06 8:03:437 mins ago0xfae8df192790b1ce96eb081eaf7c7653ba785b77 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00002023
0xcad9089c78240da50881ff61dd8d91dbfdd4694aa4e6460a5fe4cf36330fa0a3Do Multiple Inte...286512642022-10-06 8:02:079 mins ago0x13bf4152e8b499b15f272fd61eb4dabe88072d9f IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00002077
0x6bd7b1052a7df4e941f36a858b8b787e4182867206e735218f9e27bed603bee0Do Multiple Inte...286508412022-10-06 7:59:2312 mins ago0x54e6627177165aa7b6f31752c744c306fac993db IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001958
0x3e258737804b36664d174834e272d948f009013eee897a86b62a02a1e2c74f3aDo Multiple Inte...286493152022-10-06 7:48:4222 mins ago0xd2dc3adde00faf9f89b40a71071837410e321727 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001791
0xc29af11c77b0551c161f689785ef206bff77a5229466bb916a5e0c7baa18fc0bDo Multiple Inte...286492622022-10-06 7:48:2223 mins ago0xd2dc3adde00faf9f89b40a71071837410e321727 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001865
0x0c7702a38b5f85bf3ee435b5efb9756d12cafc565f08d1e53751b9cacf1ac3b4Do Multiple Inte...286492162022-10-06 7:48:0123 mins ago0xd2dc3adde00faf9f89b40a71071837410e321727 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001586
0xa6928a5278101ad25e08c30eaaff0af39e390d6032d45551beb248a23636a005Do Multiple Inte...286491662022-10-06 7:47:4223 mins ago0xd2dc3adde00faf9f89b40a71071837410e321727 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.0000182
0x6f718cddca9992f209a922f138cb0c2db3de8b68e667aa93348de66d2e0f91c3Do Multiple Inte...286486232022-10-06 7:44:0727 mins ago0xd2dc3adde00faf9f89b40a71071837410e321727 IN  0xc32eb36f886f638fffd836df44c124074cfe35840.005 ETH0.00001235
0xa798e8ee1c8a078ef7fa4836fd75e2e8f345e9e49d7cfca3563c596ff852e54bDo Multiple Inte...286482682022-10-06 7:41:3229 mins ago0xbd2a95d30761333cb573be00e4c76a0995c7d07a IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.0000189
0x1d5225537f354dfb3145a242c2af59db451b72f0a803456a90ea412ca8307dc9Do Multiple Inte...286482142022-10-06 7:41:1130 mins ago0xbd2a95d30761333cb573be00e4c76a0995c7d07a IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001609
0x767a668fd6ff144090a6b64ae57b5005b78d4e25dcebe5d2f9d3def587edc76eDo Multiple Inte...286480322022-10-06 7:39:5531 mins ago0xbd2a95d30761333cb573be00e4c76a0995c7d07a IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001897
0xe79865b4d4f8c0b8076c2a2aaf33e1d4bee71ae8e3aa0f9394a3ffe03fd30817Do Multiple Inte...286479102022-10-06 7:38:5632 mins ago0x0b0116af4f320de41e04840273908262616455f3 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00002073
0xe130a2713bea743bd7fc2103506dbb731cc9b927e386c788381f90f6c9bd47b2Do Multiple Inte...286474982022-10-06 7:36:1735 mins ago0xbd2a95d30761333cb573be00e4c76a0995c7d07a IN  0xc32eb36f886f638fffd836df44c124074cfe35840.015 ETH0.00001216
0xd3120234e63d061a57b8a665e19dcd23e380450e2940c4c8d45e625050654517Do Multiple Inte...286474812022-10-06 7:36:1035 mins ago0x0b0116af4f320de41e04840273908262616455f3 IN  0xc32eb36f886f638fffd836df44c124074cfe35840.005 ETH0.00001384
0xf274a376b2b037099dd82dfe49e46d7cbea1aeda90421aee280e7c57f78d187fDo Multiple Inte...286473812022-10-06 7:35:3635 mins ago0xbd2a95d30761333cb573be00e4c76a0995c7d07a IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001788
0x1d086e30d2081f603d16f1a17a6a535c6e7266ce95d6272be29a9c61e7aa01d7Do Multiple Inte...286468772022-10-06 7:32:0639 mins ago0x695ed4801272fa829c5c5d9d2f52176c67d58010 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001873
0x3162b89b56743d302749c31bc33083c2b3b7978db05dc0f64e393a9075d842fdDo Multiple Inte...286465032022-10-06 7:29:4041 mins ago0x695ed4801272fa829c5c5d9d2f52176c67d58010 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001831
0x0a2d4f05ab2a3a34889086eaa1df360171840fa15ee4f1c890de1749fe47fa3bDo Multiple Inte...286459612022-10-06 7:25:4045 mins ago0x695ed4801272fa829c5c5d9d2f52176c67d58010 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001884
0xc9eb8dfd19777e3832c19846075e29daafacff1b8823709b042e853e7521704cDo Multiple Inte...286457772022-10-06 7:24:3146 mins ago0x695ed4801272fa829c5c5d9d2f52176c67d58010 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001563
0xd8147367919b4d87f867f3e7e1e2ba181708035873ab41ce47710d53af94af5aDo Multiple Inte...286455452022-10-06 7:22:5848 mins ago0x695ed4801272fa829c5c5d9d2f52176c67d58010 IN  0xc32eb36f886f638fffd836df44c124074cfe35840 ETH0.00001353
0xaf6a40026b31da6d152f229f350201092db903b57c6a9bbcc12ff012c3de04d6Do Multiple Inte...286449862022-10-06 7:19:2951 mins ago0x695ed4801272fa829c5c5d9d2f52176c67d58010 IN  0xc32eb36f886f638fffd836df44c124074cfe35840.015 ETH0.00000976
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x34fbfa5e99a49df1d4698505cbd92e675eb9fa76c6ef1cc6356d3ed1e67b0fbd286522932022-10-06 8:09:321 min ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x34fbfa5e99a49df1d4698505cbd92e675eb9fa76c6ef1cc6356d3ed1e67b0fbd286522932022-10-06 8:09:321 min ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x017f182a835a88fef96a14f4bd2436438b03ef0332f7c6fb2ddf7496a764cf66286521462022-10-06 8:08:263 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x017f182a835a88fef96a14f4bd2436438b03ef0332f7c6fb2ddf7496a764cf66286521462022-10-06 8:08:263 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0xc5e0638a483a9ff5442bb43a363614ef52d133bc70ff7789f6e43a9e3ab7353b286519302022-10-06 8:06:504 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0xc5e0638a483a9ff5442bb43a363614ef52d133bc70ff7789f6e43a9e3ab7353b286519302022-10-06 8:06:504 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x59076be3f9b8083da7297172b21fd4e1a91655155df9a39af8dcc2110713522c286517422022-10-06 8:05:216 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x59076be3f9b8083da7297172b21fd4e1a91655155df9a39af8dcc2110713522c286517422022-10-06 8:05:216 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x7b49ef396b205ed49617c262e015ccb40d439d8a7dddd2f1fc2266fb43081fac286515042022-10-06 8:03:437 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x7b49ef396b205ed49617c262e015ccb40d439d8a7dddd2f1fc2266fb43081fac286515042022-10-06 8:03:437 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0xcad9089c78240da50881ff61dd8d91dbfdd4694aa4e6460a5fe4cf36330fa0a3286512642022-10-06 8:02:079 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0xcad9089c78240da50881ff61dd8d91dbfdd4694aa4e6460a5fe4cf36330fa0a3286512642022-10-06 8:02:079 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0x6bd7b1052a7df4e941f36a858b8b787e4182867206e735218f9e27bed603bee0286508412022-10-06 7:59:2312 mins ago 0xc32eb36f886f638fffd836df44c124074cfe3584 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0x6bd7b1052a7df4e941f36a858b8b787e4182867206e735218f9e27bed603bee0286508412022-10-06 7:59:2312 mins ago 0xc32eb36f886f638fffd836df44c124074cfe3584 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0x3e258737804b36664d174834e272d948f009013eee897a86b62a02a1e2c74f3a286493152022-10-06 7:48:4222 mins ago 0xc32eb36f886f638fffd836df44c124074cfe3584 0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb90 ETH
0x3e258737804b36664d174834e272d948f009013eee897a86b62a02a1e2c74f3a286493152022-10-06 7:48:4222 mins ago 0xc32eb36f886f638fffd836df44c124074cfe3584 0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb90 ETH
0xc29af11c77b0551c161f689785ef206bff77a5229466bb916a5e0c7baa18fc0b286492622022-10-06 7:48:2223 mins ago 0xc32eb36f886f638fffd836df44c124074cfe3584 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0xc29af11c77b0551c161f689785ef206bff77a5229466bb916a5e0c7baa18fc0b286492622022-10-06 7:48:2223 mins ago 0xc32eb36f886f638fffd836df44c124074cfe3584 0xff970a61a04b1ca14834a43f5de4533ebddb5cc80 ETH
0x0c7702a38b5f85bf3ee435b5efb9756d12cafc565f08d1e53751b9cacf1ac3b4286492162022-10-06 7:48:0123 mins ago 0xc32eb36f886f638fffd836df44c124074cfe3584 0xda10009cbd5d07dd0cecc66161fc93d7c9000da10 ETH
0x0c7702a38b5f85bf3ee435b5efb9756d12cafc565f08d1e53751b9cacf1ac3b4286492162022-10-06 7:48:0123 mins ago 0xc32eb36f886f638fffd836df44c124074cfe3584 0xda10009cbd5d07dd0cecc66161fc93d7c9000da10 ETH
0xa6928a5278101ad25e08c30eaaff0af39e390d6032d45551beb248a23636a005286491662022-10-06 7:47:4223 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0xa6928a5278101ad25e08c30eaaff0af39e390d6032d45551beb248a23636a005286491662022-10-06 7:47:4223 mins ago 0xc32eb36f886f638fffd836df44c124074cfe35840x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f0 ETH
0xa798e8ee1c8a078ef7fa4836fd75e2e8f345e9e49d7cfca3563c596ff852e54b286482682022-10-06 7:41:3229 mins ago 0xc32eb36f886f638fffd836df44c124074cfe3584 0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb90 ETH
0xa798e8ee1c8a078ef7fa4836fd75e2e8f345e9e49d7cfca3563c596ff852e54b286482682022-10-06 7:41:3229 mins ago 0xc32eb36f886f638fffd836df44c124074cfe3584 0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb90 ETH
0x1d5225537f354dfb3145a242c2af59db451b72f0a803456a90ea412ca8307dc9286482142022-10-06 7:41:1130 mins ago 0xc32eb36f886f638fffd836df44c124074cfe3584 0xda10009cbd5d07dd0cecc66161fc93d7c9000da10 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Ocean

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 10000000 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 23 : Ocean.sol
// SPDX-License-Identifier: MIT
// Cowri Labs Inc.

// All solidity behavior related comments are in reference to this version of
// the solc compiler.
pragma solidity =0.8.10;

// OpenZeppelin ERC Interfaces
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";

// OpenZeppelin Utility Library
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

// ShellV2 Interfaces, Data Structures, and Library
import {IOceanInteractions, Interaction, InteractionType} from "./Interactions.sol";
import {IOceanFeeChange} from "./IOceanFeeChange.sol";
import {IOceanPrimitive} from "./IOceanPrimitive.sol";
import {BalanceDelta, LibBalanceDelta} from "./BalanceDelta.sol";

// ShellV2 ERC-1155 with logic related to public multitoken ledger management.
import {OceanERC1155} from "./OceanERC1155.sol";

/**
 * @title A public multitoken ledger for defi
 * @author Cowri Labs Team
 * @dev The ocean is designed to interact with contracts that implement IERC20,
 *  IERC721, IERC-1155, or IOceanPrimitive.
 * @dev The ocean is three things.
 *  1. At the highest level, it is a defi framework[0]. Users provide a list
 *   of interactions, and the ocean executes those interactions. Each
 *   interaction involves a call to an external contract. These calls result
 *   in updates to the ocean's accounting system.
 *  2. Suporting this defi framework is an accounting system that can transfer,
 *   mint, or burn tokens. Each token in the accounting system is identified by
 *   its oceanId. Every oceanId is uniquely derived from an external contract
 *   address. This external contract is the only contract able to cause mints
 *   or burns of this token[1].
 *  3. Supporting this accounting system is an ERC-1155 ledger with all the
 *   standard ERC-1155 features. Users and primitives can interact with their
 *   tokens using both the defi framework and the ERC-1155 functions.

 * [0] We call it a framework because the ocean calls predefined functions on
 *  external contracts at certain points in its exection. The lifecycle is
 *  managed by the ocean, while the business logic is managed by external
 *  contracts.  Conceptually this is quite similar to a typical web framework.
 * [1] For example, when a user wraps an ERC-20 token into the ocean, the
 *   framework calls the ERC-20 transfer function, and upon success, mints the
 *   wrapped token to the user. In another case, when a user deposits a base
 *   token into a liquidity pool to recieve liquidity provider tokens, the
 *   framework calls the liquidity pool, tells it how much of the base token it
 *   will receive, and asks it how much of the liquidity provider token it
 *   would like to mint. When the pool responds, the framework mints this
 *   amount to the user.
 *
 * @dev Getting started tips:
 *  1. Check out Interactions.sol
 *  2. Read through the implementation of Ocean.doInteraction(), glossing over
 *   the function call to _executeInteraction().
 *  3. Read through the imlementation of Ocean.doMultipleInteractions(), again
 *   glossing over the function call to _executeInteraction(). When you
 *   encounter calls to LibBalanceDelta, check out their implementations.
 *  4. Read through _executeInteraction() and all the functions it calls.
 *   Understand how this is the line separating the accounting for the external
 *   contracts and the accounting for the current user.
 *   You can read the implementations of the specific interactions in any
 *   order, but it might be good to go through them in order of increasing
 *   complexity. The called functions, in order of increasing complexity, are:
 *   wrapErc721, unwrapErc721, wrapErc1155, unwrapErc1155, computeOutputAmount,
 *   computeInputAmount, unwrapErc20, and wrapErc20.  When you get to 
 *   computeOutputAmount, check out IOceanPrimitive, IOceanToken, and the
 *   function registerNewTokens() in OceanERC1155.
 */
contract Ocean is
    IOceanInteractions,
    IOceanFeeChange,
    OceanERC1155,
    IERC721Receiver,
    IERC1155Receiver
{
    using LibBalanceDelta for BalanceDelta[];

    /// @notice this is the oceanId used for shETH
    /// @dev hexadecimal(ascii("shETH"))
    uint256 public immutable WRAPPED_ETHER_ID;

    /// @notice Used to calculate the unwrap fee
    /// unwrapFee = unwrapAmount / unwrapFeeDivisor
    /// Because this uses integer division, the fee is always rounded down
    /// If unwrapAmount < unwrapFeeDivisor, unwrapFee == 0
    uint256 public unwrapFeeDivisor;
    /// @dev this is equivalent to 5 basis points: 1 / 2000 = 0.05%
    /// @dev When limited to 5 bips or less an integer divisor is an efficient
    ///  and precise method of calculating a fee.
    /// @notice As the divisor shrinks, the fee charged grows
    uint256 private constant MIN_UNWRAP_FEE_DIVISOR = 2000;

    /// @notice wrapped ERC20 tokens are stored in an 18 decimal representation
    /// @dev this makes it easier to implement AMMs between similar tokens
    uint8 private constant NORMALIZED_DECIMALS = 18;
    /// @notice When the specifiedAmount is equal to this value, we set
    ///  specifiedAmount to the balance delta.
    uint256 private constant GET_BALANCE_DELTA = type(uint256).max;

    /// @dev Determines if a transfer callback is expected.
    /// @dev adapted from OpenZeppelin Reentrancy Guard
    uint256 private constant NOT_INTERACTION = 1;
    uint256 private constant INTERACTION = 2;
    uint256 private _ERC1155InteractionStatus;
    uint256 private _ERC721InteractionStatus;

    event ChangeUnwrapFee(uint256 oldFee, uint256 newFee, address sender);
    event Erc20Wrap(
        address indexed erc20Token,
        uint256 transferredAmount,
        uint256 wrappedAmount,
        uint256 dust,
        address indexed user,
        uint256 indexed oceanId
    );
    event Erc20Unwrap(
        address indexed erc20Token,
        uint256 transferredAmount,
        uint256 unwrappedAmount,
        uint256 feeCharged,
        address indexed user,
        uint256 indexed oceanId
    );
    event Erc721Wrap(
        address indexed erc721Token,
        uint256 erc721id,
        address indexed user,
        uint256 indexed oceanId
    );
    event Erc721Unwrap(
        address indexed erc721Token,
        uint256 erc721Id,
        address indexed user,
        uint256 indexed oceanId
    );
    event Erc1155Wrap(
        address indexed erc1155Token,
        uint256 erc1155Id,
        uint256 amount,
        address indexed user,
        uint256 indexed oceanId
    );
    event Erc1155Unwrap(
        address indexed erc1155Token,
        uint256 erc1155Id,
        uint256 amount,
        uint256 feeCharged,
        address indexed user,
        uint256 indexed oceanId
    );
    event EtherWrap(uint256 amount, address indexed user);
    event EtherUnwrap(uint256 amount, uint256 feeCharged, address indexed user);
    event ComputeOutputAmount(
        address indexed primitive,
        uint256 inputToken,
        uint256 outputToken,
        uint256 inputAmount,
        uint256 outputAmount,
        address indexed user
    );
    event ComputeInputAmount(
        address indexed primitive,
        uint256 inputToken,
        uint256 outputToken,
        uint256 inputAmount,
        uint256 outputAmount,
        address indexed user
    );
    event OceanTransaction(address indexed user, uint256 numberOfInteractions);
    event ForwardedOceanTransaction(
        address indexed forwarder,
        address indexed user,
        uint256 numberOfInteractions
    );

    /**
     * @dev Creates custom ERC-1155 with passed uri_, sets DAO address, and
     *  initializes ERC-1155 transfer guard.
     * @notice initializes the fee divisor to uint256 max, which results in
     *  a fee of zero unless unwrapAmount == type(uint256).max, in which
     *  case the fee is one part in 1.16 * 10^77.
     */
    constructor(string memory uri_) OceanERC1155(uri_) {
        unwrapFeeDivisor = type(uint256).max;
        _ERC1155InteractionStatus = NOT_INTERACTION;
        _ERC721InteractionStatus = NOT_INTERACTION;
        WRAPPED_ETHER_ID = _calculateOceanId(address(0x4574686572), 0); // hexadecimal(ascii("Ether"))
    }

    /**
     * @dev ERC1155 Approvals also function as permission to execute
     *  interactions on a user's behalf
     * @param userAddress the address passed by the forwarder
     *
     * Because poorly chosen interactions are vulnerable to economic attacks,
     *  calling do{Interaction|MultipleInteractions} on a user's behalf must
     *  require the  same level of trust as direct balance transfers.
     */
    modifier onlyApprovedForwarder(address userAddress) {
        require(
            isApprovedForAll(userAddress, msg.sender),
            "Forwarder not approved"
        );
        _;
    }

    /**
     * @notice this changes the unwrap fee immediately
     * @notice The governance structure must appropriately handle any
     *  time lock or other mechanism for managing fee changes
     * @param nextUnwrapFeeDivisor the reciprocal of the next fee percentage.
     */
    function changeUnwrapFee(uint256 nextUnwrapFeeDivisor)
        external
        override
        onlyOwner
    {
        /// @notice as the divisor gets smaller, the fee charged gets larger
        require(MIN_UNWRAP_FEE_DIVISOR <= nextUnwrapFeeDivisor);
        emit ChangeUnwrapFee(
            unwrapFeeDivisor,
            nextUnwrapFeeDivisor,
            msg.sender
        );
        unwrapFeeDivisor = nextUnwrapFeeDivisor;
    }

    /**
     * @notice Execute interactions `interaction`
     * @notice Does not need ids because a single interaction does not require
     *  the accounting system
     * @dev MUST HAVE nonReentrant modifier.
     * @dev call to _doInteraction() binds msg.sender to userAddress
     * @param interaction Executed to produce a set of balance updates
     */
    function doInteraction(Interaction calldata interaction)
        external
        override
        nonReentrant
        returns (
            uint256 burnId,
            uint256 burnAmount,
            uint256 mintId,
            uint256 mintAmount
        )
    {
        emit OceanTransaction(msg.sender, 1);
        return _doInteraction(interaction, msg.sender);
    }

    /**
     * @notice Execute interactions `interactions` with tokens `ids`
     * @notice ids must include all tokens invoked during the transaction
     * @notice ids are used for memory allocation in the intra-transaction
     *  accounting system.
     * @dev MUST HAVE nonReentrant modifier.
     * @dev call to _doMultipleInteractions() binds msg.sender to userAddress
     * @param interactions Executed to produce a set of balance updates
     * @param ids Ocean IDs of the tokens invoked by the interactions.
     */
    function doMultipleInteractions(
        Interaction[] calldata interactions,
        uint256[] calldata ids
    )
        external
        payable
        override
        nonReentrant
        returns (
            uint256[] memory burnIds,
            uint256[] memory burnAmounts,
            uint256[] memory mintIds,
            uint256[] memory mintAmounts
        )
    {
        emit OceanTransaction(msg.sender, interactions.length);
        return _doMultipleInteractions(interactions, ids, msg.sender);
    }

    /**
     * @notice Execute interactions `interactions` on behalf of `userAddress`
     * @notice Does not need ids because a single interaction does not require
     *  the overhead of the intra-transaction accounting system
     * @dev MUST HAVE nonReentrant modifier.
     * @dev MUST HAVE onlyApprovedForwarder modifer.
     * @dev call to _doMultipleInteractions() forwards the userAddress
     * @param interaction Executed to produce a set of balance updates
     * @param userAddress interactions are executed on behalf of this address
     */
    function forwardedDoInteraction(
        Interaction calldata interaction,
        address userAddress
    )
        external
        override
        nonReentrant
        onlyApprovedForwarder(userAddress)
        returns (
            uint256 burnId,
            uint256 burnAmount,
            uint256 mintId,
            uint256 mintAmount
        )
    {
        emit ForwardedOceanTransaction(msg.sender, userAddress, 1);
        return _doInteraction(interaction, userAddress);
    }

    /**
     * @notice Execute interactions `interactions` with tokens `ids` on behalf of `userAddress`
     * @notice ids must include all tokens invoked during the transaction
     * @notice ids are used for memory allocation in the intra-transaction
     *  accounting system.
     * @dev MUST HAVE nonReentrant modifier.
     * @dev MUST HAVE onlyApprovedForwarder modifer.
     * @dev call to _doMultipleInteractions() forwards the userAddress
     * @param interactions Executed to produce a set of balance updates
     * @param ids Ocean IDs of the tokens invoked by the interactions.
     * @param userAddress interactions are executed on behalf of this address
     */
    function forwardedDoMultipleInteractions(
        Interaction[] calldata interactions,
        uint256[] calldata ids,
        address userAddress
    )
        external
        payable
        override
        nonReentrant
        onlyApprovedForwarder(userAddress)
        returns (
            uint256[] memory burnIds,
            uint256[] memory burnAmounts,
            uint256[] memory mintIds,
            uint256[] memory mintAmounts
        )
    {
        emit ForwardedOceanTransaction(
            msg.sender,
            userAddress,
            interactions.length
        );
        return _doMultipleInteractions(interactions, ids, userAddress);
    }

    /**
     * @dev This callback is part of IERC1155Receiver, which we must implement
     *  to wrap ERC-1155 tokens.
     */
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(OceanERC1155, IERC165)
        returns (bool)
    {
        return
            interfaceId == type(IERC1155Receiver).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external view override returns (bytes4) {
        if (_ERC721InteractionStatus == INTERACTION) {
            return IERC721Receiver.onERC721Received.selector;
        } else {
            return 0;
        }
    }

    /**
     * @dev This callback is part of IERC1155Receiver, which we must implement
     *  to wrap ERC-1155 tokens.
     * @dev The Ocean only accepts ERC1155 transfers initiated by the Ocean
     *  while executing interactions.
     * @dev We don't revert, prefering to let the external contract
     *  decide what it wants to do when safeTransfer is called on a contract
     *  that does not return the expected selector.
     */
    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes calldata
    ) external view override returns (bytes4) {
        if (_ERC1155InteractionStatus == INTERACTION) {
            return IERC1155Receiver.onERC1155Received.selector;
        } else {
            return 0;
        }
    }

    /**
     * @dev This callback is part of IERC1155Receiver, which we must implement
     *  to wrap ERC-1155 tokens.
     * @dev The Ocean never initiates ERC1155 Batch Transfers.
     * @dev We don't revert, prefering to let the external contract
     *  decide what it wants to do when safeTransfer is called on a contract
     *  that does not return the expected selector.
     */
    function onERC1155BatchReceived(
        address,
        address,
        uint256[] calldata,
        uint256[] calldata,
        bytes calldata
    ) external pure override returns (bytes4) {
        return 0;
    }

    /**
     * @dev This function handles both forwarded and non-forwarded single
     *  interactions
     * @dev the external functions that pass through their arguments to this
     *  function have more information about the arguments.
     * @param interaction the current interaction passed by the caller
     * @param userAddress In the case of a forwarded interaction, this value
     *  is passed by the caller, and this value must be validated against the
     *  approvals set on this address and the caller's address (`msg.sender`)
     *  In the case of a non-forwarded interaction, this value is the caller's
     *  address.
     */
    function _doInteraction(
        Interaction calldata interaction,
        address userAddress
    )
        internal
        returns (
            uint256 inputToken,
            uint256 inputAmount,
            uint256 outputToken,
            uint256 outputAmount
        )
    {
        // Begin by unpacking the interaction type and the external contract
        (
            InteractionType interactionType,
            address externalContract
        ) = _unpackInteractionTypeAndAddress(interaction);

        // Determine the specified token based on the interaction type and the
        // interaction's external contract address, inputToken, outputToken,
        // and metadata fields. The specified token is the token
        // whose amount the user specifies.
        uint256 specifiedToken = _getSpecifiedToken(
            interactionType,
            externalContract,
            interaction
        );

        // Here we call _executeInteraction(), which is just a big
        // if... else if... block branching on interaction type.
        // Each branch sets the inputToken and outputToken and their
        // respective amounts. This abstraction is what lets us treat
        // interactions uniformly.
        (
            inputToken,
            inputAmount,
            outputToken,
            outputAmount
        ) = _executeInteraction(
            interaction,
            interactionType,
            externalContract,
            specifiedToken,
            interaction.specifiedAmount,
            userAddress
        );

        // if _executeInteraction returned a positive value for inputAmount,
        // this amount must be deducted from the user's Ocean balance
        if (inputAmount > 0) {
            // since uint, same as (inputAmount != 0)
            _burn(userAddress, inputToken, inputAmount);
        }

        // if _executeInteraction returned a positive value for outputAmount,
        // this amount must be credited to the user's Ocean balance
        if (outputAmount > 0) {
            // since uint, same as (outputAmount != 0)
            _mint(userAddress, outputToken, outputAmount);
        }
    }

    /**
     * @dev This function handles both forwarded and non-forwarded multiple
     *  interactions
     * @dev the external functions that pass through their arguments to this
     *  function have more information about the arguments.
     * @param interactions The interactions passed by the caller
     * @param ids the ids passed by the caller
     * @param userAddress In the case of a forwarded interaction, this value
     *  is passed by the caller, and this value must be validated against the
     *  approvals set on this address and the caller's address (`msg.sender`)
     *  In the case of a non-forwarded interaction, this value is the caller's
     *  address.
     */
    function _doMultipleInteractions(
        Interaction[] calldata interactions,
        uint256[] calldata ids,
        address userAddress
    )
        internal
        returns (
            uint256[] memory burnIds,
            uint256[] memory burnAmounts,
            uint256[] memory mintIds,
            uint256[] memory mintAmounts
        )
    {
        // Use the passed ids to create an array of balance deltas, used in
        // the intra-transaction accounting system.
        BalanceDelta[] memory balanceDeltas = new BalanceDelta[](ids.length);
        for (uint256 i = 0; i < ids.length; ++i) {
            balanceDeltas[i] = BalanceDelta(ids[i], 0);
        }

        // Ether payments are push only.  We always wrap ERC-X tokens using pull
        // payments, so we cannot wrap Ether using the same pattern.
        // We unwrap ERC-X tokens using push payments, so we can unwrap Ether
        // the same way.
        if (msg.value != 0) {
            // If msg.value != 0 and the user did not pass the WRAPPED_ETHER_ID
            // as an id in the ids array, the balance delta library will revert
            // This protects users who accidentally provide a msg.value.
            balanceDeltas.increaseBalanceDelta(WRAPPED_ETHER_ID, msg.value);
            emit EtherWrap(msg.value, userAddress);
        }

        // Execute the interactions
        {
            /**
             * @dev Solidity does not reuse memory that has gone out of scope
             *  and the gas cost of memory usage grows quadratically.
             * @dev We passed interactions as calldata to lower memory usage.
             *  However, accessing the members of a calldata structure uses
             *  more local identifiers than accessing the members of an
             *  in-memory structure. We're right up against the limit on
             *  local identifiers. To solve this, we allocate a single
             *  structure in memory and copy the calldata structures over one
             *  by one as we process them.
             */
            Interaction memory interaction;
            // This pulls the user's address to the top of the stack, above
            // the ids array, which we won't need again. We're right up against
            // the locals limit and this does the trick. Is there a better way?
            address userAddress_ = userAddress;
            for (uint256 i = 0; i < interactions.length; ++i) {
                interaction = interactions[i];

                (
                    InteractionType interactionType,
                    address externalContract
                ) = _unpackInteractionTypeAndAddress(interaction);

                // specifiedToken is the token whose amount the user specifies
                uint256 specifiedToken = _getSpecifiedToken(
                    interactionType,
                    externalContract,
                    interaction
                );

                // A user can pass uint256.max as the specifiedAmount when they
                // want to use the total amount of the token held in the
                // balance delta. Otherwise, the specifiedAmount is just the
                // amount the user passed for this interaction.
                uint256 specifiedAmount;
                if (interaction.specifiedAmount == GET_BALANCE_DELTA) {
                    specifiedAmount = balanceDeltas.getBalanceDelta(
                        interactionType,
                        specifiedToken
                    );
                } else {
                    specifiedAmount = interaction.specifiedAmount;
                }

                (
                    uint256 inputToken,
                    uint256 inputAmount,
                    uint256 outputToken,
                    uint256 outputAmount
                ) = _executeInteraction(
                        interaction,
                        interactionType,
                        externalContract,
                        specifiedToken,
                        specifiedAmount,
                        userAddress_
                    );

                // inputToken is given up by the user during the interaction
                if (inputAmount > 0) {
                    // equivalent to (inputAmount != 0)
                    balanceDeltas.decreaseBalanceDelta(inputToken, inputAmount);
                }

                // outputToken is gained by the user during the interaction
                if (outputAmount > 0) {
                    // equivalent to (outputAmount != 0)
                    balanceDeltas.increaseBalanceDelta(
                        outputToken,
                        outputAmount
                    );
                }
            }
        }

        // Persist intra-transaction balance deltas to the Ocean's public ledger
        {
            // Place positive deltas into mintIds and mintAmounts
            // Place negative deltas into burnIds and burnAmounts
            (mintIds, mintAmounts, burnIds, burnAmounts) = balanceDeltas
                .createMintAndBurnArrays();

            // Here we should know that uint[] memory arr = new uint[](0);
            // produces a reference to an empty array called arr with property
            // (arr.length == 0)

            // mint the positive deltas to the user's balances
            if (mintIds.length == 1) {
                // if there's only one we can just use the more semantically
                // appropriate _mint
                _mint(userAddress, mintIds[0], mintAmounts[0]);
            } else if (mintIds.length > 1) {
                // if there's more than one we use _mintBatch
                _mintBatch(userAddress, mintIds, mintAmounts);
            } // if there are none, we do nothing

            // burn the positive deltas from the user's balances
            if (burnIds.length == 1) {
                // if there's only one we can just use the more semantically
                // appropriate _burn
                _burn(userAddress, burnIds[0], burnAmounts[0]);
            } else if (burnIds.length > 1) {
                // if there's more than one we use _burnBatch
                _burnBatch(userAddress, burnIds, burnAmounts);
            } // if there are none, we do nothing
        }
    }

    /**
     * @dev Here is the core logic shared between doInteraction and
     *  doMultipleInteractions
     * @dev State mutations on the external ledgers happen during wraps/unwraps
     * @dev State mutations on the Ocean's ledger for the externalContract
     *  happen during computeInputAmount/computeOutputAmount
     * @dev State mutations for the userAddress MUST happen in the calling
     *  context based on the return values of this function.
     * @param interaction the current interaction passed from calldata
     * @param interactionType the type of interaction unpacked by caller
     * @param externalContract the address of the external contract parsed by caller
     * @param specifiedToken the token in this interaction that specifiedAmount
     *  refers to
     * @param specifiedAmount the amount of specifiedToken being used in this
     *  interaction
     * @param userAddress the address of the user this interaction is being
     *  executed on behalf of. This is passed to the external contract.
     * @return inputToken The token on the Ocean that the user is giving up
     * @return inputAmount The amount of inputToken that the user is giving up
     * @return outputToken The token on the Ocean that the user is gaining
     * @return outputAmount The amount of ouputToken that the user is gaining
     */
    function _executeInteraction(
        Interaction memory interaction,
        InteractionType interactionType,
        address externalContract,
        uint256 specifiedToken,
        uint256 specifiedAmount,
        address userAddress
    )
        internal
        returns (
            uint256 inputToken,
            uint256 inputAmount,
            uint256 outputToken,
            uint256 outputAmount
        )
    {
        if (interactionType == InteractionType.ComputeOutputAmount) {
            inputToken = specifiedToken;
            inputAmount = specifiedAmount;
            outputToken = interaction.outputToken;
            outputAmount = _computeOutputAmount(
                externalContract,
                inputToken,
                outputToken,
                inputAmount,
                userAddress,
                interaction.metadata
            );
        } else if (interactionType == InteractionType.ComputeInputAmount) {
            inputToken = interaction.inputToken;
            outputToken = specifiedToken;
            outputAmount = specifiedAmount;
            inputAmount = _computeInputAmount(
                externalContract,
                inputToken,
                outputToken,
                outputAmount,
                userAddress,
                interaction.metadata
            );
        } else if (interactionType == InteractionType.WrapErc20) {
            inputToken = 0;
            inputAmount = 0;
            outputToken = specifiedToken;
            outputAmount = specifiedAmount;
            _erc20Wrap(
                externalContract,
                outputAmount,
                userAddress,
                outputToken
            );
        } else if (interactionType == InteractionType.UnwrapErc20) {
            inputToken = specifiedToken;
            inputAmount = specifiedAmount;
            outputToken = 0;
            outputAmount = 0;
            _erc20Unwrap(
                externalContract,
                inputAmount,
                userAddress,
                inputToken
            );
        } else if (interactionType == InteractionType.WrapErc721) {
            // An ERC-20 or ERC-1155 contract can have a transfer with
            // any amount including zero. Here, we need to require that
            // the specifiedAmount is equal to one, since the external
            // call to the ERC-721 contract does not include an amount,
            // and the ledger is mutated based on the specifiedAmount.
            require(specifiedAmount == 1, "NFT amount != 1");
            inputToken = 0;
            inputAmount = 0;
            outputToken = specifiedToken;
            outputAmount = specifiedAmount;
            _erc721Wrap(
                externalContract,
                uint256(interaction.metadata),
                userAddress,
                outputToken
            );
        } else if (interactionType == InteractionType.UnwrapErc721) {
            // See the comment in the preceeding else if block.
            require(specifiedAmount == 1, "NFT amount != 1");
            inputToken = specifiedToken;
            inputAmount = specifiedAmount;
            outputToken = 0;
            outputAmount = 0;
            _erc721Unwrap(
                externalContract,
                uint256(interaction.metadata),
                userAddress,
                inputToken
            );
        } else if (interactionType == InteractionType.WrapErc1155) {
            inputToken = 0;
            inputAmount = 0;
            outputToken = specifiedToken;
            outputAmount = specifiedAmount;
            _erc1155Wrap(
                externalContract,
                uint256(interaction.metadata),
                outputAmount,
                userAddress,
                outputToken
            );
        } else if (interactionType == InteractionType.UnwrapErc1155) {
            inputToken = specifiedToken;
            inputAmount = specifiedAmount;
            outputToken = 0;
            outputAmount = 0;
            _erc1155Unwrap(
                externalContract,
                uint256(interaction.metadata),
                inputAmount,
                userAddress,
                inputToken
            );
        } else {
            assert(
                interactionType == InteractionType.UnwrapEther &&
                    specifiedToken == WRAPPED_ETHER_ID
            );
            inputToken = specifiedToken;
            inputAmount = specifiedAmount;
            outputToken = 0;
            outputAmount = 0;
            _etherUnwrap(inputAmount, userAddress);
        }
    }

    /**
     * @param interaction the interaction
     * @dev the first byte contains the interactionType
     * @dev the next eleven bytes are IGNORED
     * @dev the final twenty bytes are the address targeted by the interaction
     */
    function _unpackInteractionTypeAndAddress(Interaction memory interaction)
        internal
        pure
        returns (InteractionType interactionType, address externalContract)
    {
        bytes32 interactionTypeAndAddress = interaction
            .interactionTypeAndAddress;
        interactionType = InteractionType(uint8(interactionTypeAndAddress[0]));
        externalContract = address(uint160(uint256(interactionTypeAndAddress)));
    }

    /**
     * @param interactionType determines how we derive the specifiedToken
     * @param externalContract is the target of the interaction's external call
     * @param interaction the interaction's fields are interpreted based on
     *  the Interaction type. See the declarations in Interactions.sol
     * @return specifiedToken is the ocean's internal ID for the token in a
     *  interaction that's amount is specified by the user.
     */
    function _getSpecifiedToken(
        InteractionType interactionType,
        address externalContract,
        Interaction memory interaction
    ) internal view returns (uint256 specifiedToken) {
        if (
            interactionType == InteractionType.WrapErc20 ||
            interactionType == InteractionType.UnwrapErc20
        ) {
            specifiedToken = _calculateOceanId(externalContract, 0);
        } else if (
            interactionType == InteractionType.WrapErc721 ||
            interactionType == InteractionType.WrapErc1155 ||
            interactionType == InteractionType.UnwrapErc721 ||
            interactionType == InteractionType.UnwrapErc1155
        ) {
            specifiedToken = _calculateOceanId(
                externalContract,
                uint256(interaction.metadata)
            );
        } else if (interactionType == InteractionType.ComputeInputAmount) {
            specifiedToken = interaction.outputToken;
        } else if (interactionType == InteractionType.ComputeOutputAmount) {
            specifiedToken = interaction.inputToken;
        } else {
            assert(interactionType == InteractionType.UnwrapEther);
            specifiedToken = WRAPPED_ETHER_ID;
        }
    }

    /**
     * @dev A primitive is an external smart contract that establishes a market
     *  between two or more tokens.
     * @dev the external contract's Ocean balances are mutated
     *  immediately after the external call returns. If the external
     *  contract does not want to receive the inputToken, it should revert
     *  the transaction.
     * @param primitive A contract that implements IOceanPrimitive
     * @param inputToken The token offered to the contract
     * @param outputToken The token requested from the contract
     * @param inputAmount The amount of the inputToken offered
     * @param userAddress The address of the user whose balances are being
     *  updated during the transaction.
     * @param metadata The function of this parameter is up to the contract
     *  it is the responsibility of the caller to know what the called contract
     *  expects.
     * @return outputAmount the amount of the outputToken the contract gives
     *  in return for the inputAmount of the inputToken.
     */
    function _computeOutputAmount(
        address primitive,
        uint256 inputToken,
        uint256 outputToken,
        uint256 inputAmount,
        address userAddress,
        bytes32 metadata
    ) internal returns (uint256 outputAmount) {
        outputAmount = IOceanPrimitive(primitive).computeOutputAmount(
            inputToken,
            outputToken,
            inputAmount,
            userAddress,
            metadata
        );

        _updateBalancesOfPrimitive(
            primitive,
            inputToken,
            inputAmount,
            outputToken,
            outputAmount
        );

        emit ComputeOutputAmount(
            primitive,
            inputToken,
            outputToken,
            inputAmount,
            outputAmount,
            userAddress
        );
    }

    /**
     * @dev A primitive is an external smart contract that establishes a market
     *  between two or more tokens.
     * @dev the external contract's Ocean balances are mutated
     *  immediately after the external call returns. If the external
     *  contract does not want to receive the outputToken, it should revert
     *  the transaction.
     * @param primitive A contract that implements IOceanPrimitive
     * @param inputToken The token offered to the contract
     * @param outputToken The token requested from the contract
     * @param outputAmount The amount of the outputToken offered
     * @param userAddress The address of the user whose balances are being
     *  updated during the transaction.
     * @param metadata The function of this parameter is up to the contract
     *  it is the responsibility of the caller to know what the called contract
     *  expects.
     * @return inputAmount the amount of the inputToken the contract gives
     *  in return for the outputAmount of the outputToken.
     */
    function _computeInputAmount(
        address primitive,
        uint256 inputToken,
        uint256 outputToken,
        uint256 outputAmount,
        address userAddress,
        bytes32 metadata
    ) internal returns (uint256 inputAmount) {
        inputAmount = IOceanPrimitive(primitive).computeInputAmount(
            inputToken,
            outputToken,
            outputAmount,
            userAddress,
            metadata
        );

        _updateBalancesOfPrimitive(
            primitive,
            inputToken,
            inputAmount,
            outputToken,
            outputAmount
        );

        emit ComputeInputAmount(
            primitive,
            inputToken,
            outputToken,
            inputAmount,
            outputAmount,
            userAddress
        );
    }

    /**
     * @dev Wrap an ERC-20 token into the ocean. The Ocean ID is
     *  derived from the contract address and a tokenId of 0.
     * @notice Token amounts are normalized to 18 decimal places.
     * @dev This means that to wrap 5 units of token A, which has 6 decimals,
     *  and 5 units of token B, which has 18 decimals, the user would specify
     *  5 * 10**18 for both token A and B.
     * @param tokenAddress address of the ERC-20 token
     * @param amount amount of the ERC-20 token to be wrapped, in terms of
     *  18-decimal fixed point
     * @param userAddress the address of the user who is wrapping the token
     */
    function _erc20Wrap(
        address tokenAddress,
        uint256 amount,
        address userAddress,
        uint256 outputToken
    ) private {
        try IERC20Metadata(tokenAddress).decimals() returns (uint8 decimals) {
            /// @dev the amount passed as an argument to the external token
            uint256 transferAmount;
            /// @dev the leftover amount accumulated by the Ocean.
            uint256 dust;

            (transferAmount, dust) = _determineTransferAmount(amount, decimals);

            // If the user is unwrapping a delta, the residual dust could be
            // written to the user's ledger balance. However, it costs the
            // same amount of gas to place the dust on the owner's balance,
            // and accumulation of dust may eventually result in
            // transferrable units again.
            _grantFeeToOcean(outputToken, dust);

            SafeERC20.safeTransferFrom(
                IERC20(tokenAddress),
                userAddress,
                address(this),
                transferAmount
            );

            emit Erc20Wrap(
                tokenAddress,
                transferAmount,
                amount,
                dust,
                userAddress,
                outputToken
            );
        } catch {
            revert("Could not get decimals()");
        }
    }

    /**
     * @dev Unwrap an ERC-20 token out of the ocean. The Ocean ID is
     *  derived from the contract address and a tokenId of 0.
     * @notice tokens are normalized to 18 decimal places.
     * @notice unwrap amounts may be subject to a fee that reduces the amount
     *  moved on the external token's ledger. To unwrap an exact amount, the
     *  caller should compute off-chain what specifiedAmount results in the
     *  desired unwrap amount.
     * @dev This means that to wrap 5 units of token A, which has 6 decimals,
     *  and 5 units of token B, which has 18 decimals, when the fee is zero,
     *  the user would specify 5 * 10**18 for both token A and B.
     * @dev If the fee is 1 basis point, the user would specify
     *  5000500050005000500
     *  This value was found by solving for x in the equation:
     *      x - Floor[x * (1/10000)] = 5000000000000000000
     * @param tokenAddress address of the ERC-20 token
     * @param amount amount of the ERC-20 token to be unwrapped, in terms of
     *  18-decimal fixed point
     * @param userAddress the address of the user who is unwrapping the token
     */
    function _erc20Unwrap(
        address tokenAddress,
        uint256 amount,
        address userAddress,
        uint256 inputToken
    ) private {
        try IERC20Metadata(tokenAddress).decimals() returns (uint8 decimals) {
            uint256 feeCharged = _calculateUnwrapFee(amount);
            uint256 amountRemaining = amount - feeCharged;

            (uint256 transferAmount, uint256 truncated) = _convertDecimals(
                NORMALIZED_DECIMALS,
                decimals,
                amountRemaining
            );
            feeCharged += truncated;

            _grantFeeToOcean(inputToken, feeCharged);

            SafeERC20.safeTransfer(
                IERC20(tokenAddress),
                userAddress,
                transferAmount
            );
            emit Erc20Unwrap(
                tokenAddress,
                transferAmount,
                amount,
                feeCharged,
                userAddress,
                inputToken
            );
        } catch {
            revert("Could not get decimals()");
        }
    }

    /**
     * @dev wrap an ERC-721 NFT into the Ocean. The Ocean ID is derived from
     *  tokenAddress and tokenId.
     * @param tokenAddress address of the ERC-721 contract
     * @param tokenId ID of the NFT on the ERC-721 ledger
     * @param userAddress the address of the user who is wrapping the NFT
     */
    function _erc721Wrap(
        address tokenAddress,
        uint256 tokenId,
        address userAddress,
        uint256 oceanId
    ) private {
        _ERC721InteractionStatus = INTERACTION;
        IERC721(tokenAddress).safeTransferFrom(
            userAddress,
            address(this),
            tokenId
        );
        _ERC721InteractionStatus = NOT_INTERACTION;
        emit Erc721Wrap(tokenAddress, tokenId, userAddress, oceanId);
    }

    /**
     * @dev Unwrap an ERC-721 NFT out of the Ocean. The Ocean ID is derived
     *  from tokenAddress and tokenId.
     * @param tokenAddress address of the ERC-721 contract
     * @param tokenId ID of the NFT on the ERC-721 ledger
     * @param userAddress the address of the user who is unwrapping the NFT
     */
    function _erc721Unwrap(
        address tokenAddress,
        uint256 tokenId,
        address userAddress,
        uint256 oceanId
    ) private {
        IERC721(tokenAddress).safeTransferFrom(
            address(this),
            userAddress,
            tokenId
        );
        emit Erc721Unwrap(tokenAddress, tokenId, userAddress, oceanId);
    }

    /**
     * @dev Wrap an ERC-1155 token into the Ocean. The Ocean ID is derived
     *  from tokenAddress and tokenId.
     * @notice ERC-1155 amounts and in-Ocean amounts are equal. If a token
     *  implemented using ERC-1155 should have the same value as other tokens
     *  implemented using ERC-20, the ERC-1155 should use an 18-decimal
     *  representation.
     * @param tokenAddress address of the ERC-1155 contract
     * @param tokenId ID of the token on the ERC-1155 ledger
     * @param amount the amount of the token being wrapped.
     * @param userAddress the address of the user who is wrapping the token
     */
    function _erc1155Wrap(
        address tokenAddress,
        uint256 tokenId,
        uint256 amount,
        address userAddress,
        uint256 oceanId
    ) private {
        require(tokenAddress != address(this), "No recursive wraps");
        _ERC1155InteractionStatus = INTERACTION;
        IERC1155(tokenAddress).safeTransferFrom(
            userAddress,
            address(this),
            tokenId,
            amount,
            ""
        );
        _ERC1155InteractionStatus = NOT_INTERACTION;
        emit Erc1155Wrap(tokenAddress, tokenId, amount, userAddress, oceanId);
    }

    /**
     * @dev Unwrap an ERC-1155 token out of the Ocean. The Ocean ID is derived
     *  from tokenAddress and tokenId.
     * @notice ERC-1155 amounts and in-Ocean amounts are equal. If a token
     *  implemented using ERC-1155 should have the same value as other tokens
     *  implemented using ERC-20, the ERC-1155 should use an 18-decimal
     *  representation.
     * @notice unwrap amounts may be subject to a fee that reduces the amount
     *  moved on the external token's ledger. To unwrap an exact amount, the
     *  caller should compute off-chain what specifiedAmount results in the
     *  desired unwrap amount. If the user wants to receive 100_000 of a token
     *  and the fee is 1 basis point, the user should specify 100_010
     *  This value was found by solving for x in the equation:
     *      x - Floor[x * (1/10000)] = 100000
     * @param tokenAddress address of the ERC-1155 contract
     * @param tokenId ID of the token on the ERC-1155 ledger
     * @param amount the amount of the token being wrapped.
     * @param userAddress the address of the user who is wrapping the token
     */
    function _erc1155Unwrap(
        address tokenAddress,
        uint256 tokenId,
        uint256 amount,
        address userAddress,
        uint256 oceanId
    ) private {
        require(tokenAddress != address(this), "No recursive unwraps");
        uint256 feeCharged = _calculateUnwrapFee(amount);
        uint256 amountRemaining = amount - feeCharged;
        _grantFeeToOcean(oceanId, feeCharged);
        IERC1155(tokenAddress).safeTransferFrom(
            address(this),
            userAddress,
            tokenId,
            amountRemaining,
            ""
        );
        emit Erc1155Unwrap(
            tokenAddress,
            tokenId,
            amount,
            feeCharged,
            userAddress,
            oceanId
        );
    }

    /**
     * @dev Unwrap Ether out of the ocean.  The Ocean ID of shETH is computed
     *  in the constructor using the address of the ocean and a tokenId of 0.
     * @param amount The amount of Ether to unwrap.
     * @param userAddress The user performing the unwrap.
     */
    function _etherUnwrap(uint256 amount, address userAddress) private {
        uint256 feeCharged = _calculateUnwrapFee(amount);
        _grantFeeToOcean(WRAPPED_ETHER_ID, feeCharged);
        uint256 transferAmount = amount - feeCharged;
        payable(userAddress).transfer(transferAmount);
        emit EtherUnwrap(transferAmount, feeCharged, userAddress);
    }

    /**
     * @notice If the primitive registered the inputToken, it does not
     *  receive any of the inputToken.
     * @notice If the primitive registered the outputToken, it does not
     *  lose any of the outputToken.
     * @notice look at the public function registerNewTokens()
     * @notice We cannot keep the primitive's balance changes in memory.
     *  A primitive relies on the ocean for its accounting, so it must always
     *  receive a correct answer when it queries balanceOf() or
     *  balanceOfBatch().
     *  When the ocean receives a balanceOf() call, this call has its own
     *  memory space. The ocean cannot reach down through the call stack to
     *  get a delta stored in the memory of an earlier call.
     *      primitive -> ocean.balanceOf(address(this), token)  [mem_space_2]
     *      ocean -> primitive.computeOutputAmount()          [mem_space_1]
     *      EOA -> ocean.doMultipleInteractions()               [mem_space_0]
     *  Because we have no way of maintaining coherence between dirty memory
     *  and stale storage, our only option is to always have up-to-date values
     *  in storage.
     */
    function _updateBalancesOfPrimitive(
        address primitive,
        uint256 inputToken,
        uint256 inputAmount,
        uint256 outputToken,
        uint256 outputAmount
    ) internal {
        // If the input token is not one of the primitive's registered tokens,
        // the primitive receives the input amount it was passed.
        // Otherwise, the tokens will be implicitly burned by the primitive
        // later in the transaction
        if (
            _isNotTokenOfPrimitive(inputToken, primitive) && (inputAmount > 0)
        ) {
            // Since the primitive consented to receiving this token by not
            // reverting when it was called, we mint the token without
            // doing a safe transfer acceptance check. This breaks the
            // ERC1155 specification but in a way we hope is inconsequential, since
            // all primitives are developed by people who must be
            // aware of how the ocean works.
            _mintWithoutSafeTransferAcceptanceCheck(
                primitive,
                inputToken,
                inputAmount
            );
        }

        // If the output token is not one of the primitive's tokens, the
        // primitive loses the output amount it just computed.
        // Otherwise, the tokens will be implicitly minted by the primitive
        // later in the transaction
        if (
            _isNotTokenOfPrimitive(outputToken, primitive) && (outputAmount > 0)
        ) {
            _burn(primitive, outputToken, outputAmount);
        }
    }

    /**
     * @dev This function determines the correct argument to pass to
     *  the external token contract
     * @dev Say the in-Ocean unwrap amount (in 18-decimal) is 0.123456789012345678
     *      If the external token uses decimals == 6:
     *          transferAmount == 123456
     *          dust == 789012345678
     *      If the external token uses decimals == 18:
     *          transferAmount == 123456789012345678
     *          dust == 0
     *      If the external token uses decimals == 21:
     *          transferAmount == 123456789012345678000
     *          dust == 0
     * @param amount the amount of in-Ocean tokens being unwrapped
     * @param decimals returned by IERC20(token).decimals()
     * @return transferAmount the amount passed to SafeERC20.safeTransfer()
     * @return dust The amount of in-Ocean token that are not unwrapped
     *  due to the mismatch between the external token's decimal basis and the
     *  Ocean's NORMALIZED_DECIMALS basis.
     */
    function _determineTransferAmount(uint256 amount, uint8 decimals)
        private
        pure
        returns (uint256 transferAmount, uint256 dust)
    {
        // if (decimals < 18), then converting 18-decimal amount to decimals
        // transferAmount will likely result in amount being truncated. This
        // case is most likely to occur when a user is wrapping a delta as the
        // final interaction in a transaction.
        uint256 truncated;

        (transferAmount, truncated) = _convertDecimals(
            NORMALIZED_DECIMALS,
            decimals,
            amount
        );

        if (truncated > 0) {
            // Here, FLOORish(x) is not to the nearest integer less than `x`,
            // but rather to the nearest value with `decimals` precision less
            // than `x`. Likewise with CEILish(x).
            // When truncating, transferAmount is FLOORish(amount), but to
            // fully cover a potential delta, we need to transfer CEILish(amount)
            // if truncated == 0, FLOORish(amount) == CEILish(amount)
            // When truncated > 0, FLOORish(amount) + 1 == CEILish(AMOUNT)
            transferAmount += 1;
            // Now that we are transferring enough to cover the delta, we
            // need to determine how much of the token the user is actually
            // wrapping, in terms of 18-decimals.
            (
                uint256 normalizedTransferAmount,
                uint256 normalizedTruncatedAmount
            ) = _convertDecimals(decimals, NORMALIZED_DECIMALS, transferAmount);
            // If we truncated earlier, converting the other direction is adding
            // precision, which cannot truncate.
            assert(normalizedTruncatedAmount == 0);
            assert(normalizedTransferAmount > amount);
            dust = normalizedTransferAmount - amount;
        } else {
            // if truncated == 0, then we don't need to do anything fancy to
            // determine transferAmount, the result _convertDecimals() returns
            // is correct.
            dust = 0;
        }
    }

    /**
     * @dev convert a uint256 from one fixed point decimal basis to another,
     *   returning the truncated amount if a truncation occurs.
     * @dev fn(from, to, a) => b
     * @dev a = (x * 10**from) => b = (x * 10**to), where x is constant.
     * @param amountToConvert the amount being converted
     * @param decimalsFrom the fixed decimal basis of amountToConvert
     * @param decimalsTo the fixed decimal basis of the returned convertedAmount
     * @return convertedAmount the amount after conversion
     * @return truncatedAmount if (from > to), there may be some truncation, it
     *  is up to the caller to decide what to do with the truncated amount.
     */
    function _convertDecimals(
        uint8 decimalsFrom,
        uint8 decimalsTo,
        uint256 amountToConvert
    ) internal pure returns (uint256 convertedAmount, uint256 truncatedAmount) {
        if (decimalsFrom == decimalsTo) {
            // no shift
            convertedAmount = amountToConvert;
            truncatedAmount = 0;
        } else if (decimalsFrom < decimalsTo) {
            // Decimal shift left (add precision)
            uint256 shift = 10**(uint256(decimalsTo - decimalsFrom));
            convertedAmount = amountToConvert * shift;
            truncatedAmount = 0;
        } else {
            // Decimal shift right (remove precision) -> truncation
            uint256 shift = 10**(uint256(decimalsFrom - decimalsTo));
            convertedAmount = amountToConvert / shift;
            truncatedAmount = amountToConvert % shift;
        }
    }

    /**
     * @dev Divides an amount by the unwrapFeeDivisor state variable to
     *  determine the amount to deduct from the unwrap.
     * @dev when unwrapFeeDivisor > unwrapAmount, feeCharged == 0 due to floor
     *  behavior of integer division
     * @param unwrapAmount the amount being unwrapped
     * @return feeCharged the amount to be deducted from the unwrapAmount and
     *  granted to the Ocean's owner.
     */
    function _calculateUnwrapFee(uint256 unwrapAmount)
        private
        view
        returns (uint256 feeCharged)
    {
        feeCharged = unwrapAmount / unwrapFeeDivisor;
    }

    /**
     * @dev Updates the DAO's balance of a token when the fee is assessed.
     * @dev We only call the mint function when the fee amount is non-zero.
     */
    function _grantFeeToOcean(uint256 oceanId, uint256 amount) private {
        if (amount > 0) {
            // since uint, same as (amount != 0)
            _mintWithoutSafeTransferAcceptanceCheck(owner(), oceanId, amount);
        }
    }
}

File 2 of 23 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 3 of 23 : IERC20.sol
// SPDX-License-Identifier: MIT
// 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 4 of 23 : IERC165.sol
// 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 5 of 23 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 6 of 23 : IERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @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 7 of 23 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @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 8 of 23 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 9 of 23 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

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

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 10 of 23 : Interactions.sol
// SPDX-License-Identifier: unlicensed
// Cowri Labs Inc.

pragma solidity =0.8.10;

/**
 * @param interactionTypeAndAddress the type of interaction and the external
 *  contract called during this interaction.
 * @param inputToken this field is ignored except when the interaction type
 *  begins with "Compute".  During a "Compute" interaction, this token is given
 *  to the external contract.
 * @param outputToken this field is ignored except when the interaction type
 *  begins with "Compute".  During a "Compute" interaction, this token is
 *  received from the external contract.
 * @param specifiedAmount This value is the amount of the specified token.
 *  See the comment above the declaration for InteractionType for information
 *  on specified tokens.  When this value is equal to type(uint256).max, it is
 *  a request by the user to use the intra-transaction delta of the specified
 *  token as the specified amount.  See LibBalanceDelta for more information
 *  about this.  When the Ocean executes an interaction, it resolves the
 *  specifiedAmount before calling the external contract.  During a "721"
 *  interaction, the resolved specifiedAmount must be identically "1".
 * @param metadata This value is used in two ways.  During "Compute"
 *  interactions, it is forwarded to the external contract.  The external
 *  contract can define whatever expectations it wants for these 32 bytes.  The
 *  caller is expected to be aware of the expectations of the external contract
 *  invoked during the interaction.  During 721/1155 and wraps and unwraps,
 *  these bytes are cast to uint256 and used as the external ledger's token ID
 *  for the interaction.
 */
struct Interaction {
    bytes32 interactionTypeAndAddress;
    uint256 inputToken;
    uint256 outputToken;
    uint256 specifiedAmount;
    bytes32 metadata;
}

/**
 * InteractionType determines how the properties of Interaction are interpreted
 *
 * The interface implemented by the external contract, the specified token
 *  for the interaction, and what sign (+/-) of delta can be used are
 *  determined by the InteractionType.
 *
 * @param WrapErc20
 *      type(externalContract).interfaceId == IERC20
 *      specifiedToken == calculateOceanId(externalContract, 0)
 *      negative delta can be used as specifiedAmount
 *
 * @param UnwrapErc20
 *      type(externalContract).interfaceId == IERC20
 *      specifiedToken == calculateOceanId(externalContract, 0)
 *      positive delta can be used as specifiedAmount
 *
 * @param WrapErc721
 *      type(externalContract).interfaceId == IERC721
 *      specifiedToken == calculateOceanId(externalContract, metadata)
 *      negative delta can be used as specifiedAmount
 *
 * @param UnwrapErc721
 *      type(externalContract).interfaceId == IERC721
 *      specifiedToken == calculateOceanId(externalContract, metadata)
 *      positive delta can be used as specifiedAmount
 *
 * @param WrapErc1155
 *      type(externalContract).interfaceId == IERC1155
 *      specifiedToken == calculateOceanId(externalContract, metadata)
 *      negative delta can be used as specifiedAmount
 *
 * @param WrapErc1155
 *      type(externalContract).interfaceId == IERC1155
 *      specifiedToken == calculateOceanId(externalContract, metadata)
 *      positive delta can be used as specifiedAmount
 *
 * @param ComputeInputAmount
 *      type(externalContract).interfaceId == IOceanexternalContract
 *      specifiedToken == outputToken
 *      negative delta can be used as specifiedAmount
 *
 * @param ComputeOutputAmount
 *      type(externalContract).interfaceId == IOceanexternalContract
 *      specifiedToken == inputToken
 *      positive delta can be used as specifiedAmount
 */
enum InteractionType {
    WrapErc20,
    UnwrapErc20,
    WrapErc721,
    UnwrapErc721,
    WrapErc1155,
    UnwrapErc1155,
    ComputeInputAmount,
    ComputeOutputAmount,
    UnwrapEther
}

interface IOceanInteractions {
    function doMultipleInteractions(
        Interaction[] calldata interactions,
        uint256[] calldata ids
    )
        external
        payable
        returns (
            uint256[] memory burnIds,
            uint256[] memory burnAmounts,
            uint256[] memory mintIds,
            uint256[] memory mintAmounts
        );

    function forwardedDoMultipleInteractions(
        Interaction[] calldata interactions,
        uint256[] calldata ids,
        address userAddress
    )
        external
        payable
        returns (
            uint256[] memory burnIds,
            uint256[] memory burnAmounts,
            uint256[] memory mintIds,
            uint256[] memory mintAmounts
        );

    function doInteraction(Interaction calldata interaction)
        external
        returns (
            uint256 burnId,
            uint256 burnAmount,
            uint256 mintId,
            uint256 mintAmount
        );

    function forwardedDoInteraction(
        Interaction calldata interaction,
        address userAddress
    )
        external
        returns (
            uint256 burnId,
            uint256 burnAmount,
            uint256 mintId,
            uint256 mintAmount
        );
}

File 11 of 23 : IOceanFeeChange.sol
// SPDX-License-Identifier: unlicensed
// Cowri Labs Inc.

pragma solidity =0.8.10;

/// @notice to be implemented by a contract that is the Ocean.owner()
interface IOceanFeeChange {
    function changeUnwrapFee(uint256 nextUnwrapFeeDivisor) external;
}

File 12 of 23 : IOceanPrimitive.sol
// SPDX-License-Identifier: unlicensed
// Cowri Labs Inc.

pragma solidity =0.8.10;

/// @notice Implementing this allows a primitive to be called by the Ocean's
///  defi framework.
interface IOceanPrimitive {
    function computeOutputAmount(
        uint256 inputToken,
        uint256 outputToken,
        uint256 inputAmount,
        address userAddress,
        bytes32 metadata
    ) external returns (uint256 outputAmount);

    function computeInputAmount(
        uint256 inputToken,
        uint256 outputToken,
        uint256 outputAmount,
        address userAddress,
        bytes32 metadata
    ) external returns (uint256 inputAmount);

    function getTokenSupply(uint256 tokenId)
        external
        view
        returns (uint256 totalSupply);
}

File 13 of 23 : BalanceDelta.sol
// SPDX-License-Identifier: MIT
// Cowri Labs Inc.

pragma solidity =0.8.10;

import {InteractionType} from "./Interactions.sol";

/**
 * A BalanceDelta structure tracks a user's intra-transaction balance change
 *  for a particular token
 * @param tokenId ID of the tracked token in the accounting system
 * @param delta a signed integer that records the user's accumulated debit
 *  or credit.
 *
 * Examples:
 * BalanceDelta positiveDelta = BalanceDelta(0xDE..AD, 100);
 * BalanceDelta negativeDelta = BalanceDelta(0xBE..EF, -100);
 *
 * At the end of the transaction the deltas are applied to the user's balances
 *  to persist the effects of the transaction.
 */
struct BalanceDelta {
    uint256 tokenId;
    int256 delta;
}

/**
 * @dev Functions relating to the intra-transaction accounting system.
 * @dev This library relies on the fact that arrays in solidity are passed by
 *  reference, rather than by value.
 *
 * `self` is an array of BalanceDelta structures.
 *
 * @dev each function uses a greedy linear search, so if there are duplicate
 *  deltas for the same tokenId, only the first delta is operated on. The
 *  duplicates will always have {tokenID: $DUPLICATE, delta: 0}.  If an tokenId
 *  is missing, the library functions will revert the transaction.  If there is
 *  an unecessary tokenId or a duplicated tokenId, the only consequence is
 *  wasted gas, so the incentive for the user is to provide the minimal set of
 *  tokenIds.
 *
 * Because the delta is a signed integer, it can be positive or negative.
 *
 * At the end of the transaction, positive deltas are minted to the user and
 *  negative deltas are burned from the user.  This is done using the ERC-1155's
 *  _mintBatch() and _burnBatch().  Each take an array of IDs and an array
 *  of amounts.  BalanceDelta => (ids[i], amounts[i])
 */
library LibBalanceDelta {
    /**
     * @dev a BalanceDelta holds an int256 delta while the caller passes
     *  a uint256.  We need to make sure the cast won't silently truncate
     *  the most significant bit.
     * @dev because solidity numbers are two's complement representation,
     *  the absolute value of the maximum value is one unit higher than the
     *  maximum value of the minimum value.  By testing against
     *  type(int256).max, we know that amount will safely cast to both positive
     *  and negative int256 values.
     */
    modifier safeCast(uint256 amount) {
        require(
            uint256(type(int256).max) > amount,
            "Delta :: amount > int256max"
        );
        _;
    }

    /**
     * @dev increase a given tokenId's delta by an amount.
     */
    function increaseBalanceDelta(
        BalanceDelta[] memory self,
        uint256 tokenId,
        uint256 amount
    ) internal pure safeCast(amount) {
        uint256 index = _findIndexOfTokenId(self, tokenId);
        self[index].delta += int256(amount);
        return;
    }

    /**
     * @dev decrease a given tokenId's delta by an amount.
     */
    function decreaseBalanceDelta(
        BalanceDelta[] memory self,
        uint256 tokenId,
        uint256 amount
    ) internal pure safeCast(amount) {
        uint256 index = _findIndexOfTokenId(self, tokenId);
        self[index].delta -= int256(amount);
        return;
    }

    /**
     * @dev This function returns an unsigned amount given a tokenId and an
     *  interaction type.
     * @dev This function reverts when the sign of the tokenId's delta
     *  does not match the sign expected by the interaction type.
     *
     *  - All interaction types expect unsigned amounts as arguments.
     *  - Some interaction types, like wraps, increase a user's balance.
     *  - Others, like unwraps, decrease a user's balance.
     *  - The interactions that increase a user's balance can take a negative
     *   delta as an input. In effect, the debit represented by the delta is
     *   offset by the credit from the interaction.
     *  - Similarly, interactions that decrease a user's balance can take
     *   a positive delta as an input.
     *  - When a delta is of the wrong sign for the interaction type, we need
     *   to revert the transaction.
     *
     * EXAMPLE 1. Convert 100 DAI into as many USDC as possible
     * [0]  BalanceDelta[] = [ BalanceDelta(DAI, 0), BalanceDelta(USDC, 0) ]
     *  wrap(token: DAI, amount: 100)
     * [1]  BalanceDelta[] = [ BalanceDelta(DAI, 100), BalanceDelta(USDC, 0) ]
     *  computeOutputAmount(input: DAI, output: USDC, amount: GET_BALANCE_DELTA)
     * [2]  BalanceDelta[] = [ BalanceDelta(DAI, 0), BalanceDelta(USDC, 99.997) ]
     *  unwrap(token: USDC, amount: GET_BALANCE_DELTA)
     *
     * EXAMPLE 2. Convert as few DAI as possible into exactly 100 USDC
     * [0]  BalanceDelta[] = [ BalanceDelta(DAI, 0), BalanceDelta(USDC, 0) ]
     *  unwrap(token: USDC, amount: 100)
     * [1]  BalanceDelta[] = [ BalanceDelta(DAI, 0), BalanceDelta(USDC, -100) ]
     *  computeInputAmount(input: DAI, output: USDC, amount: GET_BALANCE_DELTA)
     * [2]  BalanceDelta[] = [ BalanceDelta(DAI, -100.003), BalanceDelta(USDC, 0) ]
     *  wrap(token: DAI, amount: GET_BALANCE_DELTA)
     *
     * EXAMPLE 3. Unwrap DAI twice (reverts)
     * [0]  BalanceDelta[] = [ BalanceDelta(DAI, 0), ]
     *  unwrap(token: DAI, amount: 100)
     * [1]  BalanceDelta[] = [ BalanceDelta(DAI, -100), ]
     *  unwrap(token: DAI, amount: GET_BALANCE_DELTA)
     * !!! Throw("PosDelta :: amount < 0") !!!
     */
    function getBalanceDelta(
        BalanceDelta[] memory self,
        InteractionType interaction,
        uint256 tokenId
    ) internal pure returns (uint256) {
        if (
            interaction == InteractionType.UnwrapErc20 ||
            interaction == InteractionType.UnwrapErc721 ||
            interaction == InteractionType.UnwrapErc1155 ||
            interaction == InteractionType.UnwrapEther ||
            interaction == InteractionType.ComputeOutputAmount
        ) {
            return _getPositiveBalanceDelta(self, tokenId);
        } else {
            // interaction == (Wrap* || ComputeInputAmount)
            return _getNegativeBalanceDelta(self, tokenId);
        }
    }

    /**
     * @dev This function transforms the accumulated deltas into the arguments
     *  expected by ERC-1155 _mintBatch() and _burnBatch so that the caller
     *  can apply the deltas to the ledger.
     * @dev ERC-1155 expects an array of ids and an array of amounts, paired by
     *  index.
     *  +-------+-------+-----------+
     *  | index | ids[] | amounts[] |
     *  +-------+-------+-----------+
     *  |  0    |  808  |  35       | <= BalanceDelta(tokenId: 808, delta: 35)
     *  |  1    |  310  |  12       | <= BalanceDelta(tokenId: 310, delta: 12)
     *  |  2    |  408  |  19       | <= BalanceDelta(tokenId: 408, delta: 19)
     *  +-------+-------+-----------+
     * @dev Positive deltas are minted to the user's balances
     * @dev Negative deltas are burned from the user's balances
     * @dev for an entry where (delta == 0), nothing is done
     * @notice the returned arrays may be empty (arr.length == 0) or singleton
     *  arrays (arr.length == 1).
     * @return mintIds array of IDs expected by ERC-1155 _mintBatch
     * @return mintAmounts array of amounts expected by ERC-1155 _mintBatch
     * @return burnIds array of IDs expected by ERC-1155 _burnBatch
     * @return burnAmounts array of amounts expected by ERC-1155 _burnBatch
     */
    function createMintAndBurnArrays(BalanceDelta[] memory self)
        internal
        pure
        returns (
            uint256[] memory mintIds,
            uint256[] memory mintAmounts,
            uint256[] memory burnIds,
            uint256[] memory burnAmounts
        )
    {
        (uint256 numberOfMints, uint256 numberOfBurns) = _getMintsAndBurns(
            self
        );

        mintIds = new uint256[](numberOfMints);
        mintAmounts = new uint256[](numberOfMints);

        burnIds = new uint256[](numberOfBurns);
        burnAmounts = new uint256[](numberOfBurns);

        _copyDeltasToMintAndBurnArrays(
            self,
            mintIds,
            mintAmounts,
            burnIds,
            burnAmounts
        );
    }

    /**
     * @dev Count the number of positive deltas and the number of negative
     *  deltas among the accumulated deltas.
     * @dev The return values of this function are used to allocate memory
     *  arrays.  This function is necessary because in-memory arrays in
     *  solidity do not support push() and pop() style operations.
     * @return numberOfMints the number of positive deltas
     * @return numberOfBurns the number of negative deltas
     */
    function _getMintsAndBurns(BalanceDelta[] memory self)
        private
        pure
        returns (uint256 numberOfMints, uint256 numberOfBurns)
    {
        uint256 numberOfZeros = 0;
        for (uint256 i = 0; i < self.length; ++i) {
            int256 delta = self[i].delta;
            if (delta > 0) {
                ++numberOfMints;
            } else if (delta < 0) {
                ++numberOfBurns;
            } else {
                ++numberOfZeros;
            }
        }
        assert((numberOfMints + numberOfBurns + numberOfZeros) == self.length);
    }

    /**
     * @dev Now that we have allocated a pair of mint arrays and a pair of burn
     *  arrays, we iterate over the balance deltas again, this time moving the
     *  positive deltas, along with their assosciated tokenIds into the mints
     *  arrays, and moving the negative deltas and their assosciated tokenIds
     *  into the burns arrays.
     */
    function _copyDeltasToMintAndBurnArrays(
        BalanceDelta[] memory self,
        uint256[] memory mintIds,
        uint256[] memory mintAmounts,
        uint256[] memory burnIds,
        uint256[] memory burnAmounts
    ) private pure {
        uint256 mintsSoFar = 0;
        uint256 burnsSoFar = 0;
        for (uint256 i = 0; i < self.length; ++i) {
            int256 delta = self[i].delta;
            if (delta > 0) {
                mintIds[mintsSoFar] = self[i].tokenId;
                mintAmounts[mintsSoFar] = uint256(delta);
                mintsSoFar += 1;
            } else if (delta < 0) {
                burnIds[burnsSoFar] = self[i].tokenId;
                burnAmounts[burnsSoFar] = uint256(-delta);
                burnsSoFar += 1;
            }
        }
        assert(
            (mintsSoFar == mintIds.length) && (burnsSoFar == burnIds.length)
        );
    }

    /**
     * @dev returns a delta for a interaction type that expects a positive delta
     *
     * SteInterps that take a positive delta:
     *   Unwrap*
     *   ComputeOutputAmount
     */
    function _getPositiveBalanceDelta(
        BalanceDelta[] memory self,
        uint256 tokenId
    ) private pure returns (uint256) {
        uint256 index = _findIndexOfTokenId(self, tokenId);
        int256 amount = self[index].delta;
        require(amount >= 0, "PosDelta :: amount < 0");
        return uint256(amount);
    }

    /**
     * @dev returns a delta for a interaction type that expects a negative delta
     *
     * Interactions that take a negative delta:
     *   Wrap*
     *   ComputeInputAmount
     */
    function _getNegativeBalanceDelta(
        BalanceDelta[] memory self,
        uint256 tokenId
    ) private pure returns (uint256) {
        uint256 index = _findIndexOfTokenId(self, tokenId);
        int256 amount = self[index].delta;
        require(amount <= 0, "NegDelta :: amount > 0");
        return uint256(-amount);
    }

    /**
     @dev a linear search for the first BalanceDelta with a certain tokenId
     @param tokenId the key we're searching for
     @return index the location of the key
     */
    function _findIndexOfTokenId(BalanceDelta[] memory self, uint256 tokenId)
        private
        pure
        returns (uint256 index)
    {
        for (index = 0; index < self.length; ++index) {
            if (self[index].tokenId == tokenId) {
                return index;
            }
        }
        revert("Delta :: missing token ID");
    }
}

File 14 of 23 : OceanERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.3.2 (token/ERC1155/ERC1155.sol)
// Cowri Labs, Inc., modifications licensed under: MIT

pragma solidity =0.8.10;

import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";

// OpenZeppelin Inherited Contracts
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

// ShellV2 Interface
import {IOceanToken} from "./IOceanToken.sol";

// ShellV2 Permit Signature
import {ERC1155PermitSignatureExtension} from "./ERC1155PermitSignatureExtension.sol";

/**
 * @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._
 * @dev modifications include removing unused hooks, creating a minting
 *  function that does not do a safeTransferAcceptanceCheck, and adding a
 *  mapping and functions to register and manage authority over tokens.
 * @dev Registered Tokens are Ocean-native issuances, such as Liquidity
 *  Provider tokens issued by an AMM built on top of the Ocean.
 */
contract OceanERC1155 is
    Context,
    ERC165,
    ERC1155PermitSignatureExtension,
    IERC1155,
    IERC1155MetadataURI,
    IOceanToken,
    Ownable,
    ReentrancyGuard
{
    using Address for address;

    /// @notice Mapping from token ID to address with authority over token's issuance
    mapping(uint256 => address) public tokensToPrimitives;

    uint256 constant FUSE_INTACT = 1;
    uint256 constant FUSE_BROKEN = 0;
    uint256 public permitFuse;

    // 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;

    event PermitFuseBroken(address indexed breakerAddress);
    event NewTokensRegistered(
        address indexed creator,
        uint256[] tokens,
        uint256[] nonces
    );

    /**
     * @dev See {_setURI}.
     */
    constructor(string memory uri_)
        ERC1155PermitSignatureExtension(
            bytes("shell-protocol-ocean"),
            bytes("1")
        )
    {
        _setURI(uri_);
        permitFuse = FUSE_INTACT;
    }

    function breakPermitFuse() external onlyOwner {
        permitFuse = FUSE_BROKEN;
        emit PermitFuseBroken(msg.sender);
    }

    /**
     * @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), "balanceOf(address(0))");
        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, "accounts.length != ids.length");

        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 nonReentrant {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "not owner nor 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 nonReentrant {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "not owner nor approved"
        );
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }

    /**
     * @dev Registered Tokens are tokens issued directly on the ocean's 1155 ledger.
     * @dev These are tokens that cannot be wrapped or unwrapped.
     * @dev We don't validate the inputs.  The happy path usage is for callers
     *  to obtain authority over tokens that have their ids derived from
     *  successive nonces.
     *
     *  registerNewTokens(0, n):
     *      _calculateOceanId(caller, 0)
     *      _calculateOceanId(caller, 1)
     *      ...
     *      _calculateOceanId(caller, n)
     *
     *  Since the ocean tracks the one to one relationship of:
     *    token => authority
     *  but not the one to many relationship of:
     *    authority => tokens
     *  it is nice UX to be able to re-derive the tokens on the fly from the
     *  authority's address and successive (predictable) nonces are used.
     *
     *  However, if the caller wants to use this interface in a different way,
     *  they could easily make a call like:
     *  registerNewTokens($SOME_NUMBER, 1); to use $SOME_NUMBER
     *  as the nonce.  A user could request to buy an in-ocean nft with a
     *  specific seed value, and the external contract gains authority over
     *  this id on the fly in order to sell it.
     *
     *  If the caller tries to reassert authority over a token they've already
     *  registered, they just waste gas.  If a caller expects to create
     *  new tokens over time, it should track how many tokens it has already
     *  created
     * @dev the guiding philosophy is to track only essential information in
     *  the Ocean's state, and let users (both EOAs and contracts) track other
     *  information as they see fit.
     * @param currentNumberOfTokens the starting nonce
     * @param numberOfAdditionalTokens the number of new tokens registered
     * @return oceanIds Ocean IDs of the tokens the caller now has authority over
     */
    function registerNewTokens(
        uint256 currentNumberOfTokens,
        uint256 numberOfAdditionalTokens
    ) external override returns (uint256[] memory oceanIds) {
        oceanIds = new uint256[](numberOfAdditionalTokens);
        uint256[] memory nonces = new uint256[](numberOfAdditionalTokens);

        for (uint256 i = 0; i < numberOfAdditionalTokens; ++i) {
            uint256 tokenNonce = currentNumberOfTokens + i;
            uint256 newToken = _calculateOceanId(msg.sender, tokenNonce);
            nonces[i] = tokenNonce;
            oceanIds[i] = newToken;
            tokensToPrimitives[newToken] = msg.sender;
        }
        emit NewTokensRegistered(msg.sender, oceanIds, nonces);
    }

    function _signaturesEnabled() internal view override returns (bool) {
        return bool(permitFuse == FUSE_INTACT);
    }

    /**
     * @dev returns true when a primitive did NOT register an ID
     *
     * Used  to determine if the Ocean needs to explicitly mint/burn tokens
     *  balance a transaction.
     */
    function _isNotTokenOfPrimitive(uint256 oceanId, address primitive)
        internal
        view
        returns (bool)
    {
        return (tokensToPrimitives[oceanId] != primitive);
    }

    /**
     * @dev calculates a collision-resistant token ID
     *
     * OceanIds are derived from their origin. The origin can be:
     *  - ERC20 contracts that have their token wrapped into the ocean
     *  - ERC721 contracts that have tokens with IDs wrapped into the ocean
     *  - ERC1155 contracts that have tokens with IDs wrapped into the ocean
     *  - Contracts that issue Ocean-native tokens
     *      When a contract registers a new token, the token has an associated
     *      nonce, which functions just like an ERC721 or ERC1155 token ID.
     *
     * The oceanId is calculated by using the contract address of the origin
     *      and the relevant ID.  For ERC20 tokens, the ID is always 0.
     */
    function _calculateOceanId(address tokenContract, uint256 tokenId)
        internal
        pure
        returns (uint256)
    {
        return uint256(keccak256(abi.encodePacked(tokenContract, tokenId)));
    }

    /**
     * @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), "transfer to the zero address");

        address operator = _msgSender();

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "insufficient balance");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _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, "ids.length != amounts.length");
        require(to != address(0), "transfer to the zero address");

        address operator = _msgSender();

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "insufficient balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _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:
     *
     * - Should only be called by
     *      - _mint(...)
     *      - LiquidityOcean._computeOutputAmount(...)
     *      - LiquidityOcean._computeInputAmount(...)
     *      - LiquidityOcean._grantFeeToOcean(...)
     *
     * - When called by _mint(...) this function complies with the ERC-1155 spec
     * - When called by the LiquidityOcean functions, this function breaks the
     *      ERC-1155 spec deliberately.  The contract that is the target of a
     *      compute*() call can revert the transaction if it does not want to
     *      receive the tokens, so the safeTransferAcceptanceCheck is redundant.
     *      The address receiving the fees (immutable DAO) is required to handle
     *      receiving fees without a safeTransferCheck.  By avoiding an SLOAD
     *      and an external call during the fee assignment, we save users gas.
     */
    function _mintWithoutSafeTransferAcceptanceCheck(
        address to,
        uint256 id,
        uint256 amount
    ) internal returns (address) {
        address operator = _msgSender();

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        return operator;
    }

    /**
     * @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
    ) internal virtual {
        assert(to != address(0));

        address operator = _mintWithoutSafeTransferAcceptanceCheck(
            to,
            id,
            amount
        );

        _doSafeTransferAcceptanceCheck(
            operator,
            address(0),
            to,
            id,
            amount,
            ""
        );
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
     *
     * 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
    ) internal virtual {
        assert(to != address(0));
        assert(ids.length == amounts.length);

        address operator = _msgSender();

        for (uint256 i = 0; i < ids.length; ++i) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(
            operator,
            address(0),
            to,
            ids,
            amounts,
            ""
        );
    }

    /**
     * @dev Destroys `amount` tokens of token type `id` from `from`
     *
     * 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 {
        assert(from != address(0));

        address operator = _msgSender();

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "burn amount exceeds balance");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     */
    function _burnBatch(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        assert(from != address(0));
        assert(ids.length == amounts.length);

        address operator = _msgSender();

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits a {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal override {
        require(owner != operator, "Set approval for self");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    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("ERC1155Receiver rejected");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("non-ERC1155Receiver");
            }
        }
    }

    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("ERC1155Receiver rejected");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("non-ERC1155Receiver");
            }
        }
    }
}

File 15 of 23 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 16 of 23 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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 functionCall(target, data, "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");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(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) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(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) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason 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 {
            // 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 17 of 23 : IERC1155MetadataURI.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)

pragma solidity ^0.8.0;

import "../IERC1155.sol";

/**
 * @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 18 of 23 : Context.sol
// SPDX-License-Identifier: MIT
// 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 19 of 23 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @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 20 of 23 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 21 of 23 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 22 of 23 : IOceanToken.sol
// SPDX-License-Identifier: unlicensed
// Cowri Labs Inc.

pragma solidity =0.8.10;

/**
 * @title Interface for external contracts that issue tokens on the Ocean's
 *  public multitoken ledger
 * @dev Implemented by OceanERC1155.
 */
interface IOceanToken {
    function registerNewTokens(
        uint256 currentNumberOfTokens,
        uint256 numberOfAdditionalTokens
    ) external returns (uint256[] memory);
}

File 23 of 23 : ERC1155PermitSignatureExtension.sol
// SPDX-License-Identifier: MIT
// Cowri Labs Inc.

pragma solidity =0.8.10;

abstract contract ERC1155PermitSignatureExtension {
    /// @notice EIP-712 Ethereum typed structured data hashing and signing
    bytes32 public immutable DOMAIN_SEPARATOR;
    bytes32 public immutable SETPERMITFORALL_TYPEHASH;

    /// @notice Nonces used for EIP-2612 sytle permits
    mapping(address => uint256) public approvalNonces;

    constructor(bytes memory name, bytes memory version) {
        bytes memory EIP712Domain = bytes(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256(EIP712Domain),
                keccak256(name),
                keccak256(version),
                block.chainid,
                address(this)
            )
        );
        SETPERMITFORALL_TYPEHASH = keccak256(
            "SetPermitForAll(address owner,address operator,bool approved,uint256 nonce,uint256 deadline)"
        );
    }

    function setPermitForAll(
        address owner,
        address operator,
        bool approved,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(_signaturesEnabled(), "Permit Signature Disabled");
        require(deadline >= block.timestamp);
        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(
                    abi.encode(
                        SETPERMITFORALL_TYPEHASH,
                        owner,
                        operator,
                        approved,
                        approvalNonces[owner]++,
                        deadline
                    )
                )
            )
        );
        address recoveredAddress = ecrecover(digest, v, r, s);
        require(recoveredAddress != address(0) && recoveredAddress == owner);
        _setApprovalForAll(owner, operator, approved);
    }

    function _signaturesEnabled() internal virtual returns (bool);

    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10000000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"string","name":"uri_","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"ChangeUnwrapFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"primitive","type":"address"},{"indexed":false,"internalType":"uint256","name":"inputToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outputToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"inputAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outputAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"ComputeInputAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"primitive","type":"address"},{"indexed":false,"internalType":"uint256","name":"inputToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outputToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"inputAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outputAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"ComputeOutputAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc1155Token","type":"address"},{"indexed":false,"internalType":"uint256","name":"erc1155Id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeCharged","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"oceanId","type":"uint256"}],"name":"Erc1155Unwrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc1155Token","type":"address"},{"indexed":false,"internalType":"uint256","name":"erc1155Id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"oceanId","type":"uint256"}],"name":"Erc1155Wrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc20Token","type":"address"},{"indexed":false,"internalType":"uint256","name":"transferredAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unwrappedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeCharged","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"oceanId","type":"uint256"}],"name":"Erc20Unwrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc20Token","type":"address"},{"indexed":false,"internalType":"uint256","name":"transferredAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wrappedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dust","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"oceanId","type":"uint256"}],"name":"Erc20Wrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc721Token","type":"address"},{"indexed":false,"internalType":"uint256","name":"erc721Id","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"oceanId","type":"uint256"}],"name":"Erc721Unwrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc721Token","type":"address"},{"indexed":false,"internalType":"uint256","name":"erc721id","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"oceanId","type":"uint256"}],"name":"Erc721Wrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeCharged","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"EtherUnwrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"EtherWrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"forwarder","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"numberOfInteractions","type":"uint256"}],"name":"ForwardedOceanTransaction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokens","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"nonces","type":"uint256[]"}],"name":"NewTokensRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"numberOfInteractions","type":"uint256"}],"name":"OceanTransaction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"breakerAddress","type":"address"}],"name":"PermitFuseBroken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SETPERMITFORALL_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WRAPPED_ETHER_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvalNonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"breakPermitFuse","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nextUnwrapFeeDivisor","type":"uint256"}],"name":"changeUnwrapFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"interactionTypeAndAddress","type":"bytes32"},{"internalType":"uint256","name":"inputToken","type":"uint256"},{"internalType":"uint256","name":"outputToken","type":"uint256"},{"internalType":"uint256","name":"specifiedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"}],"internalType":"struct Interaction","name":"interaction","type":"tuple"}],"name":"doInteraction","outputs":[{"internalType":"uint256","name":"burnId","type":"uint256"},{"internalType":"uint256","name":"burnAmount","type":"uint256"},{"internalType":"uint256","name":"mintId","type":"uint256"},{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"interactionTypeAndAddress","type":"bytes32"},{"internalType":"uint256","name":"inputToken","type":"uint256"},{"internalType":"uint256","name":"outputToken","type":"uint256"},{"internalType":"uint256","name":"specifiedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"}],"internalType":"struct Interaction[]","name":"interactions","type":"tuple[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"doMultipleInteractions","outputs":[{"internalType":"uint256[]","name":"burnIds","type":"uint256[]"},{"internalType":"uint256[]","name":"burnAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"mintIds","type":"uint256[]"},{"internalType":"uint256[]","name":"mintAmounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"interactionTypeAndAddress","type":"bytes32"},{"internalType":"uint256","name":"inputToken","type":"uint256"},{"internalType":"uint256","name":"outputToken","type":"uint256"},{"internalType":"uint256","name":"specifiedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"}],"internalType":"struct Interaction","name":"interaction","type":"tuple"},{"internalType":"address","name":"userAddress","type":"address"}],"name":"forwardedDoInteraction","outputs":[{"internalType":"uint256","name":"burnId","type":"uint256"},{"internalType":"uint256","name":"burnAmount","type":"uint256"},{"internalType":"uint256","name":"mintId","type":"uint256"},{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"interactionTypeAndAddress","type":"bytes32"},{"internalType":"uint256","name":"inputToken","type":"uint256"},{"internalType":"uint256","name":"outputToken","type":"uint256"},{"internalType":"uint256","name":"specifiedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"}],"internalType":"struct Interaction[]","name":"interactions","type":"tuple[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address","name":"userAddress","type":"address"}],"name":"forwardedDoMultipleInteractions","outputs":[{"internalType":"uint256[]","name":"burnIds","type":"uint256[]"},{"internalType":"uint256[]","name":"burnAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"mintIds","type":"uint256[]"},{"internalType":"uint256[]","name":"mintAmounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permitFuse","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"currentNumberOfTokens","type":"uint256"},{"internalType":"uint256","name":"numberOfAdditionalTokens","type":"uint256"}],"name":"registerNewTokens","outputs":[{"internalType":"uint256[]","name":"oceanIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"setPermitForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokensToPrimitives","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unwrapFeeDivisor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]

60e06040523480156200001157600080fd5b5060405162005f6138038062005f618339810160408190526200003491620002c3565b806040518060400160405280601481526020017f7368656c6c2d70726f746f636f6c2d6f6365616e000000000000000000000000815250604051806040016040528060018152602001603160f81b815250600060405180608001604052806052815260200162005f0f60529139805160208083019190912085518683012085518684012060408051948501939093529183015260608201524660808201523060a082015290915060c00160408051808303601f19018152919052805160209091012060805250507f7aea62eb64c80d70d6f7fceb0f80c4901e6585fbeca774ca10ae898d1af3af6760a052506200012b336200019c565b60016002556200013b81620001ee565b50600160048190556000196008556009819055600a55604080517045746865720000000000000000000000006020808301919091526000603480840191909152835180840390910181526054909201909252805191012060c05250620003dc565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b80516200020390600790602084019062000207565b5050565b82805462000215906200039f565b90600052602060002090601f01602090048101928262000239576000855562000284565b82601f106200025457805160ff191683800117855562000284565b8280016001018555821562000284579182015b828111156200028457825182559160200191906001019062000267565b506200029292915062000296565b5090565b5b8082111562000292576000815560010162000297565b634e487b7160e01b600052604160045260246000fd5b60006020808385031215620002d757600080fd5b82516001600160401b0380821115620002ef57600080fd5b818501915085601f8301126200030457600080fd5b815181811115620003195762000319620002ad565b604051601f8201601f19908116603f01168101908382118183101715620003445762000344620002ad565b8160405282815288868487010111156200035d57600080fd5b600093505b8284101562000381578484018601518185018701529285019262000362565b82841115620003935760008684830101525b98975050505050505050565b600181811c90821680620003b457607f821691505b60208210811415620003d657634e487b7160e01b600052602260045260246000fd5b50919050565b60805160a05160c051615ad9620004366000396000818161045001528181611e010152818161244f015281816127440152613e5f01526000818161023b0152610dee01526000818161037c0152610dcc0152615ad96000f3fe6080604052600436106101c15760003560e01c8063739653ba116100f7578063c7d15f8411610095578063e985e9c511610064578063e985e9c5146105e3578063f23a6e6114610639578063f242432a14610659578063f2fde38b1461067957600080fd5b8063c7d15f8414610540578063d44f70b414610583578063e363839914610596578063e4288c07146105b657600080fd5b80638fec8009116100d15780638fec8009146104be578063a22cb465146104d4578063b369799e146104f4578063bc197c811461051757600080fd5b8063739653ba1461041e57806388a5cac31461043e5780638da5cb5b1461047257600080fd5b80632eb2c2d6116101645780634b7ac8d41161013e5780634b7ac8d4146103b35780634e1273f4146103d35780636f6e25c7146103f3578063715018a61461040957600080fd5b80632eb2c2d6146103485780633644e5151461036a57806343d8e5061461039e57600080fd5b80630e89341c116101a05780630e89341c1461025d578063150b7a021461028a578063215835b3146102db578063286450f11461030857600080fd5b8062fdd58e146101c657806301ffc9a7146101f957806306bea54514610229575b600080fd5b3480156101d257600080fd5b506101e66101e1366004614978565b610699565b6040519081526020015b60405180910390f35b34801561020557600080fd5b506102196102143660046149d0565b610755565b60405190151581526020016101f0565b34801561023557600080fd5b506101e67f000000000000000000000000000000000000000000000000000000000000000081565b34801561026957600080fd5b5061027d6102783660046149ed565b6107ab565b6040516101f09190614a7c565b34801561029657600080fd5b506102aa6102a5366004614ad8565b61083f565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016101f0565b3480156102e757600080fd5b506102fb6102f6366004614b47565b610880565b6040516101f09190614ba4565b34801561031457600080fd5b50610328610323366004614bcf565b610a59565b6040805194855260208501939093529183015260608201526080016101f0565b34801561035457600080fd5b50610368610363366004614da4565b610bd9565b005b34801561037657600080fd5b506101e67f000000000000000000000000000000000000000000000000000000000000000081565b3480156103aa57600080fd5b50610368610cf1565b3480156103bf57600080fd5b506103686103ce366004614e6b565b610d2a565b3480156103df57600080fd5b506102fb6103ee366004614ee1565b610fe4565b3480156103ff57600080fd5b506101e660045481565b34801561041557600080fd5b50610368611116565b34801561042a57600080fd5b50610328610439366004614fac565b61112a565b34801561044a57600080fd5b506101e67f000000000000000000000000000000000000000000000000000000000000000081565b34801561047e57600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101f0565b3480156104ca57600080fd5b506101e660085481565b3480156104e057600080fd5b506103686104ef366004614fc8565b6111f4565b610507610502366004615089565b611203565b6040516101f0949392919061510a565b34801561052357600080fd5b506102aa610532366004615157565b600098975050505050505050565b34801561054c57600080fd5b5061049961055b3660046149ed565b60036020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b610507610591366004615212565b611388565b3480156105a257600080fd5b506103686105b13660046149ed565b611457565b3480156105c257600080fd5b506101e66105d136600461527e565b60006020819052908152604090205481565b3480156105ef57600080fd5b506102196105fe366004615299565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260066020908152604080832093909416825291909152205460ff1690565b34801561064557600080fd5b506102aa6106543660046152c3565b6114b4565b34801561066557600080fd5b5061036861067436600461533b565b6114f6565b34801561068557600080fd5b5061036861069436600461527e565b611602565b600073ffffffffffffffffffffffffffffffffffffffff831661071d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f62616c616e63654f66286164647265737328302929000000000000000000000060448201526064015b60405180910390fd5b50600081815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff861684529091529020545b92915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000148061074f575061074f826116b9565b6060600780546107ba906153a0565b80601f01602080910402602001604051908101604052809291908181526020018280546107e6906153a0565b80156108335780601f1061080857610100808354040283529160200191610833565b820191906000526020600020905b81548152906001019060200180831161081657829003601f168201915b50505050509050919050565b60006002600a54141561087357507f150b7a0200000000000000000000000000000000000000000000000000000000610877565b5060005b95945050505050565b60608167ffffffffffffffff81111561089b5761089b614c03565b6040519080825280602002602001820160405280156108c4578160200160208202803683370190505b50905060008267ffffffffffffffff8111156108e2576108e2614c03565b60405190808252806020026020018201604052801561090b578160200160208202803683370190505b50905060005b83811015610a01576000610925828761541d565b604080513360601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602080830191909152603480830185905283518084039091018152605490920190925280519101209091508184848151811061098d5761098d615435565b602002602001018181525050808584815181106109ac576109ac615435565b6020908102919091018101919091526000918252600390526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001633179055506109fa81615464565b9050610911565b503373ffffffffffffffffffffffffffffffffffffffff167f69f4cd026fceaa224d78e03ab43e6c20f9d54eae52f88e23573a5ac5bc9c4cd78383604051610a4a92919061549d565b60405180910390a25092915050565b600080600080600280541415610acb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610714565b6002805573ffffffffffffffffffffffffffffffffffffffff85166000908152600660209081526040808320338452909152902054859060ff16610b6b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f466f72776172646572206e6f7420617070726f766564000000000000000000006044820152606401610714565b6040516001815273ffffffffffffffffffffffffffffffffffffffff87169033907f6eb0debd3c0f189ccf977863c81ba259fe0eb500503c3f78777771a68bf8e4899060200160405180910390a3610bc3878761179c565b6001600255929a91995097509095509350505050565b600280541415610c45576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610714565b6002805573ffffffffffffffffffffffffffffffffffffffff8516331480610c725750610c7285336105fe565b610cd8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6e6f74206f776e6572206e6f7220617070726f766564000000000000000000006044820152606401610714565b610ce58585858585611834565b50506001600255505050565b610cf9611aff565b6000600481905560405133917f6a48aa731704180ea30bb9c36eb6e31f04c276642647d76c25f20caaa50f884791a2565b600454600114610d96576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f5065726d6974205369676e61747572652044697361626c6564000000000000006044820152606401610714565b42841015610da357600080fd5b73ffffffffffffffffffffffffffffffffffffffff8716600090815260208190526040812080547f0000000000000000000000000000000000000000000000000000000000000000917f0000000000000000000000000000000000000000000000000000000000000000918b918b918b9187610e1e83615464565b9091555060408051602081019690965273ffffffffffffffffffffffffffffffffffffffff948516908601529290911660608401521515608083015260a082015260c0810187905260e00160405160208183030381529060405280519060200120604051602001610ec19291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015610f4a573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590610fc557508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b610fce57600080fd5b610fd9898989611b80565b505050505050505050565b60608151835114611051576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6163636f756e74732e6c656e67746820213d206964732e6c656e6774680000006044820152606401610714565b6000835167ffffffffffffffff81111561106d5761106d614c03565b604051908082528060200260200182016040528015611096578160200160208202803683370190505b50905060005b845181101561110e576110e18582815181106110ba576110ba615435565b60200260200101518583815181106110d4576110d4615435565b6020026020010151610699565b8282815181106110f3576110f3615435565b602090810291909101015261110781615464565b905061109c565b509392505050565b61111e611aff565b6111286000611cae565b565b60008060008060028054141561119c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610714565b600280556040516001815233907f1ae805a3773324a90592b8a87b99151f93a76c32229b924c6a8199c2acda49f99060200160405180910390a26111e0853361179c565b600160025592989197509550909350915050565b6111ff338383611b80565b5050565b606080606080600280541415611275576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610714565b6002805573ffffffffffffffffffffffffffffffffffffffff85166000908152600660209081526040808320338452909152902054859060ff16611315576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f466f72776172646572206e6f7420617070726f766564000000000000000000006044820152606401610714565b60405189815273ffffffffffffffffffffffffffffffffffffffff87169033907f6eb0debd3c0f189ccf977863c81ba259fe0eb500503c3f78777771a68bf8e4899060200160405180910390a361136f8a8a8a8a8a611d25565b6001600255929d919c509a509098509650505050505050565b6060806060806002805414156113fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610714565b6002805560405187815233907f1ae805a3773324a90592b8a87b99151f93a76c32229b924c6a8199c2acda49f99060200160405180910390a26114408888888833611d25565b6001600255929b919a509850909650945050505050565b61145f611aff565b806107d0111561146e57600080fd5b60085460408051918252602082018390523382820152517f418da9cf3f702319d14530681ec7bf242aeb71f4959c849ad3de431599b43cc49181900360600190a1600855565b6000600260095414156114e857507ff23a6e61000000000000000000000000000000000000000000000000000000006114ec565b5060005b9695505050505050565b600280541415611562576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610714565b6002805573ffffffffffffffffffffffffffffffffffffffff851633148061158f575061158f85336105fe565b6115f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6e6f74206f776e6572206e6f7220617070726f766564000000000000000000006044820152606401610714565b610ce58585858585612081565b61160a611aff565b73ffffffffffffffffffffffffffffffffffffffff81166116ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610714565b6116b681611cae565b50565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fd9b67a2600000000000000000000000000000000000000000000000000000000148061174c57507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e89341c00000000000000000000000000000000000000000000000000000000145b8061074f57507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161461074f565b600080808080806117ba6117b5368a90038a018a6154c2565b61225c565b909250905060006117da83836117d5368d90038d018d6154c2565b612280565b90506117fc6117ee368b90038b018b6154c2565b8484848d606001358d612476565b92995090975095509350851561181757611817888888612795565b8315611828576118288886866128cf565b50505092959194509250565b815183511461189f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f6964732e6c656e67746820213d20616d6f756e74732e6c656e677468000000006044820152606401610714565b73ffffffffffffffffffffffffffffffffffffffff841661191c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7472616e7366657220746f20746865207a65726f2061646472657373000000006044820152606401610714565b3360005b8451811015611a6a57600085828151811061193d5761193d615435565b60200260200101519050600085838151811061195b5761195b615435565b602090810291909101810151600084815260058352604080822073ffffffffffffffffffffffffffffffffffffffff8e168352909352919091205490915081811015611a03576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e73756666696369656e742062616c616e63650000000000000000000000006044820152606401610714565b600083815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff8e8116855292528083208585039055908b16825281208054849290611a4f90849061541d565b9250508190555050505080611a6390615464565b9050611920565b508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8787604051611ae192919061549d565b60405180910390a4611af7818787878787612925565b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611128576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610714565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611c16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53657420617070726f76616c20666f722073656c6600000000000000000000006044820152606401610714565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526006602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6001805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b606080808060008667ffffffffffffffff811115611d4557611d45614c03565b604051908082528060200260200182016040528015611d8a57816020015b6040805180820190915260008082526020820152815260200190600190039081611d635790505b50905060005b87811015611df45760405180604001604052808a8a84818110611db557611db5615435565b9050602002013581526020016000815250828281518110611dd857611dd8615435565b602002602001018190525080611ded90615464565b9050611d90565b503415611e7757611e26817f000000000000000000000000000000000000000000000000000000000000000034612b64565b8573ffffffffffffffffffffffffffffffffffffffff167f55f83f01c0664bd25aad3d627119249b43acc06873e584be1db2ddd46ca3bdeb34604051611e6e91815260200190565b60405180910390a25b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091528660005b8b811015611f98578c8c82818110611ec057611ec0615435565b905060a00201803603810190611ed691906154c2565b9250600080611ee48561225c565b915091506000611ef5838388612280565b905060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87606001511415611f3757611f30888584612c31565b9050611f3e565b5060608601515b600080600080611f528b898989898f612476565b93509350935093506000831115611f6e57611f6e8c8585612cd1565b8015611f7f57611f7f8c8383612b64565b505050505050505080611f9190615464565b9050611ea6565b505050611fa481612d94565b83519198509650919450925060011415611ffc57611ff78684600081518110611fcf57611fcf615435565b602002602001015184600081518110611fea57611fea615435565b60200260200101516128cf565b612011565b60018351111561201157612011868484612ed4565b84516001141561205f5761205a868660008151811061203257612032615435565b60200260200101518660008151811061204d5761204d615435565b6020026020010151612795565b612074565b6001855111156120745761207486868661305b565b5095509550955095915050565b73ffffffffffffffffffffffffffffffffffffffff84166120fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7472616e7366657220746f20746865207a65726f2061646472657373000000006044820152606401610714565b600083815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff8916845290915290205433908381101561219a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f696e73756666696369656e742062616c616e63650000000000000000000000006044820152606401610714565b600085815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff8b81168552925280832087850390559088168252812080548692906121e690849061541d565b9091555050604080518681526020810186905273ffffffffffffffffffffffffffffffffffffffff808916928a821692918616917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a4612253828888888888613240565b50505050505050565b8051600090819080821a600881111561227757612277615532565b94909350915050565b60008084600881111561229557612295615532565b14806122b2575060018460088111156122b0576122b0615532565b145b156123155760408051606085901b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602080830191909152600060348084019190915283518084039091018152605490920190925280519101205b905061246f565b600284600881111561232957612329615532565b14806123465750600484600881111561234457612344615532565b145b806123625750600384600881111561236057612360615532565b145b8061237e5750600584600881111561237c5761237c615532565b145b156123e157608082015160408051606086901b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602080830191909152603480830194909452825180830390940184526054909101909152815191012061230e565b60068460088111156123f5576123f5615532565b14156124065750604081015161246f565b600784600881111561241a5761241a615532565b141561242b5750602081015161246f565b600884600881111561243f5761243f615532565b1461244c5761244c615561565b507f00000000000000000000000000000000000000000000000000000000000000005b9392505050565b6000808080600789600881111561248f5761248f615532565b14156124bb57869350859250896040015191506124b488858486898f608001516133c7565b9050612788565b60068960088111156124cf576124cf615532565b14156124fb57896020015193508691508590506124f488858484898f608001516134f8565b9250612788565b600089600881111561250f5761250f615532565b14156125325750600092508291508590508461252d8882878561361b565b612788565b600189600881111561254657612546615532565b14156125645750859250849150600090508061252d88848787613798565b600289600881111561257857612578615532565b141561260957856001146125e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f4e465420616d6f756e7420213d203100000000000000000000000000000000006044820152606401610714565b600093506000925086915085905061252d888b6080015160001c878561393e565b600389600881111561261d5761261d615532565b14156126ae578560011461268d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f4e465420616d6f756e7420213d203100000000000000000000000000000000006044820152606401610714565b869350859250600091506000905061252d888b6080015160001c8787613a39565b60048960088111156126c2576126c2615532565b14156126ea57600093506000925086915085905061252d888b6080015160001c838886613b27565b60058960088111156126fe576126fe615532565b141561272657869350859250600091506000905061252d888b6080015160001c858888613ca9565b600889600881111561273a5761273a615532565b14801561276657507f000000000000000000000000000000000000000000000000000000000000000087145b61277257612772615561565b5085925084915060009050806127888386613e4d565b9650965096509692505050565b73ffffffffffffffffffffffffffffffffffffffff83166127b8576127b8615561565b600082815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152902054339082811015612854576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f6275726e20616d6f756e7420657863656564732062616c616e636500000000006044820152606401610714565b600084815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff898116808652918452828520888703905582518981529384018890529092908616917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f6291015b60405180910390a45050505050565b73ffffffffffffffffffffffffffffffffffffffff83166128f2576128f2615561565b60006128ff848484613f2e565b905061291f81600086868660405180602001604052806000815250613240565b50505050565b73ffffffffffffffffffffffffffffffffffffffff84163b15611af7576040517fbc197c8100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063bc197c819061299c9089908990889088908890600401615590565b6020604051808303816000875af19250505080156129f5575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526129f2918101906155fb565b60015b612ab957612a01615618565b806308c379a01415612a555750612a16615634565b80612a215750612a57565b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107149190614a7c565b505b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f6e6f6e2d455243313135355265636569766572000000000000000000000000006044820152606401610714565b7fffffffff0000000000000000000000000000000000000000000000000000000081167fbc197c810000000000000000000000000000000000000000000000000000000014612253576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4552433131353552656365697665722072656a656374656400000000000000006044820152606401610714565b80807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff11612bee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f44656c7461203a3a20616d6f756e74203e20696e743235366d617800000000006044820152606401610714565b6000612bfa8585613fd6565b905082858281518110612c0f57612c0f615435565b6020026020010151602001818151612c2791906156dc565b9052505050505050565b60006001836008811115612c4757612c47615532565b1480612c6457506003836008811115612c6257612c62615532565b145b80612c8057506005836008811115612c7e57612c7e615532565b145b80612c9c57506008836008811115612c9a57612c9a615532565b145b80612cb857506007836008811115612cb657612cb6615532565b145b15612cc75761230e848361407e565b61230e8483614122565b80807f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff11612d5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f44656c7461203a3a20616d6f756e74203e20696e743235366d617800000000006044820152606401610714565b6000612d678585613fd6565b905082858281518110612d7c57612d7c615435565b6020026020010151602001818151612c279190615750565b606080606080600080612da6876141c7565b915091508167ffffffffffffffff811115612dc357612dc3614c03565b604051908082528060200260200182016040528015612dec578160200160208202803683370190505b5095508167ffffffffffffffff811115612e0857612e08614c03565b604051908082528060200260200182016040528015612e31578160200160208202803683370190505b5094508067ffffffffffffffff811115612e4d57612e4d614c03565b604051908082528060200260200182016040528015612e76578160200160208202803683370190505b5093508067ffffffffffffffff811115612e9257612e92614c03565b604051908082528060200260200182016040528015612ebb578160200160208202803683370190505b509250612ecb8787878787614273565b50509193509193565b73ffffffffffffffffffffffffffffffffffffffff8316612ef757612ef7615561565b8051825114612f0857612f08615561565b3360005b8351811015612fbd57828181518110612f2757612f27615435565b602002602001015160056000868481518110612f4557612f45615435565b6020026020010151815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612fa7919061541d565b90915550612fb6905081615464565b9050612f0c565b508373ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb868660405161303592919061549d565b60405180910390a461291f81600086868660405180602001604052806000815250612925565b73ffffffffffffffffffffffffffffffffffffffff831661307e5761307e615561565b805182511461308f5761308f615561565b3360005b83518110156131ba5760008482815181106130b0576130b0615435565b6020026020010151905060008483815181106130ce576130ce615435565b602090810291909101810151600084815260058352604080822073ffffffffffffffffffffffffffffffffffffffff8c168352909352919091205490915081811015613176576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f6275726e20616d6f756e7420657863656564732062616c616e636500000000006044820152606401610714565b600092835260056020908152604080852073ffffffffffffffffffffffffffffffffffffffff8b168652909152909220910390556131b381615464565b9050613093565b50600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb868660405161323292919061549d565b60405180910390a450505050565b73ffffffffffffffffffffffffffffffffffffffff84163b15611af7576040517ff23a6e6100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063f23a6e61906132b790899089908890889088906004016157c4565b6020604051808303816000875af1925050508015613310575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261330d918101906155fb565b60015b61331c57612a01615618565b7fffffffff0000000000000000000000000000000000000000000000000000000081167ff23a6e610000000000000000000000000000000000000000000000000000000014612253576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4552433131353552656365697665722072656a656374656400000000000000006044820152606401610714565b6040517f3fc20d4f00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044810184905273ffffffffffffffffffffffffffffffffffffffff83811660648301526084820183905260009190881690633fc20d4f9060a4016020604051808303816000875af1158015613455573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134799190615809565b905061348887878688856143bf565b60408051878152602081018790529081018590526060810182905273ffffffffffffffffffffffffffffffffffffffff80851691908916907fc6baad430667a9b3f12a9f115e0af95c8538e5e2595e248f2d068f6c93c6101e906080015b60405180910390a39695505050505050565b6040517fe92ebd3a00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044810184905273ffffffffffffffffffffffffffffffffffffffff8381166064830152608482018390526000919088169063e92ebd3a9060a4016020604051808303816000875af1158015613586573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135aa9190615809565b90506135b987878388886143bf565b60408051878152602081018790529081018290526060810185905273ffffffffffffffffffffffffffffffffffffffff80851691908916907fff21d0dd1bae0672a5771fb6668afecf9485c8e6e82a7d2f1ee58a96277c630e906080016134e6565b8373ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156136a0575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261369d91810190615822565b60015b613706576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f436f756c64206e6f742067657420646563696d616c73282900000000000000006044820152606401610714565b6000806137138684614456565b909250905061372284826144d1565b61372e87863085614505565b6040805183815260208101889052908101829052849073ffffffffffffffffffffffffffffffffffffffff80881691908a16907fd86e46c0b98ab82d234497fd7d7e31711dfa27764034b1763f3575c521f6ff94906060015b60405180910390a450505050505050565b8373ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801561381d575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261381a91810190615822565b60015b613883576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f436f756c64206e6f742067657420646563696d616c73282900000000000000006044820152606401610714565b600061388e856145e1565b9050600061389c828761583f565b90506000806138ad601286856145f1565b90925090506138bc818561541d565b93506138c886856144d1565b6138d389888461468b565b60408051838152602081018a9052908101859052869073ffffffffffffffffffffffffffffffffffffffff808a1691908c16907f62a282d05dd7f7ec454ec1e3ebe4c23b911f1fedecfa6f241f4b663896e592cf9060600160405180910390a4505050505050505050565b6002600a556040517f42842e0e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152306024830152604482018590528516906342842e0e90606401600060405180830381600087803b1580156139b957600080fd5b505af11580156139cd573d6000803e3d6000fd5b505050506001600a81905550808273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f7672c4a63cc0b66a7449c3e1051810a6f128034121aa8314de5b008254b2389f8660405161323291815260200190565b6040517f42842e0e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8381166024830152604482018590528516906342842e0e90606401600060405180830381600087803b158015613aaf57600080fd5b505af1158015613ac3573d6000803e3d6000fd5b50505050808273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fe67983c06675e145002b5c4392d05e59790fd0cc13fb15ed1a6201f9d78e17ef8660405161323291815260200190565b73ffffffffffffffffffffffffffffffffffffffff8516301415613ba7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f4e6f2072656375727369766520777261707300000000000000000000000000006044820152606401610714565b60026009556040517ff242432a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152306024830152604482018690526064820185905260a06084830152600060a483015286169063f242432a9060c401600060405180830381600087803b158015613c3757600080fd5b505af1158015613c4b573d6000803e3d6000fd5b5050600160095550506040805185815260208101859052829173ffffffffffffffffffffffffffffffffffffffff80861692908916917f9e8d41f891cc91b6fbf90fec18d6464e22e7d95db0113fefec8c8e6815c9608491016128c0565b73ffffffffffffffffffffffffffffffffffffffff8516301415613d29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f4e6f2072656375727369766520756e77726170730000000000000000000000006044820152606401610714565b6000613d34846145e1565b90506000613d42828661583f565b9050613d4e83836144d1565b6040517ff242432a00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8581166024830152604482018890526064820183905260a06084830152600060a483015288169063f242432a9060c401600060405180830381600087803b158015613dd957600080fd5b505af1158015613ded573d6000803e3d6000fd5b5050604080518981526020810189905290810185905285925073ffffffffffffffffffffffffffffffffffffffff80881692508a16907f285f7151736e0529314aabab94f541d8837226ed08c744dbe86bb81c76b13b3d90606001613787565b6000613e58836145e1565b9050613e847f0000000000000000000000000000000000000000000000000000000000000000826144d1565b6000613e90828561583f565b60405190915073ffffffffffffffffffffffffffffffffffffffff84169082156108fc029083906000818181858888f19350505050158015613ed6573d6000803e3d6000fd5b50604080518281526020810184905273ffffffffffffffffffffffffffffffffffffffff8516917fa99566a5bc143aef6dfe90cacd94612c00db3ce770f4687cccc7146a841aeb42910160405180910390a250505050565b600082815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281208054339184918490613f7090849061541d565b9091555050604080518581526020810185905273ffffffffffffffffffffffffffffffffffffffff80881692600092918516917fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62910160405180910390a4949350505050565b60005b825181101561401c5781838281518110613ff557613ff5615435565b602002602001015160000151141561400c5761074f565b61401581615464565b9050613fd9565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f44656c7461203a3a206d697373696e6720746f6b656e204944000000000000006044820152606401610714565b60008061408b8484613fd6565b905060008482815181106140a1576140a1615435565b6020026020010151602001519050600081121561411a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f506f7344656c7461203a3a20616d6f756e74203c2030000000000000000000006044820152606401610714565b949350505050565b60008061412f8484613fd6565b9050600084828151811061414557614145615435565b602002602001015160200151905060008113156141be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4e656744656c7461203a3a20616d6f756e74203e2030000000000000000000006044820152606401610714565b61087781615856565b6000806000805b84518110156142485760008582815181106141eb576141eb615435565b602002602001015160200151905060008113156142125761420b85615464565b9450614237565b600081121561422b5761422484615464565b9350614237565b61423483615464565b92505b5061424181615464565b90506141ce565b50835181614256848661541d565b614260919061541d565b1461426d5761426d615561565b50915091565b60008060005b87518110156143a257600088828151811061429657614296615435565b60200260200101516020015190506000811315614319578882815181106142bf576142bf615435565b6020026020010151600001518885815181106142dd576142dd615435565b602002602001018181525050808785815181106142fc576142fc615435565b602090810291909101015261431260018561541d565b9350614391565b60008112156143915788828151811061433457614334615435565b60200260200101516000015186848151811061435257614352615435565b602090810291909101015261436681615856565b85848151811061437857614378615435565b602090810291909101015261438e60018461541d565b92505b5061439b81615464565b9050614279565b508551821480156143b35750835181145b61225357612253615561565b60008481526003602052604090205473ffffffffffffffffffffffffffffffffffffffff8681169116141580156143f65750600083115b1561440857614406858585613f2e565b505b60008281526003602052604090205473ffffffffffffffffffffffffffffffffffffffff86811691161415801561443f5750600081115b1561444f5761444f858383612795565b5050505050565b6000806000614467601285876145f1565b909350905080156144c45761447d60018461541d565b925060008061448e866012876145f1565b91509150806000146144a2576144a2615561565b8682116144b1576144b1615561565b6144bb878361583f565b935050506144c9565b600091505b509250929050565b80156111ff576145006144f960015473ffffffffffffffffffffffffffffffffffffffff1690565b8383613f2e565b505050565b60405173ffffffffffffffffffffffffffffffffffffffff8085166024830152831660448201526064810182905261291f9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526146e1565b60006008548261074f91906158be565b6000808360ff168560ff16141561460d57508190506000614683565b8360ff168560ff16101561464d57600061462786866158d2565b6146359060ff16600a615a0d565b90506146418185615a19565b92506000915050614683565b600061465985876158d2565b6146679060ff16600a615a0d565b905061467381856158be565b925061467f8185615a56565b9150505b935093915050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526145009084907fa9059cbb000000000000000000000000000000000000000000000000000000009060640161455f565b6000614743826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166147ed9092919063ffffffff16565b80519091501561450057808060200190518101906147619190615a6a565b614500576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610714565b606061411a84846000858573ffffffffffffffffffffffffffffffffffffffff85163b614876576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610714565b6000808673ffffffffffffffffffffffffffffffffffffffff16858760405161489f9190615a87565b60006040518083038185875af1925050503d80600081146148dc576040519150601f19603f3d011682016040523d82523d6000602084013e6148e1565b606091505b50915091506148f18282866148fc565b979650505050505050565b6060831561490b57508161246f565b82511561491b5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107149190614a7c565b803573ffffffffffffffffffffffffffffffffffffffff8116811461497357600080fd5b919050565b6000806040838503121561498b57600080fd5b6149948361494f565b946020939093013593505050565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146116b657600080fd5b6000602082840312156149e257600080fd5b813561246f816149a2565b6000602082840312156149ff57600080fd5b5035919050565b60005b83811015614a21578181015183820152602001614a09565b8381111561291f5750506000910152565b60008151808452614a4a816020860160208601614a06565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061246f6020830184614a32565b60008083601f840112614aa157600080fd5b50813567ffffffffffffffff811115614ab957600080fd5b602083019150836020828501011115614ad157600080fd5b9250929050565b600080600080600060808688031215614af057600080fd5b614af98661494f565b9450614b076020870161494f565b935060408601359250606086013567ffffffffffffffff811115614b2a57600080fd5b614b3688828901614a8f565b969995985093965092949392505050565b60008060408385031215614b5a57600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b83811015614b9957815187529582019590820190600101614b7d565b509495945050505050565b60208152600061246f6020830184614b69565b600060a08284031215614bc957600080fd5b50919050565b60008060c08385031215614be257600080fd5b614bec8484614bb7565b9150614bfa60a0840161494f565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116810181811067ffffffffffffffff82111715614c7657614c76614c03565b6040525050565b600067ffffffffffffffff821115614c9757614c97614c03565b5060051b60200190565b600082601f830112614cb257600080fd5b81356020614cbf82614c7d565b604051614ccc8282614c32565b83815260059390931b8501820192828101915086841115614cec57600080fd5b8286015b84811015614d075780358352918301918301614cf0565b509695505050505050565b600082601f830112614d2357600080fd5b813567ffffffffffffffff811115614d3d57614d3d614c03565b604051614d7260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160182614c32565b818152846020838601011115614d8757600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a08688031215614dbc57600080fd5b614dc58661494f565b9450614dd36020870161494f565b9350604086013567ffffffffffffffff80821115614df057600080fd5b614dfc89838a01614ca1565b94506060880135915080821115614e1257600080fd5b614e1e89838a01614ca1565b93506080880135915080821115614e3457600080fd5b50614e4188828901614d12565b9150509295509295909350565b80151581146116b657600080fd5b60ff811681146116b657600080fd5b600080600080600080600060e0888a031215614e8657600080fd5b614e8f8861494f565b9650614e9d6020890161494f565b95506040880135614ead81614e4e565b9450606088013593506080880135614ec481614e5c565b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215614ef457600080fd5b823567ffffffffffffffff80821115614f0c57600080fd5b818501915085601f830112614f2057600080fd5b81356020614f2d82614c7d565b604051614f3a8282614c32565b83815260059390931b8501820192828101915089841115614f5a57600080fd5b948201945b83861015614f7f57614f708661494f565b82529482019490820190614f5f565b96505086013592505080821115614f9557600080fd5b50614fa285828601614ca1565b9150509250929050565b600060a08284031215614fbe57600080fd5b61246f8383614bb7565b60008060408385031215614fdb57600080fd5b614fe48361494f565b91506020830135614ff481614e4e565b809150509250929050565b60008083601f84011261501157600080fd5b50813567ffffffffffffffff81111561502957600080fd5b60208301915083602060a083028501011115614ad157600080fd5b60008083601f84011261505657600080fd5b50813567ffffffffffffffff81111561506e57600080fd5b6020830191508360208260051b8501011115614ad157600080fd5b6000806000806000606086880312156150a157600080fd5b853567ffffffffffffffff808211156150b957600080fd5b6150c589838a01614fff565b909750955060208801359150808211156150de57600080fd5b506150eb88828901615044565b90945092506150fe90506040870161494f565b90509295509295909350565b60808152600061511d6080830187614b69565b828103602084015261512f8187614b69565b905082810360408401526151438186614b69565b905082810360608401526148f18185614b69565b60008060008060008060008060a0898b03121561517357600080fd5b61517c8961494f565b975061518a60208a0161494f565b9650604089013567ffffffffffffffff808211156151a757600080fd5b6151b38c838d01615044565b909850965060608b01359150808211156151cc57600080fd5b6151d88c838d01615044565b909650945060808b01359150808211156151f157600080fd5b506151fe8b828c01614a8f565b999c989b5096995094979396929594505050565b6000806000806040858703121561522857600080fd5b843567ffffffffffffffff8082111561524057600080fd5b61524c88838901614fff565b9096509450602087013591508082111561526557600080fd5b5061527287828801615044565b95989497509550505050565b60006020828403121561529057600080fd5b61246f8261494f565b600080604083850312156152ac57600080fd5b6152b58361494f565b9150614bfa6020840161494f565b60008060008060008060a087890312156152dc57600080fd5b6152e58761494f565b95506152f36020880161494f565b94506040870135935060608701359250608087013567ffffffffffffffff81111561531d57600080fd5b61532989828a01614a8f565b979a9699509497509295939492505050565b600080600080600060a0868803121561535357600080fd5b61535c8661494f565b945061536a6020870161494f565b93506040860135925060608601359150608086013567ffffffffffffffff81111561539457600080fd5b614e4188828901614d12565b600181811c908216806153b457607f821691505b60208210811415614bc9577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115615430576154306153ee565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415615496576154966153ee565b5060010190565b6040815260006154b06040830185614b69565b82810360208401526108778185614b69565b600060a082840312156154d457600080fd5b60405160a0810181811067ffffffffffffffff821117156154f7576154f7614c03565b806040525082358152602083013560208201526040830135604082015260608301356060820152608083013560808201528091505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525060a060408301526155c960a0830186614b69565b82810360608401526155db8186614b69565b905082810360808401526155ef8185614a32565b98975050505050505050565b60006020828403121561560d57600080fd5b815161246f816149a2565b600060033d11156156315760046000803e5060005160e01c5b90565b600060443d10156156425790565b6040517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc803d016004833e81513d67ffffffffffffffff816024840111818411171561569057505050505090565b82850191508151818111156156a85750505050505090565b843d87010160208285010111156156c25750505050505090565b6156d160208286010187614c32565b509095945050505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615615716576157166153ee565b827f800000000000000000000000000000000000000000000000000000000000000003841281161561574a5761574a6153ee565b50500190565b6000808312837f80000000000000000000000000000000000000000000000000000000000000000183128115161561578a5761578a6153ee565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183138116156157be576157be6153ee565b50500390565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015283606083015260a060808301526148f160a0830184614a32565b60006020828403121561581b57600080fd5b5051919050565b60006020828403121561583457600080fd5b815161246f81614e5c565b600082821015615851576158516153ee565b500390565b60007f8000000000000000000000000000000000000000000000000000000000000000821415615888576158886153ee565b5060000390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826158cd576158cd61588f565b500490565b600060ff821660ff8416808210156158ec576158ec6153ee565b90039392505050565b600181815b808511156144c957817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115615934576159346153ee565b8085161561594157918102915b93841c93908002906158fa565b60008261595d5750600161074f565b8161596a5750600061074f565b8160018114615980576002811461598a576159a6565b600191505061074f565b60ff84111561599b5761599b6153ee565b50506001821b61074f565b5060208310610133831016604e8410600b84101617156159c9575081810a61074f565b6159d383836158f5565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115615a0557615a056153ee565b029392505050565b600061246f838361594e565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615a5157615a516153ee565b500290565b600082615a6557615a6561588f565b500690565b600060208284031215615a7c57600080fd5b815161246f81614e4e565b60008251615a99818460208701614a06565b919091019291505056fea2646970667358221220275381e632bc900f8fd27db10413053f5d8df41bb0bd7891272c039c25e0988464736f6c634300080a0033454950373132446f6d61696e28737472696e67206e616d652c737472696e672076657273696f6e2c75696e7432353620636861696e49642c6164647265737320766572696679696e67436f6e74726163742900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000

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

00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : uri_ (string):

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.