ETH Price: $3,725.30 (+3.07%)
Gas: 0.1 GWei

Contract

0x00cD000000003f7F682BE4813200893d4e690000

Overview

ETH Balance

Scroll LogoScroll LogoScroll Logo0 ETH

ETH Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Bridge116370502024-12-04 11:11:289 mins ago1733310688IN
0x00cD0000...d4e690000
2.01 ETH0.000014780.07
Bridge116370092024-12-04 11:09:2411 mins ago1733310564IN
0x00cD0000...d4e690000
3 ETH0.000014120.07
Bridge116365152024-12-04 10:43:2137 mins ago1733309001IN
0x00cD0000...d4e690000
4.2344257 ETH0.000014850.05712175
Bridge116363002024-12-04 10:32:2548 mins ago1733308345IN
0x00cD0000...d4e690000
0 ETH0.000018080.05
Bridge116354422024-12-04 9:48:161 hr ago1733305696IN
0x00cD0000...d4e690000
4.5 ETH0.00001810.05368002
Bridge116352012024-12-04 9:35:261 hr ago1733304926IN
0x00cD0000...d4e690000
0.00065 ETH0.000016790.05350585
Bridge116350912024-12-04 9:29:321 hr ago1733304572IN
0x00cD0000...d4e690000
0.0303 ETH0.000018380.04571057
Bridge116347402024-12-04 9:11:262 hrs ago1733303486IN
0x00cD0000...d4e690000
0.1 ETH0.000018050.06
Bridge116347082024-12-04 9:09:492 hrs ago1733303389IN
0x00cD0000...d4e690000
0 ETH0.000028450.04559467
Bridge116333112024-12-04 7:58:273 hrs ago1733299107IN
0x00cD0000...d4e690000
10 ETH0.000013580.07
Bridge116327202024-12-04 7:28:323 hrs ago1733297312IN
0x00cD0000...d4e690000
1 ETH0.000014710.08
Bridge116324902024-12-04 7:16:414 hrs ago1733296601IN
0x00cD0000...d4e690000
0.00001 ETH0.00001180.05055337
Bridge116322812024-12-04 7:05:564 hrs ago1733295956IN
0x00cD0000...d4e690000
0 ETH0.000016310.0689762
Bridge116317182024-12-04 6:37:244 hrs ago1733294244IN
0x00cD0000...d4e690000
6 ETH0.000015010.09
Bridge116288612024-12-04 4:05:177 hrs ago1733285117IN
0x00cD0000...d4e690000
0.00024 ETH0.000011870.04953554
Bridge116280442024-12-04 3:22:597 hrs ago1733282579IN
0x00cD0000...d4e690000
9 ETH0.000012340.07
Bridge116274592024-12-04 2:52:578 hrs ago1733280777IN
0x00cD0000...d4e690000
0 ETH0.000012990.04192432
Bridge116260992024-12-04 1:42:019 hrs ago1733276521IN
0x00cD0000...d4e690000
7 ETH0.000011380.04217767
Bridge116253342024-12-04 1:02:4510 hrs ago1733274165IN
0x00cD0000...d4e690000
4.327251 ETH0.000018820.05997901
Bridge116251552024-12-04 0:53:5810 hrs ago1733273638IN
0x00cD0000...d4e690000
4 ETH0.000014060.06
Bridge116248002024-12-04 0:33:0610 hrs ago1733272386IN
0x00cD0000...d4e690000
2 ETH0.000016830.07
Bridge116245852024-12-04 0:20:2811 hrs ago1733271628IN
0x00cD0000...d4e690000
2 ETH0.00001530.07
Bridge116223772024-12-03 22:17:1313 hrs ago1733264233IN
0x00cD0000...d4e690000
2.7 ETH0.000018680.09
Bridge116222932024-12-03 22:12:4113 hrs ago1733263961IN
0x00cD0000...d4e690000
2.7 ETH0.000020240.1
Bridge116212572024-12-03 21:14:5214 hrs ago1733260492IN
0x00cD0000...d4e690000
0.5 ETH0.000017910.0606
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
116370502024-12-04 11:11:289 mins ago1733310688
0x00cD0000...d4e690000
2.01 ETH
116370092024-12-04 11:09:2411 mins ago1733310564
0x00cD0000...d4e690000
3 ETH
116369932024-12-04 11:08:3612 mins ago1733310516
0x00cD0000...d4e690000
0.00178294 ETH
116369932024-12-04 11:08:3612 mins ago1733310516
0x00cD0000...d4e690000
0.00178294 ETH
116365152024-12-04 10:43:2137 mins ago1733309001
0x00cD0000...d4e690000
4.2344257 ETH
116354422024-12-04 9:48:161 hr ago1733305696
0x00cD0000...d4e690000
4.5 ETH
116352012024-12-04 9:35:261 hr ago1733304926
0x00cD0000...d4e690000
0.00065 ETH
116350912024-12-04 9:29:321 hr ago1733304572
0x00cD0000...d4e690000
0.0303 ETH
116347402024-12-04 9:11:262 hrs ago1733303486
0x00cD0000...d4e690000
0.1 ETH
116333112024-12-04 7:58:273 hrs ago1733299107
0x00cD0000...d4e690000
10 ETH
116327202024-12-04 7:28:323 hrs ago1733297312
0x00cD0000...d4e690000
1 ETH
116324902024-12-04 7:16:414 hrs ago1733296601
0x00cD0000...d4e690000
0.00001 ETH
116317182024-12-04 6:37:244 hrs ago1733294244
0x00cD0000...d4e690000
6 ETH
116288612024-12-04 4:05:177 hrs ago1733285117
0x00cD0000...d4e690000
0.00024 ETH
116280442024-12-04 3:22:597 hrs ago1733282579
0x00cD0000...d4e690000
9 ETH
116260992024-12-04 1:42:019 hrs ago1733276521
0x00cD0000...d4e690000
7 ETH
116253342024-12-04 1:02:4510 hrs ago1733274165
0x00cD0000...d4e690000
4.327251 ETH
116251552024-12-04 0:53:5810 hrs ago1733273638
0x00cD0000...d4e690000
4 ETH
116248002024-12-04 0:33:0610 hrs ago1733272386
0x00cD0000...d4e690000
2 ETH
116245852024-12-04 0:20:2811 hrs ago1733271628
0x00cD0000...d4e690000
2 ETH
116223772024-12-03 22:17:1313 hrs ago1733264233
0x00cD0000...d4e690000
2.7 ETH
116222932024-12-03 22:12:4113 hrs ago1733263961
0x00cD0000...d4e690000
2.7 ETH
116212572024-12-03 21:14:5214 hrs ago1733260492
0x00cD0000...d4e690000
0.5 ETH
116212112024-12-03 21:12:2714 hrs ago1733260347
0x00cD0000...d4e690000
0.5 ETH
116203492024-12-03 20:26:4914 hrs ago1733257609
0x00cD0000...d4e690000
0.45474292 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FastBridgeRouterV2

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

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

