ETH Price: $3,819.62 (+5.58%)

Contract

0x27FD7c2d4C6541e9C87d98c8156c8aB61ea185f6
Transaction Hash
Method
Block
From
To
Add Pool1122049902023-11-15 0:39:17385 days ago1700008757IN
0x27FD7c2d...61ea185f6
0 ETH0.0000752788720.1
Add Pool1122049902023-11-15 0:39:17385 days ago1700008757IN
0x27FD7c2d...61ea185f6
0 ETH0.0000702244320.1
Add Pool1122049902023-11-15 0:39:17385 days ago1700008757IN
0x27FD7c2d...61ea185f6
0 ETH0.0000708610320.1
Add Pool1122049902023-11-15 0:39:17385 days ago1700008757IN
0x27FD7c2d...61ea185f6
0 ETH0.0000710984240.1
Add Pool1122049902023-11-15 0:39:17385 days ago1700008757IN
0x27FD7c2d...61ea185f6
0 ETH0.0000702244320.1
Add Pool1122049902023-11-15 0:39:17385 days ago1700008757IN
0x27FD7c2d...61ea185f6
0 ETH0.0000789894080.1
Add Pool1122049902023-11-15 0:39:17385 days ago1700008757IN
0x27FD7c2d...61ea185f6
0 ETH0.0000708610320.1
Add Pool1122049902023-11-15 0:39:17385 days ago1700008757IN
0x27FD7c2d...61ea185f6
0 ETH0.0000716982390.1
Add Pool1122049902023-11-15 0:39:17385 days ago1700008757IN
0x27FD7c2d...61ea185f6
0 ETH0.0000764345470.1
Add Pool1122049902023-11-15 0:39:17385 days ago1700008757IN
0x27FD7c2d...61ea185f6
0 ETH0.0000764333470.1
Add Pool1122049902023-11-15 0:39:17385 days ago1700008757IN
0x27FD7c2d...61ea185f6
0 ETH0.0000771081320.1
Add Pool1092407712023-09-07 9:51:59454 days ago1694080319IN
0x27FD7c2d...61ea185f6
0 ETH0.0000291877130.00000006
Add Pool1092407712023-09-07 9:51:59454 days ago1694080319IN
0x27FD7c2d...61ea185f6
0 ETH0.0000291877120.00000006
Add Pool1092407712023-09-07 9:51:59454 days ago1694080319IN
0x27FD7c2d...61ea185f6
0 ETH0.0000291877110.00000006
Add Pool1092407712023-09-07 9:51:59454 days ago1694080319IN
0x27FD7c2d...61ea185f6
0 ETH0.000029187720.00000006
Add Pool1092407712023-09-07 9:51:59454 days ago1694080319IN
0x27FD7c2d...61ea185f6
0 ETH0.0000291877120.00000006
Add Pool1092407712023-09-07 9:51:59454 days ago1694080319IN
0x27FD7c2d...61ea185f6
0 ETH0.0000291877130.00000006
Add Pool1092407712023-09-07 9:51:59454 days ago1694080319IN
0x27FD7c2d...61ea185f6
0 ETH0.0000290609940.00000006

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LinkedPool

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion
File 1 of 14 : LinkedPool.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {IPausable} from "./interfaces/IPausable.sol";
import {IndexedToken, IPoolModule} from "./interfaces/IPoolModule.sol";
import {ILinkedPool} from "./interfaces/ILinkedPool.sol";
import {IDefaultPool} from "./interfaces/IDefaultPool.sol";
import {Action} from "./libs/Structs.sol";
import {UniversalTokenLib} from "./libs/UniversalToken.sol";
import {TokenTree} from "./tree/TokenTree.sol";

import {Address} from "@openzeppelin/contracts-4.5.0/utils/Address.sol";
import {Ownable} from "@openzeppelin/contracts-4.5.0/access/Ownable.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts-4.5.0/token/ERC20/utils/SafeERC20.sol";

/// LinkedPool is using an internal Token Tree to aggregate a collection of pools with correlated
/// tokens into a single wrapper, conforming to IDefaultPool interface.
/// The internal Token Tree allows to store up to 256 tokens, which should be enough for most use cases.
/// Note: unlike traditional Default pools, tokens in LinkedPool could be duplicated.
/// This contract is supposed to be used in conjunction with Synapse:Bridge:
/// - The bridged token has index == 0, and could not be duplicated in the tree.
/// - Other tokens (correlated to bridge token) could be duplicated in the tree. Every node token in the tree
/// is represented by a trade path from root token to node token.
/// > This is the reason why token could be duplicated. `nUSD -> USDC` and `nUSD -> USDT -> USDC` both represent
/// > USDC token, but via different paths from nUSD, the bridge token.
/// In addition to the standard IDefaultPool interface, LinkedPool also implements getters to observe the internal
/// tree, as well as the best path finder between any two tokens in the tree.
/// Note: LinkedPool assumes that the added pool tokens have no transfer fees enabled.
contract LinkedPool is TokenTree, Ownable, ILinkedPool {
    using SafeERC20 for IERC20;
    using Address for address;
    using UniversalTokenLib for address;

    error LinkedPool__DeadlineExceeded(uint256 timestamp, uint256 deadline);
    error LinkedPool__EqualSwapIndexes(uint8 index);
    error LinkedPool__MinDyNotMet(uint256 amountOut, uint256 minDy);
    error LinkedPool__EmptyPoolAddress();

    /// @notice Replicates signature of `TokenSwap` event from Default Pools.
    event TokenSwap(address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId);

    constructor(address bridgeToken, address owner_) TokenTree(bridgeToken) {
        transferOwnership(owner_);
    }

    // ═════════════════════════════════════════════════ EXTERNAL ══════════════════════════════════════════════════════

    /// @notice Adds a pool having `N` pool tokens to the tree by adding `N-1` new nodes
    /// as the children of the given node. Given node needs to represent a token from the pool.
    /// @dev `poolModule` should be set to address(this) if the pool conforms to IDefaultPool interface.
    /// Otherwise, it should be set to the address of the contract that implements the logic for pool handling.
    /// @param nodeIndex        The index of the node to which the pool will be added
    /// @param pool             The address of the pool
    /// @param poolModule       The address of the pool module
    function addPool(
        uint256 nodeIndex,
        address pool,
        address poolModule
    ) external onlyOwner checkIndex(nodeIndex) {
        if (pool == address(0)) revert LinkedPool__EmptyPoolAddress();
        _addPool(nodeIndex, pool, poolModule);
    }

    /// @notice Updates the pool module logic address for the given pool.
    /// @dev Will revert if the pool is not present in the tree, or if the new pool module
    /// produces a different token list for the pool.
    function updatePoolModule(address pool, address newPoolModule) external onlyOwner {
        _updatePoolModule(pool, newPoolModule);
    }

    /// @inheritdoc ILinkedPool
    function swap(
        uint8 nodeIndexFrom,
        uint8 nodeIndexTo,
        uint256 dx,
        uint256 minDy,
        uint256 deadline
    ) external checkIndex(nodeIndexFrom) checkIndex(nodeIndexTo) returns (uint256 amountOut) {
        // solhint-disable-next-line not-rely-on-time
        if (block.timestamp > deadline) revert LinkedPool__DeadlineExceeded(block.timestamp, deadline);
        if (nodeIndexFrom == nodeIndexTo) revert LinkedPool__EqualSwapIndexes(nodeIndexFrom);
        // Pull initial token from the user. LinkedPool assumes that the tokens have no transfer fees enabled,
        // thus the balance checks are omitted.
        address tokenIn = _nodes[nodeIndexFrom].token;
        IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), dx);
        amountOut = _multiSwap(nodeIndexFrom, nodeIndexTo, dx).amountOut;
        if (amountOut < minDy) revert LinkedPool__MinDyNotMet(amountOut, minDy);
        // Transfer the tokens to the user
        IERC20(_nodes[nodeIndexTo].token).safeTransfer(msg.sender, amountOut);
        // Emit the event
        emit TokenSwap(msg.sender, dx, amountOut, nodeIndexFrom, nodeIndexTo);
    }

    // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════

    /// Note: this calculates a quote for a predefined swap path between two tokens. If any of the tokens is
    /// presented more than once in the internal tree, there might be a better quote. Integration should use
    /// findBestPath() instead. This function is present for backwards compatibility.
    /// @inheritdoc ILinkedPool
    function calculateSwap(
        uint8 nodeIndexFrom,
        uint8 nodeIndexTo,
        uint256 dx
    ) external view returns (uint256 amountOut) {
        uint256 totalTokens = _nodes.length;
        // Check that the token indexes are within range
        if (nodeIndexFrom >= totalTokens || nodeIndexTo >= totalTokens) {
            return 0;
        }
        // Check that the token indexes are not the same
        if (nodeIndexFrom == nodeIndexTo) {
            return 0;
        }
        // Calculate the quote by following the path from "tokenFrom" node to "tokenTo" node in the stored tree
        // This function might be called by Synapse:Bridge before the swap, so we don't waste gas checking if pool is paused,
        // as the swap will fail anyway if it is.
        amountOut = _getMultiSwapQuote({
            nodeIndexFrom: nodeIndexFrom,
            nodeIndexTo: nodeIndexTo,
            amountIn: dx,
            probePaused: false
        }).amountOut;
    }

    /// @inheritdoc ILinkedPool
    function areConnectedTokens(address tokenIn, address tokenOut) external view returns (bool areConnected) {
        // Tokens are considered connected, if they are both present in the tree
        return _tokenNodes[tokenIn].length > 0 && _tokenNodes[tokenOut].length > 0;
    }

    /// Note: this could be potentially a gas expensive operation. This is used by SwapQuoterV2 to get the best quote
    /// for tokenIn -> tokenOut swap request (the call to SwapQuoter is an off-chain call).
    /// This should NOT be used as a part of "find path + perform a swap" on-chain flow.
    /// Instead, do an off-chain call to findBestPath() and then perform a swap using the found node indexes.
    /// As pair of token nodes defines only a single trade path (tree has no cycles), it will be possible to go
    /// through the found path by simply supplying the found indexes (instead of searching for the best path again).
    /// @inheritdoc ILinkedPool
    function findBestPath(
        address tokenIn,
        address tokenOut,
        uint256 amountIn
    )
        external
        view
        returns (
            uint8 nodeIndexFromBest,
            uint8 nodeIndexToBest,
            uint256 amountOutBest
        )
    {
        // Check that the tokens are not the same and that the amount is not zero
        if (tokenIn == tokenOut || amountIn == 0) {
            return (0, 0, 0);
        }
        uint256 nodesFrom = _tokenNodes[tokenIn].length;
        uint256 nodesTo = _tokenNodes[tokenOut].length;
        // Go through every node that represents `tokenIn`
        for (uint256 i = 0; i < nodesFrom; ++i) {
            uint256 nodeIndexFrom = _tokenNodes[tokenIn][i];
            // Go through every node that represents `tokenOut`
            for (uint256 j = 0; j < nodesTo; ++j) {
                uint256 nodeIndexTo = _tokenNodes[tokenOut][j];
                // Calculate the quote by following the path from "tokenFrom" node to "tokenTo" node in the stored tree
                // We discard any paths with paused pools, as it's not possible to swap via them anyway.
                uint256 amountOut = _getMultiSwapQuote({
                    nodeIndexFrom: nodeIndexFrom,
                    nodeIndexTo: nodeIndexTo,
                    amountIn: amountIn,
                    probePaused: true
                }).amountOut;
                if (amountOut > amountOutBest) {
                    amountOutBest = amountOut;
                    nodeIndexFromBest = uint8(nodeIndexFrom);
                    nodeIndexToBest = uint8(nodeIndexTo);
                }
            }
        }
    }

    /// @inheritdoc ILinkedPool
    function getToken(uint8 index) external view checkIndex(index) returns (address token) {
        return _nodes[index].token;
    }

    /// @inheritdoc ILinkedPool
    function tokenNodesAmount() external view returns (uint256) {
        return _nodes.length;
    }

    /// @inheritdoc ILinkedPool
    function getAttachedPools(uint8 index) external view checkIndex(index) returns (address[] memory pools) {
        pools = new address[](_pools.length);
        uint256 amountAttached = 0;
        uint256 poolsMask = _attachedPools[index];
        for (uint256 i = 0; i < pools.length; ) {
            // Check if _pools[i] is attached to the node at `index`
            unchecked {
                if ((poolsMask >> i) & 1 == 1) {
                    pools[amountAttached++] = _pools[i];
                }
                ++i;
            }
        }
        // Use assembly to shrink the array to the actual size
        // solhint-disable-next-line no-inline-assembly
        assembly {
            mstore(pools, amountAttached)
        }
    }

    /// @inheritdoc ILinkedPool
    function getTokenIndexes(address token) external view returns (uint256[] memory nodes) {
        nodes = _tokenNodes[token];
    }

    /// @inheritdoc ILinkedPool
    function getPoolModule(address pool) external view returns (address) {
        return _poolMap[pool].module;
    }

    /// @inheritdoc ILinkedPool
    function getNodeParent(uint256 nodeIndex)
        external
        view
        checkIndex(nodeIndex)
        returns (uint256 parentIndex, address parentPool)
    {
        uint8 depth = _nodes[nodeIndex].depth;
        // Check if node is root, in which case there is no parent
        if (depth > 0) {
            parentIndex = _extractNodeIndex(_rootPath[nodeIndex], depth - 1);
            parentPool = _pools[_nodes[nodeIndex].poolIndex];
        }
    }

    // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════

    /// @dev Performs a single swap between two nodes using the given pool.
    /// Assumes that the initial token is already in this contract.
    function _poolSwap(
        address poolModule,
        address pool,
        uint256 nodeIndexFrom,
        uint256 nodeIndexTo,
        uint256 amountIn
    ) internal override returns (uint256 amountOut) {
        address tokenFrom = _nodes[nodeIndexFrom].token;
        address tokenTo = _nodes[nodeIndexTo].token;
        // Approve pool to spend the token, if needed
        if (poolModule == address(this)) {
            tokenFrom.universalApproveInfinity(pool, amountIn);
            // Pool conforms to IDefaultPool interface. Note: we check minDy and deadline outside of this function.
            amountOut = IDefaultPool(pool).swap({
                tokenIndexFrom: tokenIndexes[pool][tokenFrom],
                tokenIndexTo: tokenIndexes[pool][tokenTo],
                dx: amountIn,
                minDy: 0,
                deadline: type(uint256).max
            });
        } else {
            // Here we pass both token address and its index to the pool module, so it doesn't need to store
            // index<>token mapping. This allows Pool Module to be implemented in a stateless way, as some
            // pools require token index for interactions, while others require token address.
            // poolSwap(pool, tokenFrom, tokenTo, amountIn)
            bytes memory payload = abi.encodeWithSelector(
                IPoolModule.poolSwap.selector,
                pool,
                IndexedToken({index: tokenIndexes[pool][tokenFrom], token: tokenFrom}),
                IndexedToken({index: tokenIndexes[pool][tokenTo], token: tokenTo}),
                amountIn
            );
            // Delegate swap logic to Pool Module. It should approve the pool to spend the token, if needed.
            // Note that poolModule address is set by the contract owner, so it's safe to delegatecall it.
            // Using OZ library here to bubble up the revert reason if the call fails.
            bytes memory result = poolModule.functionDelegateCall(payload);
            // Pool Modules are whitelisted, so we can trust the returned amountOut value.
            amountOut = abi.decode(result, (uint256));
        }
    }

    // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════

    /// @dev Returns the amount of tokens that will be received from a single swap.
    function _getPoolQuote(
        address poolModule,
        address pool,
        uint256 nodeIndexFrom,
        uint256 nodeIndexTo,
        uint256 amountIn,
        bool probePaused
    ) internal view override returns (uint256 amountOut) {
        if (poolModule == address(this)) {
            // Check if pool is paused, if requested
            if (probePaused) {
                // We issue a static call in case the pool does not conform to IPausable interface.
                (bool success, bytes memory returnData) = pool.staticcall(
                    abi.encodeWithSelector(IPausable.paused.selector)
                );
                if (success && abi.decode(returnData, (bool))) {
                    // Pool is paused, return zero
                    return 0;
                }
            }
            // Pool conforms to IDefaultPool interface.
            try
                IDefaultPool(pool).calculateSwap({
                    tokenIndexFrom: tokenIndexes[pool][_nodes[nodeIndexFrom].token],
                    tokenIndexTo: tokenIndexes[pool][_nodes[nodeIndexTo].token],
                    dx: amountIn
                })
            returns (uint256 amountOut_) {
                amountOut = amountOut_;
            } catch {
                // Return zero if the pool getter reverts for any reason
                amountOut = 0;
            }
        } else {
            // Ask Pool Module to calculate the quote
            address tokenFrom = _nodes[nodeIndexFrom].token;
            address tokenTo = _nodes[nodeIndexTo].token;
            // Here we pass both token address and its index to the pool module, so it doesn't need to store
            // index<>token mapping. This allows Pool Module to be implemented in a stateless way, as some
            // pools require token index for interactions, while others require token address.
            try
                IPoolModule(poolModule).getPoolQuote(
                    pool,
                    IndexedToken({index: tokenIndexes[pool][tokenFrom], token: tokenFrom}),
                    IndexedToken({index: tokenIndexes[pool][tokenTo], token: tokenTo}),
                    amountIn,
                    probePaused
                )
            returns (uint256 amountOut_) {
                amountOut = amountOut_;
            } catch {
                // Return zero if the pool module getter reverts for any reason
                amountOut = 0;
            }
        }
    }

    /// @dev Returns the tokens in the pool at the given address.
    function _getPoolTokens(address poolModule, address pool) internal view override returns (address[] memory tokens) {
        if (poolModule == address(this)) {
            // Pool conforms to IDefaultPool interface.
            // First, figure out how many tokens there are in the pool
            uint256 numTokens = 0;
            while (true) {
                try IDefaultPool(pool).getToken(uint8(numTokens)) returns (address) {
                    unchecked {
                        ++numTokens;
                    }
                } catch {
                    break;
                }
            }
            // Then allocate the memory, and get the tokens
            tokens = new address[](numTokens);
            for (uint256 i = 0; i < numTokens; ) {
                tokens[i] = IDefaultPool(pool).getToken(uint8(i));
                unchecked {
                    ++i;
                }
            }
        } else {
            // Ask Pool Module to return the tokens
            // Note: this will revert if pool is not supported by the module, enforcing the invariant
            // that the added pools are supported by their specified module.
            tokens = IPoolModule(poolModule).getPoolTokens(pool);
        }
    }
}

