Overview
ETH Balance
0 ETH
ETH Value
$0.00Token Holdings
More Info
Private Name Tags
ContractCreator
Latest 18 from a total of 18 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Add Pool | 112204990 | 385 days ago | IN | 0 ETH | 0.000075278872 | ||||
Add Pool | 112204990 | 385 days ago | IN | 0 ETH | 0.000070224432 | ||||
Add Pool | 112204990 | 385 days ago | IN | 0 ETH | 0.000070861032 | ||||
Add Pool | 112204990 | 385 days ago | IN | 0 ETH | 0.000071098424 | ||||
Add Pool | 112204990 | 385 days ago | IN | 0 ETH | 0.000070224432 | ||||
Add Pool | 112204990 | 385 days ago | IN | 0 ETH | 0.000078989408 | ||||
Add Pool | 112204990 | 385 days ago | IN | 0 ETH | 0.000070861032 | ||||
Add Pool | 112204990 | 385 days ago | IN | 0 ETH | 0.000071698239 | ||||
Add Pool | 112204990 | 385 days ago | IN | 0 ETH | 0.000076434547 | ||||
Add Pool | 112204990 | 385 days ago | IN | 0 ETH | 0.000076433347 | ||||
Add Pool | 112204990 | 385 days ago | IN | 0 ETH | 0.000077108132 | ||||
Add Pool | 109240771 | 454 days ago | IN | 0 ETH | 0.000029187713 | ||||
Add Pool | 109240771 | 454 days ago | IN | 0 ETH | 0.000029187712 | ||||
Add Pool | 109240771 | 454 days ago | IN | 0 ETH | 0.000029187711 | ||||
Add Pool | 109240771 | 454 days ago | IN | 0 ETH | 0.00002918772 | ||||
Add Pool | 109240771 | 454 days ago | IN | 0 ETH | 0.000029187712 | ||||
Add Pool | 109240771 | 454 days ago | IN | 0 ETH | 0.000029187713 | ||||
Add Pool | 109240771 | 454 days ago | IN | 0 ETH | 0.000029060994 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
LinkedPool
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 200 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface IPausable { function paused() external view returns (bool); }
// 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); }
// 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); }
// 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); }
// 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); } }
// 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(); } }
// 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; } }
// 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); } } } }
// 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); } }
// 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"); } } }
// 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();
// 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; } }
// 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); }
{ "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
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
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
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
OP | 100.00% | $0.181755 | 1 | $0.1817 |
[ 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.