import {DefaultRouter, DeadlineExceeded, InsufficientOutputAmount} from "../router/DefaultRouter.sol";
import {UniversalTokenLib} from "../router/libs/UniversalToken.sol";
import {ActionLib, LimitedToken} from "../router/libs/Structs.sol";
import {IFastBridge} from "./interfaces/IFastBridge.sol";
import {IFastBridgeRouter, SwapQuery} from "./interfaces/IFastBridgeRouter.sol";
import {ISwapQuoter} from "./interfaces/ISwapQuoter.sol";

import {Ownable} from "@openzeppelin/contracts-4.5.0/access/Ownable.sol";

contract FastBridgeRouterV2 is DefaultRouter, Ownable, IFastBridgeRouter {
    using UniversalTokenLib for address;

    error FastBridgeRouterV2__OriginSenderNotSpecified();

    /// @notice Emitted when the swap quoter is set.
    /// @param newSwapQuoter The new swap quoter.
    event SwapQuoterSet(address newSwapQuoter);

    /// @notice Emitted when the new FastBridge contract is set.
    /// @param newFastBridge The new FastBridge contract.
    event FastBridgeSet(address newFastBridge);

    /// @inheritdoc IFastBridgeRouter
    bytes1 public constant GAS_REBATE_FLAG = 0x2A;

    /// @inheritdoc IFastBridgeRouter
    address public fastBridge;
    /// @inheritdoc IFastBridgeRouter
    address public swapQuoter;

    constructor(address owner_) {
        transferOwnership(owner_);
    }

    /// @inheritdoc IFastBridgeRouter
    function setFastBridge(address fastBridge_) external onlyOwner {
        fastBridge = fastBridge_;
        emit FastBridgeSet(fastBridge_);
    }

    /// @inheritdoc IFastBridgeRouter
    function setSwapQuoter(address swapQuoter_) external onlyOwner {
        swapQuoter = swapQuoter_;
        emit SwapQuoterSet(swapQuoter_);
    }

    /// @inheritdoc IFastBridgeRouter
    function bridge(
        address recipient,
        uint256 chainId,
        address token,
        uint256 amount,
        SwapQuery memory originQuery,
        SwapQuery memory destQuery
    ) external payable {
        address originSender = _getOriginSender(destQuery.rawParams);
        if (originSender == address(0)) {
            revert FastBridgeRouterV2__OriginSenderNotSpecified();
        }
        if (originQuery.hasAdapter()) {
            // Perform a swap using the swap adapter, set this contract as recipient
            (token, amount) = _doSwap(address(this), token, amount, originQuery);
        } else {
            // Otherwise, pull the token from the user to this contract
            // We still need to perform the deadline and amountOut checks
            // solhint-disable-next-line not-rely-on-time
            if (block.timestamp > originQuery.deadline) {
                revert DeadlineExceeded();
            }
            if (amount < originQuery.minAmountOut) {
                revert InsufficientOutputAmount();
            }
            amount = _pullToken(address(this), token, amount);
        }
        IFastBridge.BridgeParams memory params = IFastBridge.BridgeParams({
            dstChainId: uint32(chainId),
            sender: originSender,
            to: recipient,
            originToken: token,
            destToken: destQuery.tokenOut,
            originAmount: amount,
            destAmount: destQuery.minAmountOut,
            sendChainGas: _chainGasRequested(destQuery.rawParams),
            deadline: destQuery.deadline
        });
        token.universalApproveInfinity(fastBridge, amount);
        uint256 msgValue = token == UniversalTokenLib.ETH_ADDRESS ? amount : 0;
        IFastBridge(fastBridge).bridge{value: msgValue}(params);
    }

    /// @inheritdoc IFastBridgeRouter
    function getOriginAmountOut(
        address tokenIn,
        address[] memory rfqTokens,
        uint256 amountIn
    ) external view returns (SwapQuery[] memory originQueries) {
        uint256 len = rfqTokens.length;
        originQueries = new SwapQuery[](len);
        for (uint256 i = 0; i < len; ++i) {
            originQueries[i] = ISwapQuoter(swapQuoter).getAmountOut(
                LimitedToken({actionMask: ActionLib.allActions(), token: tokenIn}),
                rfqTokens[i],
                amountIn
            );
            // Adjust the Adapter address if it exists
            if (originQueries[i].hasAdapter()) {
                originQueries[i].routerAdapter = address(this);
            }
        }
    }

    /// @dev Retrieves the origin sender from the raw params.
    /// Note: falls back to msg.sender if origin sender is not specified in the raw params, but
    /// msg.sender is an EOA.
    function _getOriginSender(bytes memory rawParams) internal view returns (address originSender) {
        // Origin sender (if present) is encoded as 20 bytes following the rebate flag
        if (rawParams.length >= 21) {
            // The easiest way to read from memory is to use assembly
            // solhint-disable-next-line no-inline-assembly
            assembly {
                // Skip the rawParams.length (32 bytes) and the rebate flag (1 byte)
                originSender := mload(add(rawParams, 33))
                // The address is in the highest 160 bits. Shift right by 96 to get it in the lowest 160 bits
                originSender := shr(96, originSender)
            }
        }
        if (originSender == address(0) && msg.sender.code.length == 0) {
            // Fall back to msg.sender if it is an EOA. This maintains backward compatibility
            // for cases where we can safely assume that the origin sender is the same as msg.sender.
            originSender = msg.sender;
        }
    }

    /// @dev Checks if the explicit instruction to send gas to the destination chain was provided.
    function _chainGasRequested(bytes memory rawParams) internal pure returns (bool) {
        return rawParams.length > 0 && rawParams[0] == GAS_REBATE_FLAG;
    }
}

File 2 of 18 : DefaultRouter.sol
// 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;
        }
    }
}

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

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