File 2 of 14 : IPausable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

interface IPausable {
    function paused() external view returns (bool);
}

File 3 of 14 : IPoolModule.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {IndexedToken} from "../libs/Structs.sol";

interface IPoolModule {
    /// @notice Performs a swap via the given pool, assuming `tokenFrom` is already in the contract.
    /// After the call, the contract should have custody over the received `tokenTo` tokens.
    /// @dev This will be used via delegatecall from LinkedPool, which will have the custody over the initial tokens,
    /// and will only use the correct pool address for interacting with the Pool Module.
    /// Note: Pool Module is responsible for issuing the token approvals, if `pool` requires them.
    /// Note: execution needs to be reverted, if swap fails for any reason.
    /// @param pool         Address of the pool
    /// @param tokenFrom    Token to swap from
    /// @param tokenTo      Token to swap to
    /// @param amountIn     Amount of tokenFrom to swap
    /// @return amountOut   Amount of tokenTo received after the swap
    function poolSwap(
        address pool,
        IndexedToken memory tokenFrom,
        IndexedToken memory tokenTo,
        uint256 amountIn
    ) external returns (uint256 amountOut);

    /// @notice Returns a quote for a swap via the given pool.
    /// @dev This will be used by LinkedPool, which is supposed to pass only the correct pool address.
    /// Note: Pool Module should bubble the revert, if pool quote fails for any reason.
    /// Note: Pool Module should only revert if the pool is paused, if `probePaused` is true.
    /// @param pool         Address of the pool
    /// @param tokenFrom    Token to swap from
    /// @param tokenTo      Token to swap to
    /// @param amountIn     Amount of tokenFrom to swap
    /// @param probePaused  Whether to check if the pool is paused
    /// @return amountOut   Amount of tokenTo received after the swap
    function getPoolQuote(
        address pool,
        IndexedToken memory tokenFrom,
        IndexedToken memory tokenTo,
        uint256 amountIn,
        bool probePaused
    ) external view returns (uint256 amountOut);

    /// @notice Returns the list of tokens in the pool. Tokens should be returned in the same order
    /// that is used by the pool for indexing.
    /// @dev Execution needs to be reverted, if pool tokens retrieval fails for any reason, e.g.
    /// if the given pool is not compatible with the Pool Module.
    /// @param pool         Address of the pool
    /// @return tokens      Array of tokens in the pool
    function getPoolTokens(address pool) external view returns (address[] memory tokens);
}

File 4 of 14 : ILinkedPool.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

interface ILinkedPool {
    /// @notice Wrapper for IDefaultPool.swap()
    /// @param tokenIndexFrom    the token the user wants to swap from
    /// @param tokenIndexTo      the token the user wants to swap to
    /// @param dx                the amount of tokens the user wants to swap from
    /// @param minDy             the min amount the user would like to receive, or revert.
    /// @param deadline          latest timestamp to accept this transaction
    /// @return amountOut        amount of tokens bought
    function swap(
        uint8 tokenIndexFrom,
        uint8 tokenIndexTo,
        uint256 dx,
        uint256 minDy,
        uint256 deadline
    ) external returns (uint256 amountOut);

    // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════

    /// @notice Wrapper for IDefaultPool.calculateSwap()
    /// @param tokenIndexFrom    the token the user wants to sell
    /// @param tokenIndexTo      the token the user wants to buy
    /// @param dx                the amount of tokens the user wants to sell. If the token charges
    ///                          a fee on transfers, use the amount that gets transferred after the fee.
    /// @return amountOut        amount of tokens the user will receive
    function calculateSwap(
        uint8 tokenIndexFrom,
        uint8 tokenIndexTo,
        uint256 dx
    ) external view returns (uint256 amountOut);

    /// @notice Wrapper for IDefaultPool.getToken()
    /// @param index     the index of the token
    /// @return token    address of the token at given index
    function getToken(uint8 index) external view returns (address token);

    /// @notice Checks if a path exists between the two tokens, using any of the supported pools.
    /// @dev This is used by SwapQuoterV2 to check if a path exists between two tokens using the LinkedPool.
    /// Note: this only checks if both tokens are present in the tree, but doesn't check if any of the pools
    /// connecting the two tokens are paused. This is done to enable caching of the result, the paused/duplicated
    /// pools will be discarded, when `findBestPath` is called to fetch the quote.
    /// @param tokenIn          Token address to begin from
    /// @param tokenOut         Token address to end up with
    /// @return areConnected    True if a path exists between the two tokens, false otherwise
    function areConnectedTokens(address tokenIn, address tokenOut) external view returns (bool areConnected);

    /// @notice Returns the best path for swapping the given amount of tokens. All possible paths
    /// present in the internal tree are considered, if any of the tokens are present in the tree more than once.
    /// Note: paths that have the same pool more than once are not considered.
    /// @dev Will return zero values if no path is found.
    /// @param tokenIn          the token the user wants to sell
    /// @param tokenOut         the token the user wants to buy
    /// @param amountIn         the amount of tokens the user wants to sell
    /// @return tokenIndexFrom  the index of the token the user wants to sell
    /// @return tokenIndexTo    the index of the token the user wants to buy
    /// @return amountOut       amount of tokens the user will receive
    function findBestPath(
        address tokenIn,
        address tokenOut,
        uint256 amountIn
    )
        external
        view
        returns (
            uint8 tokenIndexFrom,
            uint8 tokenIndexTo,
            uint256 amountOut
        );

    /// @notice Returns the full amount of the "token nodes" in the internal tree.
    /// Note that some of the tokens might be duplicated, as the node in the tree represents
    /// a given path frm the bridge token to the node token using a series of pools.
    function tokenNodesAmount() external view returns (uint256);

    /// @notice Returns the list of pools that are "attached" to a node.
    /// Pool is attached to a node, if it connects the node to one of its children.
    /// Note: pool that is connecting the node to its parent is not considered attached.
    function getAttachedPools(uint8 index) external view returns (address[] memory pools);

    /// @notice Returns the list of indexes that represent a given token in the tree.
    /// @dev Will return empty array for tokens that are not added to the tree.
    function getTokenIndexes(address token) external view returns (uint256[] memory indexes);

    /// @notice Returns the pool module logic address, that is used to get swap quotes, token indexes and perform swaps.
    /// @dev Will return address(0) for pools that are not added to the tree.
    /// Will return address(this) for pools that conform to IDefaultPool interface.
    function getPoolModule(address pool) external view returns (address poolModule);

    /// @notice Returns the index of a parent node for the given node, as well as the pool that connects the two nodes.
    /// @dev Will return zero values for the root node. Will revert if index is out of range.
    function getNodeParent(uint256 nodeIndex) external view returns (uint256 parentIndex, address parentPool);
}

File 5 of 14 : IDefaultPool.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

interface IDefaultPool {
    function swap(
        uint8 tokenIndexFrom,
        uint8 tokenIndexTo,
        uint256 dx,
        uint256 minDy,
        uint256 deadline
    ) external returns (uint256 amountOut);

    function calculateSwap(
        uint8 tokenIndexFrom,
        uint8 tokenIndexTo,
        uint256 dx
    ) external view returns (uint256 amountOut);

    function getToken(uint8 index) external view returns (address token);
}

File 6 of 14 : Structs.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.13; // "using A for B global" requires 0.8.13 or higher

// ══════════════════════════════════════════ TOKEN AND POOL DESCRIPTION ═══════════════════════════════════════════════

/// @notice Struct representing a bridge token. Used as the return value in view functions.
/// @param symbol   Bridge token symbol: unique token ID consistent among all chains
/// @param token    Bridge token address
struct BridgeToken {
    string symbol;
    address token;
}

/// @notice Struct used by IPoolHandler to represent a token in a pool
/// @param index    Token index in the pool
/// @param token    Token address
struct IndexedToken {
    uint8 index;
    address token;
}

/// @notice Struct representing a token, and the available Actions for performing a swap.
/// @param actionMask   Bitmask representing what actions (see ActionLib) are available for swapping a token
/// @param token        Token address
struct LimitedToken {
    uint256 actionMask;
    address token;
}

/// @notice Struct representing how pool tokens are stored by `SwapQuoter`.
/// @param isWeth   Whether the token represents Wrapped ETH.
/// @param token    Token address.
struct PoolToken {
    bool isWeth;
    address token;
}

/// @notice Struct representing a liquidity pool. Used as the return value in view functions.
/// @param pool         Pool address.
/// @param lpToken      Address of pool's LP token.
/// @param tokens       List of pool's tokens.
struct Pool {
    address pool;
    address lpToken;
    PoolToken[] tokens;
}

// ════════════════════════════════════════════════ ROUTER STRUCTS ═════════════════════════════════════════════════════

