More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 2,568 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Bridge | 18777719 | 332 days ago | IN | 0 ETH | 0.01687115 | ||||
Bridge | 18777708 | 332 days ago | IN | 0 ETH | 0.00751248 | ||||
Bridge | 18777699 | 332 days ago | IN | 0 ETH | 0.00716675 | ||||
Bridge | 18777602 | 332 days ago | IN | 0 ETH | 0.01492305 | ||||
Bridge | 18777347 | 332 days ago | IN | 0 ETH | 0.00577899 | ||||
Bridge | 18776863 | 332 days ago | IN | 0 ETH | 0.00722774 | ||||
Bridge | 18776153 | 332 days ago | IN | 0 ETH | 0.01426183 | ||||
Bridge | 18775903 | 332 days ago | IN | 0 ETH | 0.01295902 | ||||
Bridge | 18775101 | 332 days ago | IN | 0 ETH | 0.00666946 | ||||
Bridge | 18774665 | 332 days ago | IN | 0 ETH | 0.00761975 | ||||
Bridge | 18773901 | 332 days ago | IN | 0 ETH | 0.00874805 | ||||
Bridge | 18773657 | 332 days ago | IN | 0 ETH | 0.00813289 | ||||
Bridge | 18773332 | 332 days ago | IN | 0 ETH | 0.0086611 | ||||
Bridge | 18773087 | 332 days ago | IN | 0 ETH | 0.00836011 | ||||
Bridge | 18772960 | 333 days ago | IN | 0 ETH | 0.0027298 | ||||
Bridge | 18772944 | 333 days ago | IN | 0 ETH | 0.01032923 | ||||
Bridge | 18772651 | 333 days ago | IN | 0 ETH | 0.00988513 | ||||
Bridge | 18772569 | 333 days ago | IN | 0 ETH | 0.00850235 | ||||
Bridge | 18772159 | 333 days ago | IN | 0 ETH | 0.01620123 | ||||
Bridge | 18772019 | 333 days ago | IN | 0 ETH | 0.01469633 | ||||
Bridge | 18772005 | 333 days ago | IN | 0 ETH | 0.00747021 | ||||
Bridge | 18771551 | 333 days ago | IN | 0 ETH | 0.01011826 | ||||
Bridge | 18771543 | 333 days ago | IN | 0 ETH | 0.02165356 | ||||
Bridge | 18771536 | 333 days ago | IN | 0 ETH | 0.01050344 | ||||
Bridge | 18771478 | 333 days ago | IN | 0 ETH | 0.01791553 |
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers. Name tag integration is not available in advanced view.
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | ||||
---|---|---|---|---|---|---|---|
18777719 | 332 days ago | 0 ETH | |||||
18777719 | 332 days ago | 0 ETH | |||||
18777719 | 332 days ago | 0 ETH | |||||
18777719 | 332 days ago | 0 ETH | |||||
18777719 | 332 days ago | 0 ETH | |||||
18777719 | 332 days ago | 0 ETH | |||||
18777719 | 332 days ago | 0 ETH | |||||
18777719 | 332 days ago | 0 ETH | |||||
18777719 | 332 days ago | 0 ETH | |||||
18777719 | 332 days ago | 0 ETH | |||||
18777719 | 332 days ago | 0 ETH | |||||
18777708 | 332 days ago | 0 ETH | |||||
18777708 | 332 days ago | 0 ETH | |||||
18777708 | 332 days ago | 0 ETH | |||||
18777708 | 332 days ago | 0 ETH | |||||
18777708 | 332 days ago | 0 ETH | |||||
18777699 | 332 days ago | 0 ETH | |||||
18777699 | 332 days ago | 0 ETH | |||||
18777699 | 332 days ago | 0 ETH | |||||
18777699 | 332 days ago | 0 ETH | |||||
18777699 | 332 days ago | 0 ETH | |||||
18777602 | 332 days ago | 0 ETH | |||||
18777602 | 332 days ago | 0 ETH | |||||
18777602 | 332 days ago | 0 ETH | |||||
18777602 | 332 days ago | 0 ETH |
Loading...
Loading
Contract Name:
SynapseCCTPRouter
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {ISynapseCCTP} from "./interfaces/ISynapseCCTP.sol"; import {ISynapseCCTPFees} from "./interfaces/ISynapseCCTPFees.sol"; import {BridgeToken, DestRequest, SwapQuery, ISynapseCCTPRouter} from "./interfaces/ISynapseCCTPRouter.sol"; import {ITokenMinter} from "./interfaces/ITokenMinter.sol"; import {UnknownRequestAction} from "./libs/RouterErrors.sol"; import {RequestLib} from "./libs/Request.sol"; import {MsgValueIncorrect, DefaultRouter} from "../router/DefaultRouter.sol"; import {IDefaultPool} from "../router/interfaces/IDefaultPool.sol"; import {Action, DefaultParams} from "../router/libs/Structs.sol"; import {SafeERC20, IERC20} from "@openzeppelin/contracts-4.5.0/token/ERC20/utils/SafeERC20.sol"; import {Pausable} from "@openzeppelin/contracts-4.5.0/security/Pausable.sol"; contract SynapseCCTPRouter is DefaultRouter, ISynapseCCTPRouter { using SafeERC20 for IERC20; address public immutable synapseCCTP; constructor(address _synapseCCTP) { synapseCCTP = _synapseCCTP; } // ════════════════════════════════════════════ BRIDGE INTERACTIONS ════════════════════════════════════════════════ /// @inheritdoc ISynapseCCTPRouter function bridge( address recipient, uint256 chainId, address token, uint256 amount, SwapQuery memory originQuery, SwapQuery memory destQuery ) external payable { if (originQuery.hasAdapter()) { // Perform a swap using the swap adapter, set this contract as recipient (token, amount) = _doSwap(address(this), token, amount, originQuery); } else { // If no swap is required, msg.value must be left as zero if (msg.value != 0) revert MsgValueIncorrect(); // Pull the token from the user to this contract amount = _pullToken(address(this), token, amount); } // Either way, this contract has `amount` worth of `token` (uint32 requestVersion, bytes memory swapParams) = _deriveCCTPSwapParams(destQuery); // Approve SynapseCCTP to spend the token _approveToken(token, synapseCCTP, amount); ISynapseCCTP(synapseCCTP).sendCircleToken({ recipient: recipient, chainId: chainId, burnToken: token, amount: amount, requestVersion: requestVersion, swapParams: swapParams }); } // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════ /// @inheritdoc ISynapseCCTPRouter function calculateFeeAmount( address token, uint256 amount, bool isSwap ) external view returns (uint256 fee) { return ISynapseCCTPFees(synapseCCTP).calculateFeeAmount(token, amount, isSwap); } /// @inheritdoc ISynapseCCTPRouter function feeStructures(address token) external view returns ( uint40 relayerFee, uint72 minBaseFee, uint72 minSwapFee, uint72 maxFee ) { return ISynapseCCTPFees(synapseCCTP).feeStructures(token); } /// @inheritdoc ISynapseCCTPRouter function getConnectedBridgeTokens(address tokenOut) external view returns (BridgeToken[] memory tokens) { BridgeToken[] memory cctpTokens = ISynapseCCTPFees(synapseCCTP).getBridgeTokens(); uint256 length = cctpTokens.length; bool[] memory isConnected = new bool[](length); uint256 count = 0; for (uint256 i = 0; i < length; ++i) { address circleToken = cctpTokens[i].token; if (circleToken == tokenOut || _isConnected(circleToken, tokenOut)) { isConnected[i] = true; ++count; } } // Populate the returned array with connected tokens tokens = new BridgeToken[](count); // This will track the index of the next element to be inserted in the returned array count = 0; for (uint256 i = 0; i < length; ++i) { if (isConnected[i]) { tokens[count++] = cctpTokens[i]; } } } /// @inheritdoc ISynapseCCTPRouter function getOriginAmountOut( address tokenIn, string[] memory tokenSymbols, uint256 amountIn ) external view returns (SwapQuery[] memory originQueries) { uint256 length = tokenSymbols.length; originQueries = new SwapQuery[](length); address tokenMinter = ISynapseCCTP(synapseCCTP).tokenMessenger().localMinter(); // Check if it is possible to send Circle tokens (it is always possible to receive them though). bool isPaused = Pausable(synapseCCTP).paused(); for (uint256 i = 0; i < length; ++i) { address circleToken = ISynapseCCTPFees(synapseCCTP).symbolToToken(tokenSymbols[i]); address pool = ISynapseCCTP(synapseCCTP).circleTokenPool(circleToken); // Get the quote for tokenIn -> circleToken swap // Note: this only populates `tokenOut`, `minAmountOut` and `rawParams` fields. originQueries[i] = _getAmountOut(pool, tokenIn, circleToken, amountIn); // Check if the amount out is higher than the burn limit uint256 burnLimit = ITokenMinter(tokenMinter).burnLimitsPerMessage(circleToken); if (originQueries[i].minAmountOut > burnLimit || isPaused) { // Nullify the query, leaving tokenOut intact (this allows SDK to get the bridge token address) originQueries[i].minAmountOut = 0; originQueries[i].rawParams = ""; } else { // Fill the remaining fields, use this contract as "Router Adapter" originQueries[i].fillAdapterAndDeadline({routerAdapter: address(this)}); } } } /// @inheritdoc ISynapseCCTPRouter function getDestinationAmountOut(DestRequest[] memory requests, address tokenOut) external view returns (SwapQuery[] memory destQueries) { uint256 length = requests.length; destQueries = new SwapQuery[](length); for (uint256 i = 0; i < length; ++i) { address circleToken = ISynapseCCTPFees(synapseCCTP).symbolToToken(requests[i].symbol); address pool = ISynapseCCTP(synapseCCTP).circleTokenPool(circleToken); // Calculate the relayer fee amount uint256 amountIn = requests[i].amountIn; uint256 feeAmount = ISynapseCCTPFees(synapseCCTP).calculateFeeAmount({ token: circleToken, amount: amountIn, isSwap: circleToken != tokenOut }); // Only populate the query if the amountIn is higher than the feeAmount if (amountIn > feeAmount) { // Get the quote for circleToken -> tokenOut swap after the fee is applied // Note: this only populates `tokenOut`, `minAmountOut` and `rawParams` fields. destQueries[i] = _getAmountOut(pool, circleToken, tokenOut, amountIn - feeAmount); // Fill the remaining fields, use this contract as "Router Adapter" destQueries[i].fillAdapterAndDeadline({routerAdapter: address(this)}); } else { // Fill only tokenOut otherwise destQueries[i].tokenOut = tokenOut; } } } // ══════════════════════════════════════════════ INTERNAL LOGIC ═══════════════════════════════════════════════════ /// @dev Approves the token to be spent by the given spender indefinitely by giving infinite allowance. /// Doesn't modify the allowance if it's already enough for the given amount. function _approveToken( address token, address spender, uint256 amount ) internal { uint256 allowance = IERC20(token).allowance(address(this), spender); if (allowance < amount) { // Reset allowance to 0 before setting it to the new value. if (allowance != 0) IERC20(token).safeApprove(spender, 0); IERC20(token).safeApprove(spender, type(uint256).max); } } // ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════ /// @dev Finds the quote for tokenIn -> tokenOut swap using a given pool. /// Note: only populates `tokenOut`, `minAmountOut` and `rawParams` fields. function _getAmountOut( address pool, address tokenIn, address tokenOut, uint256 amountIn ) internal view returns (SwapQuery memory query) { query.tokenOut = tokenOut; if (tokenIn == tokenOut) { query.minAmountOut = amountIn; // query.rawParams is "", indicating that no further action is required return query; } if (pool == address(0)) { // query.minAmountOut is 0, indicating that no quote was found // query.rawParams is "", indicating that no further action is required return query; } address[] memory poolTokens = _getPoolTokens(pool); uint256 numTokens = poolTokens.length; // Iterate over all valid (tokenIndexFrom, tokenIndexTo) combinations for tokenIn -> tokenOut swap for (uint8 tokenIndexFrom = 0; tokenIndexFrom < numTokens; ++tokenIndexFrom) { // We are only interested in the tokenFrom == tokenIn case if (poolTokens[tokenIndexFrom] != tokenIn) continue; for (uint8 tokenIndexTo = 0; tokenIndexTo < numTokens; ++tokenIndexTo) { // We are only interested in the tokenTo == tokenOut case if (poolTokens[tokenIndexTo] != tokenOut) continue; uint256 amountOut = _getPoolSwapQuote(pool, tokenIndexFrom, tokenIndexTo, amountIn); // Update the query if the new quote is better than the previous one if (amountOut > query.minAmountOut) { query.minAmountOut = amountOut; query.rawParams = abi.encode(DefaultParams(Action.Swap, pool, tokenIndexFrom, tokenIndexTo)); } } } } /// @dev Checks if a token is connected to a Circle token: whether the token is in the whitelisted liquidity pool /// for the Circle token. function _isConnected(address circleToken, address token) internal view returns (bool) { // Get the whitelisted liquidity pool for the Circle token address pool = ISynapseCCTP(synapseCCTP).circleTokenPool(circleToken); if (pool == address(0)) return false; // Iterate over pool tokens to check if the token is in the pool (meaning it is connected to the Circle token) for (uint8 index = 0; ; ++index) { try IDefaultPool(pool).getToken(index) returns (address poolToken) { if (poolToken == token) return true; } catch { // End of pool reached break; } } return false; } /// @dev Derives the `swapParams` for following interaction with SynapseCCTP contract. function _deriveCCTPSwapParams(SwapQuery memory destQuery) internal pure returns (uint32 requestVersion, bytes memory swapParams) { // Check if any action was specified in `destQuery` if (destQuery.routerAdapter == address(0)) { // No action was specified, so no swap is required return (RequestLib.REQUEST_BASE, ""); } DefaultParams memory params = abi.decode(destQuery.rawParams, (DefaultParams)); // Check if the action is a swap if (params.action != Action.Swap) { // Actions other than swap are not supported for Circle tokens on the destination chain revert UnknownRequestAction(); } requestVersion = RequestLib.REQUEST_SWAP; swapParams = RequestLib.formatSwapParams({ tokenIndexFrom: params.tokenIndexFrom, tokenIndexTo: params.tokenIndexTo, deadline: destQuery.deadline, minAmountOut: destQuery.minAmountOut }); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {ITokenMessenger} from "./ITokenMessenger.sol"; interface ISynapseCCTP { /// @notice Send a Circle token supported by CCTP to a given chain /// with the request for the action to take on the destination chain. /// @dev The request is a bytes array containing information about the end recipient of the tokens, /// as well as an optional swap action to take on the destination chain. /// `chainId` refers to value from EIP-155 (block.chainid). /// @param recipient Recipient of the tokens on destination chain /// @param chainId Chain ID of the destination chain /// @param burnToken Address of Circle token to burn /// @param amount Amount of tokens to burn /// @param requestVersion Version of the request format /// @param swapParams Swap parameters for the action to take on the destination chain (could be empty) function sendCircleToken( address recipient, uint256 chainId, address burnToken, uint256 amount, uint32 requestVersion, bytes memory swapParams ) external; /// @notice Receive Circle token supported by CCTP with the request for the action to take. /// @dev The request is a bytes array containing information about the end recipient of the tokens, /// as well as an optional swap action to take on this chain. /// @dev The relayers need to use SynapseCCTP.chainGasAmount() as `msg.value` when calling this function, /// or the call will revert. /// @param message Message raw bytes emitted by CCTP MessageTransmitter on origin chain /// @param signature Circle's attestation for the message obtained from Circle's API /// @param requestVersion Version of the request format /// @param formattedRequest Formatted request for the action to take on this chain function receiveCircleToken( bytes calldata message, bytes calldata signature, uint32 requestVersion, bytes memory formattedRequest ) external payable; // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════ /// @notice Returns the whitelisted liquidity pool for a given Circle token. /// @dev Returns address(0) if the token bridge+swap is not supported. function circleTokenPool(address token) external view returns (address pool); /// @notice Returns the address of Circle's TokenMessenger contract used for bridging Circle tokens. function tokenMessenger() external view returns (ITokenMessenger); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {BridgeToken} from "../../router/libs/Structs.sol"; interface ISynapseCCTPFees { /// @notice Calculates the fee amount for bridging a token to this chain using CCTP. /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported. /// @param token Address of the Circle token /// @param amount Amount of the Circle tokens to be bridged to this chain /// @param isSwap Whether the request is a swap request /// @return fee Fee amount function calculateFeeAmount( address token, uint256 amount, bool isSwap ) external view returns (uint256 fee); /// @notice Gets the fee structure for bridging a token to this chain. /// @dev Will return 0 for all fields if the token is not supported. /// @param token Address of the Circle token /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR` /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request /// @return maxFee Maximum fee for bridging a token to this chain function feeStructures(address token) external view returns ( uint40 relayerFee, uint72 minBaseFee, uint72 minSwapFee, uint72 maxFee ); /// @notice Returns the list of all supported bridge tokens and their symbols. function getBridgeTokens() external view returns (BridgeToken[] memory bridgeTokens); /// @notice Returns the address of the CCTP token for a given symbol. /// @dev Will return address(0) if the token is not supported. function symbolToToken(string memory symbol) external view returns (address token); /// @notice Returns the symbol of a given CCTP token. /// @dev Will return empty string if the token is not supported. function tokenToSymbol(address token) external view returns (string memory symbol); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {BridgeToken, DestRequest, SwapQuery} from "../../router/libs/Structs.sol"; interface ISynapseCCTPRouter { /// @notice Initiate a bridge transaction with an optional swap on both origin and destination chains. /// @dev Note that method is payable. /// If token is ETH_ADDRESS, this method should be invoked with `msg.value = amountIn`. /// If token is ERC20, the tokens will be pulled from msg.sender (use `msg.value = 0`). /// Make sure to approve this contract for spending `token` beforehand. /// originQuery.tokenOut should never be ETH_ADDRESS, bridge only works with ERC20 tokens. /// /// `originQuery` is supposed to be fetched using Router.getOriginAmountOut(). /// Alternatively one could use an external adapter for more complex swaps on the origin chain. /// /// `destQuery` is supposed to be fetched using Router.getDestinationAmountOut(). /// Complex swaps on destination chain are not supported for the time being. /// @param recipient Address to receive tokens on destination chain /// @param chainId Destination chain id /// @param token Initial token for the bridge transaction to be pulled from the user /// @param amount Amount of the initial tokens for the bridge transaction /// @param originQuery Origin swap query. Empty struct indicates no swap is required /// @param destQuery Destination swap query. Empty struct indicates no swap is required function bridge( address recipient, uint256 chainId, address token, uint256 amount, SwapQuery memory originQuery, SwapQuery memory destQuery ) external payable; // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════ /// @notice Calculates the fee amount for bridging a token to this chain using CCTP. /// @dev Will not check if fee exceeds the token amount. Will return 0 if the token is not supported. /// @param token Address of the Circle token /// @param amount Amount of the Circle tokens to be bridged to this chain /// @param isSwap Whether the request is a swap request /// @return fee Fee amount function calculateFeeAmount( address token, uint256 amount, bool isSwap ) external view returns (uint256 fee); /// @notice Gets the fee structure for bridging a token to this chain. /// @dev Will return 0 for all fields if the token is not supported. /// @param token Address of the Circle token /// @return relayerFee Fee % for bridging a token to this chain, multiplied by `FEE_DENOMINATOR` /// @return minBaseFee Minimum fee for bridging a token to this chain using a base request /// @return minSwapFee Minimum fee for bridging a token to this chain using a swap request /// @return maxFee Maximum fee for bridging a token to this chain function feeStructures(address token) external view returns ( uint40 relayerFee, uint72 minBaseFee, uint72 minSwapFee, uint72 maxFee ); /// @notice Gets the list of all bridge tokens (and their symbols), such that destination swap /// from a bridge token to `tokenOut` is possible. /// @param tokenOut Token address to swap to on destination chain /// @return tokens List of structs with following information: /// - symbol: unique token ID consistent among all chains /// - token: bridge token address function getConnectedBridgeTokens(address tokenOut) external view returns (BridgeToken[] memory tokens); /// @notice Finds the best path between `tokenIn` and every supported bridge token from the given list, /// treating the swap as "origin swap", without putting any restrictions on the swap. /// @dev Will NOT revert if any of the tokens are not supported, instead will return an empty query for that symbol. /// Check (query.minAmountOut != 0): this is true only if the swap is possible and bridge token is supported. /// The returned queries with minAmountOut != 0 could be used as `originQuery` with SynapseRouter. /// Note: it is possible to form a SwapQuery off-chain using alternative SwapAdapter for the origin swap. /// @param tokenIn Initial token that user wants to bridge/swap /// @param tokenSymbols List of symbols representing bridge tokens /// @param amountIn Amount of tokens user wants to bridge/swap /// @return originQueries List of structs that could be used as `originQuery` in SynapseRouter. /// minAmountOut and deadline fields will need to be adjusted based on the user settings. function getOriginAmountOut( address tokenIn, string[] memory tokenSymbols, uint256 amountIn ) external view returns (SwapQuery[] memory originQueries); /// @notice Finds the best path between every supported bridge token from the given list and `tokenOut`, /// treating the swap as "destination swap", limiting possible actions to those available for every bridge token. /// @dev Will NOT revert if any of the tokens are not supported, instead will return an empty query for that symbol. /// Note: it is NOT possible to form a SwapQuery off-chain using alternative SwapAdapter for the destination swap. /// For the time being, only swaps through the Synapse-supported pools are available on destination chain. /// @param requests List of structs with following information: /// - symbol: unique token ID consistent among all chains /// - amountIn: amount of bridge token to start with, before the bridge fee is applied /// @param tokenOut Token user wants to receive on destination chain /// @return destQueries List of structs that could be used as `destQuery` in SynapseRouter. /// minAmountOut and deadline fields will need to be adjusted based on the user settings. function getDestinationAmountOut(DestRequest[] memory requests, address tokenOut) external view returns (SwapQuery[] memory destQueries); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface ITokenMinter { /** * @notice Mints `amount` of local tokens corresponding to the * given (`sourceDomain`, `burnToken`) pair, to `to` address. * @dev reverts if the (`sourceDomain`, `burnToken`) pair does not * map to a nonzero local token address. This mapping can be queried using * getLocalToken(). * @param sourceDomain Source domain where `burnToken` was burned. * @param burnToken Burned token address as bytes32. * @param to Address to receive minted tokens, corresponding to `burnToken`, * on this domain. * @param amount Amount of tokens to mint. Must be less than or equal * to the minterAllowance of this TokenMinter for given `_mintToken`. * @return mintToken token minted. */ function mint( uint32 sourceDomain, bytes32 burnToken, address to, uint256 amount ) external returns (address mintToken); /** * @notice Burn tokens owned by this ITokenMinter. * @param burnToken burnable token. * @param amount amount of tokens to burn. Must be less than or equal to this ITokenMinter's * account balance of the given `_burnToken`. */ function burn(address burnToken, uint256 amount) external; /** * @notice Get the local token associated with the given remote domain and token. * @param remoteDomain Remote domain * @param remoteToken Remote token * @return local token address */ function getLocalToken(uint32 remoteDomain, bytes32 remoteToken) external view returns (address); // local token (address) => maximum burn amounts per message function burnLimitsPerMessage(address token) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; error UnknownRequestAction();
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {IncorrectRequestLength, UnknownRequestVersion} from "./Errors.sol"; /// # Base Request layout /// /// | Field | Type | Description | /// | --------------- | ------- | ---------------------------------------------- | /// | originDomain | uint32 | Domain of the origin chain used by Circle CCTP | /// | nonce | uint64 | Nonce of the CCTP message on origin chain | /// | originBurnToken | address | Circle token that was burned on origin chain | /// | amount | uint256 | Amount of tokens burned on origin chain | /// | recipient | address | Recipient of the tokens on destination chain | /// /// # Swap Params layout /// /// | Field | Type | Description | /// | -------------- | ------- | ------------------------------------------------------------- | /// | tokenIndexFrom | uint8 | Index of the minted Circle token in the pool | /// | tokenIndexTo | uint8 | Index of the final token in the pool | /// | deadline | uint256 | Latest timestamp to execute the swap | /// | minAmountOut | uint256 | Minimum amount of tokens to receive from the swap | library RequestLib { uint32 internal constant REQUEST_BASE = 0; uint32 internal constant REQUEST_SWAP = 1; /// @notice Length of the encoded base request. uint256 internal constant REQUEST_BASE_LENGTH = 5 * 32; /// @notice Length of the encoded swap parameters. uint256 internal constant SWAP_PARAMS_LENGTH = 4 * 32; /// @notice Length of the encoded swap request. /// Need 2 extra words for each `bytes` field to store its offset in the full payload, and length. uint256 internal constant REQUEST_SWAP_LENGTH = 4 * 32 + REQUEST_BASE_LENGTH + SWAP_PARAMS_LENGTH; // ════════════════════════════════════════════════ FORMATTING ═════════════════════════════════════════════════════ /// @notice Formats the base request into a bytes array. /// @param originDomain Domain of the origin chain /// @param nonce Nonce of the CCTP message on origin chain /// @param originBurnToken Circle token that was burned on origin chain /// @param amount Amount of tokens burned on origin chain /// @param recipient Recipient of the tokens on destination chain /// @return formattedRequest Properly formatted base request function formatBaseRequest( uint32 originDomain, uint64 nonce, address originBurnToken, uint256 amount, address recipient ) internal pure returns (bytes memory formattedRequest) { return abi.encode(originDomain, nonce, originBurnToken, amount, recipient); } /// @notice Formats the swap parameters part of the swap request into a bytes array. /// @param tokenIndexFrom Index of the minted Circle token in the pool /// @param tokenIndexTo Index of the final token in the pool /// @param deadline Latest timestamp to execute the swap /// @param minAmountOut Minimum amount of tokens to receive from the swap /// @return formattedSwapParams Properly formatted swap parameters function formatSwapParams( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut ) internal pure returns (bytes memory formattedSwapParams) { return abi.encode(tokenIndexFrom, tokenIndexTo, deadline, minAmountOut); } /// @notice Formats the request into a bytes array. /// @dev Will revert if the either of these is true: /// - Request version is unknown. /// - Base request is not properly formatted. /// - Swap parameters are specified for a base request. /// - Swap parameters are not properly formatted. /// @param requestVersion Version of the request format /// @param baseRequest Formatted base request /// @param swapParams Formatted swap parameters /// @return formattedRequest Properly formatted request function formatRequest( uint32 requestVersion, bytes memory baseRequest, bytes memory swapParams ) internal pure returns (bytes memory formattedRequest) { if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength(); if (requestVersion == REQUEST_BASE) { if (swapParams.length != 0) revert IncorrectRequestLength(); // swapParams is empty, so we can just return the base request return baseRequest; } else if (requestVersion == REQUEST_SWAP) { if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength(); // Encode both the base request and the swap parameters return abi.encode(baseRequest, swapParams); } else { revert UnknownRequestVersion(); } } // ═════════════════════════════════════════════════ DECODING ══════════════════════════════════════════════════════ /// @notice Decodes the base request from a bytes array. /// @dev Will revert if the request is not properly formatted. /// @param baseRequest Formatted base request /// @return originDomain Domain of the origin chain /// @return nonce Nonce of the CCTP message on origin domain /// @return originBurnToken Circle token that was burned on origin domain /// @return amount Amount of tokens to burn /// @return recipient Recipient of the tokens on destination domain function decodeBaseRequest(bytes memory baseRequest) internal pure returns ( uint32 originDomain, uint64 nonce, address originBurnToken, uint256 amount, address recipient ) { if (baseRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength(); return abi.decode(baseRequest, (uint32, uint64, address, uint256, address)); } /// @notice Decodes the swap parameters from a bytes array. /// @dev Will revert if the swap parameters are not properly formatted. /// @param swapParams Formatted swap parameters /// @return tokenIndexFrom Index of the minted Circle token in the pool /// @return tokenIndexTo Index of the final token in the pool /// @return deadline Latest timestamp to execute the swap /// @return minAmountOut Minimum amount of tokens to receive from the swap function decodeSwapParams(bytes memory swapParams) internal pure returns ( uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 deadline, uint256 minAmountOut ) { if (swapParams.length != SWAP_PARAMS_LENGTH) revert IncorrectRequestLength(); return abi.decode(swapParams, (uint8, uint8, uint256, uint256)); } /// @notice Decodes the versioned request from a bytes array. /// @dev Will revert if the either of these is true: /// - Request version is unknown. /// - Request is not properly formatted. /// @param requestVersion Version of the request format /// @param formattedRequest Formatted request /// @return baseRequest Formatted base request /// @return swapParams Formatted swap parameters function decodeRequest(uint32 requestVersion, bytes memory formattedRequest) internal pure returns (bytes memory baseRequest, bytes memory swapParams) { if (requestVersion == REQUEST_BASE) { if (formattedRequest.length != REQUEST_BASE_LENGTH) revert IncorrectRequestLength(); return (formattedRequest, ""); } else if (requestVersion == REQUEST_SWAP) { if (formattedRequest.length != REQUEST_SWAP_LENGTH) revert IncorrectRequestLength(); return abi.decode(formattedRequest, (bytes, bytes)); } else { revert UnknownRequestVersion(); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {DefaultAdapter} from "./adapters/DefaultAdapter.sol"; import {IRouterAdapter} from "./interfaces/IRouterAdapter.sol"; import {DeadlineExceeded, InsufficientOutputAmount, MsgValueIncorrect, TokenNotETH} from "./libs/Errors.sol"; import {Action, DefaultParams, SwapQuery} from "./libs/Structs.sol"; import {UniversalTokenLib} from "./libs/UniversalToken.sol"; import {SafeERC20, IERC20} from "@openzeppelin/contracts-4.5.0/token/ERC20/utils/SafeERC20.sol"; /// @title DefaultRouter /// @notice Base contract for all Synapse Routers, that is able to natively work with Default Pools /// due to the fact that it inherits from DefaultAdapter. abstract contract DefaultRouter is DefaultAdapter { using SafeERC20 for IERC20; using UniversalTokenLib for address; /// @dev Performs a "swap from tokenIn" following instructions from `query`. /// `query` will include the router adapter to use, and the exact type of "tokenIn -> tokenOut swap" /// should be encoded in `query.rawParams`. function _doSwap( address recipient, address tokenIn, uint256 amountIn, SwapQuery memory query ) internal returns (address tokenOut, uint256 amountOut) { // First, check the deadline for the swap // solhint-disable-next-line not-rely-on-time if (block.timestamp > query.deadline) revert DeadlineExceeded(); // Pull initial token from the user to specified router adapter amountIn = _pullToken(query.routerAdapter, tokenIn, amountIn); tokenOut = query.tokenOut; address routerAdapter = query.routerAdapter; if (routerAdapter == address(this)) { // If the router adapter is this contract, we can perform the swap directly and trust the result amountOut = _adapterSwap(recipient, tokenIn, amountIn, tokenOut, query.rawParams); } else { // Otherwise, we need to call the router adapter. Adapters are permissionless, so we verify the result // Record tokenOut balance before swap amountOut = tokenOut.universalBalanceOf(recipient); IRouterAdapter(routerAdapter).adapterSwap{value: msg.value}({ recipient: recipient, tokenIn: tokenIn, amountIn: amountIn, tokenOut: tokenOut, rawParams: query.rawParams }); // Use the difference between the recorded balance and the current balance as the amountOut amountOut = tokenOut.universalBalanceOf(recipient) - amountOut; } // Finally, check that the recipient received at least as much as they wanted if (amountOut < query.minAmountOut) revert InsufficientOutputAmount(); } /// @dev Pulls a requested token from the user to the requested recipient. /// Or, if msg.value was provided, check that ETH_ADDRESS was used and msg.value is correct. function _pullToken( address recipient, address token, uint256 amount ) internal returns (uint256 amountPulled) { if (msg.value == 0) { token.assertIsContract(); // Record token balance before transfer amountPulled = IERC20(token).balanceOf(recipient); // Token needs to be pulled only if msg.value is zero // This way user can specify WETH as the origin asset IERC20(token).safeTransferFrom(msg.sender, recipient, amount); // Use the difference between the recorded balance and the current balance as the amountPulled amountPulled = IERC20(token).balanceOf(recipient) - amountPulled; } else { // Otherwise, we need to check that ETH was specified if (token != UniversalTokenLib.ETH_ADDRESS) revert TokenNotETH(); // And that amount matches msg.value if (amount != msg.value) revert MsgValueIncorrect(); // We will forward msg.value in the external call later, if recipient is not this contract amountPulled = msg.value; } } }
// 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 // 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 // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface ITokenMessenger { /** * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint * on the destination domain must be called by `destinationCaller`. * WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible * to broadcast the message on the destination domain. This is an advanced feature, and the standard * depositForBurn() should be preferred for use cases where a specific destination caller is not required. * Emits a `DepositForBurn` event. * @dev reverts if: * - given destinationCaller is zero address * - given burnToken is not supported * - given destinationDomain has no TokenMessenger registered * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance * to this contract is less than `amount`. * - burn() reverts. For example, if `amount` is 0. * - MessageTransmitter returns false or reverts. * @param amount amount of tokens to burn * @param destinationDomain destination domain * @param mintRecipient address of mint recipient on destination domain * @param burnToken address of contract to burn deposited tokens, on local domain * @param destinationCaller caller on the destination domain, as bytes32 * @return nonce unique nonce reserved by message */ function depositForBurnWithCaller( uint256 amount, uint32 destinationDomain, bytes32 mintRecipient, address burnToken, bytes32 destinationCaller ) external returns (uint64 nonce); /** * @notice Handles an incoming message received by the local MessageTransmitter, * and takes the appropriate action. For a burn message, mints the * associated token to the requested recipient on the local domain. * @dev Validates the local sender is the local MessageTransmitter, and the * remote sender is a registered remote TokenMessenger for `remoteDomain`. * @param remoteDomain The domain where the message originated from. * @param sender The sender of the message (remote TokenMessenger). * @param messageBody The message body bytes. * @return success Bool, true if successful. */ function handleReceiveMessage( uint32 remoteDomain, bytes32 sender, bytes calldata messageBody ) external returns (bool success); // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════ // Local Message Transmitter responsible for sending and receiving messages to/from remote domains function localMessageTransmitter() external view returns (address); // Minter responsible for minting and burning tokens on the local domain function localMinter() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; error CastOverflow(); error IncorrectRequestLength(); error UnknownRequestVersion(); error CCTPGasRescueFailed(); error CCTPIncorrectChainId(); error CCTPIncorrectConfig(); error CCTPIncorrectDomain(); error CCTPIncorrectGasAmount(); error CCTPIncorrectProtocolFee(); error CCTPInsufficientAmount(); error CCTPSymbolAlreadyAdded(); error CCTPSymbolIncorrect(); error CCTPTokenAlreadyAdded(); error CCTPTokenNotFound(); error CCTPZeroAddress(); error CCTPZeroAmount(); error CCTPMessageNotReceived(); error RemoteCCTPDeploymentNotSet(); error RemoteCCTPTokenNotSet(); error ForwarderDeploymentFailed();
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {IDefaultPool, IDefaultExtendedPool} from "../interfaces/IDefaultExtendedPool.sol"; import {IRouterAdapter} from "../interfaces/IRouterAdapter.sol"; import {IWETH9} from "../interfaces/IWETH9.sol"; import {MsgValueIncorrect, PoolNotFound, TokenAddressMismatch, TokensIdentical} from "../libs/Errors.sol"; import {Action, DefaultParams} from "../libs/Structs.sol"; import {UniversalTokenLib} from "../libs/UniversalToken.sol"; import {SafeERC20, IERC20} from "@openzeppelin/contracts-4.5.0/token/ERC20/utils/SafeERC20.sol"; contract DefaultAdapter is IRouterAdapter { using SafeERC20 for IERC20; using UniversalTokenLib for address; /// @notice Enable this contract to receive Ether when withdrawing from WETH. /// @dev Consider implementing rescue functions to withdraw Ether from this contract. receive() external payable {} /// @inheritdoc IRouterAdapter function adapterSwap( address recipient, address tokenIn, uint256 amountIn, address tokenOut, bytes memory rawParams ) external payable returns (uint256 amountOut) { return _adapterSwap(recipient, tokenIn, amountIn, tokenOut, rawParams); } /// @dev Internal logic for doing a tokenIn -> tokenOut swap. /// Note: `tokenIn` is assumed to have already been transferred to this contract. function _adapterSwap( address recipient, address tokenIn, uint256 amountIn, address tokenOut, bytes memory rawParams ) internal virtual returns (uint256 amountOut) { // We define a few phases for the whole Adapter's swap process. // (?) means the phase is optional. // (!) means the phase is mandatory. // PHASE 0(!): CHECK ALL THE PARAMS DefaultParams memory params = _checkParams(tokenIn, tokenOut, rawParams); // PHASE 1(?): WRAP RECEIVED ETH INTO WETH tokenIn = _wrapReceivedETH(tokenIn, amountIn, tokenOut, params); // After PHASE 1 this contract has `amountIn` worth of `tokenIn`, tokenIn != ETH_ADDRESS // PHASE 2(?): PREPARE TO UNWRAP SWAPPED WETH address tokenSwapTo = _deriveTokenSwapTo(tokenIn, tokenOut, params); // We need to perform tokenIn -> tokenSwapTo action in PHASE 3. // if tokenOut == ETH_ADDRESS, we need to unwrap WETH in PHASE 4. // Recipient will receive `tokenOut` in PHASE 5. // PHASE 3(?): PERFORM A REQUESTED SWAP amountOut = _performPoolAction(tokenIn, amountIn, tokenSwapTo, params); // After PHASE 3 this contract has `amountOut` worth of `tokenSwapTo`, tokenSwapTo != ETH_ADDRESS // PHASE 4(?): UNWRAP SWAPPED WETH // Check if the final token is native ETH if (tokenOut == UniversalTokenLib.ETH_ADDRESS) { // PHASE 2: WETH address was stored as `tokenSwapTo` _unwrapETH(tokenSwapTo, amountOut); } // PHASE 5(!): TRANSFER SWAPPED TOKENS TO RECIPIENT // Note: this will transfer native ETH, if tokenOut == ETH_ADDRESS // Note: this is a no-op if recipient == address(this) tokenOut.universalTransfer(recipient, amountOut); } /// @dev Checks the params and decodes them into a struct. function _checkParams( address tokenIn, address tokenOut, bytes memory rawParams ) internal pure returns (DefaultParams memory params) { if (tokenIn == tokenOut) revert TokensIdentical(); // Decode params for swapping via a Default pool params = abi.decode(rawParams, (DefaultParams)); // Swap pool should exist, if action other than HandleEth was requested if (params.pool == address(0) && params.action != Action.HandleEth) revert PoolNotFound(); } /// @dev Wraps native ETH into WETH, if requested. /// Returns the address of the token this contract ends up with. function _wrapReceivedETH( address tokenIn, uint256 amountIn, address tokenOut, DefaultParams memory params ) internal returns (address wrappedTokenIn) { // tokenIn was already transferred to this contract, check if we start from native ETH if (tokenIn == UniversalTokenLib.ETH_ADDRESS) { // Determine WETH address: this is either tokenOut (if no swap is needed), // or a pool token with index `tokenIndexFrom` (if swap is needed). wrappedTokenIn = _deriveWethAddress({token: tokenOut, params: params, isTokenFromWeth: true}); // Wrap ETH into WETH and leave it in this contract _wrapETH(wrappedTokenIn, amountIn); } else { wrappedTokenIn = tokenIn; // For ERC20 tokens msg.value should be zero if (msg.value != 0) revert MsgValueIncorrect(); } } /// @dev Derives the address of token to be received after an action defined in `params`. function _deriveTokenSwapTo( address tokenIn, address tokenOut, DefaultParams memory params ) internal view returns (address tokenSwapTo) { // Check if swap to native ETH was requested if (tokenOut == UniversalTokenLib.ETH_ADDRESS) { // Determine WETH address: this is either tokenIn (if no swap is needed), // or a pool token with index `tokenIndexTo` (if swap is needed). tokenSwapTo = _deriveWethAddress({token: tokenIn, params: params, isTokenFromWeth: false}); } else { tokenSwapTo = tokenOut; } } /// @dev Performs an action defined in `params` and returns the amount of `tokenSwapTo` received. function _performPoolAction( address tokenIn, uint256 amountIn, address tokenSwapTo, DefaultParams memory params ) internal returns (uint256 amountOut) { // Determine if we need to perform a swap if (params.action == Action.HandleEth) { // If no swap is required, amountOut doesn't change amountOut = amountIn; } else { // Record balance before the swap amountOut = IERC20(tokenSwapTo).balanceOf(address(this)); // Approve the pool for spending exactly `amountIn` of `tokenIn` IERC20(tokenIn).safeIncreaseAllowance(params.pool, amountIn); if (params.action == Action.Swap) { _swap(params.pool, params, amountIn, tokenSwapTo); } else if (params.action == Action.AddLiquidity) { _addLiquidity(params.pool, params, amountIn, tokenSwapTo); } else { // The only remaining action is RemoveLiquidity _removeLiquidity(params.pool, params, amountIn, tokenSwapTo); } // Use the difference between the balance after the swap and the recorded balance as `amountOut` amountOut = IERC20(tokenSwapTo).balanceOf(address(this)) - amountOut; } } // ═══════════════════════════════════════ INTERNAL LOGIC: SWAP ACTIONS ════════════════════════════════════════════ /// @dev Performs a swap through the given pool. /// Note: The pool should be already approved for spending `tokenIn`. function _swap( address pool, DefaultParams memory params, uint256 amountIn, address tokenOut ) internal { // tokenOut should match the "swap to" token if (IDefaultPool(pool).getToken(params.tokenIndexTo) != tokenOut) revert TokenAddressMismatch(); // amountOut and deadline are not checked in RouterAdapter IDefaultPool(pool).swap({ tokenIndexFrom: params.tokenIndexFrom, tokenIndexTo: params.tokenIndexTo, dx: amountIn, minDy: 0, deadline: type(uint256).max }); } /// @dev Adds liquidity in a form of a single token to the given pool. /// Note: The pool should be already approved for spending `tokenIn`. function _addLiquidity( address pool, DefaultParams memory params, uint256 amountIn, address tokenOut ) internal { uint256 numTokens = _getPoolNumTokens(pool); address lpToken = _getPoolLPToken(pool); // tokenOut should match the LP token if (lpToken != tokenOut) revert TokenAddressMismatch(); uint256[] memory amounts = new uint256[](numTokens); amounts[params.tokenIndexFrom] = amountIn; // amountOut and deadline are not checked in RouterAdapter IDefaultExtendedPool(pool).addLiquidity({amounts: amounts, minToMint: 0, deadline: type(uint256).max}); } /// @dev Removes liquidity in a form of a single token from the given pool. /// Note: The pool should be already approved for spending `tokenIn`. function _removeLiquidity( address pool, DefaultParams memory params, uint256 amountIn, address tokenOut ) internal { // tokenOut should match the "swap to" token if (IDefaultPool(pool).getToken(params.tokenIndexTo) != tokenOut) revert TokenAddressMismatch(); // amountOut and deadline are not checked in RouterAdapter IDefaultExtendedPool(pool).removeLiquidityOneToken({ tokenAmount: amountIn, tokenIndex: params.tokenIndexTo, minAmount: 0, deadline: type(uint256).max }); } // ═════════════════════════════════════════ INTERNAL LOGIC: POOL LENS ═════════════════════════════════════════════ /// @dev Returns the LP token address of the given pool. function _getPoolLPToken(address pool) internal view returns (address lpToken) { (, , , , , , lpToken) = IDefaultExtendedPool(pool).swapStorage(); } /// @dev Returns the number of tokens in the given pool. function _getPoolNumTokens(address pool) internal view returns (uint256 numTokens) { // Iterate over all tokens in the pool until the end is reached for (uint8 index = 0; ; ++index) { try IDefaultPool(pool).getToken(index) returns (address) {} catch { // End of pool reached numTokens = index; break; } } } /// @dev Returns the tokens in the given pool. function _getPoolTokens(address pool) internal view returns (address[] memory tokens) { uint256 numTokens = _getPoolNumTokens(pool); tokens = new address[](numTokens); for (uint8 i = 0; i < numTokens; ++i) { // This will not revert because we already know the number of tokens in the pool tokens[i] = IDefaultPool(pool).getToken(i); } } /// @dev Returns the quote for a swap through the given pool. /// Note: will return 0 on invalid swaps. function _getPoolSwapQuote( address pool, uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 amountIn ) internal view returns (uint256 amountOut) { try IDefaultPool(pool).calculateSwap(tokenIndexFrom, tokenIndexTo, amountIn) returns (uint256 dy) { amountOut = dy; } catch { // Return 0 instead of reverting amountOut = 0; } } // ════════════════════════════════════════ INTERNAL LOGIC: ETH <> WETH ════════════════════════════════════════════ /// @dev Wraps ETH into WETH. function _wrapETH(address weth, uint256 amount) internal { if (amount != msg.value) revert MsgValueIncorrect(); // Deposit in order to have WETH in this contract IWETH9(weth).deposit{value: amount}(); } /// @dev Unwraps WETH into ETH. function _unwrapETH(address weth, uint256 amount) internal { // Withdraw ETH to this contract IWETH9(weth).withdraw(amount); } /// @dev Derives WETH address from swap parameters. function _deriveWethAddress( address token, DefaultParams memory params, bool isTokenFromWeth ) internal view returns (address weth) { if (params.action == Action.HandleEth) { // If we only need to wrap/unwrap ETH, WETH address should be specified as the other token weth = token; } else { // Otherwise, we need to get WETH address from the liquidity pool weth = address( IDefaultPool(params.pool).getToken(isTokenFromWeth ? params.tokenIndexFrom : params.tokenIndexTo) ); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface IRouterAdapter { /// @notice Performs a tokenIn -> tokenOut swap, according to the provided params. /// If tokenIn is ETH_ADDRESS, this method should be invoked with `msg.value = amountIn`. /// If tokenIn is ERC20, the tokens should be already transferred to this contract (using `msg.value = 0`). /// If tokenOut is ETH_ADDRESS, native ETH will be sent to the recipient (be aware of potential reentrancy). /// If tokenOut is ERC20, the tokens will be transferred to the recipient. /// @dev Contracts implementing {IRouterAdapter} interface are required to enforce the above restrictions. /// On top of that, they must ensure that exactly `amountOut` worth of `tokenOut` is transferred to the recipient. /// Swap deadline and slippage is checked outside of this contract. /// @param recipient Address to receive the swapped token /// @param tokenIn Token to sell (use ETH_ADDRESS to start from native ETH) /// @param amountIn Amount of tokens to sell /// @param tokenOut Token to buy (use ETH_ADDRESS to end with native ETH) /// @param rawParams Additional swap parameters /// @return amountOut Amount of bought tokens function adapterSwap( address recipient, address tokenIn, uint256 amountIn, address tokenOut, bytes calldata rawParams ) external payable returns (uint256 amountOut); }
// 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 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 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 // 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); }
// 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 (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 pragma solidity 0.8.17; import {IDefaultPool} from "./IDefaultPool.sol"; interface IDefaultExtendedPool is IDefaultPool { function addLiquidity( uint256[] calldata amounts, uint256 minToMint, uint256 deadline ) external returns (uint256); function removeLiquidityOneToken( uint256 tokenAmount, uint8 tokenIndex, uint256 minAmount, uint256 deadline ) external returns (uint256); // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════ function swapStorage() external view returns ( uint256 initialA, uint256 futureA, uint256 initialATime, uint256 futureATime, uint256 swapFee, uint256 adminFee, address lpToken ); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface IWETH9 { function deposit() external payable; function withdraw(uint256 wad) external; }
{ "remappings": [ "@boringcrypto/=node_modules/@boringcrypto/", "@ensdomains/=node_modules/@ensdomains/", "@openzeppelin/=node_modules/@openzeppelin/", "ds-test/=lib/forge-std/lib/ds-test/src/", "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": { "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":"_synapseCCTP","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DeadlineExceeded","type":"error"},{"inputs":[],"name":"InsufficientOutputAmount","type":"error"},{"inputs":[],"name":"MsgValueIncorrect","type":"error"},{"inputs":[],"name":"PoolNotFound","type":"error"},{"inputs":[],"name":"TokenAddressMismatch","type":"error"},{"inputs":[],"name":"TokenNotContract","type":"error"},{"inputs":[],"name":"TokenNotETH","type":"error"},{"inputs":[],"name":"TokensIdentical","type":"error"},{"inputs":[],"name":"UnknownRequestAction","type":"error"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"bytes","name":"rawParams","type":"bytes"}],"name":"adapterSwap","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"address","name":"routerAdapter","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"rawParams","type":"bytes"}],"internalType":"struct SwapQuery","name":"originQuery","type":"tuple"},{"components":[{"internalType":"address","name":"routerAdapter","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"rawParams","type":"bytes"}],"internalType":"struct SwapQuery","name":"destQuery","type":"tuple"}],"name":"bridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"isSwap","type":"bool"}],"name":"calculateFeeAmount","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"feeStructures","outputs":[{"internalType":"uint40","name":"relayerFee","type":"uint40"},{"internalType":"uint72","name":"minBaseFee","type":"uint72"},{"internalType":"uint72","name":"minSwapFee","type":"uint72"},{"internalType":"uint72","name":"maxFee","type":"uint72"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenOut","type":"address"}],"name":"getConnectedBridgeTokens","outputs":[{"components":[{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"token","type":"address"}],"internalType":"struct BridgeToken[]","name":"tokens","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"internalType":"struct DestRequest[]","name":"requests","type":"tuple[]"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"getDestinationAmountOut","outputs":[{"components":[{"internalType":"address","name":"routerAdapter","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"rawParams","type":"bytes"}],"internalType":"struct SwapQuery[]","name":"destQueries","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"string[]","name":"tokenSymbols","type":"string[]"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"getOriginAmountOut","outputs":[{"components":[{"internalType":"address","name":"routerAdapter","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"rawParams","type":"bytes"}],"internalType":"struct SwapQuery[]","name":"originQueries","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"synapseCCTP","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60a06040523480156200001157600080fd5b50604051620033ca380380620033ca833981016040819052620000349162000046565b6001600160a01b031660805262000078565b6000602082840312156200005957600080fd5b81516001600160a01b03811681146200007157600080fd5b9392505050565b6080516132db620000ef600039600081816101cf015281816102710152818161033e0152818161040501528181610559015281816106460152818161072d015281816107be0152818161088701528181610ab201528181610aee01528181610b6f01528181610ded015261152301526132db6000f3fe60806040526004361061007f5760003560e01c8063c22881471161004e578063c228814714610122578063d38e788814610137578063dc72495b14610164578063e701d26e146101bd57600080fd5b8063077e11991461008b5780630d25aafe146100c157806324a98f11146100ef5780633bc758fd1461010257600080fd5b3661008657005b600080fd5b34801561009757600080fd5b506100ab6100a6366004612864565b610209565b6040516100b891906129b2565b60405180910390f35b3480156100cd57600080fd5b506100e16100dc366004612a61565b610528565b6040519081526020016100b8565b6100e16100fd366004612aa3565b6105d0565b34801561010e57600080fd5b506100ab61011d366004612b21565b6105e9565b610135610130366004612c91565b610a49565b005b34801561014357600080fd5b50610157610152366004612d2b565b610b69565b6040516100b89190612d48565b34801561017057600080fd5b5061018461017f366004612d2b565b610dc5565b6040805164ffffffffff909516855268ffffffffffffffffff9384166020860152918316918401919091521660608201526080016100b8565b3480156101c957600080fd5b506101f17f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100b8565b8151606090806001600160401b0381111561022657610226612731565b60405190808252806020026020018201604052801561025f57816020015b61024c6126f0565b8152602001906001900390816102445790505b50915060005b818110156105205760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a5bc29c28784815181106102b0576102b0612db9565b6020026020010151600001516040518263ffffffff1660e01b81526004016102d89190612dcf565b602060405180830381865afa1580156102f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103199190612de2565b60405163292c740d60e21b81526001600160a01b0380831660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063a4b1d03490602401602060405180830381865afa158015610385573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a99190612de2565b905060008784815181106103bf576103bf612db9565b6020908102919091018101510151604051630692d57f60e11b81526001600160a01b0385811660048301819052602483018490528a8216141560448301529192506000917f00000000000000000000000000000000000000000000000000000000000000001690630d25aafe90606401602060405180830381865afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190612dff565b9050808211156104dd5761048f83858a61048a8587612e2e565b610e67565b8786815181106104a1576104a1612db9565b60200260200101819052506104d8308887815181106104c2576104c2612db9565b6020026020010151610fdf90919063ffffffff16565b61050b565b878786815181106104f0576104f0612db9565b6020908102919091018101516001600160a01b039092169101525b505050508061051990612e41565b9050610265565b505092915050565b604051630692d57f60e11b81526001600160a01b0384811660048301526024820184905282151560448301526000917f000000000000000000000000000000000000000000000000000000000000000090911690630d25aafe90606401602060405180830381865afa1580156105a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c69190612dff565b90505b9392505050565b60006105df868686868661101a565b9695505050505050565b8151606090806001600160401b0381111561060657610606612731565b60405190808252806020026020018201604052801561063f57816020015b61062c6126f0565b8152602001906001900390816106245790505b50915060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663461178306040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c69190612de2565b6001600160a01b031663cb75c11c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610703573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107279190612de2565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635c975abb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610789573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ad9190612e5a565b905060005b83811015610a3e5760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a5bc29c28984815181106107fd576107fd612db9565b60200260200101516040518263ffffffff1660e01b81526004016108219190612dcf565b602060405180830381865afa15801561083e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108629190612de2565b60405163292c740d60e21b81526001600160a01b0380831660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063a4b1d03490602401602060405180830381865afa1580156108ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f29190612de2565b9050610900818b848b610e67565b87848151811061091257610912612db9565b60209081029190910101526040516352b7631960e11b81526001600160a01b0383811660048301526000919087169063a56ec63290602401602060405180830381865afa158015610967573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061098b9190612dff565b9050808885815181106109a0576109a0612db9565b60200260200101516040015111806109b55750845b15610a145760008885815181106109ce576109ce612db9565b6020026020010151604001818152505060405180602001604052806000815250888581518110610a0057610a00612db9565b602002602001015160800181905250610a2a565b610a2a308986815181106104c2576104c2612db9565b50505080610a3790612e41565b90506107b2565b505050509392505050565b81516001600160a01b031615610a6f57610a65308585856110a3565b9094509250610a9c565b3415610a8e576040516381de0bf360e01b815260040160405180910390fd5b610a993085856111ee565b92505b600080610aa88361136f565b91509150610ad7867f00000000000000000000000000000000000000000000000000000000000000008761144a565b604051630c1376d360e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063304ddb4c90610b2d908b908b908b908b9089908990600401612e77565b600060405180830381600087803b158015610b4757600080fd5b505af1158015610b5b573d6000803e3d6000fd5b505050505050505050505050565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639c1d060e6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610bcb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610bf39190810190612ebd565b80519091506000816001600160401b03811115610c1257610c12612731565b604051908082528060200260200182016040528015610c3b578160200160208202803683370190505b5090506000805b83811015610cd7576000858281518110610c5e57610c5e612db9565b6020026020010151602001519050876001600160a01b0316816001600160a01b03161480610c915750610c9181896114ff565b15610cc6576001848381518110610caa57610caa612db9565b91151560209283029190910190910152610cc383612e41565b92505b50610cd081612e41565b9050610c42565b50806001600160401b03811115610cf057610cf0612731565b604051908082528060200260200182016040528015610d3657816020015b604080518082019091526060815260006020820152815260200190600190039081610d0e5790505b5094506000905060005b83811015610dbb57828181518110610d5a57610d5a612db9565b602002602001015115610dab57848181518110610d7957610d79612db9565b6020026020010151868380610d8d90612e41565b945081518110610d9f57610d9f612db9565b60200260200101819052505b610db481612e41565b9050610d40565b5050505050919050565b60405163dc72495b60e01b81526001600160a01b0382811660048301526000918291829182917f0000000000000000000000000000000000000000000000000000000000000000169063dc72495b90602401608060405180830381865afa158015610e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e589190613009565b93509350935093509193509193565b610e6f6126f0565b6001600160a01b038084166020830181905290851603610e955760408101829052610fd7565b6001600160a01b03851615610fd7576000610eaf86611655565b805190915060005b818160ff161015610fd357866001600160a01b0316838260ff1681518110610ee157610ee1612db9565b60200260200101516001600160a01b031603610fc35760005b828160ff161015610fc157866001600160a01b0316848260ff1681518110610f2457610f24612db9565b60200260200101516001600160a01b031603610fb1576000610f488a84848a611764565b90508560400151811115610faf5760408681018290528051608081018252600081526001600160a01b038c1660208083019190915260ff80871683850152851660608301529151610f99920161307f565b60408051601f1981840301815291905260808701525b505b610fba816130da565b9050610efa565b505b610fcc816130da565b9050610eb7565b5050505b949350505050565b8160400151600003610fef575050565b608082015151156110005780611003565b60005b6001600160a01b0316825250600019606090910152565b6000806110288685856117ee565b9050611036868686846118a7565b9550600061104587868461190c565b905061105387878385611947565b925073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03861601611084576110848184611acc565b6110986001600160a01b0386168985611b2a565b505095945050505050565b60008082606001514211156110cb5760405163559895a360e01b815260040160405180910390fd5b82516110d89086866111ee565b602084015184519195509250306001600160a01b0382160361110c5761110587878786886080015161101a565b91506111bf565b61111f6001600160a01b03841688611c1b565b9150806001600160a01b03166324a98f1134898989888a608001516040518763ffffffff1660e01b815260040161115a9594939291906130f9565b60206040518083038185885af1158015611178573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061119d9190612dff565b50816111b26001600160a01b03851689611c1b565b6111bc9190612e2e565b91505b83604001518210156111e4576040516342301c2360e01b815260040160405180910390fd5b5094509492505050565b6000346000036113095761120a836001600160a01b0316611cc4565b6040516370a0823160e01b81526001600160a01b0385811660048301528416906370a0823190602401602060405180830381865afa158015611250573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112749190612dff565b905061128b6001600160a01b038416338685611d30565b6040516370a0823160e01b81526001600160a01b0385811660048301528291908516906370a0823190602401602060405180830381865afa1580156112d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112f89190612dff565b6113029190612e2e565b90506105c9565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461134657604051632eac7efb60e01b815260040160405180910390fd5b348214611366576040516381de0bf360e01b815260040160405180910390fd5b50349392505050565b80516000906060906001600160a01b031661139c5750506040805160208101909152600080825292909150565b600083608001518060200190518101906113b69190613145565b90506000815160038111156113cd576113cd613069565b146113eb5760405163d98412c360e01b815260040160405180910390fd5b6001925061144281604001518260600151866060015187604001516040805160ff9586166020820152939094168385015260608301919091526080808301919091528251808303909101815260a090910190915290565b915050915091565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa15801561149a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114be9190612dff565b9050818110156114f95780156114e3576114e36001600160a01b038516846000611d9b565b6114f96001600160a01b03851684600019611d9b565b50505050565b60405163292c740d60e21b81526001600160a01b03838116600483015260009182917f0000000000000000000000000000000000000000000000000000000000000000169063a4b1d03490602401602060405180830381865afa15801561156a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061158e9190612de2565b90506001600160a01b0381166115a857600091505061164f565b60005b60405162415c3360e91b815260ff821660048201526001600160a01b038316906382b8660090602401602060405180830381865afa92505050801561160d575060408051601f3d908101601f1916820190925261160a91810190612de2565b60015b1561164857846001600160a01b0316816001600160a01b031603611637576001935050505061164f565b50611641816130da565b90506115ab565b5060009150505b92915050565b6060600061166283611eb0565b9050806001600160401b0381111561167c5761167c612731565b6040519080825280602002602001820160405280156116a5578160200160208202803683370190505b50915060005b818160ff16101561175d5760405162415c3360e91b815260ff821660048201526001600160a01b038516906382b8660090602401602060405180830381865afa1580156116fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117209190612de2565b838260ff168151811061173557611735612db9565b6001600160a01b0390921660209283029190910190910152611756816130da565b90506116ab565b5050919050565b60405163a95b089f60e01b815260ff808516600483015283166024820152604481018290526000906001600160a01b0386169063a95b089f90606401602060405180830381865afa9250505080156117d9575060408051601f3d908101601f191682019092526117d691810190612dff565b60015b6117e557506000610fd7565b95945050505050565b604080516080810182526000808252602082018190529181018290526060810191909152826001600160a01b0316846001600160a01b03160361184457604051630b839a1f60e01b815260040160405180910390fd5b818060200190518101906118589190613145565b60208101519091506001600160a01b0316158015611889575060038151600381111561188657611886613069565b14155b156105c9576040516301dbb3ff60e61b815260040160405180910390fd5b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b038616016118eb576118da83836001611f3c565b90506118e68185611fe9565b610fd7565b50833415610fd7576040516381de0bf360e01b815260040160405180910390fd5b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161193f5761130284836000611f3c565b509092915050565b600060038251600381111561195e5761195e613069565b0361196a575082610fd7565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156119ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d29190612dff565b60208301519091506119ef906001600160a01b0387169086612061565b600082516003811115611a0457611a04613069565b03611a1e57611a198260200151838686612113565b611a58565b600182516003811115611a3357611a33613069565b03611a4857611a19826020015183868661224c565b611a588260200151838686612383565b6040516370a0823160e01b815230600482015281906001600160a01b038516906370a0823190602401602060405180830381865afa158015611a9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ac29190612dff565b6117e59190612e2e565b604051632e1a7d4d60e01b8152600481018290526001600160a01b03831690632e1a7d4d90602401600060405180830381600087803b158015611b0e57600080fd5b505af1158015611b22573d6000803e3d6000fd5b505050505050565b306001600160a01b03831603611b3f57505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601611c02576000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611bb1576040519150601f19603f3d011682016040523d82523d6000602084013e611bb6565b606091505b50509050806114f95760405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b60448201526064015b60405180910390fd5b611c166001600160a01b038416838361246a565b505050565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601611c5357506001600160a01b0381163161164f565b6040516370a0823160e01b81526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa158015611c99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cbd9190612dff565b905061164f565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03821601611d0257604051630fea47fd60e31b815260040160405180910390fd5b806001600160a01b03163b600003611d2d57604051630fea47fd60e31b815260040160405180910390fd5b50565b6040516001600160a01b03808516602483015283166044820152606481018290526114f99085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261249a565b801580611e155750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611def573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e139190612dff565b155b611e805760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401611bf9565b6040516001600160a01b038316602482015260448101829052611c1690849063095ea7b360e01b90606401611d64565b6000805b60405162415c3360e91b815260ff821660048201526001600160a01b038416906382b8660090602401602060405180830381865afa925050508015611f16575060408051601f3d908101601f19168201909252611f1391810190612de2565b60015b611f25578060ff169150611f36565b50611f2f816130da565b9050611eb4565b50919050565b6000600383516003811115611f5357611f53613069565b03611f5f5750826105c9565b82602001516001600160a01b03166382b8660083611f81578460600151611f87565b84604001515b6040516001600160e01b031960e084901b16815260ff9091166004820152602401602060405180830381865afa158015611fc5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c69190612de2565b348114612009576040516381de0bf360e01b815260040160405180910390fd5b816001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561204457600080fd5b505af1158015612058573d6000803e3d6000fd5b50505050505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa1580156120b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120d69190612dff565b6120e091906131c9565b6040516001600160a01b0385166024820152604481018290529091506114f990859063095ea7b360e01b90606401611d64565b606083015160405162415c3360e91b815260ff90911660048201526001600160a01b0382811691908616906382b8660090602401602060405180830381865afa158015612164573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121889190612de2565b6001600160a01b0316146121af57604051631438b5c960e11b815260040160405180910390fd5b604080840151606085015191516348b4aac360e11b815260ff918216600482015291166024820152604481018390526000606482015260001960848201526001600160a01b0385169063916955869060a4015b6020604051808303816000875af1158015612221573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122459190612dff565b5050505050565b600061225785611eb0565b905060006122648661256c565b9050826001600160a01b0316816001600160a01b03161461229857604051631438b5c960e11b815260040160405180910390fd5b6000826001600160401b038111156122b2576122b2612731565b6040519080825280602002602001820160405280156122db578160200160208202803683370190505b5090508481876040015160ff16815181106122f8576122f8612db9565b6020908102919091010152604051634d49e87d60e01b81526001600160a01b03881690634d49e87d90612336908490600090600019906004016131dc565b6020604051808303816000875af1158015612355573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123799190612dff565b5050505050505050565b606083015160405162415c3360e91b815260ff90911660048201526001600160a01b0382811691908616906382b8660090602401602060405180830381865afa1580156123d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f89190612de2565b6001600160a01b03161461241f57604051631438b5c960e11b815260040160405180910390fd5b60608301516040516301f1d0ab60e51b81526004810184905260ff90911660248201526000604482015260001960648201526001600160a01b03851690633e3a156090608401612202565b6040516001600160a01b038316602482015260448101829052611c1690849063a9059cbb60e01b90606401611d64565b60006124ef826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166125dc9092919063ffffffff16565b805190915015611c16578080602001905181019061250d9190612e5a565b611c165760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401611bf9565b6000816001600160a01b0316635fd65f0f6040518163ffffffff1660e01b815260040160e060405180830381865afa1580156125ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125d09190613229565b98975050505050505050565b60606105c68484600085856001600160a01b0385163b61263e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611bf9565b600080866001600160a01b0316858760405161265a9190613289565b60006040518083038185875af1925050503d8060008114612697576040519150601f19603f3d011682016040523d82523d6000602084013e61269c565b606091505b50915091506126ac8282866126b7565b979650505050505050565b606083156126c65750816105c9565b8251156126d65782518084602001fd5b8160405162461bcd60e51b8152600401611bf99190612dcf565b6040518060a0016040528060006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001606081525090565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561276957612769612731565b60405290565b604051601f8201601f191681016001600160401b038111828210171561279757612797612731565b604052919050565b60006001600160401b038211156127b8576127b8612731565b5060051b60200190565b60006001600160401b038211156127db576127db612731565b50601f01601f191660200190565b600082601f8301126127fa57600080fd5b813561280d612808826127c2565b61276f565b81815284602083860101111561282257600080fd5b816020850160208301376000918101602001919091529392505050565b6001600160a01b0381168114611d2d57600080fd5b803561285f8161283f565b919050565b600080604080848603121561287857600080fd5b83356001600160401b038082111561288f57600080fd5b818601915086601f8301126128a357600080fd5b813560206128b36128088361279f565b82815260059290921b8401810191818101908a8411156128d257600080fd5b8286015b84811015612944578035868111156128ee5760008081fd5b8701808d03601f19018913156129045760008081fd5b61290c612747565b858201358881111561291e5760008081fd5b61292c8f88838601016127e9565b825250908901358582015283529183019183016128d6565b5097506129549050888201612854565b955050505050509250929050565b60005b8381101561297d578181015183820152602001612965565b50506000910152565b6000815180845261299e816020860160208601612962565b601f01601f19169290920160200192915050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015612a4557888303603f19018552815180516001600160a01b03908116855288820151168885015286810151878501526060808201519085015260809081015160a091850182905290612a3181860183612986565b9689019694505050908601906001016129d9565b509098975050505050505050565b8015158114611d2d57600080fd5b600080600060608486031215612a7657600080fd5b8335612a818161283f565b9250602084013591506040840135612a9881612a53565b809150509250925092565b600080600080600060a08688031215612abb57600080fd5b8535612ac68161283f565b94506020860135612ad68161283f565b9350604086013592506060860135612aed8161283f565b915060808601356001600160401b03811115612b0857600080fd5b612b14888289016127e9565b9150509295509295909350565b600080600060608486031215612b3657600080fd5b8335612b418161283f565b92506020848101356001600160401b0380821115612b5e57600080fd5b818701915087601f830112612b7257600080fd5b8135612b806128088261279f565b81815260059190911b8301840190848101908a831115612b9f57600080fd5b8585015b83811015612bd757803585811115612bbb5760008081fd5b612bc98d89838a01016127e9565b845250918601918601612ba3565b50979a979950505050604095909501359450505050565b600060a08284031215612c0057600080fd5b60405160a081016001600160401b038282108183111715612c2357612c23612731565b8160405282935084359150612c378261283f565b908252602084013590612c498261283f565b81602084015260408501356040840152606085013560608401526080850135915080821115612c7757600080fd5b50612c84858286016127e9565b6080830152505092915050565b60008060008060008060c08789031215612caa57600080fd5b8635612cb58161283f565b9550602087013594506040870135612ccc8161283f565b93506060870135925060808701356001600160401b0380821115612cef57600080fd5b612cfb8a838b01612bee565b935060a0890135915080821115612d1157600080fd5b50612d1e89828a01612bee565b9150509295509295509295565b600060208284031215612d3d57600080fd5b81356105c98161283f565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015612a4557888303603f1901855281518051878552612d9388860182612986565b918901516001600160a01b03169489019490945294870194925090860190600101612d6f565b634e487b7160e01b600052603260045260246000fd5b6020815260006105c96020830184612986565b600060208284031215612df457600080fd5b81516105c98161283f565b600060208284031215612e1157600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561164f5761164f612e18565b600060018201612e5357612e53612e18565b5060010190565b600060208284031215612e6c57600080fd5b81516105c981612a53565b6001600160a01b03878116825260208201879052851660408201526060810184905263ffffffff8316608082015260c060a082018190526000906125d090830184612986565b60006020808385031215612ed057600080fd5b82516001600160401b0380821115612ee757600080fd5b818501915085601f830112612efb57600080fd5b8151612f096128088261279f565b81815260059190911b83018401908481019088831115612f2857600080fd5b8585015b83811015612fe357805185811115612f445760008081fd5b86016040818c03601f1901811315612f5c5760008081fd5b612f64612747565b8983015188811115612f765760008081fd5b8301603f81018e13612f885760008081fd5b8a810151612f98612808826127c2565b8181528f85838501011115612fad5760008081fd5b612fbc828e8301878601612962565b835250509181015191612fce8361283f565b808a0192909252508352918601918601612f2c565b5098975050505050505050565b805168ffffffffffffffffff8116811461285f57600080fd5b6000806000806080858703121561301f57600080fd5b845164ffffffffff8116811461303457600080fd5b935061304260208601612ff0565b925061305060408601612ff0565b915061305e60608601612ff0565b905092959194509250565b634e487b7160e01b600052602160045260246000fd5b81516080820190600481106130a457634e487b7160e01b600052602160045260246000fd5b8083525060018060a01b03602084015116602083015260ff604084015116604083015260ff606084015116606083015292915050565b600060ff821660ff81036130f0576130f0612e18565b60010192915050565b6001600160a01b0386811682528581166020830152604082018590528316606082015260a0608082018190526000906126ac90830184612986565b805160ff8116811461285f57600080fd5b60006080828403121561315757600080fd5b604051608081018181106001600160401b038211171561317957613179612731565b60405282516004811061318b57600080fd5b8152602083015161319b8161283f565b60208201526131ac60408401613134565b60408201526131bd60608401613134565b60608201529392505050565b8082018082111561164f5761164f612e18565b606080825284519082018190526000906020906080840190828801845b82811015613215578151845292840192908401906001016131f9565b505050908301949094525060400152919050565b600080600080600080600060e0888a03121561324457600080fd5b875196506020880151955060408801519450606088015193506080880151925060a0880151915060c08801516132798161283f565b8091505092959891949750929550565b6000825161329b818460208701612962565b919091019291505056fea2646970667358221220614c851d553726072f54e78fb5f62e20f9ef3e3f33a0da635aef4730e9cc50c464736f6c63430008110033000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e84
Deployed Bytecode
0x60806040526004361061007f5760003560e01c8063c22881471161004e578063c228814714610122578063d38e788814610137578063dc72495b14610164578063e701d26e146101bd57600080fd5b8063077e11991461008b5780630d25aafe146100c157806324a98f11146100ef5780633bc758fd1461010257600080fd5b3661008657005b600080fd5b34801561009757600080fd5b506100ab6100a6366004612864565b610209565b6040516100b891906129b2565b60405180910390f35b3480156100cd57600080fd5b506100e16100dc366004612a61565b610528565b6040519081526020016100b8565b6100e16100fd366004612aa3565b6105d0565b34801561010e57600080fd5b506100ab61011d366004612b21565b6105e9565b610135610130366004612c91565b610a49565b005b34801561014357600080fd5b50610157610152366004612d2b565b610b69565b6040516100b89190612d48565b34801561017057600080fd5b5061018461017f366004612d2b565b610dc5565b6040805164ffffffffff909516855268ffffffffffffffffff9384166020860152918316918401919091521660608201526080016100b8565b3480156101c957600080fd5b506101f17f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e8481565b6040516001600160a01b0390911681526020016100b8565b8151606090806001600160401b0381111561022657610226612731565b60405190808252806020026020018201604052801561025f57816020015b61024c6126f0565b8152602001906001900390816102445790505b50915060005b818110156105205760007f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e846001600160a01b031663a5bc29c28784815181106102b0576102b0612db9565b6020026020010151600001516040518263ffffffff1660e01b81526004016102d89190612dcf565b602060405180830381865afa1580156102f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103199190612de2565b60405163292c740d60e21b81526001600160a01b0380831660048301529192506000917f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e84169063a4b1d03490602401602060405180830381865afa158015610385573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a99190612de2565b905060008784815181106103bf576103bf612db9565b6020908102919091018101510151604051630692d57f60e11b81526001600160a01b0385811660048301819052602483018490528a8216141560448301529192506000917f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e841690630d25aafe90606401602060405180830381865afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190612dff565b9050808211156104dd5761048f83858a61048a8587612e2e565b610e67565b8786815181106104a1576104a1612db9565b60200260200101819052506104d8308887815181106104c2576104c2612db9565b6020026020010151610fdf90919063ffffffff16565b61050b565b878786815181106104f0576104f0612db9565b6020908102919091018101516001600160a01b039092169101525b505050508061051990612e41565b9050610265565b505092915050565b604051630692d57f60e11b81526001600160a01b0384811660048301526024820184905282151560448301526000917f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e8490911690630d25aafe90606401602060405180830381865afa1580156105a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c69190612dff565b90505b9392505050565b60006105df868686868661101a565b9695505050505050565b8151606090806001600160401b0381111561060657610606612731565b60405190808252806020026020018201604052801561063f57816020015b61062c6126f0565b8152602001906001900390816106245790505b50915060007f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e846001600160a01b031663461178306040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c69190612de2565b6001600160a01b031663cb75c11c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610703573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107279190612de2565b905060007f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e846001600160a01b0316635c975abb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610789573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ad9190612e5a565b905060005b83811015610a3e5760007f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e846001600160a01b031663a5bc29c28984815181106107fd576107fd612db9565b60200260200101516040518263ffffffff1660e01b81526004016108219190612dcf565b602060405180830381865afa15801561083e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108629190612de2565b60405163292c740d60e21b81526001600160a01b0380831660048301529192506000917f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e84169063a4b1d03490602401602060405180830381865afa1580156108ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f29190612de2565b9050610900818b848b610e67565b87848151811061091257610912612db9565b60209081029190910101526040516352b7631960e11b81526001600160a01b0383811660048301526000919087169063a56ec63290602401602060405180830381865afa158015610967573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061098b9190612dff565b9050808885815181106109a0576109a0612db9565b60200260200101516040015111806109b55750845b15610a145760008885815181106109ce576109ce612db9565b6020026020010151604001818152505060405180602001604052806000815250888581518110610a0057610a00612db9565b602002602001015160800181905250610a2a565b610a2a308986815181106104c2576104c2612db9565b50505080610a3790612e41565b90506107b2565b505050509392505050565b81516001600160a01b031615610a6f57610a65308585856110a3565b9094509250610a9c565b3415610a8e576040516381de0bf360e01b815260040160405180910390fd5b610a993085856111ee565b92505b600080610aa88361136f565b91509150610ad7867f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e848761144a565b604051630c1376d360e21b81526001600160a01b037f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e84169063304ddb4c90610b2d908b908b908b908b9089908990600401612e77565b600060405180830381600087803b158015610b4757600080fd5b505af1158015610b5b573d6000803e3d6000fd5b505050505050505050505050565b606060007f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e846001600160a01b0316639c1d060e6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610bcb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610bf39190810190612ebd565b80519091506000816001600160401b03811115610c1257610c12612731565b604051908082528060200260200182016040528015610c3b578160200160208202803683370190505b5090506000805b83811015610cd7576000858281518110610c5e57610c5e612db9565b6020026020010151602001519050876001600160a01b0316816001600160a01b03161480610c915750610c9181896114ff565b15610cc6576001848381518110610caa57610caa612db9565b91151560209283029190910190910152610cc383612e41565b92505b50610cd081612e41565b9050610c42565b50806001600160401b03811115610cf057610cf0612731565b604051908082528060200260200182016040528015610d3657816020015b604080518082019091526060815260006020820152815260200190600190039081610d0e5790505b5094506000905060005b83811015610dbb57828181518110610d5a57610d5a612db9565b602002602001015115610dab57848181518110610d7957610d79612db9565b6020026020010151868380610d8d90612e41565b945081518110610d9f57610d9f612db9565b60200260200101819052505b610db481612e41565b9050610d40565b5050505050919050565b60405163dc72495b60e01b81526001600160a01b0382811660048301526000918291829182917f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e84169063dc72495b90602401608060405180830381865afa158015610e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e589190613009565b93509350935093509193509193565b610e6f6126f0565b6001600160a01b038084166020830181905290851603610e955760408101829052610fd7565b6001600160a01b03851615610fd7576000610eaf86611655565b805190915060005b818160ff161015610fd357866001600160a01b0316838260ff1681518110610ee157610ee1612db9565b60200260200101516001600160a01b031603610fc35760005b828160ff161015610fc157866001600160a01b0316848260ff1681518110610f2457610f24612db9565b60200260200101516001600160a01b031603610fb1576000610f488a84848a611764565b90508560400151811115610faf5760408681018290528051608081018252600081526001600160a01b038c1660208083019190915260ff80871683850152851660608301529151610f99920161307f565b60408051601f1981840301815291905260808701525b505b610fba816130da565b9050610efa565b505b610fcc816130da565b9050610eb7565b5050505b949350505050565b8160400151600003610fef575050565b608082015151156110005780611003565b60005b6001600160a01b0316825250600019606090910152565b6000806110288685856117ee565b9050611036868686846118a7565b9550600061104587868461190c565b905061105387878385611947565b925073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03861601611084576110848184611acc565b6110986001600160a01b0386168985611b2a565b505095945050505050565b60008082606001514211156110cb5760405163559895a360e01b815260040160405180910390fd5b82516110d89086866111ee565b602084015184519195509250306001600160a01b0382160361110c5761110587878786886080015161101a565b91506111bf565b61111f6001600160a01b03841688611c1b565b9150806001600160a01b03166324a98f1134898989888a608001516040518763ffffffff1660e01b815260040161115a9594939291906130f9565b60206040518083038185885af1158015611178573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061119d9190612dff565b50816111b26001600160a01b03851689611c1b565b6111bc9190612e2e565b91505b83604001518210156111e4576040516342301c2360e01b815260040160405180910390fd5b5094509492505050565b6000346000036113095761120a836001600160a01b0316611cc4565b6040516370a0823160e01b81526001600160a01b0385811660048301528416906370a0823190602401602060405180830381865afa158015611250573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112749190612dff565b905061128b6001600160a01b038416338685611d30565b6040516370a0823160e01b81526001600160a01b0385811660048301528291908516906370a0823190602401602060405180830381865afa1580156112d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112f89190612dff565b6113029190612e2e565b90506105c9565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461134657604051632eac7efb60e01b815260040160405180910390fd5b348214611366576040516381de0bf360e01b815260040160405180910390fd5b50349392505050565b80516000906060906001600160a01b031661139c5750506040805160208101909152600080825292909150565b600083608001518060200190518101906113b69190613145565b90506000815160038111156113cd576113cd613069565b146113eb5760405163d98412c360e01b815260040160405180910390fd5b6001925061144281604001518260600151866060015187604001516040805160ff9586166020820152939094168385015260608301919091526080808301919091528251808303909101815260a090910190915290565b915050915091565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa15801561149a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114be9190612dff565b9050818110156114f95780156114e3576114e36001600160a01b038516846000611d9b565b6114f96001600160a01b03851684600019611d9b565b50505050565b60405163292c740d60e21b81526001600160a01b03838116600483015260009182917f000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e84169063a4b1d03490602401602060405180830381865afa15801561156a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061158e9190612de2565b90506001600160a01b0381166115a857600091505061164f565b60005b60405162415c3360e91b815260ff821660048201526001600160a01b038316906382b8660090602401602060405180830381865afa92505050801561160d575060408051601f3d908101601f1916820190925261160a91810190612de2565b60015b1561164857846001600160a01b0316816001600160a01b031603611637576001935050505061164f565b50611641816130da565b90506115ab565b5060009150505b92915050565b6060600061166283611eb0565b9050806001600160401b0381111561167c5761167c612731565b6040519080825280602002602001820160405280156116a5578160200160208202803683370190505b50915060005b818160ff16101561175d5760405162415c3360e91b815260ff821660048201526001600160a01b038516906382b8660090602401602060405180830381865afa1580156116fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117209190612de2565b838260ff168151811061173557611735612db9565b6001600160a01b0390921660209283029190910190910152611756816130da565b90506116ab565b5050919050565b60405163a95b089f60e01b815260ff808516600483015283166024820152604481018290526000906001600160a01b0386169063a95b089f90606401602060405180830381865afa9250505080156117d9575060408051601f3d908101601f191682019092526117d691810190612dff565b60015b6117e557506000610fd7565b95945050505050565b604080516080810182526000808252602082018190529181018290526060810191909152826001600160a01b0316846001600160a01b03160361184457604051630b839a1f60e01b815260040160405180910390fd5b818060200190518101906118589190613145565b60208101519091506001600160a01b0316158015611889575060038151600381111561188657611886613069565b14155b156105c9576040516301dbb3ff60e61b815260040160405180910390fd5b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b038616016118eb576118da83836001611f3c565b90506118e68185611fe9565b610fd7565b50833415610fd7576040516381de0bf360e01b815260040160405180910390fd5b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161193f5761130284836000611f3c565b509092915050565b600060038251600381111561195e5761195e613069565b0361196a575082610fd7565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156119ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d29190612dff565b60208301519091506119ef906001600160a01b0387169086612061565b600082516003811115611a0457611a04613069565b03611a1e57611a198260200151838686612113565b611a58565b600182516003811115611a3357611a33613069565b03611a4857611a19826020015183868661224c565b611a588260200151838686612383565b6040516370a0823160e01b815230600482015281906001600160a01b038516906370a0823190602401602060405180830381865afa158015611a9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ac29190612dff565b6117e59190612e2e565b604051632e1a7d4d60e01b8152600481018290526001600160a01b03831690632e1a7d4d90602401600060405180830381600087803b158015611b0e57600080fd5b505af1158015611b22573d6000803e3d6000fd5b505050505050565b306001600160a01b03831603611b3f57505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601611c02576000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611bb1576040519150601f19603f3d011682016040523d82523d6000602084013e611bb6565b606091505b50509050806114f95760405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b60448201526064015b60405180910390fd5b611c166001600160a01b038416838361246a565b505050565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601611c5357506001600160a01b0381163161164f565b6040516370a0823160e01b81526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa158015611c99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cbd9190612dff565b905061164f565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03821601611d0257604051630fea47fd60e31b815260040160405180910390fd5b806001600160a01b03163b600003611d2d57604051630fea47fd60e31b815260040160405180910390fd5b50565b6040516001600160a01b03808516602483015283166044820152606481018290526114f99085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261249a565b801580611e155750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611def573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e139190612dff565b155b611e805760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401611bf9565b6040516001600160a01b038316602482015260448101829052611c1690849063095ea7b360e01b90606401611d64565b6000805b60405162415c3360e91b815260ff821660048201526001600160a01b038416906382b8660090602401602060405180830381865afa925050508015611f16575060408051601f3d908101601f19168201909252611f1391810190612de2565b60015b611f25578060ff169150611f36565b50611f2f816130da565b9050611eb4565b50919050565b6000600383516003811115611f5357611f53613069565b03611f5f5750826105c9565b82602001516001600160a01b03166382b8660083611f81578460600151611f87565b84604001515b6040516001600160e01b031960e084901b16815260ff9091166004820152602401602060405180830381865afa158015611fc5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c69190612de2565b348114612009576040516381de0bf360e01b815260040160405180910390fd5b816001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561204457600080fd5b505af1158015612058573d6000803e3d6000fd5b50505050505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa1580156120b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120d69190612dff565b6120e091906131c9565b6040516001600160a01b0385166024820152604481018290529091506114f990859063095ea7b360e01b90606401611d64565b606083015160405162415c3360e91b815260ff90911660048201526001600160a01b0382811691908616906382b8660090602401602060405180830381865afa158015612164573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121889190612de2565b6001600160a01b0316146121af57604051631438b5c960e11b815260040160405180910390fd5b604080840151606085015191516348b4aac360e11b815260ff918216600482015291166024820152604481018390526000606482015260001960848201526001600160a01b0385169063916955869060a4015b6020604051808303816000875af1158015612221573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122459190612dff565b5050505050565b600061225785611eb0565b905060006122648661256c565b9050826001600160a01b0316816001600160a01b03161461229857604051631438b5c960e11b815260040160405180910390fd5b6000826001600160401b038111156122b2576122b2612731565b6040519080825280602002602001820160405280156122db578160200160208202803683370190505b5090508481876040015160ff16815181106122f8576122f8612db9565b6020908102919091010152604051634d49e87d60e01b81526001600160a01b03881690634d49e87d90612336908490600090600019906004016131dc565b6020604051808303816000875af1158015612355573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123799190612dff565b5050505050505050565b606083015160405162415c3360e91b815260ff90911660048201526001600160a01b0382811691908616906382b8660090602401602060405180830381865afa1580156123d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f89190612de2565b6001600160a01b03161461241f57604051631438b5c960e11b815260040160405180910390fd5b60608301516040516301f1d0ab60e51b81526004810184905260ff90911660248201526000604482015260001960648201526001600160a01b03851690633e3a156090608401612202565b6040516001600160a01b038316602482015260448101829052611c1690849063a9059cbb60e01b90606401611d64565b60006124ef826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166125dc9092919063ffffffff16565b805190915015611c16578080602001905181019061250d9190612e5a565b611c165760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401611bf9565b6000816001600160a01b0316635fd65f0f6040518163ffffffff1660e01b815260040160e060405180830381865afa1580156125ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125d09190613229565b98975050505050505050565b60606105c68484600085856001600160a01b0385163b61263e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611bf9565b600080866001600160a01b0316858760405161265a9190613289565b60006040518083038185875af1925050503d8060008114612697576040519150601f19603f3d011682016040523d82523d6000602084013e61269c565b606091505b50915091506126ac8282866126b7565b979650505050505050565b606083156126c65750816105c9565b8251156126d65782518084602001fd5b8160405162461bcd60e51b8152600401611bf99190612dcf565b6040518060a0016040528060006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001606081525090565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561276957612769612731565b60405290565b604051601f8201601f191681016001600160401b038111828210171561279757612797612731565b604052919050565b60006001600160401b038211156127b8576127b8612731565b5060051b60200190565b60006001600160401b038211156127db576127db612731565b50601f01601f191660200190565b600082601f8301126127fa57600080fd5b813561280d612808826127c2565b61276f565b81815284602083860101111561282257600080fd5b816020850160208301376000918101602001919091529392505050565b6001600160a01b0381168114611d2d57600080fd5b803561285f8161283f565b919050565b600080604080848603121561287857600080fd5b83356001600160401b038082111561288f57600080fd5b818601915086601f8301126128a357600080fd5b813560206128b36128088361279f565b82815260059290921b8401810191818101908a8411156128d257600080fd5b8286015b84811015612944578035868111156128ee5760008081fd5b8701808d03601f19018913156129045760008081fd5b61290c612747565b858201358881111561291e5760008081fd5b61292c8f88838601016127e9565b825250908901358582015283529183019183016128d6565b5097506129549050888201612854565b955050505050509250929050565b60005b8381101561297d578181015183820152602001612965565b50506000910152565b6000815180845261299e816020860160208601612962565b601f01601f19169290920160200192915050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015612a4557888303603f19018552815180516001600160a01b03908116855288820151168885015286810151878501526060808201519085015260809081015160a091850182905290612a3181860183612986565b9689019694505050908601906001016129d9565b509098975050505050505050565b8015158114611d2d57600080fd5b600080600060608486031215612a7657600080fd5b8335612a818161283f565b9250602084013591506040840135612a9881612a53565b809150509250925092565b600080600080600060a08688031215612abb57600080fd5b8535612ac68161283f565b94506020860135612ad68161283f565b9350604086013592506060860135612aed8161283f565b915060808601356001600160401b03811115612b0857600080fd5b612b14888289016127e9565b9150509295509295909350565b600080600060608486031215612b3657600080fd5b8335612b418161283f565b92506020848101356001600160401b0380821115612b5e57600080fd5b818701915087601f830112612b7257600080fd5b8135612b806128088261279f565b81815260059190911b8301840190848101908a831115612b9f57600080fd5b8585015b83811015612bd757803585811115612bbb5760008081fd5b612bc98d89838a01016127e9565b845250918601918601612ba3565b50979a979950505050604095909501359450505050565b600060a08284031215612c0057600080fd5b60405160a081016001600160401b038282108183111715612c2357612c23612731565b8160405282935084359150612c378261283f565b908252602084013590612c498261283f565b81602084015260408501356040840152606085013560608401526080850135915080821115612c7757600080fd5b50612c84858286016127e9565b6080830152505092915050565b60008060008060008060c08789031215612caa57600080fd5b8635612cb58161283f565b9550602087013594506040870135612ccc8161283f565b93506060870135925060808701356001600160401b0380821115612cef57600080fd5b612cfb8a838b01612bee565b935060a0890135915080821115612d1157600080fd5b50612d1e89828a01612bee565b9150509295509295509295565b600060208284031215612d3d57600080fd5b81356105c98161283f565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015612a4557888303603f1901855281518051878552612d9388860182612986565b918901516001600160a01b03169489019490945294870194925090860190600101612d6f565b634e487b7160e01b600052603260045260246000fd5b6020815260006105c96020830184612986565b600060208284031215612df457600080fd5b81516105c98161283f565b600060208284031215612e1157600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561164f5761164f612e18565b600060018201612e5357612e53612e18565b5060010190565b600060208284031215612e6c57600080fd5b81516105c981612a53565b6001600160a01b03878116825260208201879052851660408201526060810184905263ffffffff8316608082015260c060a082018190526000906125d090830184612986565b60006020808385031215612ed057600080fd5b82516001600160401b0380821115612ee757600080fd5b818501915085601f830112612efb57600080fd5b8151612f096128088261279f565b81815260059190911b83018401908481019088831115612f2857600080fd5b8585015b83811015612fe357805185811115612f445760008081fd5b86016040818c03601f1901811315612f5c5760008081fd5b612f64612747565b8983015188811115612f765760008081fd5b8301603f81018e13612f885760008081fd5b8a810151612f98612808826127c2565b8181528f85838501011115612fad5760008081fd5b612fbc828e8301878601612962565b835250509181015191612fce8361283f565b808a0192909252508352918601918601612f2c565b5098975050505050505050565b805168ffffffffffffffffff8116811461285f57600080fd5b6000806000806080858703121561301f57600080fd5b845164ffffffffff8116811461303457600080fd5b935061304260208601612ff0565b925061305060408601612ff0565b915061305e60608601612ff0565b905092959194509250565b634e487b7160e01b600052602160045260246000fd5b81516080820190600481106130a457634e487b7160e01b600052602160045260246000fd5b8083525060018060a01b03602084015116602083015260ff604084015116604083015260ff606084015116606083015292915050565b600060ff821660ff81036130f0576130f0612e18565b60010192915050565b6001600160a01b0386811682528581166020830152604082018590528316606082015260a0608082018190526000906126ac90830184612986565b805160ff8116811461285f57600080fd5b60006080828403121561315757600080fd5b604051608081018181106001600160401b038211171561317957613179612731565b60405282516004811061318b57600080fd5b8152602083015161319b8161283f565b60208201526131ac60408401613134565b60408201526131bd60608401613134565b60608201529392505050565b8082018082111561164f5761164f612e18565b606080825284519082018190526000906020906080840190828801845b82811015613215578151845292840192908401906001016131f9565b505050908301949094525060400152919050565b600080600080600080600060e0888a03121561324457600080fd5b875196506020880151955060408801519450606088015193506080880151925060a0880151915060c08801516132798161283f565b8091505092959891949750929550565b6000825161329b818460208701612962565b919091019291505056fea2646970667358221220614c851d553726072f54e78fb5f62e20f9ef3e3f33a0da635aef4730e9cc50c464736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e84
-----Decoded View---------------
Arg [0] : _synapseCCTP (address): 0xfB2Bfc368a7edfD51aa2cbEC513ad50edEa74E84
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000fb2bfc368a7edfd51aa2cbec513ad50edea74e84
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ 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.