library UniversalTokenLib {
    using SafeERC20 for IERC20;

    address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

using SwapQueryLib for SwapQuery global;

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

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

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

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

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

using ActionLib for Action global;

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

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

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

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

File 5 of 18 : IFastBridge.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// TODO: This should be pulled from the sanguine repo (requires publish and relaxing the ^0.8.20 pragma)
interface IFastBridge {
    struct BridgeTransaction {
        uint32 originChainId;
        uint32 destChainId;
        address originSender; // user (origin)
        address destRecipient; // user (dest)
        address originToken;
        address destToken;
        uint256 originAmount; // amount in on origin bridge less originFeeAmount
        uint256 destAmount;
        uint256 originFeeAmount;
        bool sendChainGas;
        uint256 deadline;
        uint256 nonce;
    }

    struct BridgeProof {
        uint96 timestamp;
        address relayer;
    }

    // ============ Events ============

    event BridgeRequested(bytes32 transactionId, address sender, bytes request);
    event BridgeRelayed(
        bytes32 transactionId,
        address relayer,
        address to,
        address token,
        uint256 amount,
        uint256 chainGasAmount
    );
    event BridgeProofProvided(bytes32 transactionId, address relayer, bytes32 transactionHash);
    event BridgeProofDisputed(bytes32 transactionId, address relayer);
    event BridgeDepositClaimed(bytes32 transactionId, address relayer, address to, address token, uint256 amount);
    event BridgeDepositRefunded(bytes32 transactionId, address to, address token, uint256 amount);

    // ============ Methods ============

    struct BridgeParams {
        uint32 dstChainId;
        address sender;
        address to;
        address originToken;
        address destToken;
        uint256 originAmount; // should include protocol fee (if any)
        uint256 destAmount; // should include relayer fee
        bool sendChainGas;
        uint256 deadline;
    }

    /// @notice Initiates bridge on origin chain to be relayed by off-chain relayer
    /// @param params The parameters required to bridge
    function bridge(BridgeParams memory params) external payable;

    /// @notice Relays destination side of bridge transaction by off-chain relayer
    /// @param request The encoded bridge transaction to relay on destination chain
    function relay(bytes memory request) external payable;

    /// @notice Provides proof on origin side that relayer provided funds on destination side of bridge transaction
    /// @param request The encoded bridge transaction to prove on origin chain
    /// @param destTxHash The destination tx hash proving bridge transaction was relayed
    function prove(bytes memory request, bytes32 destTxHash) external;

    /// @notice Completes bridge transaction on origin chain by claiming originally deposited capital
    /// @param request The encoded bridge transaction to claim on origin chain
    /// @param to The recipient address of the funds
    function claim(bytes memory request, address to) external;

    /// @notice Disputes an outstanding proof in case relayer provided dest chain tx is invalid
    /// @param transactionId The transaction id associated with the encoded bridge transaction to dispute
    function dispute(bytes32 transactionId) external;

    /// @notice Refunds an outstanding bridge transaction in case optimistic bridging failed
    /// @param request The encoded bridge transaction to refund
    /// @param to The recipient address of the funds
    function refund(bytes memory request, address to) external;

    // ============ Views ============

    /// @notice Decodes bridge request into a bridge transaction
    /// @param request The bridge request to decode
    function getBridgeTransaction(bytes memory request) external pure returns (BridgeTransaction memory);

    /// @notice Checks if the dispute period has passed so bridge deposit can be claimed
    /// @param transactionId The transaction id associated with the encoded bridge transaction to check
    /// @param relayer The address of the relayer attempting to claim
    function canClaim(bytes32 transactionId, address relayer) external view returns (bool);
}

File 6 of 18 : IFastBridgeRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {SwapQuery} from "../../router/libs/Structs.sol";

interface IFastBridgeRouter {
    /// @notice Sets the address of the FastBridge contract
    /// @dev This function is only callable by the owner
    /// @param fastBridge_      The address of the FastBridge contract
    function setFastBridge(address fastBridge_) external;

    /// @notice Sets the address of the SwapQuoter contract
    /// @dev This function is only callable by the owner
    /// @param swapQuoter_      The address of the SwapQuoter contract
    function setSwapQuoter(address swapQuoter_) external;

    /// @notice Initiate an RFQ transaction with an optional swap on origin chain,
    /// and an optional gas rebate on destination chain.
    /// @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` is supposed to be fetched using FastBridgeRouter.getOriginAmountOut().
    /// Alternatively one could use an external adapter for more complex swaps on the origin chain.
    /// `destQuery.rawParams` signals whether the user wants to receive a gas rebate on the destination chain:
    /// - If the first byte of `destQuery.rawParams` is GAS_REBATE_FLAG, the user wants to receive a gas rebate.
    /// - Otherwise, the user does not want to receive a gas rebate.
    ///
    /// Cross-chain RFQ swap will be performed between tokens: `originQuery.tokenOut` and `destQuery.tokenOut`.
    /// Note: both tokens could be ETH_ADDRESS or ERC20.
    /// Full proceeds of the origin swap are considered the bid for the cross-chain swap.
    /// `destQuery.minAmountOut` is considered the ask for the cross-chain swap.
    /// Note: applying slippage to `destQuery.minAmountOut` will result in a worse price for the user,
    /// the full Relayer quote should be used instead.
    /// @param recipient        Address to receive tokens on destination chain
    /// @param chainId          Destination chain id
    /// @param token            Initial token to be pulled from the user
    /// @param amount           Amount of the initial tokens to be pulled from the user
    /// @param originQuery      Origin swap query (see above)
    /// @param destQuery        Destination swap query (see above)
    function bridge(
        address recipient,
        uint256 chainId,
        address token,
        uint256 amount,
        SwapQuery memory originQuery,
        SwapQuery memory destQuery
    ) external payable;

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

    /// @notice Finds the best path between `tokenIn` and every RFQ token from the given list,
    /// treating the swap as "origin swap", without putting any restrictions on the swap.
    /// @dev Check (query.minAmountOut != 0): this is true only if the swap is possible.
    /// The returned queries with minAmountOut != 0 could be used as `originQuery` with FastBridgeRouter.
    /// 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 rfqTokens        List of RFQ tokens
    /// @param amountIn         Amount of tokens user wants to bridge/swap
    /// @return originQueries   List of structs that could be used as `originQuery` in FastBridgeRouter.
    ///                         minAmountOut and deadline fields will need to be adjusted based on the user settings.
    function getOriginAmountOut(
        address tokenIn,
        address[] memory rfqTokens,
        uint256 amountIn
    ) external view returns (SwapQuery[] memory originQueries);

    /// @notice Magic value that indicates that the user wants to receive gas rebate on the destination chain.
    /// This is the answer to the ultimate question of life, the universe, and everything.
    function GAS_REBATE_FLAG() external view returns (bytes1);

    /// @notice Address of the FastBridge contract, used to initiate cross-chain RFQ swaps.
    function fastBridge() external view returns (address);

    /// @notice Address of the SwapQuoter contract, used to fetch quotes for the origin swap.
    function swapQuoter() external view returns (address);
}

File 7 of 18 : ISwapQuoter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {LimitedToken, SwapQuery} from "../../router/libs/Structs.sol";

interface ISwapQuoter {
    function getAmountOut(
        LimitedToken memory tokenIn,
        address tokenOut,
        uint256 amountIn
    ) external view returns (SwapQuery memory query);
}

File 8 of 18 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

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

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

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

File 9 of 18 : DefaultAdapter.sol
// 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)
            );
        }
    }
}

File 10 of 18 : IRouterAdapter.sol
// 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);
}

File 11 of 18 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

error DeadlineExceeded();
error InsufficientOutputAmount();

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

File 12 of 18 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/Address.sol";

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

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

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

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

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

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

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

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