/// @notice Struct representing a quote request for swapping a bridge token.
/// Used in destination chain's SynapseRouter, hence the name "Destination Request".
/// @dev tokenOut is passed externally.
/// @param symbol   Bridge token symbol: unique token ID consistent among all chains
/// @param amountIn Amount of bridge token to start with, before the bridge fee is applied
struct DestRequest {
    string symbol;
    uint256 amountIn;
}

/// @notice Struct representing a swap request for SynapseRouter.
/// @dev tokenIn is supplied separately.
/// @param routerAdapter    Contract that will perform the swap for the Router. Address(0) specifies a "no swap" query.
/// @param tokenOut         Token address to swap to.
/// @param minAmountOut     Minimum amount of tokens to receive after the swap, or tx will be reverted.
/// @param deadline         Latest timestamp for when the transaction needs to be executed, or tx will be reverted.
/// @param rawParams        ABI-encoded params for the swap that will be passed to `routerAdapter`.
///                         Should be DefaultParams for swaps via DefaultAdapter.
struct SwapQuery {
    address routerAdapter;
    address tokenOut;
    uint256 minAmountOut;
    uint256 deadline;
    bytes rawParams;
}

using SwapQueryLib for SwapQuery global;

library SwapQueryLib {
    /// @notice Checks whether the router adapter was specified in the query.
    /// Query without a router adapter specifies that no action needs to be taken.
    function hasAdapter(SwapQuery memory query) internal pure returns (bool) {
        return query.routerAdapter != address(0);
    }

    /// @notice Fills `routerAdapter` and `deadline` fields in query, if it specifies one of the supported Actions,
    /// and if a path for this action was found.
    function fillAdapterAndDeadline(SwapQuery memory query, address routerAdapter) internal pure {
        // Fill the fields only if some path was found.
        if (query.minAmountOut == 0) return;
        // Empty params indicates no action needs to be done, thus no adapter is needed.
        query.routerAdapter = query.rawParams.length == 0 ? address(0) : routerAdapter;
        // Set default deadline to infinity. Not using the value of 0,
        // which would lead to every swap to revert by default.
        query.deadline = type(uint256).max;
    }
}

// ════════════════════════════════════════════════ ADAPTER STRUCTS ════════════════════════════════════════════════════

/// @notice Struct representing parameters for swapping via DefaultAdapter.
/// @param action           Action that DefaultAdapter needs to perform.
/// @param pool             Liquidity pool that will be used for Swap/AddLiquidity/RemoveLiquidity actions.
/// @param tokenIndexFrom   Token index to swap from. Used for swap/addLiquidity actions.
/// @param tokenIndexTo     Token index to swap to. Used for swap/removeLiquidity actions.
struct DefaultParams {
    Action action;
    address pool;
    uint8 tokenIndexFrom;
    uint8 tokenIndexTo;
}

/// @notice All possible actions that DefaultAdapter could perform.
enum Action {
    Swap, // swap between two pools tokens
    AddLiquidity, // add liquidity in a form of a single pool token
    RemoveLiquidity, // remove liquidity in a form of a single pool token
    HandleEth // ETH <> WETH interaction
}

using ActionLib for Action global;

/// @notice Library for dealing with bit masks which describe what set of Actions is available.
library ActionLib {
    /// @notice Returns a bitmask with all possible actions set to True.
    function allActions() internal pure returns (uint256 actionMask) {
        actionMask = type(uint256).max;
    }

    /// @notice Returns whether the given action is set to True in the bitmask.
    function isIncluded(Action action, uint256 actionMask) internal pure returns (bool) {
        return actionMask & mask(action) != 0;
    }

    /// @notice Returns a bitmask with only the given action set to True.
    function mask(Action action) internal pure returns (uint256) {
        return 1 << uint256(action);
    }

    /// @notice Returns a bitmask with only two given actions set to True.
    function mask(Action a, Action b) internal pure returns (uint256) {
        return mask(a) | mask(b);
    }
}

File 7 of 14 : UniversalToken.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {TokenNotContract} from "./Errors.sol";
import {SafeERC20, IERC20} from "@openzeppelin/contracts-4.5.0/token/ERC20/utils/SafeERC20.sol";

library UniversalTokenLib {
    using SafeERC20 for IERC20;

    address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /// @notice Transfers tokens to the given account. Reverts if transfer is not successful.
    /// @dev This might trigger fallback, if ETH is transferred to the contract.
    /// Make sure this can not lead to reentrancy attacks.
    function universalTransfer(
        address token,
        address to,
        uint256 value
    ) internal {
        // Don't do anything, if need to send tokens to this address
        if (to == address(this)) return;
        if (token == ETH_ADDRESS) {
            /// @dev Note: this can potentially lead to executing code in `to`.
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, ) = to.call{value: value}("");
            require(success, "ETH transfer failed");
        } else {
            IERC20(token).safeTransfer(to, value);
        }
    }

    /// @notice Issues an infinite allowance to the spender, if the current allowance is insufficient
    /// to spend the given amount.
    function universalApproveInfinity(
        address token,
        address spender,
        uint256 amountToSpend
    ) internal {
        // ETH Chad doesn't require your approval
        if (token == ETH_ADDRESS) return;
        // No-op if allowance is already sufficient
        uint256 allowance = IERC20(token).allowance(address(this), spender);
        if (allowance >= amountToSpend) return;
        // Otherwise, reset approval to 0 and set to max allowance
        if (allowance > 0) IERC20(token).safeApprove(spender, 0);
        IERC20(token).safeApprove(spender, type(uint256).max);
    }

    /// @notice Returns the balance of the given token (or native ETH) for the given account.
    function universalBalanceOf(address token, address account) internal view returns (uint256) {
        if (token == ETH_ADDRESS) {
            return account.balance;
        } else {
            return IERC20(token).balanceOf(account);
        }
    }

    /// @dev Checks that token is a contract and not ETH_ADDRESS.
    function assertIsContract(address token) internal view {
        // Check that ETH_ADDRESS was not used (in case this is a predeploy on any of the chains)
        if (token == UniversalTokenLib.ETH_ADDRESS) revert TokenNotContract();
        // Check that token is not an EOA
        if (token.code.length == 0) revert TokenNotContract();
    }
}

File 8 of 14 : TokenTree.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

/// TokenTree implements the internal logic for storing a set of tokens in a rooted tree.
/// - Root node represents a Synapse-bridged token.
/// - The root token could not appear more than once in the tree.
/// - Other tree nodes represent tokens that are correlated with the root token.
/// - These other tokens could appear more than once in the tree.
/// - Every edge between a child and a parent node is associated with a single liquidity pool that contains both tokens.
/// - The tree is rooted => the root of the tree has a zero depth. A child node depth is one greater than its parent.
/// - Every node can have arbitrary amount of children.
/// - New nodes are added to the tree by "attaching" a pool to an existing node. This adds all the other pool tokens
/// as new children of the existing node (which represents one of the tokens from the pool).
/// - Pool could be only attached once to any given node. Pool could not be attached to a node, if it connects the node
/// with its parent.
/// - Pool could be potentially attached to two different nodes in the tree.
/// - By definition a tree has no cycles, so there exists only one path between any two nodes. Every edge on this path
/// represents a liquidity pool, and the whole path represents a series of swaps that lead from one token to another.
/// - Paths that contain a pool more than once are not allowed, and are not used for quotes/swaps. This is due to
/// the fact that it's impossible to get an accurate quote for the second swap through the pool in the same tx.
/// > This contract is only responsible for storing and traversing the tree. The swap/quote logic, as well as
/// > transformation of the inner tree into IDefaultPool interface is implemented in the child contract.
abstract contract TokenTree {
    error TokenTree__DifferentTokenLists();
    error TokenTree__IndexOutOfRange(uint256 index);
    error TokenTree__NodeTokenNotInPool();
    error TokenTree__PoolAlreadyAttached();
    error TokenTree__PoolAlreadyOnRootPath();
    error TokenTree__SwapPoolUsedTwice(address pool);
    error TokenTree__TooManyNodes();
    error TokenTree__TooManyPools();
    error TokenTree__UnknownPool();

    event TokenNodeAdded(uint256 childIndex, address token, address parentPool);
    event PoolAdded(uint256 parentIndex, address pool, address poolModule);
    event PoolModuleUpdated(address pool, address oldPoolModule, address newPoolModule);

    /// @notice Struct so store the tree nodes
    /// @param token        Address of the token represented by this node
    /// @param depth        Depth of the node in the tree
    /// @param poolIndex    Index of the pool that connects this node to its parent (0 if root)
    struct Node {
        address token;
        uint8 depth;
        uint8 poolIndex;
    }

    /// @notice Struct to store the liquidity pools
    /// @dev Module address is used for delegate calls to get swap quotes, token indexes, etc.
    /// Set to address(this) if pool conforms to IDefaultPool interface. Set to 0x0 if pool is not supported.
    /// @param module       Address of the module contract for this pool
    /// @param index        Index of the pool in the `_pools` array
    struct Pool {
        address module;
        uint8 index;
    }

    /// @notice Struct to get around stack too deep error
    /// @param from         Node representing the token we are swapping from
    /// @param to           Node representing the token we are swapping to
    struct Request {
        Node from;
        Node to;
        bool probePaused;
    }

    /// @notice Struct to get around stack too deep error
    /// @param visitedPoolsMask     Bitmask of pools visited so far
    /// @param amountOut            Amount of tokens received so far
    struct Route {
        uint256 visitedPoolsMask;
        uint256 amountOut;
    }

    // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════

    // The nodes of the tree are stored in an array. The root node is at index 0.
    Node[] internal _nodes;

    // The list of all supported liquidity pools. All values are unique.
    address[] internal _pools;

    // (pool address => pool description)
    mapping(address => Pool) internal _poolMap;

    // (pool => token => tokenIndex) for each pool, stores the index of each token in the pool.
    mapping(address => mapping(address => uint8)) public tokenIndexes;

    // (token => nodes) for each token, stores the indexes of all nodes that represent this token.
    mapping(address => uint256[]) internal _tokenNodes;

    // The full path from every node to the root is stored using bitmasks in the following way:
    // - For a node with index i at depth N, lowest N + 1 bytes of _rootPath[i] are used to store the path to the root.
    // - The lowest byte is always the root index. This is always 0, but we store this for consistency.
    // - The highest byte is always the node index. This is always i, but we store this for consistency.
    // - The remaining bytes are indexes of the nodes on the path from the node to the root (from highest to lowest).
    // This way the finding the lowest common ancestor of two nodes is reduced to finding the lowest differing byte
    // in node's encoded root paths.
    uint256[] internal _rootPath;

    // (node => bitmask with all attached pools to the node).
    // Note: This excludes the pool that connects the node to its parent.
    mapping(uint256 => uint256) internal _attachedPools;

    // ════════════════════════════════════════════════ CONSTRUCTOR ════════════════════════════════════════════════════

    constructor(address bridgeToken) {
        // Push the empty pool so that `poolIndex` for non-root nodes is never 0
        _pools.push(address(0));
        // The root node is always the bridge token
        _addNode({token: bridgeToken, depth: 0, poolIndex: 0, rootPathParent: 0});
    }

    modifier checkIndex(uint256 nodeIndex) {
        if (nodeIndex >= _nodes.length) revert TokenTree__IndexOutOfRange(nodeIndex);
        _;
    }

    // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════

    /// @dev Adds a pool having `N` pool tokens to the tree by adding `N-1` new nodes
    /// as the children of the given node. Given node needs to represent a token from the pool.
    /// Note: assumes that nodeIndex is valid, and that pool is not a zero address.
    function _addPool(
        uint256 nodeIndex,
        address pool,
        address poolModule
    ) internal {
        Node memory node = _nodes[nodeIndex];
        if (poolModule == address(0)) poolModule = address(this);
        (bool wasAdded, uint8 poolIndex) = (false, _poolMap[pool].index);
        // Save the pool and emit an event if it's not been added before
        if (poolIndex == 0) {
            if (_pools.length > type(uint8).max) revert TokenTree__TooManyPools();
            // Can do the unsafe cast here, as we just checked that pool index fits into uint8
            poolIndex = uint8(_pools.length);
            _pools.push(pool);
            _poolMap[pool] = Pool({module: poolModule, index: poolIndex});
            wasAdded = true;
            emit PoolAdded(nodeIndex, pool, poolModule);
        } else {
            // Check if the existing pool could be added to the node. This enforces some sanity checks,
            // as well the invariant that any path from root to node doesn't contain the same pool twice.
            _checkPoolAddition(nodeIndex, node.depth, poolIndex);
        }
        // Remember that the pool is attached to the node
        _attachedPools[nodeIndex] |= 1 << poolIndex;
        address[] memory tokens = _getPoolTokens(poolModule, pool);
        uint256 numTokens = tokens.length;
        bool nodeFound = false;
        unchecked {
            uint8 childDepth = node.depth + 1;
            uint256 rootPathParent = _rootPath[nodeIndex];
            for (uint256 i = 0; i < numTokens; ++i) {
                address token = tokens[i];
                // Save token indexes if this is a new pool
                if (wasAdded) {
                    tokenIndexes[pool][token] = uint8(i);
                }
                // Add new nodes to the tree
                if (token == node.token) {
                    nodeFound = true;
                    continue;
                }
                _addNode(token, childDepth, poolIndex, rootPathParent);
            }
        }
        if (!nodeFound) revert TokenTree__NodeTokenNotInPool();
    }

    /// @dev Adds a new node to the tree and saves its path to the root.
    function _addNode(
        address token,
        uint8 depth,
        uint8 poolIndex,
        uint256 rootPathParent
    ) internal {
        // Index of the newly inserted child node
        uint256 nodeIndex = _nodes.length;
        if (nodeIndex > type(uint8).max) revert TokenTree__TooManyNodes();
        // Don't add the bridge token (root) twice. This may happen if we add a new pool containing the bridge token
        // to a few existing nodes. E.g. we have old nUSD/USDC/USDT pool, and we add a new nUSD/USDC pool. In this case
        // we attach nUSD/USDC pool to root, and then attach old nUSD/USDC/USDT pool to the newly added USDC node
        // to enable nUSD -> USDC -> USDT swaps via new + old pools.
        if (_nodes.length > 0 && token == _nodes[0].token) {
            return;
        }
        _nodes.push(Node({token: token, depth: depth, poolIndex: poolIndex}));
        _tokenNodes[token].push(nodeIndex);
        // Push the root path for the new node. The root path is the inserted node index + the parent's root path.
        _rootPath.push((nodeIndex << (8 * depth)) | rootPathParent);
        emit TokenNodeAdded(nodeIndex, token, _pools[poolIndex]);
    }

    /// @dev Updates the Pool Module for the given pool.
    /// Will revert, if the pool was not previously added, or if the new pool module produces a different list of tokens.
    function _updatePoolModule(address pool, address newPoolModule) internal {
        // Check that pool was previously added
        address oldPoolModule = _poolMap[pool].module;
        if (oldPoolModule == address(0)) revert TokenTree__UnknownPool();
        // Sanity check that pool modules produce the same list of tokens
        address[] memory oldTokens = _getPoolTokens(oldPoolModule, pool);
        address[] memory newTokens = _getPoolTokens(newPoolModule, pool);
        if (oldTokens.length != newTokens.length) revert TokenTree__DifferentTokenLists();
        for (uint256 i = 0; i < oldTokens.length; ++i) {
            if (oldTokens[i] != newTokens[i]) revert TokenTree__DifferentTokenLists();
        }
        // Update the pool module
        _poolMap[pool].module = newPoolModule;
        emit PoolModuleUpdated(pool, oldPoolModule, newPoolModule);
    }

    // ══════════════════════════════════════ INTERNAL LOGIC: MULTIPLE POOLS ═══════════════════════════════════════════

    /// @dev Performs a multi-hop swap by following the path from "tokenFrom" node to "tokenTo" node
    /// in the stored tree. Token indexes are checked to be within range and not the same.
    /// Assumes that the initial token is already in this contract.
    function _multiSwap(
        uint256 nodeIndexFrom,
        uint256 nodeIndexTo,
        uint256 amountIn
    ) internal returns (Route memory route) {
        // Struct to get around stack too deep
        Request memory req = Request({from: _nodes[nodeIndexFrom], to: _nodes[nodeIndexTo], probePaused: false});
        uint256 rootPathFrom = _rootPath[nodeIndexFrom];
        uint256 rootPathTo = _rootPath[nodeIndexTo];
        // Find the depth where the paths diverge
        uint256 depthDiff = _depthDiff(rootPathFrom, rootPathTo);
        // Check if `nodeIndexTo` is an ancestor of `nodeIndexFrom`. True if paths diverge below `nodeIndexTo`.
        if (depthDiff > req.to.depth) {
            // Path from "tokenFrom" to root includes "tokenTo",
            // so we simply go from "tokenFrom" to "tokenTo" in the "to root" direction.
            return _multiSwapToRoot(0, rootPathFrom, req.from.depth, req.to.depth, amountIn);
        }
        // Check if `nodeIndexFrom` is an ancestor of `nodeIndexTo`. True if paths diverge below `nodeIndexFrom`.
        if (depthDiff > req.from.depth) {
            // Path from "tokenTo" to root includes "tokenFrom",
            // so we simply go from "tokenTo" to "tokenFrom" in the "from root" direction.
            return _multiSwapFromRoot(0, rootPathTo, req.from.depth, req.to.depth, amountIn);
        }
        // First, we traverse up the tree from "tokenFrom" to one level deeper the lowest common ancestor.
        route = _multiSwapToRoot(0, rootPathFrom, req.from.depth, depthDiff, amountIn);
        // Check if we need to do a sibling swap. When the two nodes are connected to the same parent via the same pool,
        // we do a direct swap between the two nodes, instead of doing two swaps through the parent using the same pool.
        uint256 lastNodeIndex = _extractNodeIndex(rootPathFrom, depthDiff);
        uint256 siblingIndex = _extractNodeIndex(rootPathTo, depthDiff);
        uint256 firstPoolIndex = _nodes[lastNodeIndex].poolIndex;
        uint256 secondPoolIndex = _nodes[siblingIndex].poolIndex;
        if (firstPoolIndex == secondPoolIndex) {
            // Swap lastNode -> sibling
            (route.visitedPoolsMask, route.amountOut) = _singleSwap(
                route.visitedPoolsMask,
                firstPoolIndex,
                lastNodeIndex,
                siblingIndex,
                route.amountOut
            );
        } else {
            // Swap lastNode -> parent
            uint256 parentIndex = _extractNodeIndex(rootPathFrom, depthDiff - 1);
            (route.visitedPoolsMask, route.amountOut) = _singleSwap(
                route.visitedPoolsMask,
                firstPoolIndex,
                lastNodeIndex,
                parentIndex,
                route.amountOut
            );
            // Swap parent -> sibling
            (route.visitedPoolsMask, route.amountOut) = _singleSwap(
                route.visitedPoolsMask,
                secondPoolIndex,
                parentIndex,
                siblingIndex,
                route.amountOut
            );
        }
        // Finally, we traverse down the tree from the lowest common ancestor to "tokenTo".
        return _multiSwapFromRoot(route.visitedPoolsMask, rootPathTo, depthDiff, req.to.depth, route.amountOut);
    }

    /// @dev Performs a multi-hop swap, going in "from root direction" (where depth increases)
    /// via the given `rootPath` from `depthFrom` to `depthTo`.
    /// Assumes that the initial token is already in this contract.
    function _multiSwapFromRoot(
        uint256 visitedPoolsMask,
        uint256 rootPath,
        uint256 depthFrom,
        uint256 depthTo,
        uint256 amountIn
    ) internal returns (Route memory route) {
        uint256 nodeIndex = _extractNodeIndex(rootPath, depthFrom);
        // Traverse down the tree following `rootPath` from `depthFrom` to `depthTo`.
        for (uint256 depth = depthFrom; depth < depthTo; ) {
            // Get the child node
            unchecked {
                ++depth;
            }
            uint256 childIndex = _extractNodeIndex(rootPath, depth);
            // Swap node -> child
            (visitedPoolsMask, amountIn) = _singleSwap(
                visitedPoolsMask,
                _nodes[childIndex].poolIndex,
                nodeIndex,
                childIndex,
                amountIn
            );
            nodeIndex = childIndex;
        }
        route.visitedPoolsMask = visitedPoolsMask;
        route.amountOut = amountIn;
    }

    /// @dev Performs a multi-hop swap, going in "to root direction" (where depth decreases)
    /// via the given `rootPath` from `depthFrom` to `depthTo`.
    /// Assumes that the initial token is already in this contract.
    function _multiSwapToRoot(
        uint256 visitedPoolsMask,
        uint256 rootPath,
        uint256 depthFrom,
        uint256 depthTo,
        uint256 amountIn
    ) internal returns (Route memory route) {
        uint256 nodeIndex = _extractNodeIndex(rootPath, depthFrom);
        // Traverse up the tree following `rootPath` from `depthFrom` to `depthTo`.
        for (uint256 depth = depthFrom; depth > depthTo; ) {
            // Get the parent node
            unchecked {
                --depth; // depth > 0 so we can do unchecked math
            }
            uint256 parentIndex = _extractNodeIndex(rootPath, depth);
            // Swap node -> parent
            (visitedPoolsMask, amountIn) = _singleSwap(
                visitedPoolsMask,
                _nodes[nodeIndex].poolIndex,
                nodeIndex,
                parentIndex,
                amountIn
            );
            nodeIndex = parentIndex;
        }
        route.visitedPoolsMask = visitedPoolsMask;
        route.amountOut = amountIn;
    }

    // ════════════════════════════════════════ INTERNAL LOGIC: SINGLE POOL ════════════════════════════════════════════

    /// @dev Performs a single swap between two nodes using the given pool.
    /// Assumes that the initial token is already in this contract.
    function _poolSwap(
        address poolModule,
        address pool,
        uint256 nodeIndexFrom,
        uint256 nodeIndexTo,
        uint256 amountIn
    ) internal virtual returns (uint256 amountOut);

    /// @dev Performs a single swap between two nodes using the given pool given the set of pools
    /// we have already used on the path. Returns the updated set of pools and the amount of tokens received.
    /// Assumes that the initial token is already in this contract.
    function _singleSwap(
        uint256 visitedPoolsMask,
        uint256 poolIndex,
        uint256 nodeIndexFrom,
        uint256 nodeIndexTo,
        uint256 amountIn
    ) internal returns (uint256 visitedPoolsMask_, uint256 amountOut) {
        address pool = _pools[poolIndex];
        // If we already used this pool on the path, we can't use it again.
        if (visitedPoolsMask & (1 << poolIndex) != 0) revert TokenTree__SwapPoolUsedTwice(pool);
        // Mark the pool as visited
        visitedPoolsMask_ = visitedPoolsMask | (1 << poolIndex);
        amountOut = _poolSwap(_poolMap[pool].module, pool, nodeIndexFrom, nodeIndexTo, amountIn);
    }

    // ══════════════════════════════════════ INTERNAL VIEWS: MULTIPLE POOLS ═══════════════════════════════════════════

    /// @dev Calculates the multi-hop swap quote by following the path from "tokenFrom" node to "tokenTo" node
    /// in the stored tree. Token indexes are checked to be within range and not the same.
    function _getMultiSwapQuote(
        uint256 nodeIndexFrom,
        uint256 nodeIndexTo,
        uint256 amountIn,
        bool probePaused
    ) internal view returns (Route memory route) {
        // Struct to get around stack too deep
        Request memory req = Request({from: _nodes[nodeIndexFrom], to: _nodes[nodeIndexTo], probePaused: probePaused});
        uint256 rootPathFrom = _rootPath[nodeIndexFrom];
        uint256 rootPathTo = _rootPath[nodeIndexTo];
        // Find the depth where the paths diverge
        uint256 depthDiff = _depthDiff(rootPathFrom, rootPathTo);
        // Check if `nodeIndexTo` is an ancestor of `nodeIndexFrom`. True if paths diverge below `nodeIndexTo`.
        if (depthDiff > req.to.depth) {
            // Path from "tokenFrom" to root includes "tokenTo",
            // so we simply go from "tokenFrom" to "tokenTo" in the "to root" direction.
            return _getMultiSwapToRootQuote(0, rootPathFrom, req.from.depth, req.to.depth, amountIn, probePaused);
        }
        // Check if `nodeIndexFrom` is an ancestor of `nodeIndexTo`. True if paths diverge below `nodeIndexFrom`.
        if (depthDiff > req.from.depth) {
            // Path from "tokenTo" to root includes "tokenFrom",
            // so we simply go from "tokenTo" to "tokenFrom" in the "from root" direction.
            return _getMultiSwapFromRootQuote(0, rootPathTo, req.from.depth, req.to.depth, amountIn, probePaused);
        }
        // First, we traverse up the tree from "tokenFrom" to one level deeper the lowest common ancestor.
        route = _getMultiSwapToRootQuote(
            route.visitedPoolsMask,
            rootPathFrom,
            req.from.depth,
            depthDiff,
            amountIn,
            req.probePaused
        );
        // Check if we need to do a sibling swap. When the two nodes are connected to the same parent via the same pool,
        // we do a direct swap between the two nodes, instead of doing two swaps through the parent using the same pool.
        uint256 lastNodeIndex = _extractNodeIndex(rootPathFrom, depthDiff);
        uint256 siblingIndex = _extractNodeIndex(rootPathTo, depthDiff);
        uint256 firstPoolIndex = _nodes[lastNodeIndex].poolIndex;
        uint256 secondPoolIndex = _nodes[siblingIndex].poolIndex;
        if (firstPoolIndex == secondPoolIndex) {
            // Swap lastNode -> sibling
            (route.visitedPoolsMask, route.amountOut) = _getSingleSwapQuote(
                route.visitedPoolsMask,
                firstPoolIndex,
                lastNodeIndex,
                siblingIndex,
                route.amountOut,
                req.probePaused
            );
        } else {
            // Swap lastNode -> parent
            uint256 parentIndex = _extractNodeIndex(rootPathFrom, depthDiff - 1);
            (route.visitedPoolsMask, route.amountOut) = _getSingleSwapQuote(
                route.visitedPoolsMask,
                firstPoolIndex,
                lastNodeIndex,
                parentIndex,
                route.amountOut,
                req.probePaused
            );
            // Swap parent -> sibling
            (route.visitedPoolsMask, route.amountOut) = _getSingleSwapQuote(
                route.visitedPoolsMask,
                secondPoolIndex,
                parentIndex,
                siblingIndex,
                route.amountOut,
                req.probePaused
            );
        }
        // Finally, we traverse down the tree from the lowest common ancestor to "tokenTo".
        return
            _getMultiSwapFromRootQuote(
                route.visitedPoolsMask,
                rootPathTo,
                depthDiff,
                req.to.depth,
                route.amountOut,
                req.probePaused
            );
    }

    /// @dev Calculates the amount of tokens that will be received from a multi-hop swap,
    /// going in "from root direction" (where depth increases) via the given `rootPath` from `depthFrom` to `depthTo`.
    function _getMultiSwapFromRootQuote(
        uint256 visitedPoolsMask,
        uint256 rootPath,
        uint256 depthFrom,
        uint256 depthTo,
        uint256 amountIn,
        bool probePaused
    ) internal view returns (Route memory route) {
        uint256 nodeIndex = _extractNodeIndex(rootPath, depthFrom);
        // Traverse down the tree following `rootPath` from `depthFrom` to `depthTo`.
        for (uint256 depth = depthFrom; depth < depthTo; ) {
            // Get the child node
            unchecked {
                ++depth;
            }
            uint256 childIndex = _extractNodeIndex(rootPath, depth);
            // Swap node -> child
            (visitedPoolsMask, amountIn) = _getSingleSwapQuote(
                visitedPoolsMask,
                _nodes[childIndex].poolIndex,
                nodeIndex,
                childIndex,
                amountIn,
                probePaused
            );
            nodeIndex = childIndex;
        }
        route.visitedPoolsMask = visitedPoolsMask;
        route.amountOut = amountIn;
    }

    /// @dev Calculates the amount of tokens that will be received from a multi-hop swap,
    /// going in "to root direction" (where depth decreases) via the given `rootPath` from `depthFrom` to `depthTo`.
    function _getMultiSwapToRootQuote(
        uint256 visitedPoolsMask,
        uint256 rootPath,
        uint256 depthFrom,
        uint256 depthTo,
        uint256 amountIn,
        bool probePaused
    ) internal view returns (Route memory route) {
        uint256 nodeIndex = _extractNodeIndex(rootPath, depthFrom);
        // Traverse up the tree following `rootPath` from `depthFrom` to `depthTo`.
        for (uint256 depth = depthFrom; depth > depthTo; ) {
            // Get the parent node
            unchecked {
                --depth; // depth > 0 so we can do unchecked math
            }
            uint256 parentIndex = _extractNodeIndex(rootPath, depth);
            // Swap node -> parent
            (visitedPoolsMask, amountIn) = _getSingleSwapQuote(
                visitedPoolsMask,
                _nodes[nodeIndex].poolIndex,
                nodeIndex,
                parentIndex,
                amountIn,
                probePaused
            );
            nodeIndex = parentIndex;
        }
        route.visitedPoolsMask = visitedPoolsMask;
        route.amountOut = amountIn;
    }

    // ════════════════════════════════════════ INTERNAL VIEWS: SINGLE POOL ════════════════════════════════════════════

    /// @dev Returns the tokens in the pool at the given address.
    function _getPoolTokens(address poolModule, address pool) internal view virtual returns (address[] memory tokens);

    /// @dev Returns the amount of tokens that will be received from a single swap.
    /// Will check if the pool is paused beforehand, if requested.
    function _getPoolQuote(
        address poolModule,
        address pool,
        uint256 nodeIndexFrom,
        uint256 nodeIndexTo,
        uint256 amountIn,
        bool probePaused
    ) internal view virtual returns (uint256 amountOut);

    /// @dev Calculates the amount of tokens that will be received from a single swap given the set of pools
    /// we have already used on the path. Returns the updated set of pools and the amount of tokens received.
    function _getSingleSwapQuote(
        uint256 visitedPoolsMask,
        uint256 poolIndex,
        uint256 nodeIndexFrom,
        uint256 nodeIndexTo,
        uint256 amountIn,
        bool probePaused
    ) internal view returns (uint256 visitedPoolsMask_, uint256 amountOut) {
        if (visitedPoolsMask & (1 << poolIndex) != 0) {
            // If we already used this pool on the path, we can't use it again.
            // Return the full mask and zero amount to indicate that the swap is not possible.
            return (type(uint256).max, 0);
        }
        // Otherwise, mark the pool as visited
        visitedPoolsMask_ = visitedPoolsMask | (1 << poolIndex);
        address pool = _pools[poolIndex];
        // Pass the parameter for whether we want to check that the pool is paused or not.
        amountOut = _getPoolQuote(_poolMap[pool].module, pool, nodeIndexFrom, nodeIndexTo, amountIn, probePaused);
    }

    // ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════

    /// @dev Checks if a pool could be added to the tree at the given node. Requirements:
    /// - Pool is not already attached to the node: no need to add twice.
    /// - Pool is not present on the path from the node to root: this would invalidate swaps from added nodes to root,
    /// as this path would contain this pool twice.
    function _checkPoolAddition(
        uint256 nodeIndex,
        uint256 nodeDepth,
        uint8 poolIndex
    ) internal view {
        // Check that the pool is not already attached to the node
        if (_attachedPools[nodeIndex] & (1 << poolIndex) != 0) revert TokenTree__PoolAlreadyAttached();
        // Here we iterate over all nodes from the root to the node, and check that the pool connecting the current node
        // to its parent is not the pool we want to add. We skip the root node (depth 0), as it has no parent.
        uint256 rootPath = _rootPath[nodeIndex];
        for (uint256 d = 1; d <= nodeDepth; ) {
            uint256 nodeIndex_ = _extractNodeIndex(rootPath, d);
            if (_nodes[nodeIndex_].poolIndex == poolIndex) revert TokenTree__PoolAlreadyOnRootPath();
            unchecked {
                ++d;
            }
        }
    }

    /// @dev Finds the lowest common ancestor of two different nodes in the tree.
    /// Node is defined by the path from the root to the node, and the depth of the node.
    function _depthDiff(uint256 rootPath0, uint256 rootPath1) internal pure returns (uint256 depthDiff) {
        // Xor the paths to get the first differing byte.
        // Nodes are different => root paths are different => the result is never zero.
        rootPath0 ^= rootPath1;
        // Sanity check for invariant: rootPath0 != rootPath1
        assert(rootPath0 != 0);
        // Traverse from root to node0 and node1 until the paths diverge.
        while ((rootPath0 & 0xFF) == 0) {
            // Shift off the lowest byte which are identical in both paths.
            rootPath0 >>= 8;
            unchecked {
                depthDiff++;
            }
        }
    }

    /// @dev Returns the index of the node at the given depth on the path from the root to the node.
    function _extractNodeIndex(uint256 rootPath, uint256 depth) internal pure returns (uint256 nodeIndex) {
        // Nodes on the path are stored from root to node (lowest to highest bytes).
        return (rootPath >> (8 * depth)) & 0xFF;
    }
}