File 13 of 18 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 14 of 18 : IDefaultExtendedPool.sol
// 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 calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory);

    function calculateRemoveLiquidityOneToken(uint256 tokenAmount, uint8 tokenIndex)
        external
        view
        returns (uint256 availableTokenAmount);

    function getAPrecise() external view returns (uint256);

    function getTokenBalance(uint8 index) external view returns (uint256);

    function swapStorage()
        external
        view
        returns (
            uint256 initialA,
            uint256 futureA,
            uint256 initialATime,
            uint256 futureATime,
            uint256 swapFee,
            uint256 adminFee,
            address lpToken
        );
}

File 15 of 18 : IWETH9.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

interface IWETH9 {
    function deposit() external payable;

    function withdraw(uint256 wad) external;
}

File 16 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 17 of 18 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

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

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

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

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

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

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

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

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DeadlineExceeded","type":"error"},{"inputs":[],"name":"FastBridgeRouterV2__OriginSenderNotSpecified","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newFastBridge","type":"address"}],"name":"FastBridgeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newSwapQuoter","type":"address"}],"name":"SwapQuoterSet","type":"event"},{"inputs":[],"name":"GAS_REBATE_FLAG","outputs":[{"internalType":"bytes1","name":"","type":"bytes1"}],"stateMutability":"view","type":"function"},{"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":[],"name":"fastBridge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address[]","name":"rfqTokens","type":"address[]"},{"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":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fastBridge_","type":"address"}],"name":"setFastBridge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"swapQuoter_","type":"address"}],"name":"setSwapQuoter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapQuoter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60806040523480156200001157600080fd5b50604051620026f7380380620026f7833981016040819052620000349162000176565b6200003f3362000051565b6200004a81620000a1565b50620001a8565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000546001600160a01b03163314620001015760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6001600160a01b038116620001685760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401620000f8565b620001738162000051565b50565b6000602082840312156200018957600080fd5b81516001600160a01b0381168114620001a157600080fd5b9392505050565b61253f80620001b86000396000f3fe6080604052600436106100a05760003560e01c80638da5cb5b116100645780638da5cb5b1461016157806390491b201461017f57806396d292d0146101ac578063bf03b1ea146101dd578063c2288147146101fd578063f2fde38b1461021057600080fd5b806324a98f11146100ac57806334474c8c146100d2578063715018a61461010a5780637673eb1514610121578063804b3dff1461014157600080fd5b366100a757005b600080fd5b6100bf6100ba366004611d25565b610230565b6040519081526020015b60405180910390f35b3480156100de57600080fd5b506002546100f2906001600160a01b031681565b6040516001600160a01b0390911681526020016100c9565b34801561011657600080fd5b5061011f610249565b005b34801561012d57600080fd5b506001546100f2906001600160a01b031681565b34801561014d57600080fd5b5061011f61015c366004611da4565b610288565b34801561016d57600080fd5b506000546001600160a01b03166100f2565b34801561018b57600080fd5b5061019f61019a366004611dc1565b610307565b6040516100c99190611ee0565b3480156101b857600080fd5b506101c4601560f91b81565b6040516001600160f81b031990911681526020016100c9565b3480156101e957600080fd5b5061011f6101f8366004611da4565b610513565b61011f61020b366004612005565b61058b565b34801561021c57600080fd5b5061011f61022b366004611da4565b610783565b600061023f868686868661081e565b9695505050505050565b6000546001600160a01b0316331461027c5760405162461bcd60e51b8152600401610273906120a0565b60405180910390fd5b61028660006108a7565b565b6000546001600160a01b031633146102b25760405162461bcd60e51b8152600401610273906120a0565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f6629f03aaaa02addcc840bd2d3deb4213bcc44a19f3e6601beb3bfcea9528d51906020015b60405180910390a150565b81516060908067ffffffffffffffff81111561032557610325611c37565b60405190808252806020026020018201604052801561039a57816020015b6103876040518060a0016040528060006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001606081525090565b8152602001906001900390816103435790505b50915060005b8181101561050a57600254604080518082019091526001600160a01b039091169063e6b0000990806000198152602001896001600160a01b03168152508784815181106103ef576103ef6120d5565b6020908102919091018101516040516001600160e01b031960e086901b1681528351600482015292909101516001600160a01b03908116602484015216604482015260648101879052608401600060405180830381865afa158015610458573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261048091908101906120eb565b838281518110610492576104926120d5565b60200260200101819052506104c98382815181106104b2576104b26120d5565b6020026020010151516001600160a01b0316151590565b156104fa57308382815181106104e1576104e16120d5565b60209081029190910101516001600160a01b0390911690525b610503816121e5565b90506103a0565b50509392505050565b6000546001600160a01b0316331461053d5760405162461bcd60e51b8152600401610273906120a0565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f214d83ceb705cb04d7ef7d2fc0dc6e40a0379fff652234e5d1b302ecac39bc53906020016102fc565b600061059a82608001516108f7565b90506001600160a01b0381166105c357604051632b68e6a360e01b815260040160405180910390fd5b82516001600160a01b0316156105e9576105df3086868661092f565b9095509350610641565b826060015142111561060e5760405163559895a360e01b815260040160405180910390fd5b8260400151841015610633576040516342301c2360e01b815260040160405180910390fd5b61063e308686610a7a565b93505b60006040518061012001604052808863ffffffff168152602001836001600160a01b03168152602001896001600160a01b03168152602001876001600160a01b0316815260200184602001516001600160a01b03168152602001868152602001846040015181526020016106b88560800151610bfc565b1515815260608501516020909101526001549091506106e4906001600160a01b03888116911687610c39565b60006001600160a01b03871673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610711576000610713565b855b60015460405163116145a560e21b81529192506001600160a01b03169063458516949083906107469086906004016121fe565b6000604051808303818588803b15801561075f57600080fd5b505af1158015610773573d6000803e3d6000fd5b5050505050505050505050505050565b6000546001600160a01b031633146107ad5760405162461bcd60e51b8152600401610273906120a0565b6001600160a01b0381166108125760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610273565b61081b816108a7565b50565b60008061082c868585610d1d565b905061083a86868684610dd6565b95506000610849878684610e43565b905061085787878385610e7e565b925073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0386160161088857610888818461100c565b61089c6001600160a01b038616898561106a565b505095945050505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000601582511061090c5750602181015160601c5b6001600160a01b0381161580156109225750333b155b1561092a5750335b919050565b60008082606001514211156109575760405163559895a360e01b815260040160405180910390fd5b8251610964908686610a7a565b602084015184519195509250306001600160a01b038216036109985761099187878786886080015161081e565b9150610a4b565b6109ab6001600160a01b03841688611156565b9150806001600160a01b03166324a98f1134898989888a608001516040518763ffffffff1660e01b81526004016109e69594939291906122a9565b60206040518083038185885af1158015610a04573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610a2991906122e4565b5081610a3e6001600160a01b03851689611156565b610a4891906122fd565b91505b8360400151821015610a70576040516342301c2360e01b815260040160405180910390fd5b5094509492505050565b600034600003610b9557610a96836001600160a01b03166111f8565b6040516370a0823160e01b81526001600160a01b0385811660048301528416906370a0823190602401602060405180830381865afa158015610adc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0091906122e4565b9050610b176001600160a01b038416338685611261565b6040516370a0823160e01b81526001600160a01b0385811660048301528291908516906370a0823190602401602060405180830381865afa158015610b60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8491906122e4565b610b8e91906122fd565b9050610bf5565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610bd257604051632eac7efb60e01b815260040160405180910390fd5b348214610bf2576040516381de0bf360e01b815260040160405180910390fd5b50345b9392505050565b6000808251118015610c3357508151601560f91b908390600090610c2257610c226120d5565b01602001516001600160f81b031916145b92915050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601610c6357505050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015610cb3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cd791906122e4565b9050818110610ce65750505050565b8015610d0157610d016001600160a01b0385168460006112cc565b610d176001600160a01b038516846000196112cc565b50505050565b604080516080810182526000808252602082018190529181018290526060810191909152826001600160a01b0316846001600160a01b031603610d7357604051630b839a1f60e01b815260040160405180910390fd5b81806020019051810190610d879190612321565b60208101519091506001600160a01b0316158015610db85750600381516003811115610db557610db56123a6565b14155b15610bf5576040516301dbb3ff60e61b815260040160405180910390fd5b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03861601610e1a57610e09838360016113e1565b9050610e15818561148e565b610e3b565b50833415610e3b576040516381de0bf360e01b815260040160405180910390fd5b949350505050565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601610e7657610b8e848360006113e1565b509092915050565b6000600382516003811115610e9557610e956123a6565b03610ea1575082610e3b565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015610ee5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0991906122e4565b6020830151909150610f26906001600160a01b0387169086611506565b600082516003811115610f3b57610f3b6123a6565b03610f5557610f5082602001518386866115b8565b610f8f565b600182516003811115610f6a57610f6a6123a6565b03610f7f57610f5082602001518386866116f1565b610f8f8260200151838686611829565b6040516370a0823160e01b815230600482015281906001600160a01b038516906370a0823190602401602060405180830381865afa158015610fd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff991906122e4565b61100391906122fd565b95945050505050565b604051632e1a7d4d60e01b8152600481018290526001600160a01b03831690632e1a7d4d90602401600060405180830381600087803b15801561104e57600080fd5b505af1158015611062573d6000803e3d6000fd5b505050505050565b306001600160a01b0383160361107f57505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161113d576000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146110f1576040519150601f19603f3d011682016040523d82523d6000602084013e6110f6565b606091505b5050905080610d175760405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b6044820152606401610273565b6111516001600160a01b0384168383611910565b505050565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161118e57506001600160a01b03811631610c33565b6040516370a0823160e01b81526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa1580156111d4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf591906122e4565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0382160161123657604051630fea47fd60e31b815260040160405180910390fd5b806001600160a01b03163b60000361081b57604051630fea47fd60e31b815260040160405180910390fd5b6040516001600160a01b0380851660248301528316604482015260648101829052610d179085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611940565b8015806113465750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611320573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134491906122e4565b155b6113b15760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610273565b6040516001600160a01b03831660248201526044810182905261115190849063095ea7b360e01b90606401611295565b60006003835160038111156113f8576113f86123a6565b03611404575082610bf5565b82602001516001600160a01b03166382b866008361142657846060015161142c565b84604001515b6040516001600160e01b031960e084901b16815260ff9091166004820152602401602060405180830381865afa15801561146a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e3b91906123bc565b3481146114ae576040516381de0bf360e01b815260040160405180910390fd5b816001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114e957600080fd5b505af11580156114fd573d6000803e3d6000fd5b50505050505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa158015611557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157b91906122e4565b61158591906123d9565b6040516001600160a01b038516602482015260448101829052909150610d1790859063095ea7b360e01b90606401611295565b606083015160405162415c3360e91b815260ff90911660048201526001600160a01b0382811691908616906382b8660090602401602060405180830381865afa158015611609573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162d91906123bc565b6001600160a01b03161461165457604051631438b5c960e11b815260040160405180910390fd5b604080840151606085015191516348b4aac360e11b815260ff918216600482015291166024820152604481018390526000606482015260001960848201526001600160a01b0385169063916955869060a4015b6020604051808303816000875af11580156116c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116ea91906122e4565b5050505050565b60006116fc85611a12565b9050600061170986611a9e565b9050826001600160a01b0316816001600160a01b03161461173d57604051631438b5c960e11b815260040160405180910390fd5b60008267ffffffffffffffff81111561175857611758611c37565b604051908082528060200260200182016040528015611781578160200160208202803683370190505b5090508481876040015160ff168151811061179e5761179e6120d5565b6020908102919091010152604051634d49e87d60e01b81526001600160a01b03881690634d49e87d906117dc908490600090600019906004016123ec565b6020604051808303816000875af11580156117fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181f91906122e4565b5050505050505050565b606083015160405162415c3360e91b815260ff90911660048201526001600160a01b0382811691908616906382b8660090602401602060405180830381865afa15801561187a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189e91906123bc565b6001600160a01b0316146118c557604051631438b5c960e11b815260040160405180910390fd5b60608301516040516301f1d0ab60e51b81526004810184905260ff90911660248201526000604482015260001960648201526001600160a01b03851690633e3a1560906084016116a7565b6040516001600160a01b03831660248201526044810182905261115190849063a9059cbb60e01b90606401611295565b6000611995826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611b0e9092919063ffffffff16565b80519091501561115157808060200190518101906119b39190612439565b6111515760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610273565b6000805b60405162415c3360e91b815260ff821660048201526001600160a01b038416906382b8660090602401602060405180830381865afa925050508015611a78575060408051601f3d908101601f19168201909252611a75918101906123bc565b60015b611a87578060ff169150611a98565b50611a918161245b565b9050611a16565b50919050565b6000816001600160a01b0316635fd65f0f6040518163ffffffff1660e01b815260040160e060405180830381865afa158015611ade573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b02919061247a565b98975050505050505050565b6060610e3b8484600085856001600160a01b0385163b611b705760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610273565b600080866001600160a01b03168587604051611b8c91906124da565b60006040518083038185875af1925050503d8060008114611bc9576040519150601f19603f3d011682016040523d82523d6000602084013e611bce565b606091505b5091509150611bde828286611be9565b979650505050505050565b60608315611bf8575081610bf5565b825115611c085782518084602001fd5b8160405162461bcd60e51b815260040161027391906124f6565b6001600160a01b038116811461081b57600080fd5b634e487b7160e01b600052604160045260246000fd5b60405160a0810167ffffffffffffffff81118282101715611c7057611c70611c37565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715611c9f57611c9f611c37565b604052919050565b600067ffffffffffffffff821115611cc157611cc1611c37565b50601f01601f191660200190565b600082601f830112611ce057600080fd5b8135611cf3611cee82611ca7565b611c76565b818152846020838601011115611d0857600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a08688031215611d3d57600080fd5b8535611d4881611c22565b94506020860135611d5881611c22565b9350604086013592506060860135611d6f81611c22565b9150608086013567ffffffffffffffff811115611d8b57600080fd5b611d9788828901611ccf565b9150509295509295909350565b600060208284031215611db657600080fd5b8135610bf581611c22565b600080600060608486031215611dd657600080fd5b8335611de181611c22565b925060208481013567ffffffffffffffff80821115611dff57600080fd5b818701915087601f830112611e1357600080fd5b813581811115611e2557611e25611c37565b8060051b9150611e36848301611c76565b818152918301840191848101908a841115611e5057600080fd5b938501935b83851015611e7a5784359250611e6a83611c22565b8282529385019390850190611e55565b979a979950505050604095909501359450505050565b60005b83811015611eab578181015183820152602001611e93565b50506000910152565b60008151808452611ecc816020860160208601611e90565b601f01601f19169290920160200192915050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015611f7357888303603f19018552815180516001600160a01b03908116855288820151168885015286810151878501526060808201519085015260809081015160a091850182905290611f5f81860183611eb4565b968901969450505090860190600101611f07565b509098975050505050505050565b600060a08284031215611f9357600080fd5b611f9b611c4d565b90508135611fa881611c22565b81526020820135611fb881611c22565b806020830152506040820135604082015260608201356060820152608082013567ffffffffffffffff811115611fed57600080fd5b611ff984828501611ccf565b60808301525092915050565b60008060008060008060c0878903121561201e57600080fd5b863561202981611c22565b955060208701359450604087013561204081611c22565b935060608701359250608087013567ffffffffffffffff8082111561206457600080fd5b6120708a838b01611f81565b935060a089013591508082111561208657600080fd5b5061209389828a01611f81565b9150509295509295509295565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208083850312156120fe57600080fd5b825167ffffffffffffffff8082111561211657600080fd5b9084019060a0828703121561212a57600080fd5b612132611c4d565b825161213d81611c22565b81528284015161214c81611c22565b808583015250604083015160408201526060830151606082015260808301518281111561217857600080fd5b80840193505086601f84011261218d57600080fd5b8251915061219d611cee83611ca7565b82815287858486010111156121b157600080fd5b6121c083868301878701611e90565b60808201529695505050505050565b634e487b7160e01b600052601160045260246000fd5b6000600182016121f7576121f76121cf565b5060010190565b815163ffffffff1681526020808301516001600160a01b03169082015260408083015161012083019161223b908401826001600160a01b03169052565b50606083015161225660608401826001600160a01b03169052565b50608083015161227160808401826001600160a01b03169052565b5060a083015160a083015260c083015160c083015260e083015161229960e084018215159052565b5061010092830151919092015290565b6001600160a01b0386811682528581166020830152604082018590528316606082015260a060808201819052600090611bde90830184611eb4565b6000602082840312156122f657600080fd5b5051919050565b81810381811115610c3357610c336121cf565b805160ff8116811461092a57600080fd5b60006080828403121561233357600080fd5b6040516080810181811067ffffffffffffffff8211171561235657612356611c37565b60405282516004811061236857600080fd5b8152602083015161237881611c22565b602082015261238960408401612310565b604082015261239a60608401612310565b60608201529392505050565b634e487b7160e01b600052602160045260246000fd5b6000602082840312156123ce57600080fd5b8151610bf581611c22565b80820180821115610c3357610c336121cf565b606080825284519082018190526000906020906080840190828801845b8281101561242557815184529284019290840190600101612409565b505050908301949094525060400152919050565b60006020828403121561244b57600080fd5b81518015158114610bf557600080fd5b600060ff821660ff8103612471576124716121cf565b60010192915050565b600080600080600080600060e0888a03121561249557600080fd5b875196506020880151955060408801519450606088015193506080880151925060a0880151915060c08801516124ca81611c22565b8091505092959891949750929550565b600082516124ec818460208701611e90565b9190910192915050565b602081526000610bf56020830184611eb456fea264697066735822122002e7078b2e5cf71cd4911e8534abe08a2af8cda697974436c5a189603d73e6dc64736f6c634300081100330000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3