File 9 of 14 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 10 of 14 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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 Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        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 11 of 14 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.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));
        }
    }

    /**
     * @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 12 of 14 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

error DeadlineExceeded();
error InsufficientOutputAmount();

error MsgValueIncorrect();
error PoolNotFound();
error TokenAddressMismatch();
error TokenNotContract();
error TokenNotETH();
error TokensIdentical();

File 13 of 14 : 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 14 of 14 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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);
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "solmate/=lib/solmate/src/",
    "@boringcrypto/=node_modules/@boringcrypto/",
    "@ensdomains/=node_modules/@ensdomains/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "eth-gas-reporter/=node_modules/eth-gas-reporter/",
    "forge-std/=lib/forge-std/src/",
    "hardhat-deploy/=node_modules/hardhat-deploy/",
    "hardhat/=node_modules/hardhat/",
    "sol-explore/=node_modules/sol-explore/",
    "solmate/=lib/solmate/src/",
    "synthetix/=node_modules/synthetix/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"bridgeToken","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"LinkedPool__DeadlineExceeded","type":"error"},{"inputs":[],"name":"LinkedPool__EmptyPoolAddress","type":"error"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"LinkedPool__EqualSwapIndexes","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"minDy","type":"uint256"}],"name":"LinkedPool__MinDyNotMet","type":"error"},{"inputs":[],"name":"TokenTree__DifferentTokenLists","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"TokenTree__IndexOutOfRange","type":"error"},{"inputs":[],"name":"TokenTree__NodeTokenNotInPool","type":"error"},{"inputs":[],"name":"TokenTree__PoolAlreadyAttached","type":"error"},{"inputs":[],"name":"TokenTree__PoolAlreadyOnRootPath","type":"error"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"TokenTree__SwapPoolUsedTwice","type":"error"},{"inputs":[],"name":"TokenTree__TooManyNodes","type":"error"},{"inputs":[],"name":"TokenTree__TooManyPools","type":"error"},{"inputs":[],"name":"TokenTree__UnknownPool","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"parentIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"address","name":"poolModule","type":"address"}],"name":"PoolAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"address","name":"oldPoolModule","type":"address"},{"indexed":false,"internalType":"address","name":"newPoolModule","type":"address"}],"name":"PoolModuleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"childIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"parentPool","type":"address"}],"name":"TokenNodeAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"soldId","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"boughtId","type":"uint128"}],"name":"TokenSwap","type":"event"},{"inputs":[{"internalType":"uint256","name":"nodeIndex","type":"uint256"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"poolModule","type":"address"}],"name":"addPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"areConnectedTokens","outputs":[{"internalType":"bool","name":"areConnected","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"nodeIndexFrom","type":"uint8"},{"internalType":"uint8","name":"nodeIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"}],"name":"calculateSwap","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"findBestPath","outputs":[{"internalType":"uint8","name":"nodeIndexFromBest","type":"uint8"},{"internalType":"uint8","name":"nodeIndexToBest","type":"uint8"},{"internalType":"uint256","name":"amountOutBest","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"getAttachedPools","outputs":[{"internalType":"address[]","name":"pools","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeIndex","type":"uint256"}],"name":"getNodeParent","outputs":[{"internalType":"uint256","name":"parentIndex","type":"uint256"},{"internalType":"address","name":"parentPool","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"getPoolModule","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"getToken","outputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getTokenIndexes","outputs":[{"internalType":"uint256[]","name":"nodes","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"nodeIndexFrom","type":"uint8"},{"internalType":"uint8","name":"nodeIndexTo","type":"uint8"},{"internalType":"uint256","name":"dx","type":"uint256"},{"internalType":"uint256","name":"minDy","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swap","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"tokenIndexes","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenNodesAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"newPoolModule","type":"address"}],"name":"updatePoolModule","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b5060405162003292380380620032928339810160408190526200003491620003d0565b60018054808201825560009182527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60180546001600160a01b03191690558290620000839082908080620000a2565b506200008f336200028c565b6200009a81620002de565b505062000450565b60005460ff811115620000c857604051630be56a6f60e21b815260040160405180910390fd5b6000541580159062000106575060008081548110620000eb57620000eb62000408565b6000918252602090912001546001600160a01b038681169116145b1562000113575062000286565b604080516060810182526001600160a01b0380881680835260ff8089166020808601918252898316868801908152600080546001818101835582805298517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5639091018054955193518716600160a81b0260ff60a81b1994909716600160a01b026001600160a81b03199096169190981617939093171692909217909355908252600481529281208054928301815581529190912001819055600582620001db8660086200041e565b8254600181810185556000948552602090942060ff92831686901b9390931792019190915581547f3d015788e7bd0b08b47bbeb8eef47706ac9e0c6e79ed68f54e89df593bee087d92849289929088169081106200023d576200023d62000408565b6000918252602090912001546040516200027c9392916001600160a01b0316909283526001600160a01b03918216602084015216604082015260600190565b60405180910390a1505b50505050565b600780546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6007546001600160a01b031633146200033e5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6001600160a01b038116620003a55760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840162000335565b620003b0816200028c565b50565b80516001600160a01b0381168114620003cb57600080fd5b919050565b60008060408385031215620003e457600080fd5b620003ef83620003b3565b9150620003ff60208401620003b3565b90509250929050565b634e487b7160e01b600052603260045260246000fd5b60ff81811683821602908116908181146200044957634e487b7160e01b600052601160045260246000fd5b5092915050565b612e3280620004606000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c806382b8660011610097578063a95b089f11610066578063a95b089f1461029f578063dd3a1755146102b2578063f2fde38b146102d5578063f624d2c5146102e857600080fd5b806382b86600146102525780638da5cb5b14610265578063916955861461027657806398f80a981461029757600080fd5b80635ff2e6ec116100d35780635ff2e6ec146101d15780636ee4f71b14610215578063715018a61461022a57806380507b571461023257600080fd5b80630f2839ba14610105578063175724f61461012e5780631b6486d61461015e5780633dfea7721461019e575b600080fd5b61011861011336600461283c565b6102fb565b6040516101259190612857565b60405180910390f35b61014161013c3660046128a4565b610417565b604080519283526001600160a01b03909116602083015201610125565b61018c61016c3660046128d2565b600360209081526000928352604080842090915290825290205460ff1681565b60405160ff9091168152602001610125565b6101b16101ac36600461290b565b610507565b6040805160ff948516815293909216602084015290820152606001610125565b6101fd6101df36600461294c565b6001600160a01b039081166000908152600260205260409020541690565b6040516001600160a01b039091168152602001610125565b6102286102233660046128d2565b61063d565b005b610228610675565b61024561024036600461294c565b6106ab565b6040516101259190612969565b6101fd61026036600461283c565b610717565b6007546001600160a01b03166101fd565b6102896102843660046129a1565b610774565b604051908152602001610125565b600054610289565b6102896102ad3660046129ee565b61092a565b6102c56102c03660046128d2565b61098b565b6040519015158152602001610125565b6102286102e336600461294c565b6109d2565b6102286102f6366004612a2a565b610a6d565b60005460609060ff831690811061032d5760405163535e975d60e01b8152600481018290526024015b60405180910390fd5b60015467ffffffffffffffff81111561034857610348612a6c565b604051908082528060200260200182016040528015610371578160200160208202803683370190505b5060ff841660009081526006602052604081205491935090815b845181101561040d578082901c60011660010361040557600181815481106103b5576103b5612a82565b600091825260209091200154855160018501946001600160a01b0390921691879181106103e4576103e4612a82565b60200260200101906001600160a01b031690816001600160a01b0316815250505b60010161038b565b5050825250919050565b600080548190839081106104415760405163535e975d60e01b815260048101829052602401610324565b600080858154811061045557610455612a82565b600091825260209091200154600160a01b900460ff1690508015610500576104a96005868154811061048957610489612a82565b90600052602060002001546001836104a19190612aae565b60ff16610af6565b93506001600086815481106104c0576104c0612a82565b6000918252602090912001548154600160a81b90910460ff169081106104e8576104e8612a82565b6000918252602090912001546001600160a01b031692505b5050915091565b6000806000846001600160a01b0316866001600160a01b0316148061052a575083155b1561053d57506000915081905080610634565b6001600160a01b03808716600090815260046020526040808220549288168252812054905b82811015610630576001600160a01b038916600090815260046020526040812080548390811061059457610594612a82565b9060005260206000200154905060005b8381101561061d576001600160a01b038a1660009081526004602052604081208054839081106105d6576105d6612a82565b9060005260206000200154905060006105f284838d6001610b11565b6020015190508781111561060a578097508399508198505b50508061061690612ac7565b90506105a4565b50508061062990612ac7565b9050610562565b5050505b93509350939050565b6007546001600160a01b031633146106675760405162461bcd60e51b815260040161032490612ae0565b6106718282610e0f565b5050565b6007546001600160a01b0316331461069f5760405162461bcd60e51b815260040161032490612ae0565b6106a96000610f80565b565b6001600160a01b03811660009081526004602090815260409182902080548351818402810184019094528084526060939283018282801561070b57602002820191906000526020600020905b8154815260200190600101908083116106f7575b50505050509050919050565b6000805460ff83169081106107425760405163535e975d60e01b815260048101829052602401610324565b60008360ff168154811061075857610758612a82565b6000918252602090912001546001600160a01b03169392505050565b6000805460ff871690811061079f5760405163535e975d60e01b815260048101829052602401610324565b60005460ff87169081106107c95760405163535e975d60e01b815260048101829052602401610324565b834211156107f35760405163fd1c99dd60e01b815242600482015260248101859052604401610324565b8660ff168860ff160361081e57604051631288800960e01b815260ff89166004820152602401610324565b6000808960ff168154811061083557610835612a82565b6000918252602090912001546001600160a01b031690506108588133308a610fd2565b6108698960ff168960ff168961103d565b6020015193508584101561089a5760405163a8345d2b60e01b81526004810185905260248101879052604401610324565b6108d1338560008b60ff16815481106108b5576108b5612a82565b6000918252602090912001546001600160a01b03169190611313565b604080518881526020810186905260ff8b8116828401528a166060820152905133917fc6c1e0630dbe9130cc068028486c0d118ddcea348550819defd5cb8c257f8a38919081900360800190a250505095945050505050565b6000805460ff8516811115806109435750808460ff1610155b15610952576000915050610984565b8360ff168560ff1603610969576000915050610984565b61097c8560ff168560ff16856000610b11565b602001519150505b9392505050565b6001600160a01b038216600090815260046020526040812054158015906109c957506001600160a01b03821660009081526004602052604090205415155b90505b92915050565b6007546001600160a01b031633146109fc5760405162461bcd60e51b815260040161032490612ae0565b6001600160a01b038116610a615760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610324565b610a6a81610f80565b50565b6007546001600160a01b03163314610a975760405162461bcd60e51b815260040161032490612ae0565b60005483908110610abe5760405163535e975d60e01b815260048101829052602401610324565b6001600160a01b038316610ae55760405163b7e5ea2160e01b815260040160405180910390fd5b610af0848484611348565b50505050565b6000610b03826008612b15565b83901c60ff16905092915050565b60408051808201909152600080825260208201526000604051806060016040528060008881548110610b4557610b45612a82565b600091825260208083206040805160608101825293909101546001600160a01b038116845260ff600160a01b8204811685850152600160a81b9091041690830152908352815492019188908110610b9e57610b9e612a82565b600091825260208083206040805160608101825293909101546001600160a01b038116845260ff600160a01b8204811685850152600160a81b909104169083015290835286151592019190915260058054929350909188908110610c0457610c04612a82565b90600052602060002001549050600060058781548110610c2657610c26612a82565b906000526020600020015490506000610c3f8383611612565b905083602001516020015160ff16811115610c8457610c7960008486600001516020015160ff1687602001516020015160ff168b8b611642565b945050505050610e07565b83516020015160ff16811115610cb957610c7960008386600001516020015160ff1687602001516020015160ff168b8b6116d5565b610cda85600001518486600001516020015160ff16848b8960400151611642565b94506000610ce88483610af6565b90506000610cf68484610af6565b90506000808381548110610d0c57610d0c612a82565b60009182526020822001548154600160a81b90910460ff169250819084908110610d3857610d38612a82565b600091825260209091200154600160a81b900460ff169050808203610d7d57610d7189600001518386868d602001518d60400151611735565b60208b01528952610dd7565b6000610d9388610d8e600189612b2c565b610af6565b9050610daf8a600001518487848e602001518e60400151611735565b60208c01819052818c5260408b0151610dce9291859185918991611735565b60208c01528a52505b610dfc896000015187878b602001516020015160ff168d602001518d604001516116d5565b985050505050505050505b949350505050565b6001600160a01b038083166000908152600260205260409020541680610e485760405163b2ab734360e01b815260040160405180910390fd5b6000610e5482856117b6565b90506000610e6284866117b6565b90508051825114610e8657604051635e33813f60e11b815260040160405180910390fd5b60005b8251811015610f0657818181518110610ea457610ea4612a82565b60200260200101516001600160a01b0316838281518110610ec757610ec7612a82565b60200260200101516001600160a01b031614610ef657604051635e33813f60e11b815260040160405180910390fd5b610eff81612ac7565b9050610e89565b506001600160a01b0385811660008181526002602090815260409182902080546001600160a01b0319168986169081179091558251938452938716908301528101919091527f86972319a3e144542b16dfbf38414db2c872ea5bfe13b6e97021cc47db5ceac7906060015b60405180910390a15050505050565b600780546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6040516001600160a01b0380851660248301528316604482015260648101829052610af09085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261199c565b6040805180820190915260008082526020820152600060405180606001604052806000878154811061107157611071612a82565b600091825260208083206040805160608101825293909101546001600160a01b038116845260ff600160a01b8204811685850152600160a81b90910416908301529083528154920191879081106110ca576110ca612a82565b600091825260208083206040805160608101825293909101546001600160a01b038116845260ff600160a01b8204811685850152600160a81b9091041690830152908352919091018190526005805492935090918790811061112e5761112e612a82565b9060005260206000200154905060006005868154811061115057611150612a82565b9060005260206000200154905060006111698383611612565b905083602001516020015160ff168111156111ad576111a260008486600001516020015160ff1687602001516020015160ff168a611a6e565b945050505050610984565b83516020015160ff168111156111e1576111a260008386600001516020015160ff1687602001516020015160ff168a611aff565b6111fa60008486600001516020015160ff16848a611a6e565b945060006112088483610af6565b905060006112168484610af6565b9050600080838154811061122c5761122c612a82565b60009182526020822001548154600160a81b90910460ff16925081908490811061125857611258612a82565b600091825260209091200154600160a81b900460ff1690508082036112985761128c89600001518386868d60200151611b5f565b60208b015289526112e3565b60006112a988610d8e600189612b2c565b90506112c08a600001518487848e60200151611b5f565b60208c01819052818c526112da9190849084908890611b5f565b60208c01528a52505b611303896000015187878b602001516020015160ff168d60200151611aff565b9c9b505050505050505050505050565b6040516001600160a01b03831660248201526044810182905261134390849063a9059cbb60e01b90606401611006565b505050565b600080848154811061135c5761135c612a82565b60009182526020918290206040805160608101825292909101546001600160a01b03808216845260ff600160a01b8304811695850195909552600160a81b90910490931690820152915082166113b0573091505b6001600160a01b038316600090815260026020526040812054600160a01b900460ff168082036114da5760015460ff10156113fe576040516369236b8f60e11b815260040160405180910390fd5b50506001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6810180546001600160a01b038781166001600160a01b0319909216821790925560408051808201825287841680825260ff8087166020808501918252600087815260028252869020945185549251909316600160a01b026001600160a81b031990921692909716919091171790915581518a8152938401929092528201527ff526af69faf759a24b780dee9d979f542b6a5e8bfa39c7b04fcce83cb9982b8a9060600160405180910390a16114ec565b6114ec86846020015160ff1683611bfa565b60008681526006602052604081208054600160ff85161b17905561151085876117b6565b905060008151905060008086602001516001019050600060058b8154811061153a5761153a612a82565b9060005260206000200154905060005b848110156115e657600086828151811061156657611566612a82565b6020026020010151905088156115a9576001600160a01b038c81166000908152600360209081526040808320938516835292905220805460ff191660ff84161790555b89600001516001600160a01b0316816001600160a01b0316036115d05760019450506115de565b6115dc81858a86611cc5565b505b60010161154a565b50505080611607576040516350518a2560e01b815260040160405180910390fd5b505050505050505050565b90811890600082810361162757611627612b3f565b5b8260ff166000036109cc5760089290921c91600101611628565b604080518082019091526000808252602082015260006116628787610af6565b9050855b858111156116c25760001901600061167e8983610af6565b90506116b68a6000858154811061169757611697612a82565b600091825260209091200154600160a81b900460ff1685848a8a611735565b909a5095509150611666565b5050958652506020850152509192915050565b604080518082019091526000808252602082015260006116f58787610af6565b9050855b858110156116c25760010160006117108983610af6565b90506117298a6000838154811061169757611697612a82565b909a50955091506116f9565b6000806001871b8816156117505750600019905060006117ab565b866001901b8817915060006001888154811061176e5761176e612a82565b60009182526020808320909101546001600160a01b0390811680845260029092526040909220549092506117a791168289898989611e92565b9150505b965096945050505050565b6060306001600160a01b0384160361192e5760005b60405162415c3360e91b815260ff821660048201526001600160a01b038416906382b8660090602401602060405180830381865afa92505050801561182d575060408051601f3d908101601f1916820190925261182a91810190612b60565b60015b1561183b57506001016117cb565b8067ffffffffffffffff81111561185457611854612a6c565b60405190808252806020026020018201604052801561187d578160200160208202803683370190505b50915060005b818110156119275760405162415c3360e91b815260ff821660048201526001600160a01b038516906382b8660090602401602060405180830381865afa1580156118d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118f59190612b60565b83828151811061190757611907612a82565b6001600160a01b0390921660209283029190910190910152600101611883565b50506109cc565b60405163ca4f280360e01b81526001600160a01b03838116600483015284169063ca4f280390602401600060405180830381865afa158015611974573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109c99190810190612b7d565b60006119f1826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166121b19092919063ffffffff16565b8051909150156113435780806020019051810190611a0f9190612c42565b6113435760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610324565b60408051808201909152600080825260208201526000611a8e8686610af6565b9050845b84811115611aed57600019016000611aaa8883610af6565b9050611ae18960008581548110611ac357611ac3612a82565b600091825260209091200154600160a81b900460ff16858489611b5f565b90995094509150611a92565b50509485526020850152509192915050565b60408051808201909152600080825260208201526000611b1f8686610af6565b9050845b84811015611aed576001016000611b3a8883610af6565b9050611b538960008381548110611ac357611ac3612a82565b90995094509150611b23565b600080600060018781548110611b7757611b77612a82565b6000918252602090912001546001600160a01b031690506001871b881615611bbd5760405163a87c309760e01b81526001600160a01b0382166004820152602401610324565b6001600160a01b038082166000908152600260205260409020546001891b8a179450611bed9116828888886121c0565b9150509550959350505050565b600083815260066020526040902054600160ff83161b1615611c2f57604051630180f60d60e61b815260040160405180910390fd5b600060058481548110611c4457611c44612a82565b600091825260209091200154905060015b838111611cbe576000611c688383610af6565b90508360ff1660008281548110611c8157611c81612a82565b600091825260209091200154600160a81b900460ff1603611cb55760405163aa53d00f60e01b815260040160405180910390fd5b50600101611c55565b5050505050565b60005460ff811115611cea57604051630be56a6f60e21b815260040160405180910390fd5b60005415801590611d24575060008081548110611d0957611d09612a82565b6000918252602090912001546001600160a01b038681169116145b15611d2f5750610af0565b604080516060810182526001600160a01b0380881680835260ff8089166020808601918252898316868801908152600080546001818101835582805298517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5639091018054955193518716600160a81b0260ff60a81b1994909716600160a01b026001600160a81b03199096169190981617939093171692909217909355908252600481529281208054928301815581529190912001819055600582611df5866008612c64565b8254600181810185556000948552602090942060ff92831686901b9390931792019190915581547f3d015788e7bd0b08b47bbeb8eef47706ac9e0c6e79ed68f54e89df593bee087d9284928992908816908110611e5457611e54612a82565b600091825260209091200154604051610f719392916001600160a01b0316909283526001600160a01b03918216602084015216604082015260600190565b6000306001600160a01b0388160361208a578115611f615760408051600481526024810182526020810180516001600160e01b0316635c975abb60e01b179052905160009182916001600160a01b038a1691611eed91612cab565b600060405180830381855afa9150503d8060008114611f28576040519150601f19603f3d011682016040523d82523d6000602084013e611f2d565b606091505b5091509150818015611f4e575080806020019051810190611f4e9190612c42565b15611f5e576000925050506121a7565b50505b6001600160a01b0386166000818152600360205260408120815463a95b089f929081908a908110611f9457611f94612a82565b60009182526020808320909101546001600160a01b039081168452838201949094526040928301822054938c16825260039052908120815460ff9093169290919081908a908110611fe757611fe7612a82565b6000918252602080832091909101546001600160a01b03168352820192909252604090810190912054905160e084901b6001600160e01b031916815260ff92831660048201529116602482015260448101869052606401602060405180830381865afa925050508015612077575060408051601f3d908101601f1916820190925261207491810190612cc7565b60015b612083575060006121a7565b90506121a7565b600080868154811061209e5761209e612a82565b600091825260208220015481546001600160a01b0390911692508190879081106120ca576120ca612a82565b60009182526020808320909101546040805180820182526001600160a01b038d81168652600385528286208882168088528187528488205460ff90811685528488019190915284518086018652958316808952918752968490205490961684529383018590529051637de4c55160e01b8152939450918c1692637de4c5519261215b928d928b908b90600401612ce0565b602060405180830381865afa925050508015612194575060408051601f3d908101601f1916820190925261219191810190612cc7565b60015b6121a157600092506121a4565b92505b50505b9695505050505050565b6060610e0784846000856123d1565b600080600085815481106121d6576121d6612a82565b600091825260208220015481546001600160a01b03909116925081908690811061220257612202612a82565b6000918252602090912001546001600160a01b03908116915030908916036122ee576122386001600160a01b0383168886612502565b6001600160a01b03878116600081815260036020908152604080832087861684529091528082205493851682528082205490516348b4aac360e11b815260ff94851660048201529316602484015260448301879052606483015260001960848301529063916955869060a4016020604051808303816000875af11580156122c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122e79190612cc7565b92506123c6565b6040805180820182526001600160a01b03808a166000908152600360209081528482208784168084528183528684205460ff9081168752838701919091528651808801885294881680855291835286842054168452908301529251631a0e91a960e31b92612362928c928a90602401612d47565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152905060006123ab6001600160a01b038b16836125e0565b9050808060200190518101906123c19190612cc7565b945050505b505095945050505050565b6060824710156124325760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610324565b6001600160a01b0385163b6124895760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610324565b600080866001600160a01b031685876040516124a59190612cab565b60006040518083038185875af1925050503d80600081146124e2576040519150601f19603f3d011682016040523d82523d6000602084013e6124e7565b606091505b50915091506124f7828286612605565b979650505050505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161252c57505050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa15801561257c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125a09190612cc7565b90508181106125af5750505050565b80156125ca576125ca6001600160a01b03851684600061263e565b610af06001600160a01b0385168460001961263e565b60606109c98383604051806060016040528060278152602001612dd660279139612753565b60608315612614575081610984565b8251156126245782518084602001fd5b8160405162461bcd60e51b81526004016103249190612da2565b8015806126b85750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612692573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b69190612cc7565b155b6127235760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610324565b6040516001600160a01b03831660248201526044810182905261134390849063095ea7b360e01b90606401611006565b60606001600160a01b0384163b6127bb5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610324565b600080856001600160a01b0316856040516127d69190612cab565b600060405180830381855af49150503d8060008114612811576040519150601f19603f3d011682016040523d82523d6000602084013e612816565b606091505b50915091506121a7828286612605565b803560ff8116811461283757600080fd5b919050565b60006020828403121561284e57600080fd5b6109c982612826565b6020808252825182820181905260009190848201906040850190845b818110156128985783516001600160a01b031683529284019291840191600101612873565b50909695505050505050565b6000602082840312156128b657600080fd5b5035919050565b6001600160a01b0381168114610a6a57600080fd5b600080604083850312156128e557600080fd5b82356128f0816128bd565b91506020830135612900816128bd565b809150509250929050565b60008060006060848603121561292057600080fd5b833561292b816128bd565b9250602084013561293b816128bd565b929592945050506040919091013590565b60006020828403121561295e57600080fd5b8135610984816128bd565b6020808252825182820181905260009190848201906040850190845b8181101561289857835183529284019291840191600101612985565b600080600080600060a086880312156129b957600080fd5b6129c286612826565b94506129d060208701612826565b94979496505050506040830135926060810135926080909101359150565b600080600060608486031215612a0357600080fd5b612a0c84612826565b9250612a1a60208501612826565b9150604084013590509250925092565b600080600060608486031215612a3f57600080fd5b833592506020840135612a51816128bd565b91506040840135612a61816128bd565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60ff82811682821603908111156109cc576109cc612a98565b600060018201612ad957612ad9612a98565b5060010190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b80820281158282048414176109cc576109cc612a98565b818103818111156109cc576109cc612a98565b634e487b7160e01b600052600160045260246000fd5b8051612837816128bd565b600060208284031215612b7257600080fd5b8151610984816128bd565b60006020808385031215612b9057600080fd5b825167ffffffffffffffff80821115612ba857600080fd5b818501915085601f830112612bbc57600080fd5b815181811115612bce57612bce612a6c565b8060051b604051601f19603f83011681018181108582111715612bf357612bf3612a6c565b604052918252848201925083810185019188831115612c1157600080fd5b938501935b82851015612c3657612c2785612b55565b84529385019392850192612c16565b98975050505050505050565b600060208284031215612c5457600080fd5b8151801515811461098457600080fd5b60ff8181168382160290811690818114612c8057612c80612a98565b5092915050565b60005b83811015612ca2578181015183820152602001612c8a565b50506000910152565b60008251612cbd818460208701612c87565b9190910192915050565b600060208284031215612cd957600080fd5b5051919050565b6001600160a01b038616815260e08101612d136020830187805160ff1682526020908101516001600160a01b0316910152565b845160ff1660608301526020909401516001600160a01b0316608082015260a0810192909252151560c09091015292915050565b6001600160a01b038516815260c08101612d7a6020830186805160ff1682526020908101516001600160a01b0316910152565b835160ff1660608301526020909301516001600160a01b0316608082015260a0015292915050565b6020815260008251806020840152612dc1816040850160208701612c87565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220a20dd8a538f2931dec0e2eef157073d30e5f0ec670efe8c0a9eae6615811448564736f6c634300081100330000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101005760003560e01c806382b8660011610097578063a95b089f11610066578063a95b089f1461029f578063dd3a1755146102b2578063f2fde38b146102d5578063f624d2c5146102e857600080fd5b806382b86600146102525780638da5cb5b14610265578063916955861461027657806398f80a981461029757600080fd5b80635ff2e6ec116100d35780635ff2e6ec146101d15780636ee4f71b14610215578063715018a61461022a57806380507b571461023257600080fd5b80630f2839ba14610105578063175724f61461012e5780631b6486d61461015e5780633dfea7721461019e575b600080fd5b61011861011336600461283c565b6102fb565b6040516101259190612857565b60405180910390f35b61014161013c3660046128a4565b610417565b604080519283526001600160a01b03909116602083015201610125565b61018c61016c3660046128d2565b600360209081526000928352604080842090915290825290205460ff1681565b60405160ff9091168152602001610125565b6101b16101ac36600461290b565b610507565b6040805160ff948516815293909216602084015290820152606001610125565b6101fd6101df36600461294c565b6001600160a01b039081166000908152600260205260409020541690565b6040516001600160a01b039091168152602001610125565b6102286102233660046128d2565b61063d565b005b610228610675565b61024561024036600461294c565b6106ab565b6040516101259190612969565b6101fd61026036600461283c565b610717565b6007546001600160a01b03166101fd565b6102896102843660046129a1565b610774565b604051908152602001610125565b600054610289565b6102896102ad3660046129ee565b61092a565b6102c56102c03660046128d2565b61098b565b6040519015158152602001610125565b6102286102e336600461294c565b6109d2565b6102286102f6366004612a2a565b610a6d565b60005460609060ff831690811061032d5760405163535e975d60e01b8152600481018290526024015b60405180910390fd5b60015467ffffffffffffffff81111561034857610348612a6c565b604051908082528060200260200182016040528015610371578160200160208202803683370190505b5060ff841660009081526006602052604081205491935090815b845181101561040d578082901c60011660010361040557600181815481106103b5576103b5612a82565b600091825260209091200154855160018501946001600160a01b0390921691879181106103e4576103e4612a82565b60200260200101906001600160a01b031690816001600160a01b0316815250505b60010161038b565b5050825250919050565b600080548190839081106104415760405163535e975d60e01b815260048101829052602401610324565b600080858154811061045557610455612a82565b600091825260209091200154600160a01b900460ff1690508015610500576104a96005868154811061048957610489612a82565b90600052602060002001546001836104a19190612aae565b60ff16610af6565b93506001600086815481106104c0576104c0612a82565b6000918252602090912001548154600160a81b90910460ff169081106104e8576104e8612a82565b6000918252602090912001546001600160a01b031692505b5050915091565b6000806000846001600160a01b0316866001600160a01b0316148061052a575083155b1561053d57506000915081905080610634565b6001600160a01b03808716600090815260046020526040808220549288168252812054905b82811015610630576001600160a01b038916600090815260046020526040812080548390811061059457610594612a82565b9060005260206000200154905060005b8381101561061d576001600160a01b038a1660009081526004602052604081208054839081106105d6576105d6612a82565b9060005260206000200154905060006105f284838d6001610b11565b6020015190508781111561060a578097508399508198505b50508061061690612ac7565b90506105a4565b50508061062990612ac7565b9050610562565b5050505b93509350939050565b6007546001600160a01b031633146106675760405162461bcd60e51b815260040161032490612ae0565b6106718282610e0f565b5050565b6007546001600160a01b0316331461069f5760405162461bcd60e51b815260040161032490612ae0565b6106a96000610f80565b565b6001600160a01b03811660009081526004602090815260409182902080548351818402810184019094528084526060939283018282801561070b57602002820191906000526020600020905b8154815260200190600101908083116106f7575b50505050509050919050565b6000805460ff83169081106107425760405163535e975d60e01b815260048101829052602401610324565b60008360ff168154811061075857610758612a82565b6000918252602090912001546001600160a01b03169392505050565b6000805460ff871690811061079f5760405163535e975d60e01b815260048101829052602401610324565b60005460ff87169081106107c95760405163535e975d60e01b815260048101829052602401610324565b834211156107f35760405163fd1c99dd60e01b815242600482015260248101859052604401610324565b8660ff168860ff160361081e57604051631288800960e01b815260ff89166004820152602401610324565b6000808960ff168154811061083557610835612a82565b6000918252602090912001546001600160a01b031690506108588133308a610fd2565b6108698960ff168960ff168961103d565b6020015193508584101561089a5760405163a8345d2b60e01b81526004810185905260248101879052604401610324565b6108d1338560008b60ff16815481106108b5576108b5612a82565b6000918252602090912001546001600160a01b03169190611313565b604080518881526020810186905260ff8b8116828401528a166060820152905133917fc6c1e0630dbe9130cc068028486c0d118ddcea348550819defd5cb8c257f8a38919081900360800190a250505095945050505050565b6000805460ff8516811115806109435750808460ff1610155b15610952576000915050610984565b8360ff168560ff1603610969576000915050610984565b61097c8560ff168560ff16856000610b11565b602001519150505b9392505050565b6001600160a01b038216600090815260046020526040812054158015906109c957506001600160a01b03821660009081526004602052604090205415155b90505b92915050565b6007546001600160a01b031633146109fc5760405162461bcd60e51b815260040161032490612ae0565b6001600160a01b038116610a615760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610324565b610a6a81610f80565b50565b6007546001600160a01b03163314610a975760405162461bcd60e51b815260040161032490612ae0565b60005483908110610abe5760405163535e975d60e01b815260048101829052602401610324565b6001600160a01b038316610ae55760405163b7e5ea2160e01b815260040160405180910390fd5b610af0848484611348565b50505050565b6000610b03826008612b15565b83901c60ff16905092915050565b60408051808201909152600080825260208201526000604051806060016040528060008881548110610b4557610b45612a82565b600091825260208083206040805160608101825293909101546001600160a01b038116845260ff600160a01b8204811685850152600160a81b9091041690830152908352815492019188908110610b9e57610b9e612a82565b600091825260208083206040805160608101825293909101546001600160a01b038116845260ff600160a01b8204811685850152600160a81b909104169083015290835286151592019190915260058054929350909188908110610c0457610c04612a82565b90600052602060002001549050600060058781548110610c2657610c26612a82565b906000526020600020015490506000610c3f8383611612565b905083602001516020015160ff16811115610c8457610c7960008486600001516020015160ff1687602001516020015160ff168b8b611642565b945050505050610e07565b83516020015160ff16811115610cb957610c7960008386600001516020015160ff1687602001516020015160ff168b8b6116d5565b610cda85600001518486600001516020015160ff16848b8960400151611642565b94506000610ce88483610af6565b90506000610cf68484610af6565b90506000808381548110610d0c57610d0c612a82565b60009182526020822001548154600160a81b90910460ff169250819084908110610d3857610d38612a82565b600091825260209091200154600160a81b900460ff169050808203610d7d57610d7189600001518386868d602001518d60400151611735565b60208b01528952610dd7565b6000610d9388610d8e600189612b2c565b610af6565b9050610daf8a600001518487848e602001518e60400151611735565b60208c01819052818c5260408b0151610dce9291859185918991611735565b60208c01528a52505b610dfc896000015187878b602001516020015160ff168d602001518d604001516116d5565b985050505050505050505b949350505050565b6001600160a01b038083166000908152600260205260409020541680610e485760405163b2ab734360e01b815260040160405180910390fd5b6000610e5482856117b6565b90506000610e6284866117b6565b90508051825114610e8657604051635e33813f60e11b815260040160405180910390fd5b60005b8251811015610f0657818181518110610ea457610ea4612a82565b60200260200101516001600160a01b0316838281518110610ec757610ec7612a82565b60200260200101516001600160a01b031614610ef657604051635e33813f60e11b815260040160405180910390fd5b610eff81612ac7565b9050610e89565b506001600160a01b0385811660008181526002602090815260409182902080546001600160a01b0319168986169081179091558251938452938716908301528101919091527f86972319a3e144542b16dfbf38414db2c872ea5bfe13b6e97021cc47db5ceac7906060015b60405180910390a15050505050565b600780546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6040516001600160a01b0380851660248301528316604482015260648101829052610af09085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261199c565b6040805180820190915260008082526020820152600060405180606001604052806000878154811061107157611071612a82565b600091825260208083206040805160608101825293909101546001600160a01b038116845260ff600160a01b8204811685850152600160a81b90910416908301529083528154920191879081106110ca576110ca612a82565b600091825260208083206040805160608101825293909101546001600160a01b038116845260ff600160a01b8204811685850152600160a81b9091041690830152908352919091018190526005805492935090918790811061112e5761112e612a82565b9060005260206000200154905060006005868154811061115057611150612a82565b9060005260206000200154905060006111698383611612565b905083602001516020015160ff168111156111ad576111a260008486600001516020015160ff1687602001516020015160ff168a611a6e565b945050505050610984565b83516020015160ff168111156111e1576111a260008386600001516020015160ff1687602001516020015160ff168a611aff565b6111fa60008486600001516020015160ff16848a611a6e565b945060006112088483610af6565b905060006112168484610af6565b9050600080838154811061122c5761122c612a82565b60009182526020822001548154600160a81b90910460ff16925081908490811061125857611258612a82565b600091825260209091200154600160a81b900460ff1690508082036112985761128c89600001518386868d60200151611b5f565b60208b015289526112e3565b60006112a988610d8e600189612b2c565b90506112c08a600001518487848e60200151611b5f565b60208c01819052818c526112da9190849084908890611b5f565b60208c01528a52505b611303896000015187878b602001516020015160ff168d60200151611aff565b9c9b505050505050505050505050565b6040516001600160a01b03831660248201526044810182905261134390849063a9059cbb60e01b90606401611006565b505050565b600080848154811061135c5761135c612a82565b60009182526020918290206040805160608101825292909101546001600160a01b03808216845260ff600160a01b8304811695850195909552600160a81b90910490931690820152915082166113b0573091505b6001600160a01b038316600090815260026020526040812054600160a01b900460ff168082036114da5760015460ff10156113fe576040516369236b8f60e11b815260040160405180910390fd5b50506001805480820182557fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6810180546001600160a01b038781166001600160a01b0319909216821790925560408051808201825287841680825260ff8087166020808501918252600087815260028252869020945185549251909316600160a01b026001600160a81b031990921692909716919091171790915581518a8152938401929092528201527ff526af69faf759a24b780dee9d979f542b6a5e8bfa39c7b04fcce83cb9982b8a9060600160405180910390a16114ec565b6114ec86846020015160ff1683611bfa565b60008681526006602052604081208054600160ff85161b17905561151085876117b6565b905060008151905060008086602001516001019050600060058b8154811061153a5761153a612a82565b9060005260206000200154905060005b848110156115e657600086828151811061156657611566612a82565b6020026020010151905088156115a9576001600160a01b038c81166000908152600360209081526040808320938516835292905220805460ff191660ff84161790555b89600001516001600160a01b0316816001600160a01b0316036115d05760019450506115de565b6115dc81858a86611cc5565b505b60010161154a565b50505080611607576040516350518a2560e01b815260040160405180910390fd5b505050505050505050565b90811890600082810361162757611627612b3f565b5b8260ff166000036109cc5760089290921c91600101611628565b604080518082019091526000808252602082015260006116628787610af6565b9050855b858111156116c25760001901600061167e8983610af6565b90506116b68a6000858154811061169757611697612a82565b600091825260209091200154600160a81b900460ff1685848a8a611735565b909a5095509150611666565b5050958652506020850152509192915050565b604080518082019091526000808252602082015260006116f58787610af6565b9050855b858110156116c25760010160006117108983610af6565b90506117298a6000838154811061169757611697612a82565b909a50955091506116f9565b6000806001871b8816156117505750600019905060006117ab565b866001901b8817915060006001888154811061176e5761176e612a82565b60009182526020808320909101546001600160a01b0390811680845260029092526040909220549092506117a791168289898989611e92565b9150505b965096945050505050565b6060306001600160a01b0384160361192e5760005b60405162415c3360e91b815260ff821660048201526001600160a01b038416906382b8660090602401602060405180830381865afa92505050801561182d575060408051601f3d908101601f1916820190925261182a91810190612b60565b60015b1561183b57506001016117cb565b8067ffffffffffffffff81111561185457611854612a6c565b60405190808252806020026020018201604052801561187d578160200160208202803683370190505b50915060005b818110156119275760405162415c3360e91b815260ff821660048201526001600160a01b038516906382b8660090602401602060405180830381865afa1580156118d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118f59190612b60565b83828151811061190757611907612a82565b6001600160a01b0390921660209283029190910190910152600101611883565b50506109cc565b60405163ca4f280360e01b81526001600160a01b03838116600483015284169063ca4f280390602401600060405180830381865afa158015611974573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109c99190810190612b7d565b60006119f1826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166121b19092919063ffffffff16565b8051909150156113435780806020019051810190611a0f9190612c42565b6113435760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610324565b60408051808201909152600080825260208201526000611a8e8686610af6565b9050845b84811115611aed57600019016000611aaa8883610af6565b9050611ae18960008581548110611ac357611ac3612a82565b600091825260209091200154600160a81b900460ff16858489611b5f565b90995094509150611a92565b50509485526020850152509192915050565b60408051808201909152600080825260208201526000611b1f8686610af6565b9050845b84811015611aed576001016000611b3a8883610af6565b9050611b538960008381548110611ac357611ac3612a82565b90995094509150611b23565b600080600060018781548110611b7757611b77612a82565b6000918252602090912001546001600160a01b031690506001871b881615611bbd5760405163a87c309760e01b81526001600160a01b0382166004820152602401610324565b6001600160a01b038082166000908152600260205260409020546001891b8a179450611bed9116828888886121c0565b9150509550959350505050565b600083815260066020526040902054600160ff83161b1615611c2f57604051630180f60d60e61b815260040160405180910390fd5b600060058481548110611c4457611c44612a82565b600091825260209091200154905060015b838111611cbe576000611c688383610af6565b90508360ff1660008281548110611c8157611c81612a82565b600091825260209091200154600160a81b900460ff1603611cb55760405163aa53d00f60e01b815260040160405180910390fd5b50600101611c55565b5050505050565b60005460ff811115611cea57604051630be56a6f60e21b815260040160405180910390fd5b60005415801590611d24575060008081548110611d0957611d09612a82565b6000918252602090912001546001600160a01b038681169116145b15611d2f5750610af0565b604080516060810182526001600160a01b0380881680835260ff8089166020808601918252898316868801908152600080546001818101835582805298517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5639091018054955193518716600160a81b0260ff60a81b1994909716600160a01b026001600160a81b03199096169190981617939093171692909217909355908252600481529281208054928301815581529190912001819055600582611df5866008612c64565b8254600181810185556000948552602090942060ff92831686901b9390931792019190915581547f3d015788e7bd0b08b47bbeb8eef47706ac9e0c6e79ed68f54e89df593bee087d9284928992908816908110611e5457611e54612a82565b600091825260209091200154604051610f719392916001600160a01b0316909283526001600160a01b03918216602084015216604082015260600190565b6000306001600160a01b0388160361208a578115611f615760408051600481526024810182526020810180516001600160e01b0316635c975abb60e01b179052905160009182916001600160a01b038a1691611eed91612cab565b600060405180830381855afa9150503d8060008114611f28576040519150601f19603f3d011682016040523d82523d6000602084013e611f2d565b606091505b5091509150818015611f4e575080806020019051810190611f4e9190612c42565b15611f5e576000925050506121a7565b50505b6001600160a01b0386166000818152600360205260408120815463a95b089f929081908a908110611f9457611f94612a82565b60009182526020808320909101546001600160a01b039081168452838201949094526040928301822054938c16825260039052908120815460ff9093169290919081908a908110611fe757611fe7612a82565b6000918252602080832091909101546001600160a01b03168352820192909252604090810190912054905160e084901b6001600160e01b031916815260ff92831660048201529116602482015260448101869052606401602060405180830381865afa925050508015612077575060408051601f3d908101601f1916820190925261207491810190612cc7565b60015b612083575060006121a7565b90506121a7565b600080868154811061209e5761209e612a82565b600091825260208220015481546001600160a01b0390911692508190879081106120ca576120ca612a82565b60009182526020808320909101546040805180820182526001600160a01b038d81168652600385528286208882168088528187528488205460ff90811685528488019190915284518086018652958316808952918752968490205490961684529383018590529051637de4c55160e01b8152939450918c1692637de4c5519261215b928d928b908b90600401612ce0565b602060405180830381865afa925050508015612194575060408051601f3d908101601f1916820190925261219191810190612cc7565b60015b6121a157600092506121a4565b92505b50505b9695505050505050565b6060610e0784846000856123d1565b600080600085815481106121d6576121d6612a82565b600091825260208220015481546001600160a01b03909116925081908690811061220257612202612a82565b6000918252602090912001546001600160a01b03908116915030908916036122ee576122386001600160a01b0383168886612502565b6001600160a01b03878116600081815260036020908152604080832087861684529091528082205493851682528082205490516348b4aac360e11b815260ff94851660048201529316602484015260448301879052606483015260001960848301529063916955869060a4016020604051808303816000875af11580156122c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122e79190612cc7565b92506123c6565b6040805180820182526001600160a01b03808a166000908152600360209081528482208784168084528183528684205460ff9081168752838701919091528651808801885294881680855291835286842054168452908301529251631a0e91a960e31b92612362928c928a90602401612d47565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152905060006123ab6001600160a01b038b16836125e0565b9050808060200190518101906123c19190612cc7565b945050505b505095945050505050565b6060824710156124325760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610324565b6001600160a01b0385163b6124895760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610324565b600080866001600160a01b031685876040516124a59190612cab565b60006040518083038185875af1925050503d80600081146124e2576040519150601f19603f3d011682016040523d82523d6000602084013e6124e7565b606091505b50915091506124f7828286612605565b979650505050505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161252c57505050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa15801561257c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125a09190612cc7565b90508181106125af5750505050565b80156125ca576125ca6001600160a01b03851684600061263e565b610af06001600160a01b0385168460001961263e565b60606109c98383604051806060016040528060278152602001612dd660279139612753565b60608315612614575081610984565b8251156126245782518084602001fd5b8160405162461bcd60e51b81526004016103249190612da2565b8015806126b85750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612692573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b69190612cc7565b155b6127235760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610324565b6040516001600160a01b03831660248201526044810182905261134390849063095ea7b360e01b90606401611006565b60606001600160a01b0384163b6127bb5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610324565b600080856001600160a01b0316856040516127d69190612cab565b600060405180830381855af49150503d8060008114612811576040519150601f19603f3d011682016040523d82523d6000602084013e612816565b606091505b50915091506121a7828286612605565b803560ff8116811461283757600080fd5b919050565b60006020828403121561284e57600080fd5b6109c982612826565b6020808252825182820181905260009190848201906040850190845b818110156128985783516001600160a01b031683529284019291840191600101612873565b50909695505050505050565b6000602082840312156128b657600080fd5b5035919050565b6001600160a01b0381168114610a6a57600080fd5b600080604083850312156128e557600080fd5b82356128f0816128bd565b91506020830135612900816128bd565b809150509250929050565b60008060006060848603121561292057600080fd5b833561292b816128bd565b9250602084013561293b816128bd565b929592945050506040919091013590565b60006020828403121561295e57600080fd5b8135610984816128bd565b6020808252825182820181905260009190848201906040850190845b8181101561289857835183529284019291840191600101612985565b600080600080600060a086880312156129b957600080fd5b6129c286612826565b94506129d060208701612826565b94979496505050506040830135926060810135926080909101359150565b600080600060608486031215612a0357600080fd5b612a0c84612826565b9250612a1a60208501612826565b9150604084013590509250925092565b600080600060608486031215612a3f57600080fd5b833592506020840135612a51816128bd565b91506040840135612a61816128bd565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60ff82811682821603908111156109cc576109cc612a98565b600060018201612ad957612ad9612a98565b5060010190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b80820281158282048414176109cc576109cc612a98565b818103818111156109cc576109cc612a98565b634e487b7160e01b600052600160045260246000fd5b8051612837816128bd565b600060208284031215612b7257600080fd5b8151610984816128bd565b60006020808385031215612b9057600080fd5b825167ffffffffffffffff80821115612ba857600080fd5b818501915085601f830112612bbc57600080fd5b815181811115612bce57612bce612a6c565b8060051b604051601f19603f83011681018181108582111715612bf357612bf3612a6c565b604052918252848201925083810185019188831115612c1157600080fd5b938501935b82851015612c3657612c2785612b55565b84529385019392850192612c16565b98975050505050505050565b600060208284031215612c5457600080fd5b8151801515811461098457600080fd5b60ff8181168382160290811690818114612c8057612c80612a98565b5092915050565b60005b83811015612ca2578181015183820152602001612c8a565b50506000910152565b60008251612cbd818460208701612c87565b9190910192915050565b600060208284031215612cd957600080fd5b5051919050565b6001600160a01b038616815260e08101612d136020830187805160ff1682526020908101516001600160a01b0316910152565b845160ff1660608301526020909401516001600160a01b0316608082015260a0810192909252151560c09091015292915050565b6001600160a01b038516815260c08101612d7a6020830186805160ff1682526020908101516001600160a01b0316910152565b835160ff1660608301526020909301516001600160a01b0316608082015260a0015292915050565b6020815260008251806020840152612dc1816040850160208701612c87565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220a20dd8a538f2931dec0e2eef157073d30e5f0ec670efe8c0a9eae6615811448564736f6c63430008110033

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

0000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff850000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3

-----Decoded View---------------
Arg [0] : bridgeToken (address): 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85
Arg [1] : owner_ (address): 0x0fea3e5840334Fc758A3DECf14546bFdfBef5cd3

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85
Arg [1] : 0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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