Deployed Bytecode

0x6080604052600436106100a05760003560e01c80638da5cb5b116100645780638da5cb5b1461016157806390491b201461017f57806396d292d0146101ac578063bf03b1ea146101dd578063c2288147146101fd578063f2fde38b1461021057600080fd5b806324a98f11146100ac57806334474c8c146100d2578063715018a61461010a5780637673eb1514610121578063804b3dff1461014157600080fd5b366100a757005b600080fd5b6100bf6100ba366004611d25565b610230565b6040519081526020015b60405180910390f35b3480156100de57600080fd5b506002546100f2906001600160a01b031681565b6040516001600160a01b0390911681526020016100c9565b34801561011657600080fd5b5061011f610249565b005b34801561012d57600080fd5b506001546100f2906001600160a01b031681565b34801561014d57600080fd5b5061011f61015c366004611da4565b610288565b34801561016d57600080fd5b506000546001600160a01b03166100f2565b34801561018b57600080fd5b5061019f61019a366004611dc1565b610307565b6040516100c99190611ee0565b3480156101b857600080fd5b506101c4601560f91b81565b6040516001600160f81b031990911681526020016100c9565b3480156101e957600080fd5b5061011f6101f8366004611da4565b610513565b61011f61020b366004612005565b61058b565b34801561021c57600080fd5b5061011f61022b366004611da4565b610783565b600061023f868686868661081e565b9695505050505050565b6000546001600160a01b0316331461027c5760405162461bcd60e51b8152600401610273906120a0565b60405180910390fd5b61028660006108a7565b565b6000546001600160a01b031633146102b25760405162461bcd60e51b8152600401610273906120a0565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f6629f03aaaa02addcc840bd2d3deb4213bcc44a19f3e6601beb3bfcea9528d51906020015b60405180910390a150565b81516060908067ffffffffffffffff81111561032557610325611c37565b60405190808252806020026020018201604052801561039a57816020015b6103876040518060a0016040528060006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001606081525090565b8152602001906001900390816103435790505b50915060005b8181101561050a57600254604080518082019091526001600160a01b039091169063e6b0000990806000198152602001896001600160a01b03168152508784815181106103ef576103ef6120d5565b6020908102919091018101516040516001600160e01b031960e086901b1681528351600482015292909101516001600160a01b03908116602484015216604482015260648101879052608401600060405180830381865afa158015610458573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261048091908101906120eb565b838281518110610492576104926120d5565b60200260200101819052506104c98382815181106104b2576104b26120d5565b6020026020010151516001600160a01b0316151590565b156104fa57308382815181106104e1576104e16120d5565b60209081029190910101516001600160a01b0390911690525b610503816121e5565b90506103a0565b50509392505050565b6000546001600160a01b0316331461053d5760405162461bcd60e51b8152600401610273906120a0565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f214d83ceb705cb04d7ef7d2fc0dc6e40a0379fff652234e5d1b302ecac39bc53906020016102fc565b600061059a82608001516108f7565b90506001600160a01b0381166105c357604051632b68e6a360e01b815260040160405180910390fd5b82516001600160a01b0316156105e9576105df3086868661092f565b9095509350610641565b826060015142111561060e5760405163559895a360e01b815260040160405180910390fd5b8260400151841015610633576040516342301c2360e01b815260040160405180910390fd5b61063e308686610a7a565b93505b60006040518061012001604052808863ffffffff168152602001836001600160a01b03168152602001896001600160a01b03168152602001876001600160a01b0316815260200184602001516001600160a01b03168152602001868152602001846040015181526020016106b88560800151610bfc565b1515815260608501516020909101526001549091506106e4906001600160a01b03888116911687610c39565b60006001600160a01b03871673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610711576000610713565b855b60015460405163116145a560e21b81529192506001600160a01b03169063458516949083906107469086906004016121fe565b6000604051808303818588803b15801561075f57600080fd5b505af1158015610773573d6000803e3d6000fd5b5050505050505050505050505050565b6000546001600160a01b031633146107ad5760405162461bcd60e51b8152600401610273906120a0565b6001600160a01b0381166108125760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610273565b61081b816108a7565b50565b60008061082c868585610d1d565b905061083a86868684610dd6565b95506000610849878684610e43565b905061085787878385610e7e565b925073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0386160161088857610888818461100c565b61089c6001600160a01b038616898561106a565b505095945050505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000601582511061090c5750602181015160601c5b6001600160a01b0381161580156109225750333b155b1561092a5750335b919050565b60008082606001514211156109575760405163559895a360e01b815260040160405180910390fd5b8251610964908686610a7a565b602084015184519195509250306001600160a01b038216036109985761099187878786886080015161081e565b9150610a4b565b6109ab6001600160a01b03841688611156565b9150806001600160a01b03166324a98f1134898989888a608001516040518763ffffffff1660e01b81526004016109e69594939291906122a9565b60206040518083038185885af1158015610a04573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610a2991906122e4565b5081610a3e6001600160a01b03851689611156565b610a4891906122fd565b91505b8360400151821015610a70576040516342301c2360e01b815260040160405180910390fd5b5094509492505050565b600034600003610b9557610a96836001600160a01b03166111f8565b6040516370a0823160e01b81526001600160a01b0385811660048301528416906370a0823190602401602060405180830381865afa158015610adc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0091906122e4565b9050610b176001600160a01b038416338685611261565b6040516370a0823160e01b81526001600160a01b0385811660048301528291908516906370a0823190602401602060405180830381865afa158015610b60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8491906122e4565b610b8e91906122fd565b9050610bf5565b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14610bd257604051632eac7efb60e01b815260040160405180910390fd5b348214610bf2576040516381de0bf360e01b815260040160405180910390fd5b50345b9392505050565b6000808251118015610c3357508151601560f91b908390600090610c2257610c226120d5565b01602001516001600160f81b031916145b92915050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601610c6357505050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015610cb3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cd791906122e4565b9050818110610ce65750505050565b8015610d0157610d016001600160a01b0385168460006112cc565b610d176001600160a01b038516846000196112cc565b50505050565b604080516080810182526000808252602082018190529181018290526060810191909152826001600160a01b0316846001600160a01b031603610d7357604051630b839a1f60e01b815260040160405180910390fd5b81806020019051810190610d879190612321565b60208101519091506001600160a01b0316158015610db85750600381516003811115610db557610db56123a6565b14155b15610bf5576040516301dbb3ff60e61b815260040160405180910390fd5b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03861601610e1a57610e09838360016113e1565b9050610e15818561148e565b610e3b565b50833415610e3b576040516381de0bf360e01b815260040160405180910390fd5b949350505050565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601610e7657610b8e848360006113e1565b509092915050565b6000600382516003811115610e9557610e956123a6565b03610ea1575082610e3b565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015610ee5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0991906122e4565b6020830151909150610f26906001600160a01b0387169086611506565b600082516003811115610f3b57610f3b6123a6565b03610f5557610f5082602001518386866115b8565b610f8f565b600182516003811115610f6a57610f6a6123a6565b03610f7f57610f5082602001518386866116f1565b610f8f8260200151838686611829565b6040516370a0823160e01b815230600482015281906001600160a01b038516906370a0823190602401602060405180830381865afa158015610fd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ff991906122e4565b61100391906122fd565b95945050505050565b604051632e1a7d4d60e01b8152600481018290526001600160a01b03831690632e1a7d4d90602401600060405180830381600087803b15801561104e57600080fd5b505af1158015611062573d6000803e3d6000fd5b505050505050565b306001600160a01b0383160361107f57505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161113d576000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146110f1576040519150601f19603f3d011682016040523d82523d6000602084013e6110f6565b606091505b5050905080610d175760405162461bcd60e51b8152602060048201526013602482015272115512081d1c985b9cd9995c8819985a5b1959606a1b6044820152606401610273565b6111516001600160a01b0384168383611910565b505050565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161118e57506001600160a01b03811631610c33565b6040516370a0823160e01b81526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa1580156111d4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf591906122e4565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0382160161123657604051630fea47fd60e31b815260040160405180910390fd5b806001600160a01b03163b60000361081b57604051630fea47fd60e31b815260040160405180910390fd5b6040516001600160a01b0380851660248301528316604482015260648101829052610d179085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611940565b8015806113465750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015611320573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061134491906122e4565b155b6113b15760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610273565b6040516001600160a01b03831660248201526044810182905261115190849063095ea7b360e01b90606401611295565b60006003835160038111156113f8576113f86123a6565b03611404575082610bf5565b82602001516001600160a01b03166382b866008361142657846060015161142c565b84604001515b6040516001600160e01b031960e084901b16815260ff9091166004820152602401602060405180830381865afa15801561146a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e3b91906123bc565b3481146114ae576040516381de0bf360e01b815260040160405180910390fd5b816001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114e957600080fd5b505af11580156114fd573d6000803e3d6000fd5b50505050505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa158015611557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157b91906122e4565b61158591906123d9565b6040516001600160a01b038516602482015260448101829052909150610d1790859063095ea7b360e01b90606401611295565b606083015160405162415c3360e91b815260ff90911660048201526001600160a01b0382811691908616906382b8660090602401602060405180830381865afa158015611609573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162d91906123bc565b6001600160a01b03161461165457604051631438b5c960e11b815260040160405180910390fd5b604080840151606085015191516348b4aac360e11b815260ff918216600482015291166024820152604481018390526000606482015260001960848201526001600160a01b0385169063916955869060a4015b6020604051808303816000875af11580156116c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116ea91906122e4565b5050505050565b60006116fc85611a12565b9050600061170986611a9e565b9050826001600160a01b0316816001600160a01b03161461173d57604051631438b5c960e11b815260040160405180910390fd5b60008267ffffffffffffffff81111561175857611758611c37565b604051908082528060200260200182016040528015611781578160200160208202803683370190505b5090508481876040015160ff168151811061179e5761179e6120d5565b6020908102919091010152604051634d49e87d60e01b81526001600160a01b03881690634d49e87d906117dc908490600090600019906004016123ec565b6020604051808303816000875af11580156117fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181f91906122e4565b5050505050505050565b606083015160405162415c3360e91b815260ff90911660048201526001600160a01b0382811691908616906382b8660090602401602060405180830381865afa15801561187a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061189e91906123bc565b6001600160a01b0316146118c557604051631438b5c960e11b815260040160405180910390fd5b60608301516040516301f1d0ab60e51b81526004810184905260ff90911660248201526000604482015260001960648201526001600160a01b03851690633e3a1560906084016116a7565b6040516001600160a01b03831660248201526044810182905261115190849063a9059cbb60e01b90606401611295565b6000611995826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611b0e9092919063ffffffff16565b80519091501561115157808060200190518101906119b39190612439565b6111515760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610273565b6000805b60405162415c3360e91b815260ff821660048201526001600160a01b038416906382b8660090602401602060405180830381865afa925050508015611a78575060408051601f3d908101601f19168201909252611a75918101906123bc565b60015b611a87578060ff169150611a98565b50611a918161245b565b9050611a16565b50919050565b6000816001600160a01b0316635fd65f0f6040518163ffffffff1660e01b815260040160e060405180830381865afa158015611ade573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b02919061247a565b98975050505050505050565b6060610e3b8484600085856001600160a01b0385163b611b705760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610273565b600080866001600160a01b03168587604051611b8c91906124da565b60006040518083038185875af1925050503d8060008114611bc9576040519150601f19603f3d011682016040523d82523d6000602084013e611bce565b606091505b5091509150611bde828286611be9565b979650505050505050565b60608315611bf8575081610bf5565b825115611c085782518084602001fd5b8160405162461bcd60e51b815260040161027391906124f6565b6001600160a01b038116811461081b57600080fd5b634e487b7160e01b600052604160045260246000fd5b60405160a0810167ffffffffffffffff81118282101715611c7057611c70611c37565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715611c9f57611c9f611c37565b604052919050565b600067ffffffffffffffff821115611cc157611cc1611c37565b50601f01601f191660200190565b600082601f830112611ce057600080fd5b8135611cf3611cee82611ca7565b611c76565b818152846020838601011115611d0857600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a08688031215611d3d57600080fd5b8535611d4881611c22565b94506020860135611d5881611c22565b9350604086013592506060860135611d6f81611c22565b9150608086013567ffffffffffffffff811115611d8b57600080fd5b611d9788828901611ccf565b9150509295509295909350565b600060208284031215611db657600080fd5b8135610bf581611c22565b600080600060608486031215611dd657600080fd5b8335611de181611c22565b925060208481013567ffffffffffffffff80821115611dff57600080fd5b818701915087601f830112611e1357600080fd5b813581811115611e2557611e25611c37565b8060051b9150611e36848301611c76565b818152918301840191848101908a841115611e5057600080fd5b938501935b83851015611e7a5784359250611e6a83611c22565b8282529385019390850190611e55565b979a979950505050604095909501359450505050565b60005b83811015611eab578181015183820152602001611e93565b50506000910152565b60008151808452611ecc816020860160208601611e90565b601f01601f19169290920160200192915050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015611f7357888303603f19018552815180516001600160a01b03908116855288820151168885015286810151878501526060808201519085015260809081015160a091850182905290611f5f81860183611eb4565b968901969450505090860190600101611f07565b509098975050505050505050565b600060a08284031215611f9357600080fd5b611f9b611c4d565b90508135611fa881611c22565b81526020820135611fb881611c22565b806020830152506040820135604082015260608201356060820152608082013567ffffffffffffffff811115611fed57600080fd5b611ff984828501611ccf565b60808301525092915050565b60008060008060008060c0878903121561201e57600080fd5b863561202981611c22565b955060208701359450604087013561204081611c22565b935060608701359250608087013567ffffffffffffffff8082111561206457600080fd5b6120708a838b01611f81565b935060a089013591508082111561208657600080fd5b5061209389828a01611f81565b9150509295509295509295565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060208083850312156120fe57600080fd5b825167ffffffffffffffff8082111561211657600080fd5b9084019060a0828703121561212a57600080fd5b612132611c4d565b825161213d81611c22565b81528284015161214c81611c22565b808583015250604083015160408201526060830151606082015260808301518281111561217857600080fd5b80840193505086601f84011261218d57600080fd5b8251915061219d611cee83611ca7565b82815287858486010111156121b157600080fd5b6121c083868301878701611e90565b60808201529695505050505050565b634e487b7160e01b600052601160045260246000fd5b6000600182016121f7576121f76121cf565b5060010190565b815163ffffffff1681526020808301516001600160a01b03169082015260408083015161012083019161223b908401826001600160a01b03169052565b50606083015161225660608401826001600160a01b03169052565b50608083015161227160808401826001600160a01b03169052565b5060a083015160a083015260c083015160c083015260e083015161229960e084018215159052565b5061010092830151919092015290565b6001600160a01b0386811682528581166020830152604082018590528316606082015260a060808201819052600090611bde90830184611eb4565b6000602082840312156122f657600080fd5b5051919050565b81810381811115610c3357610c336121cf565b805160ff8116811461092a57600080fd5b60006080828403121561233357600080fd5b6040516080810181811067ffffffffffffffff8211171561235657612356611c37565b60405282516004811061236857600080fd5b8152602083015161237881611c22565b602082015261238960408401612310565b604082015261239a60608401612310565b60608201529392505050565b634e487b7160e01b600052602160045260246000fd5b6000602082840312156123ce57600080fd5b8151610bf581611c22565b80820180821115610c3357610c336121cf565b606080825284519082018190526000906020906080840190828801845b8281101561242557815184529284019290840190600101612409565b505050908301949094525060400152919050565b60006020828403121561244b57600080fd5b81518015158114610bf557600080fd5b600060ff821660ff8103612471576124716121cf565b60010192915050565b600080600080600080600060e0888a03121561249557600080fd5b875196506020880151955060408801519450606088015193506080880151925060a0880151915060c08801516124ca81611c22565b8091505092959891949750929550565b600082516124ec818460208701611e90565b9190910192915050565b602081526000610bf56020830184611eb456fea264697066735822122002e7078b2e5cf71cd4911e8534abe08a2af8cda697974436c5a189603d73e6dc64736f6c63430008110033

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

0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3

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

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


Block Transaction Gas Used Reward
view all blocks sequenced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ 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.