ETH Price: $2,950.50 (-0.48%)

Contract

0x50c8E97fEb1e629196012b7CCCA4D4785Ed1eb1f

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Age:30D
Reset Filter

Transaction Hash
Block
From
To

There are no matching entries

> 10 Internal Transactions and > 10 Token Transfers found.

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
4247829372026-01-24 16:30:497 mins ago1769272249
0x50c8E97f...85Ed1eb1f
0.2 ETH
4247752302026-01-24 15:58:4039 mins ago1769270320
0x50c8E97f...85Ed1eb1f
0.26681158 ETH
4247752302026-01-24 15:58:4039 mins ago1769270320
0x50c8E97f...85Ed1eb1f
0.26681158 ETH
4247751452026-01-24 15:58:1939 mins ago1769270299
0x50c8E97f...85Ed1eb1f
0.34179946 ETH
4247751452026-01-24 15:58:1939 mins ago1769270299
0x50c8E97f...85Ed1eb1f
0.34179946 ETH
4247723852026-01-24 15:46:5051 mins ago1769269610
0x50c8E97f...85Ed1eb1f
0.0696257 ETH
4247723852026-01-24 15:46:5051 mins ago1769269610
0x50c8E97f...85Ed1eb1f
0.0696257 ETH
4247649322026-01-24 15:15:471 hr ago1769267747
0x50c8E97f...85Ed1eb1f
0.00106452 ETH
4247649322026-01-24 15:15:471 hr ago1769267747
0x50c8E97f...85Ed1eb1f
0.00106452 ETH
4247358212026-01-24 13:14:303 hrs ago1769260470
0x50c8E97f...85Ed1eb1f
0.00069063 ETH
4247358212026-01-24 13:14:303 hrs ago1769260470
0x50c8E97f...85Ed1eb1f
0.00069063 ETH
4247221912026-01-24 12:17:394 hrs ago1769257059
0x50c8E97f...85Ed1eb1f
0.7 ETH
4247125712026-01-24 11:37:345 hrs ago1769254654
0x50c8E97f...85Ed1eb1f
0.00024646 ETH
4247125712026-01-24 11:37:345 hrs ago1769254654
0x50c8E97f...85Ed1eb1f
0.00024646 ETH
4247096182026-01-24 11:25:175 hrs ago1769253917
0x50c8E97f...85Ed1eb1f
0.00742257 ETH
4247096182026-01-24 11:25:175 hrs ago1769253917
0x50c8E97f...85Ed1eb1f
0.00742257 ETH
4247015512026-01-24 10:51:395 hrs ago1769251899
0x50c8E97f...85Ed1eb1f
0.0001285 ETH
4247015512026-01-24 10:51:395 hrs ago1769251899
0x50c8E97f...85Ed1eb1f
0.0001285 ETH
4246890482026-01-24 9:59:326 hrs ago1769248772
0x50c8E97f...85Ed1eb1f
0.01245319 ETH
4246890482026-01-24 9:59:326 hrs ago1769248772
0x50c8E97f...85Ed1eb1f
0.01245319 ETH
4246664152026-01-24 8:25:108 hrs ago1769243110
0x50c8E97f...85Ed1eb1f
0.01296114 ETH
4246664152026-01-24 8:25:108 hrs ago1769243110
0x50c8E97f...85Ed1eb1f
0.01296114 ETH
4246575552026-01-24 7:48:158 hrs ago1769240895
0x50c8E97f...85Ed1eb1f
0.00188026 ETH
4246575552026-01-24 7:48:158 hrs ago1769240895
0x50c8E97f...85Ed1eb1f
0.00188026 ETH
4246506342026-01-24 7:19:249 hrs ago1769239164
0x50c8E97f...85Ed1eb1f
0.0018 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CamelotYakRouter

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
prague EvmVersion
//       ╟╗                                                                      ╔╬
//       ╞╬╬                                                                    ╬╠╬
//      ╔╣╬╬╬                                                                  ╠╠╠╠╦
//     ╬╬╬╬╬╩                                                                  ╘╠╠╠╠╬
//    ║╬╬╬╬╬                                                                    ╘╠╠╠╠╬
//    ╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬      ╒╬╬╬╬╬╬╬╜   ╠╠╬╬╬╬╬╬╬         ╠╬╬╬╬╬╬╬    ╬╬╬╬╬╬╬╬╠╠╠╠╠╠╠╠
//    ╙╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╕    ╬╬╬╬╬╬╬╜   ╣╠╠╬╬╬╬╬╬╬╬        ╠╬╬╬╬╬╬╬   ╬╬╬╬╬╬╬╬╬╠╠╠╠╠╠╠╩
//     ╙╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬  ╔╬╬╬╬╬╬╬    ╔╠╠╠╬╬╬╬╬╬╬╬        ╠╬╬╬╬╬╬╬ ╣╬╬╬╬╬╬╬╬╬╬╬╠╠╠╠╝╙
//               ╘╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬    ╒╠╠╠╬╠╬╩╬╬╬╬╬╬       ╠╬╬╬╬╬╬╬╣╬╬╬╬╬╬╬╙
//                 ╣╬╬╬╬╬╬╬╬╬╬╠╣     ╣╬╠╠╠╬╩ ╚╬╬╬╬╬╬      ╠╬╬╬╬╬╬╬╬╬╬╬╬╬╬
//                  ╣╬╬╬╬╬╬╬╬╬╣     ╣╬╠╠╠╬╬   ╣╬╬╬╬╬╬     ╠╬╬╬╬╬╬╬╬╬╬╬╬╬╬
//                   ╟╬╬╬╬╬╬╬╩      ╬╬╠╠╠╠╬╬╬╬╬╬╬╬╬╬╬     ╠╬╬╬╬╬╬╬╠╬╬╬╬╬╬╬
//                    ╬╬╬╬╬╬╬     ╒╬╬╠╠╬╠╠╬╬╬╬╬╬╬╬╬╬╬╬    ╠╬╬╬╬╬╬╬ ╣╬╬╬╬╬╬╬
//                    ╬╬╬╬╬╬╬     ╬╬╬╠╠╠╠╝╝╝╝╝╝╝╠╬╬╬╬╬╬   ╠╬╬╬╬╬╬╬  ╚╬╬╬╬╬╬╬╬
//                    ╬╬╬╬╬╬╬    ╣╬╬╬╬╠╠╩       ╘╬╬╬╬╬╬╬  ╠╬╬╬╬╬╬╬   ╙╬╬╬╬╬╬╬╬
//

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.30;
pragma experimental ABIEncoderV2;

import {IYakRouter, Offer, Query, FormattedOffer, Trade} from "contracts/interface/IYakRouter.sol";
import {IAdapter} from "contracts/interface/IAdapter.sol";
import {IAlgebraV2Adapter} from "contracts/interface/IAlgebraV2Adapter.sol";
import {IERC20} from "contracts/interface/IERC20.sol";
import {IWETH} from "contracts/interface/IWETH.sol";
import {SafeERC20} from "contracts/lib/SafeERC20.sol";
import {OfferUtils} from "contracts/lib/YakViewUtils.sol";
import {Recoverable} from "contracts/lib/Recoverable.sol";
import {SafeERC20} from "contracts/lib/SafeERC20.sol";

/**
 * @title CamelotYakRouter
 * @notice Main router contract for finding optimal swap paths and executing trades
 * @dev Inherits from Maintainable for access control and Recoverable for token recovery
 */
contract CamelotYakRouter is Recoverable, IYakRouter {
    using SafeERC20 for IERC20;
    using OfferUtils for Offer;

    /// @notice Fee denominator for basis point calculations (10000 = 100%)
    uint256 public constant FEE_DENOMINATOR = 1e4;
    /// @notice Storage slot length in bytes
    uint256 public constant SLOT_LENGTH = 32;
    /// @notice Address representing native currency (ETH)
    address public constant NATIVE = address(0);
    /// @notice Name of this router implementation
    string public constant NAME = "CamelotYakRouter";
    /// @notice Wrapped native token address (WETH)
    address public immutable WNATIVE;
    /// @notice Minimum fee in basis points
    uint256 public MIN_FEE = 0;
    /// @notice Address that receives protocol fees
    address public FEE_CLAIMER;
    /// @notice Array of trusted intermediate tokens for multi-hop swaps
    address[] public TRUSTED_TOKENS;
    /// @notice Array of DEX adapter contracts
    address[] public ADAPTERS;

    /**
     * @notice Ensures transaction is executed before deadline
     * @param _deadline Unix timestamp deadline
     */
    modifier withDeadline(uint256 _deadline) {
        if (_deadline < block.timestamp) revert DeadlinePassed(_deadline, block.timestamp);
        _;
    }

    /**
     * @notice Initializes the router with adapters and configuration
     * @param _adapters Initial array of adapter addresses
     * @param _trustedTokens Initial array of trusted token addresses
     * @param _feeClaimer Address to receive fees
     * @param _wrapped_native Address of wrapped native token (WETH)
     */
    constructor(
        address[] memory _adapters,
        address[] memory _trustedTokens,
        address _feeClaimer,
        address _wrapped_native
    ) {
        if (_feeClaimer == address(0) || _wrapped_native == address(0)) revert AddressZero();
        setTrustedTokens(_trustedTokens);
        setFeeClaimer(_feeClaimer);
        setAdapters(_adapters);
        WNATIVE = _wrapped_native;
    }

    // -- SETTERS --

    /// @inheritdoc IYakRouter
    function setTrustedTokens(address[] memory _trustedTokens) public override onlyMaintainer {
        TRUSTED_TOKENS = _trustedTokens;
        emit UpdatedTrustedTokens(_trustedTokens);
    }

    /// @inheritdoc IYakRouter
    function setAdapters(address[] memory _adapters) public override onlyMaintainer {
        ADAPTERS = _adapters;
        emit UpdatedAdapters(_adapters);
    }

    /// @inheritdoc IYakRouter
    function setMinFee(uint256 _fee) external override onlyMaintainer {
        if (_fee > FEE_DENOMINATOR / 10) revert FeeAboveMaximum(_fee, FEE_DENOMINATOR / 10);
        uint256 oldMinFee = MIN_FEE;
        MIN_FEE = _fee;
        emit UpdatedMinFee(oldMinFee, _fee);
    }

    /// @inheritdoc IYakRouter
    function setFeeClaimer(address _claimer) public override onlyMaintainer {
        if (_claimer == address(0)) revert AddressZero();
        address oldClaimer = FEE_CLAIMER;
        FEE_CLAIMER = _claimer;
        emit UpdatedFeeClaimer(oldClaimer, FEE_CLAIMER);
    }

    //  -- GENERAL --

    /// @inheritdoc IYakRouter
    function trustedTokensCount() external view override returns (uint256) {
        return TRUSTED_TOKENS.length;
    }

    /// @inheritdoc IYakRouter
    function adaptersCount() external view override returns (uint256) {
        return ADAPTERS.length;
    }

    /**
     * @notice Fallback function to receive ETH
     */
    receive() external payable {}

    // -- HELPERS --

    /**
     * @notice Applies fee deduction to input amount
     * @param _amountIn Input amount before fees
     * @param _fee Fee in basis points
     * @return Amount after fee deduction
     */
    function _applyFee(uint256 _amountIn, uint256 _fee) internal view returns (uint256) {
        if (_fee < MIN_FEE) revert InsufficientFee(_fee, MIN_FEE);
        return (_amountIn * (FEE_DENOMINATOR - _fee)) / FEE_DENOMINATOR;
    }

    /**
     * @notice Wraps native currency to WETH
     * @param _amount Amount of native currency to wrap
     */
    function _wrap(uint256 _amount) internal {
        IWETH(WNATIVE).deposit{value: _amount}();
    }

    /**
     * @notice Unwraps WETH to native currency
     * @param _amount Amount of WETH to unwrap
     */
    function _unwrap(uint256 _amount) internal {
        IWETH(WNATIVE).withdraw(_amount);
    }

    /**
     * @notice Returns tokens to specified address
     * @param _token Token address (use address(0) for ETH)
     * @param _amount Amount to transfer
     * @param _to Recipient address
     * @dev Handles both ERC20 tokens and native ETH transfers
     */
    function _returnTokensTo(address _token, uint256 _amount, address _to) internal {
        if (address(this) != _to) {
            if (_token == NATIVE) {
                (bool success,) = payable(_to).call{value: _amount}("");
                if (!success) revert ETHTransferFailed();
            } else {
                IERC20(_token).safeTransfer(_to, _amount);
            }
        }
    }

    /**
     * @notice Transfers tokens from source to destination
     * @param token Token address to transfer
     * @param _from Source address
     * @param _to Destination address
     * @param _amount Amount to transfer
     * @dev Uses transferFrom if source is not this contract
     */
    function _transferFrom(address token, address _from, address _to, uint256 _amount) internal {
        if (_from != address(this)) IERC20(token).safeTransferFrom(_from, _to, _amount);
        else IERC20(token).safeTransfer(_to, _amount);
    }

    // -- QUERIES --

    /// @inheritdoc IYakRouter
    function queryAdapter(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        uint8 _index,
        bool _exactIn
    ) external view override returns (uint256, address) {
        IAdapter _adapter = IAdapter(ADAPTERS[_index]);
        try IAdapter(_adapter).query(_amount, _tokenIn, _tokenOut, _exactIn) returns (
            uint256 quoteAmount, address _recipient
        ) {
            return (quoteAmount, _recipient);
        } catch {
            return (0, address(0));
        }
    }

    /// @inheritdoc IYakRouter
    function queryNoSplit(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        uint8[] calldata _options,
        bool _exactIn
    ) public view override returns (Query memory) {
        Query memory bestQuery;
        for (uint8 i; i < _options.length; ++i) {
            address _adapter = ADAPTERS[_options[i]];
            try IAdapter(_adapter).query(_amount, _tokenIn, _tokenOut, _exactIn) returns (
                uint256 quoteAmount, address _recipient
            ) {
                if (
                    i == 0 || _exactIn
                        ? quoteAmount > bestQuery.amount
                        : (
                            quoteAmount != 0
                                && (bestQuery.amount == 0 || quoteAmount < bestQuery.amount)
                        )
                ) bestQuery = Query(_adapter, _recipient, _tokenIn, _tokenOut, quoteAmount);
            } catch {
                continue;
            }
        }
        return bestQuery;
    }

    /// @inheritdoc IYakRouter
    function queryNoSplit(uint256 _amount, address _tokenIn, address _tokenOut, bool _exactIn)
        public
        view
        override
        returns (Query memory)
    {
        (Query memory bestQuery,) =
            _queryNoSplitWithDeployer(_amount, _tokenIn, _tokenOut, _exactIn);
        return bestQuery;
    }

    /**
     * @notice Internal function to query adapters and return deployer info
     * @param _amount Amount to swap
     * @param _tokenIn Input token address
     * @param _tokenOut Output token address
     * @param _exactIn True for exact input, false for exact output
     * @return bestQuery Best query result
     * @return bestDeployer Deployer address for AlgebraV2 custom pools
     */
    function _queryNoSplitWithDeployer(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        bool _exactIn
    ) internal view returns (Query memory bestQuery, address bestDeployer) {
        uint256 length = ADAPTERS.length;
        for (uint8 i; i < length; ++i) {
            address _adapter = ADAPTERS[i];

            // Try to get quote with deployer info from AlgebraV2Adapter
            try IAlgebraV2Adapter(_adapter).queryWithDeployer(
                _amount, _tokenIn, _tokenOut, _exactIn
            ) returns (uint256 quoteAmount, address _recipient, address deployer) {
                if (
                    i == 0 || _exactIn
                        ? quoteAmount > bestQuery.amount
                        : (
                            quoteAmount != 0
                                && (bestQuery.amount == 0 || quoteAmount < bestQuery.amount)
                        )
                ) {
                    bestQuery = Query(_adapter, _recipient, _tokenIn, _tokenOut, quoteAmount);
                    bestDeployer = deployer;
                }
                continue;
            } catch {}

            // Fallback to regular query for other adapters
            try IAdapter(_adapter).query(_amount, _tokenIn, _tokenOut, _exactIn) returns (
                uint256 quoteAmount, address _recipient
            ) {
                if (
                    i == 0 || _exactIn
                        ? quoteAmount > bestQuery.amount
                        : (
                            quoteAmount != 0
                                && (bestQuery.amount == 0 || quoteAmount < bestQuery.amount)
                        )
                ) {
                    bestQuery = Query(_adapter, _recipient, _tokenIn, _tokenOut, quoteAmount);
                    bestDeployer = address(0);
                }
            } catch {
                continue;
            }
        }
        return (bestQuery, bestDeployer);
    }

    /// @inheritdoc IYakRouter
    function findBestPath(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        address[] memory _trustedTokens,
        uint256 _maxSteps,
        bool _exactIn
    ) external view override returns (FormattedOffer memory) {
        if (_maxSteps == 0 || _maxSteps >= 5) revert InvalidMaxSteps(_maxSteps);
        Offer memory queries = OfferUtils.newOffer(_amount, _exactIn ? _tokenIn : _tokenOut);

        uint256 ttLength = TRUSTED_TOKENS.length;
        // Concatenate default and additional trusted tokens
        address[] memory _allTrustedTokens = new address[](ttLength + _trustedTokens.length);
        for (uint256 i; i < ttLength; ++i) {
            _allTrustedTokens[i] = TRUSTED_TOKENS[i];
        }
        for (uint256 i; i < _trustedTokens.length; ++i) {
            _allTrustedTokens[ttLength + i] = _trustedTokens[i];
        }

        // Initialize empty array to track used pools
        bytes32[] memory usedPools = new bytes32[](0);
        queries = _findBestPath(
            _amount, _tokenIn, _tokenOut, _allTrustedTokens, _maxSteps, queries, _exactIn, usedPools
        );
        // If no paths are found return empty struct
        if (queries.adapters.length == 0) {
            queries.amounts = "";
            queries.path = "";
        }
        return queries.format();
    }

    /**
     * @notice Internal recursive function to find optimal swap path
     * @param _amount Amount to swap
     * @param _tokenIn Input token address
     * @param _tokenOut Output token address
     * @param _trustedTokens Array of intermediate tokens to consider
     * @param _maxSteps Maximum hops allowed
     * @param _queries Current path being built
     * @param _exactIn True for exact input, false for exact output
     * @param _usedPools Array of pool hashes already used in the path
     * @return Optimal swap path offer
     * @dev Recursively explores paths through trusted tokens, avoiding duplicate pools
     */
    function _findBestPath(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        address[] memory _trustedTokens,
        uint256 _maxSteps,
        Offer memory _queries,
        bool _exactIn,
        bytes32[] memory _usedPools
    ) internal view returns (Offer memory) {
        Offer memory bestOption = _queries.clone();
        uint256 bestAmount;

        // First check if there is a path directly from tokenIn to tokenOut
        (Query memory queryDirect, address deployer) =
            _queryNoSplitWithDeployer(_amount, _tokenIn, _tokenOut, _exactIn);

        if (queryDirect.amount > 0) {
            // Check if this pool has already been used
            bytes32 poolHash = _getPoolHash(queryDirect.adapter, _tokenIn, _tokenOut, deployer);
            if (!_isPoolUsed(poolHash, _usedPools)) {
                if (_exactIn) {
                    bestOption.addToTail(
                        queryDirect.amount,
                        queryDirect.adapter,
                        queryDirect.recipient,
                        queryDirect.tokenOut,
                        deployer
                    );
                } else {
                    // For exactOut: when adding to head, we add the input token since path goes
                    // tokenIn
                    // -> tokenOut
                    bestOption.addToHead(
                        queryDirect.amount,
                        queryDirect.adapter,
                        queryDirect.recipient,
                        queryDirect.tokenIn,
                        deployer
                    );
                }
                bestAmount = queryDirect.amount;
            }
        }

        // Only check the rest if they would go beyond step limit (Need at least 2 more steps)
        if (_maxSteps > 1 && _queries.adapters.length / SLOT_LENGTH <= _maxSteps - 2) {
            // Check for paths that pass through trusted tokens
            for (uint256 i; i < _trustedTokens.length; ++i) {
                if (_exactIn ? (_tokenIn == _trustedTokens[i]) : (_tokenOut == _trustedTokens[i])) {
                    continue;
                }

                Query memory bestSwap;
                uint256 swapAmount;

                address swapDeployer;
                if (_exactIn) {
                    // For exactIn: find swap from tokenIn to trusted token
                    (bestSwap, swapDeployer) =
                        _queryNoSplitWithDeployer(_amount, _tokenIn, _trustedTokens[i], _exactIn);
                    swapAmount = bestSwap.amount;
                } else {
                    // For exactOut: find swap from trusted token to tokenOut
                    (bestSwap, swapDeployer) =
                        _queryNoSplitWithDeployer(_amount, _trustedTokens[i], _tokenOut, _exactIn);
                    swapAmount = bestSwap.amount;
                }

                if (swapAmount == 0) continue;

                // Check if this pool has already been used
                bytes32 poolHash;
                if (_exactIn) {
                    poolHash =
                        _getPoolHash(bestSwap.adapter, _tokenIn, _trustedTokens[i], swapDeployer);
                } else {
                    poolHash =
                        _getPoolHash(bestSwap.adapter, _trustedTokens[i], _tokenOut, swapDeployer);
                }

                if (_isPoolUsed(poolHash, _usedPools)) continue;

                // Explore options that connect the current path
                Offer memory newOffer = _queries.clone();

                // Add this pool to the used pools for the recursive call
                bytes32[] memory newUsedPools = _addUsedPool(poolHash, _usedPools);

                if (_exactIn) {
                    // For exactIn: add first hop to tail, then recurse forward
                    newOffer.addToTail(
                        swapAmount,
                        bestSwap.adapter,
                        bestSwap.recipient,
                        bestSwap.tokenOut,
                        swapDeployer
                    );
                    newOffer = _findBestPath(
                        swapAmount,
                        _trustedTokens[i],
                        _tokenOut,
                        _trustedTokens,
                        _maxSteps,
                        newOffer,
                        _exactIn,
                        newUsedPools
                    );
                } else {
                    // For exactOut: add last hop to head, then recurse backward
                    // When building exactOut path, we need to add the tokenIn from the perspective
                    // of the swap direction (trustedToken -> tokenOut means trustedToken is
                    // tokenIn)
                    newOffer.addToHead(
                        swapAmount,
                        bestSwap.adapter,
                        bestSwap.recipient,
                        bestSwap.tokenIn,
                        swapDeployer
                    );
                    newOffer = _findBestPath(
                        swapAmount,
                        _tokenIn,
                        _trustedTokens[i],
                        _trustedTokens,
                        _maxSteps,
                        newOffer,
                        _exactIn,
                        newUsedPools
                    );
                }

                bool validPath = _exactIn
                    ? (_tokenOut == newOffer.getTokenOut())
                    : (_tokenIn == newOffer.getTokenIn());

                if (validPath) {
                    uint256 pathAmount = _exactIn
                        ? newOffer.getAmountOut() // Final output amount
                        : newOffer.getAmountIn(); // First input amount

                    bool isBetter = (bestAmount == 0)
                        || (_exactIn ? pathAmount > bestAmount : pathAmount < bestAmount);

                    if (isBetter) {
                        bestAmount = pathAmount;
                        bestOption = newOffer;
                    }
                }
            }
        }
        return bestOption;
    }

    /**
     * @notice Generates a unique hash for a pool based on adapter, tokens, and deployer
     * @param _adapter Address of the adapter
     * @param _tokenA First token in the pair
     * @param _tokenB Second token in the pair
     * @param _deployer Deployer address (address(0) for default pools)
     * @return Hash identifying the pool uniquely
     */
    function _getPoolHash(address _adapter, address _tokenA, address _tokenB, address _deployer)
        internal
        pure
        returns (bytes32)
    {
        // Sort tokens to ensure consistent hashing regardless of order
        (address token0, address token1) =
            _tokenA < _tokenB ? (_tokenA, _tokenB) : (_tokenB, _tokenA);
        return keccak256(abi.encodePacked(_adapter, token0, token1, _deployer));
    }

    /**
     * @notice Checks if a pool hash exists in the used pools array
     * @param _poolHash Hash of the pool to check
     * @param _usedPools Array of already used pool hashes
     * @return True if the pool has been used, false otherwise
     */
    function _isPoolUsed(bytes32 _poolHash, bytes32[] memory _usedPools)
        internal
        pure
        returns (bool)
    {
        for (uint256 i = 0; i < _usedPools.length; i++) {
            if (_usedPools[i] == _poolHash) return true;
        }
        return false;
    }

    /**
     * @notice Adds a pool hash to the used pools array
     * @param _poolHash Hash of the pool to add
     * @param _usedPools Current array of used pools
     * @return New array with the pool hash added
     */
    function _addUsedPool(bytes32 _poolHash, bytes32[] memory _usedPools)
        internal
        pure
        returns (bytes32[] memory)
    {
        bytes32[] memory newUsedPools = new bytes32[](_usedPools.length + 1);
        for (uint256 i = 0; i < _usedPools.length; i++) {
            newUsedPools[i] = _usedPools[i];
        }
        newUsedPools[_usedPools.length] = _poolHash;
        return newUsedPools;
    }

    // -- SWAPPERS --

    /**
     * @notice Internal function that executes the swap
     * @param _trade Trade parameters
     * @param _from Address tokens are swapped from
     * @param _fee Fee amount in basis points
     * @param _to Address to receive output tokens
     * @return amountOut Final output amount
     * @dev Handles fee collection and multi-hop execution
     */
    function _swapNoSplit(Trade calldata _trade, address _from, uint256 _fee, address _to)
        internal
        returns (uint256)
    {
        if (
            (_trade.path.length - 1 != _trade.adapters.length)
                || (_trade.recipients.length != _trade.adapters.length)
                || (_trade.deployers.length > 0 && _trade.deployers.length != _trade.adapters.length)
        ) revert TradeLengthDoesNotMatch();

        uint256 amountIn = _trade.amountIn;
        if (_fee > 0 || MIN_FEE > 0) {
            // Transfer fees to the claimer account and decrease initial amount
            amountIn = _applyFee(_trade.amountIn, _fee);
            _transferFrom(_trade.path[0], _from, FEE_CLAIMER, _trade.amountIn - amountIn);
        }
        uint256 recipientBalanceBefore = IERC20(_trade.path[0]).balanceOf(_trade.recipients[0]);
        _transferFrom(_trade.path[0], _from, _trade.recipients[0], amountIn);
        amountIn = IERC20(_trade.path[0]).balanceOf(_trade.recipients[0]) - recipientBalanceBefore;

        address tokenOut = _trade.path[_trade.path.length - 1];

        uint256 length = _trade.adapters.length;
        for (uint256 i; i < length; ++i) {
            // All adapters should transfer output token to the following target
            // All targets are the adapters, expect for the last swap where tokens are sent out
            address targetAddress = i < length - 1 ? _trade.recipients[i + 1] : _to;

            recipientBalanceBefore = IERC20(_trade.path[i + 1]).balanceOf(targetAddress);

            // Check if we have a deployer for this step (for AlgebraV2Adapter custom pools)
            if (_trade.deployers.length > 0 && _trade.deployers[i] != address(0)) {
                // Try to call the overloaded swap function with deployer parameter
                // (AlgebraV2Adapter)
                try IAlgebraV2Adapter(_trade.adapters[i]).swap(
                    amountIn,
                    0,
                    _trade.path[i],
                    _trade.path[i + 1],
                    targetAddress,
                    _trade.deployers[i]
                ) {} catch {
                    // If the adapter doesn't support the deployer parameter, fall back to regular
                    // swap
                    IAdapter(_trade.adapters[i]).swap(
                        amountIn, 0, _trade.path[i], _trade.path[i + 1], targetAddress
                    );
                }
            } else {
                // Regular swap without deployer
                IAdapter(_trade.adapters[i]).swap(
                    amountIn, 0, _trade.path[i], _trade.path[i + 1], targetAddress
                );
            }
            amountIn = IERC20(_trade.path[i + 1]).balanceOf(targetAddress) - recipientBalanceBefore;
        }
        uint256 amountOut = amountIn;
        if (amountOut < _trade.amountOut) {
            revert InsufficientOutputAmount(amountOut, _trade.amountOut);
        }
        emit YakSwap(_trade.path[0], tokenOut, _trade.amountIn, amountOut);
        return amountOut;
    }

    /// @inheritdoc IYakRouter
    function swapNoSplit(Trade calldata _trade, uint256 _fee, uint256 _deadline, address _to)
        public
        override
        withDeadline(_deadline)
    {
        _swapNoSplit(_trade, msg.sender, _fee, _to);
    }

    /// @inheritdoc IYakRouter
    function swapNoSplitFromETH(Trade calldata _trade, uint256 _fee, uint256 _deadline, address _to)
        external
        payable
        override
        withDeadline(_deadline)
    {
        if (msg.value != _trade.amountIn) revert IncorrectETHAmount(msg.value, _trade.amountIn);
        if (_trade.path[0] != WNATIVE) revert PathDoesNotBeginWithWETH(_trade.path[0]);
        _wrap(_trade.amountIn);
        _swapNoSplit(_trade, address(this), _fee, _to);
    }

    /// @inheritdoc IYakRouter
    function swapNoSplitToETH(Trade calldata _trade, uint256 _fee, uint256 _deadline, address _to)
        public
        override
        withDeadline(_deadline)
    {
        if (_trade.path[_trade.path.length - 1] != WNATIVE) {
            revert PathDoesNotEndWithWETH(_trade.path[_trade.path.length - 1]);
        }
        uint256 returnAmount = _swapNoSplit(_trade, msg.sender, _fee, address(this));
        _unwrap(returnAmount);
        _returnTokensTo(NATIVE, returnAmount, _to);
    }

    /// @inheritdoc IYakRouter
    function swapNoSplitWithPermit(
        Trade calldata _trade,
        uint256 _fee,
        uint256 _deadline,
        address _to,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
    ) external override {
        try IERC20(_trade.path[0]).permit(
            msg.sender, address(this), _trade.amountIn, _deadline, _v, _r, _s
        ) {
            swapNoSplit(_trade, _fee, _deadline, _to);
        } catch {
            swapNoSplit(_trade, _fee, _deadline, _to);
        }
    }

    /// @inheritdoc IYakRouter
    function swapNoSplitToETHWithPermit(
        Trade calldata _trade,
        uint256 _fee,
        uint256 _deadline,
        address _to,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
    ) external override {
        try IERC20(_trade.path[0]).permit(
            msg.sender, address(this), _trade.amountIn, _deadline, _v, _r, _s
        ) {
            swapNoSplitToETH(_trade, _fee, _deadline, _to);
        } catch {
            swapNoSplitToETH(_trade, _fee, _deadline, _to);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @title IAdapter
 * @author Yak Exchange
 * @notice Interface for DEX adapter contracts that integrate various AMMs
 * @dev Each adapter implements swap functionality for a specific DEX protocol
 */
interface IAdapter {
    /**
     * @notice Returns the name of the adapter
     * @return name The adapter's descriptive name (e.g., "UniswapV2Adapter")
     */
    function name() external view returns (string memory);

    /**
     * @notice Executes a token swap through the adapter's underlying DEX
     * @param _amountIn Amount of input tokens to swap
     * @param _amountOut Minimum amount of output tokens expected
     * @param _fromToken Address of the input token
     * @param _toToken Address of the output token
     * @param _to Address that will receive the output tokens
     * @dev Adapter must ensure _to receives at least _amountOut tokens
     */
    function swap(uint256 _amountIn, uint256 _amountOut, address _fromToken, address _toToken, address _to) external;

    /**
     * @notice Queries the adapter for expected swap output/input amount
     * @param _amount Input amount (if exactIn) or output amount (if !exactIn)
     * @param _tokenIn Address of the input token
     * @param _tokenOut Address of the output token
     * @param _exactIn True for exact input quote, false for exact output quote
     * @return amountOut The output amount (if exactIn) or required input amount (if !exactIn)
     * @return recipient The address that should receive tokens for this adapter
     * @dev Should return (0, address(0)) if swap is not available
     */
    function query(uint256 _amount, address _tokenIn, address _tokenOut, bool _exactIn) 
        external 
        view 
        returns (uint256 amountOut, address recipient);
}

// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.0;

interface IAlgebraV2Adapter {
    /// @dev Output amount is lower than expected amount out
    error InsufficientAmount();

    /// @dev Thrown when the caller is not the pool address
    error NotPoolAddress();

    /// @dev Event emitted when the quoter is set
    event QuoterSet(address quoter);

    /// @dev Event emitted when an invalid quoter gas limit is set
    error InvalidGasLimit();

    /// @dev Event emitted when the quoter gas limit is updated
    event QuoterGasLimitSet(uint256 newGasLimit);

    /// @dev Parameters struct for a quote
    struct QParams {
        address tokenIn;
        address tokenOut;
        int256 amount;
        bool exactIn;
    }

    /// @notice Returns an address for a given key and index combination
    /// @param key The keccak256 key generated by encoding tokenA and tokenB
    /// @param index The index of the array to retrieve the address from
    function deployers(bytes32 key, uint256 index) external view returns (address);

    /// @notice Returns list of whitelisted deployers for a given tokenA/tokenB combination
    /// @param key Keccack256 hash of tokenA and tokenB encoded addresses
    function getDeployers(bytes32 key) external view returns (address[] memory);

    /// @notice Whitelists a custom deployer for a given tokenA and tokenB combination
    /// @param deployer Address of deployer of pool
    /// @param tokenA The address of tokenA
    /// @param tokenB The address of tokenB
    function addCustomDeployer(address deployer, address tokenA, address tokenB) external;

    /// @notice Removes a whitelisted custom deployer for a given tokenA and tokenB combination
    /// @param tokenA The address of tokenA
    /// @param tokenB The address of tokenB
    /// @param index Index in the list of whitelisted deployers for the given pool
    function removeCustomDeployer(address tokenA, address tokenB, uint256 index) external;

    /// @notice Swaps tokens using a specific deployer's pool
    /// @param amountIn Amount of input tokens
    /// @param amountOut Minimum amount of output tokens expected
    /// @param tokenIn Address of input token
    /// @param tokenOut Address of output token
    /// @param to Address to receive output tokens
    /// @param deployer Address of the deployer for custom pool (address(0) for factory pool)
    function swap(
        uint256 amountIn,
        uint256 amountOut,
        address tokenIn,
        address tokenOut,
        address to,
        address deployer
    ) external;

    /// @notice Queries the adapter for swap quote and returns the deployer info
    /// @param amount Input amount (if exactIn) or output amount (if !exactIn)
    /// @param tokenIn Address of the input token
    /// @param tokenOut Address of the output token
    /// @param exactIn True for exact input quote, false for exact output quote
    /// @return quoteAmount The output amount (if exactIn) or required input amount (if !exactIn)
    /// @return recipient The address that should receive tokens for this adapter
    /// @return deployer The deployer address for custom pool (address(0) for factory pool)
    function queryWithDeployer(uint256 amount, address tokenIn, address tokenOut, bool exactIn)
        external
        view
        returns (uint256 quoteAmount, address recipient, address deployer);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20 {
    event Approval(address, address, uint256);
    event Transfer(address, address, uint256);

    function name() external view returns (string memory);

    function decimals() external view returns (uint8);

    function transferFrom(
        address,
        address,
        uint256
    ) external returns (bool);

    function allowance(address, address) external view returns (uint256);

    function approve(address, uint256) external returns (bool);

    function transfer(address, uint256) external returns (bool);

    function balanceOf(address) external view returns (uint256);

    function nonces(address) external view returns (uint256); // Only tokens that support permit

    function permit(
        address,
        address,
        uint256,
        uint256,
        uint8,
        bytes32,
        bytes32
    ) external; // Only tokens that support permit

    function swap(address, uint256) external; // Only Avalanche bridge tokens

    function swapSupply(address) external view returns (uint256); // Only Avalanche bridge tokens

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "contracts/interface/IERC20.sol";

interface IWETH is IERC20 {
    function withdraw(uint256 amount) external;

    function deposit() external payable;
}

//       ╟╗                                                                      ╔╬
//       ╞╬╬                                                                    ╬╠╬
//      ╔╣╬╬╬                                                                  ╠╠╠╠╦
//     ╬╬╬╬╬╩                                                                  ╘╠╠╠╠╬
//    ║╬╬╬╬╬                                                                    ╘╠╠╠╠╬
//    ╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬      ╒╬╬╬╬╬╬╬╜   ╠╠╬╬╬╬╬╬╬         ╠╬╬╬╬╬╬╬    ╬╬╬╬╬╬╬╬╠╠╠╠╠╠╠╠
//    ╙╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╕    ╬╬╬╬╬╬╬╜   ╣╠╠╬╬╬╬╬╬╬╬        ╠╬╬╬╬╬╬╬   ╬╬╬╬╬╬╬╬╬╠╠╠╠╠╠╠╩
//     ╙╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬  ╔╬╬╬╬╬╬╬    ╔╠╠╠╬╬╬╬╬╬╬╬        ╠╬╬╬╬╬╬╬ ╣╬╬╬╬╬╬╬╬╬╬╬╠╠╠╠╝╙
//               ╘╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬    ╒╠╠╠╬╠╬╩╬╬╬╬╬╬       ╠╬╬╬╬╬╬╬╣╬╬╬╬╬╬╬╙
//                 ╣╬╬╬╬╬╬╬╬╬╬╠╣     ╣╬╠╠╠╬╩ ╚╬╬╬╬╬╬      ╠╬╬╬╬╬╬╬╬╬╬╬╬╬╬
//                  ╣╬╬╬╬╬╬╬╬╬╣     ╣╬╠╠╠╬╬   ╣╬╬╬╬╬╬     ╠╬╬╬╬╬╬╬╬╬╬╬╬╬╬
//                   ╟╬╬╬╬╬╬╬╩      ╬╬╠╠╠╠╬╬╬╬╬╬╬╬╬╬╬     ╠╬╬╬╬╬╬╬╠╬╬╬╬╬╬╬
//                    ╬╬╬╬╬╬╬     ╒╬╬╠╠╬╠╠╬╬╬╬╬╬╬╬╬╬╬╬    ╠╬╬╬╬╬╬╬ ╣╬╬╬╬╬╬╬
//                    ╬╬╬╬╬╬╬     ╬╬╬╠╠╠╠╝╝╝╝╝╝╝╠╬╬╬╬╬╬   ╠╬╬╬╬╬╬╬  ╚╬╬╬╬╬╬╬╬
//                    ╬╬╬╬╬╬╬    ╣╬╬╬╬╠╠╩       ╘╬╬╬╬╬╬╬  ╠╬╬╬╬╬╬╬   ╙╬╬╬╬╬╬╬╬
//

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @notice Query struct containing adapter and swap amount information for a single hop
 * @param adapter Address of the adapter contract to use for the swap
 * @param recipient Address that will receive tokens from the swap
 * @param tokenIn Address of the input token
 * @param tokenOut Address of the output token
 * @param amount Amount of tokens (input or output depending on swap type)
 */
struct Query {
    address adapter;
    address recipient;
    address tokenIn;
    address tokenOut;
    uint256 amount;
}

/**
 * @notice Compressed offer data using bytes encoding for gas efficiency
 * @param amounts Encoded swap amounts for each hop
 * @param adapters Encoded adapter addresses for each hop
 * @param path Encoded token path for the swap route
 * @param recipients Encoded recipient addresses for each hop
 * @param deployers Encoded deployer addresses for AlgebraV2 custom pools (address(0) for other
 * adapters)
 */
struct Offer {
    bytes amounts;
    bytes adapters;
    bytes path;
    bytes recipients;
    bytes deployers;
}

/**
 * @notice Formatted offer with decoded arrays for easier consumption
 * @param amounts Array of swap amounts for each hop
 * @param adapters Array of adapter addresses for each hop
 * @param path Array of token addresses defining the swap route
 * @param recipients Array of recipient addresses for each hop
 * @param deployers Array of deployer addresses for AlgebraV2 custom pools (address(0) for other
 * adapters)
 */
struct FormattedOffer {
    uint256[] amounts;
    address[] adapters;
    address[] path;
    address[] recipients;
    address[] deployers;
}

/**
 * @notice Trade parameters for executing a swap
 * @param amountIn Amount of input tokens to swap
 * @param amountOut Minimum amount of output tokens expected
 * @param path Array of token addresses defining the swap route
 * @param adapters Array of adapter addresses for each hop
 * @param recipients Array of recipient addresses for each hop
 * @param deployers Array of deployer addresses for AlgebraV2 custom pools (address(0) for other
 * adapters)
 */
struct Trade {
    uint256 amountIn;
    uint256 amountOut;
    address[] path;
    address[] adapters;
    address[] recipients;
    address[] deployers;
}

/**
 * @title IYakRouter
 * @author Yak Exchange
 * @notice Interface for the YakRouter contract that finds optimal swap paths and executes trades
 * @dev Supports multi-hop swaps across different DEX adapters with minimal slippage
 */
interface IYakRouter {
    /**
     * @notice Thrown when a zero address is provided where it's not allowed
     */
    error AddressZero();

    /**
     * @notice Thrown when the transaction deadline has passed
     * @param _deadline The deadline timestamp that was set
     * @param _currentTime The current block timestamp
     */
    error DeadlinePassed(uint256 _deadline, uint256 _currentTime);

    /**
     * @notice Thrown when the ETH amount sent does not match the expected amount
     * @param _sentAmount The amount of ETH sent with the transaction
     * @param _expectedAmount The expected amount of ETH for the swap
     */
    error IncorrectETHAmount(uint256 _sentAmount, uint256 _expectedAmount);

    /**
     * @notice Thrown when the provided fee is above the maximum allowed fee
     * @param _fee The fee amount provided
     * @param _maxFee The maximum fee amount allowed
     */
    error FeeAboveMaximum(uint256 _fee, uint256 _maxFee);

    /**
     * @notice Thrown when the provided fee is less than the minimum required fee
     * @param _fee The fee amount provided
     * @param _minFee The minimum fee amount required
     */
    error InsufficientFee(uint256 _fee, uint256 _minFee);

    /**
     * @notice Thrown when max steps is invalid (must be between 1 and 4)
     * @param _maxSteps The invalid max steps value provided
     */
    error InvalidMaxSteps(uint256 _maxSteps);

    /**
     * @notice Thrown when the actual output amount is less than the minimum expected
     * @param _amountOut The actual output amount received
     * @param _minAmountOut The minimum output amount expected
     */
    error InsufficientOutputAmount(uint256 _amountOut, uint256 _minAmountOut);

    /**
     * @notice Thrown when swapping from ETH but the path doesn't start with WETH
     * @param _tokenIn The first token in the path that should be WETH
     */
    error PathDoesNotBeginWithWETH(address _tokenIn);

    /**
     * @notice Thrown when swapping to ETH but the path doesn't end with WETH
     * @param _tokenOut The last token in the path that should be WETH
     */
    error PathDoesNotEndWithWETH(address _tokenOut);

    /**
     * @notice Thrown when trade parameters length does not match
     */
    error TradeLengthDoesNotMatch();

    /**
     * @notice Emitted when trusted tokens list is updated
     * @param _newTrustedTokens The new array of trusted token addresses
     */
    event UpdatedTrustedTokens(address[] _newTrustedTokens);

    /**
     * @notice Emitted when adapters list is updated
     * @param _newAdapters The new array of adapter addresses
     */
    event UpdatedAdapters(address[] _newAdapters);

    /**
     * @notice Emitted when minimum fee is updated
     * @param _oldMinFee The previous minimum fee value
     * @param _newMinFee The new minimum fee value
     */
    event UpdatedMinFee(uint256 _oldMinFee, uint256 _newMinFee);

    /**
     * @notice Emitted when fee claimer address is updated
     * @param _oldFeeClaimer The previous fee claimer address
     * @param _newFeeClaimer The new fee claimer address
     */
    event UpdatedFeeClaimer(address _oldFeeClaimer, address _newFeeClaimer);

    /**
     * @notice Emitted when a swap is executed
     * @param _tokenIn The input token address
     * @param _tokenOut The output token address
     * @param _amountIn The input amount swapped
     * @param _amountOut The output amount received
     */
    event YakSwap(
        address indexed _tokenIn, address indexed _tokenOut, uint256 _amountIn, uint256 _amountOut
    );

    // ========== Admin Functions ==========

    /**
     * @notice Updates the list of trusted tokens used for finding swap paths
     * @param _trustedTokens Array of token addresses to use as intermediate tokens
     * @dev Only callable by maintainer
     */
    function setTrustedTokens(address[] memory _trustedTokens) external;

    /**
     * @notice Updates the list of DEX adapters
     * @param _adapters Array of adapter contract addresses
     * @dev Only callable by maintainer
     */
    function setAdapters(address[] memory _adapters) external;

    /**
     * @notice Sets the address that receives swap fees
     * @param _claimer Address to receive fees
     * @dev Only callable by maintainer, cannot be zero address
     */
    function setFeeClaimer(address _claimer) external;

    /**
     * @notice Sets the minimum fee in basis points (1/10000)
     * @param _fee Minimum fee amount (e.g., 30 = 0.3%)
     * @dev Only callable by maintainer
     */
    function setMinFee(uint256 _fee) external;

    // ========== View Functions ==========

    /**
     * @notice Returns the number of trusted tokens
     * @return count The total number of trusted tokens configured
     */
    function trustedTokensCount() external view returns (uint256);

    /**
     * @notice Returns the number of adapters
     * @return count The total number of adapter contracts configured
     */
    function adaptersCount() external view returns (uint256);

    // ========== Query Functions ==========

    /**
     * @notice Queries a specific adapter for swap quote
     * @param _amount Input amount (if exactIn) or output amount (if !exactIn)
     * @param _tokenIn Address of the input token
     * @param _tokenOut Address of the output token
     * @param _index Index of the adapter in the adapters array
     * @param _exactIn True for exact input, false for exact output
     * @return quoteAmount The output amount (if exactIn) or input amount (if !exactIn)
     * @return recipient The recipient address for this adapter
     */
    function queryAdapter(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        uint8 _index,
        bool _exactIn
    ) external returns (uint256, address);

    /**
     * @notice Finds the best adapter from specified options for a direct swap
     * @param _amount Input amount (if exactIn) or output amount (if !exactIn)
     * @param _tokenIn Address of the input token
     * @param _tokenOut Address of the output token
     * @param _options Array of adapter indices to check
     * @param _exactIn True for exact input, false for exact output
     * @return query The best query result with adapter and amount information
     */
    function queryNoSplit(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        uint8[] calldata _options,
        bool _exactIn
    ) external view returns (Query memory);

    /**
     * @notice Finds the best adapter from all available adapters for a direct swap
     * @param _amount Input amount (if exactIn) or output amount (if !exactIn)
     * @param _tokenIn Address of the input token
     * @param _tokenOut Address of the output token
     * @param _exactIn True for exact input, false for exact output
     * @return query The best query result with adapter and amount information
     */
    function queryNoSplit(uint256 _amount, address _tokenIn, address _tokenOut, bool _exactIn)
        external
        view
        returns (Query memory);

    /**
     * @notice Finds the optimal swap path between two tokens
     * @param _amount Input amount (if exactIn) or output amount (if !exactIn)
     * @param _tokenIn Address of the input token
     * @param _tokenOut Address of the output token
     * @param _trustedTokens Additional trusted tokens to consider for routing
     * @param _maxSteps Maximum number of hops allowed (1-4)
     * @param _exactIn True for exact input, false for exact output
     * @return offer The optimal swap path with amounts and adapters for each hop
     * @dev Combines default trusted tokens with provided ones for path finding
     */
    function findBestPath(
        uint256 _amount,
        address _tokenIn,
        address _tokenOut,
        address[] memory _trustedTokens,
        uint256 _maxSteps,
        bool _exactIn
    ) external view returns (FormattedOffer memory);

    // ========== Swap Functions ==========

    /**
     * @notice Executes a token-to-token swap
     * @param _trade Trade parameters including path and amounts
     * @param _fee Fee amount in basis points to charge (e.g., 30 = 0.3%)
     * @param _deadline Unix timestamp after which the transaction reverts
     * @param _to Address to receive the output tokens
     * @dev Requires token approval from caller
     */
    function swapNoSplit(Trade calldata _trade, uint256 _fee, uint256 _deadline, address _to)
        external;

    /**
     * @notice Executes a swap starting from ETH
     * @param _trade Trade parameters (path must start with WETH)
     * @param _fee Fee amount in basis points to charge
     * @param _deadline Unix timestamp after which the transaction reverts
     * @param _to Address to receive the output tokens
     * @dev Accepts ETH and wraps to WETH internally
     */
    function swapNoSplitFromETH(Trade calldata _trade, uint256 _fee, uint256 _deadline, address _to)
        external
        payable;

    /**
     * @notice Executes a swap ending in ETH
     * @param _trade Trade parameters (path must end with WETH)
     * @param _fee Fee amount in basis points to charge
     * @param _deadline Unix timestamp after which the transaction reverts
     * @param _to Address to receive the ETH
     * @dev Unwraps WETH to ETH before sending to recipient
     */
    function swapNoSplitToETH(Trade calldata _trade, uint256 _fee, uint256 _deadline, address _to)
        external;

    /**
     * @notice Executes a token-to-token swap using EIP-2612 permit
     * @param _trade Trade parameters including path and amounts
     * @param _fee Fee amount in basis points to charge
     * @param _deadline Unix timestamp after which the transaction reverts
     * @param _to Address to receive the output tokens
     * @param _v Permit signature v value
     * @param _r Permit signature r value
     * @param _s Permit signature s value
     * @dev Uses permit to avoid separate approval transaction
     */
    function swapNoSplitWithPermit(
        Trade calldata _trade,
        uint256 _fee,
        uint256 _deadline,
        address _to,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
    ) external;

    /**
     * @notice Executes a swap to ETH using EIP-2612 permit
     * @param _trade Trade parameters (path must end with WETH)
     * @param _fee Fee amount in basis points to charge
     * @param _deadline Unix timestamp after which the transaction reverts
     * @param _to Address to receive the ETH
     * @param _v Permit signature v value
     * @param _r Permit signature r value
     * @param _s Permit signature s value
     * @dev Uses permit and unwraps WETH to ETH
     */
    function swapNoSplitToETHWithPermit(
        Trade calldata _trade,
        uint256 _fee,
        uint256 _deadline,
        address _to,
        uint8 _v,
        bytes32 _r,
        bytes32 _s
    ) external;
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {AccessControl, Context} from "@openzeppelin/contracts/access/AccessControl.sol";

/**
 * @dev Contract module which extends the basic access control mechanism of Ownable
 * to include many maintainers, whom only the owner (DEFAULT_ADMIN_ROLE) may add and
 * remove.
 *
 * 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 this modifier:
 * `onlyMaintainer`, which can be applied to your functions to restrict their use to
 * the accounts with the role of maintainer.
 */
abstract contract Maintainable is Context, AccessControl {
    error OnlyMaintainer();

    bytes32 public constant MAINTAINER_ROLE = keccak256("MAINTAINER_ROLE");

    constructor() {
        address msgSender = _msgSender();
        // members of the DEFAULT_ADMIN_ROLE alone may revoke and grant role membership
        _setupRole(DEFAULT_ADMIN_ROLE, msgSender);
        _setupRole(MAINTAINER_ROLE, msgSender);
    }

    function addMaintainer(address addedMaintainer) public virtual {
        grantRole(MAINTAINER_ROLE, addedMaintainer);
    }

    function removeMaintainer(address removedMaintainer) public virtual {
        revokeRole(MAINTAINER_ROLE, removedMaintainer);
    }

    function renounceRole(bytes32 role) public virtual {
        address msgSender = _msgSender();
        renounceRole(role, msgSender);
    }

    function transferOwnership(address newOwner) public virtual {
        address msgSender = _msgSender();
        grantRole(DEFAULT_ADMIN_ROLE, newOwner);
        renounceRole(DEFAULT_ADMIN_ROLE, msgSender);
    }

    modifier onlyMaintainer() {
        address msgSender = _msgSender();
        if (!hasRole(MAINTAINER_ROLE, msgSender)) revert OnlyMaintainer();
        _;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {SafeERC20, IERC20} from "contracts/lib/SafeERC20.sol";
import {Maintainable} from "contracts/lib/Maintainable.sol";

abstract contract Recoverable is Maintainable {
    using SafeERC20 for IERC20;

    error ETHTransferFailed();
    error NothingToRecover();

    event Recovered(address indexed _asset, uint256 amount);

    /**
     * @notice Recover ERC20 from contract
     * @param _tokenAddress token address
     * @param _tokenAmount amount to recover
     */
    function recoverERC20(address _tokenAddress, uint256 _tokenAmount) external onlyMaintainer {
        if (_tokenAmount == 0) revert NothingToRecover();
        IERC20(_tokenAddress).safeTransfer(msg.sender, _tokenAmount);
        emit Recovered(_tokenAddress, _tokenAmount);
    }

    /**
     * @notice Recover native asset from contract
     * @param _amount amount
     */
    function recoverNative(uint256 _amount) external onlyMaintainer {
        if (_amount == 0) revert NothingToRecover();
        (bool refunded,) = msg.sender.call{value: _amount}("");
        if (!refunded) revert ETHTransferFailed();
        emit Recovered(address(0), _amount);
    }
}

// This is a simplified version of OpenZepplin's SafeERC20 library
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;

import {IERC20} from "contracts/interface/IERC20.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 ERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    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)
        );
    }

    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'
        // solhint-disable-next-line max-line-length
        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));
    }

    /**
     * @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.

        // A Solidity high level call has three parts:
        //  1. The target address is checked to verify it contains contract code
        //  2. The call itself is made, and success asserted
        //  3. The return value is decoded, which in turn checks the size of the returned data.
        // solhint-disable-next-line max-line-length

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success, "SafeERC20: low-level call failed");

        if (returndata.length > 0) {
            // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;


library TypeConversion {

    function toBytes12(address x) internal pure returns (bytes12 y) {
        assembly { y := x }
    }

    function toBytes32(address x) internal pure returns (bytes32 y) {
        assembly { y := x }
    }

    function toAddress(bytes32 x) internal pure returns (address y) {
        assembly { y := x }
    }

    function toBytes(address x) internal pure returns (bytes memory y) {
        y = new bytes(32);
        assembly { mstore(add(y, 32), x) }
    }

    function toBytes(bytes32 x) internal pure returns (bytes memory y) {
        y = new bytes(32);
        assembly { mstore(add(y, 32), x) }
    }

    function toBytes(uint x) internal pure returns (bytes memory y) {
        y = new bytes(32);
        assembly { mstore(add(y, 32), x) }
    }

    function toAddress(
        bytes memory x,
        uint offset
    ) internal pure returns (address y) {
        assembly { y := mload(add(x, offset)) }
    }

    function toUint(
        bytes memory x,
        uint offset
    ) internal pure returns (uint y) {
        assembly { y := mload(add(x, offset)) }
    }

    function toBytes12(
        bytes memory x,
        uint offset
    ) internal pure returns (bytes12 y) {
        assembly { y := mload(add(x, offset)) }
    }

    function toBytes32(
        bytes memory x,
        uint offset
    ) internal pure returns (bytes32 y) {
        assembly { y := mload(add(x, offset)) }
    }

    function toAddresses(
        bytes memory xs
    ) internal pure returns (address[] memory ys) {
        ys = new address[](xs.length/32);
        for (uint i=0; i < xs.length/32; i++) {
            ys[i] = toAddress(xs, i*32 + 32);
        }
    }

    function toUints(
        bytes memory xs
    ) internal pure returns (uint[] memory ys) {
        ys = new uint[](xs.length/32);
        for (uint i=0; i < xs.length/32; i++) {
            ys[i] = toUint(xs, i*32 + 32);
        }
    }

    function toBytes32s(
        bytes memory xs
    ) internal pure returns (bytes32[] memory ys) {
        ys = new bytes32[](xs.length/32);
        for (uint i=0; i < xs.length/32; i++) {
            ys[i] = toBytes32(xs, i*32 + 32);
        }
    }

}

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.4;

import {Offer, FormattedOffer} from "contracts/interface/IYakRouter.sol";
import {TypeConversion} from "contracts/lib/TypeConversion.sol";

library OfferUtils {
    using TypeConversion for address;
    using TypeConversion for uint256;
    using TypeConversion for bytes;

    uint256 public constant SLOT_LENGTH = 32; // Storage slot length in bytes

    function newOffer(uint256 _amount, address _token) internal pure returns (Offer memory offer) {
        offer.amounts = _amount.toBytes();
        offer.path = _token.toBytes();
    }

    /**
     * Makes a deep copy of Offer struct
     */
    function clone(Offer memory _queries) internal pure returns (Offer memory) {
        return Offer(_queries.amounts, _queries.adapters, _queries.path, _queries.recipients, _queries.deployers);
    }

    /**
     * Appends new elements to the top of Offer struct
     */
    function addToHead(
        Offer memory _queries,
        uint256 _amount,
        address _adapter,
        address _recipient,
        address _token
    ) internal pure {
        _queries.path = bytes.concat(_token.toBytes(), _queries.path);
        _queries.adapters = bytes.concat(_adapter.toBytes(), _queries.adapters);
        _queries.amounts = bytes.concat(_amount.toBytes(), _queries.amounts);
        _queries.recipients = bytes.concat(_recipient.toBytes(), _queries.recipients);
        _queries.deployers = bytes.concat(address(0).toBytes(), _queries.deployers);
    }

    function addToHead(
        Offer memory _queries,
        uint256 _amount,
        address _adapter,
        address _recipient,
        address _token,
        address _deployer
    ) internal pure {
        _queries.path = bytes.concat(_token.toBytes(), _queries.path);
        _queries.adapters = bytes.concat(_adapter.toBytes(), _queries.adapters);
        _queries.amounts = bytes.concat(_amount.toBytes(), _queries.amounts);
        _queries.recipients = bytes.concat(_recipient.toBytes(), _queries.recipients);
        _queries.deployers = bytes.concat(_deployer.toBytes(), _queries.deployers);
    }

    /**
     * Appends new elements to the end of Offer struct
     */
    function addToTail(
        Offer memory _queries,
        uint256 _amount,
        address _adapter,
        address _recipient,
        address _token
    ) internal pure {
        _queries.path = bytes.concat(_queries.path, _token.toBytes());
        _queries.adapters = bytes.concat(_queries.adapters, _adapter.toBytes());
        _queries.amounts = bytes.concat(_queries.amounts, _amount.toBytes());
        _queries.recipients = bytes.concat(_queries.recipients, _recipient.toBytes());
        _queries.deployers = bytes.concat(_queries.deployers, address(0).toBytes());
    }

    function addToTail(
        Offer memory _queries,
        uint256 _amount,
        address _adapter,
        address _recipient,
        address _token,
        address _deployer
    ) internal pure {
        _queries.path = bytes.concat(_queries.path, _token.toBytes());
        _queries.adapters = bytes.concat(_queries.adapters, _adapter.toBytes());
        _queries.amounts = bytes.concat(_queries.amounts, _amount.toBytes());
        _queries.recipients = bytes.concat(_queries.recipients, _recipient.toBytes());
        _queries.deployers = bytes.concat(_queries.deployers, _deployer.toBytes());
    }

    /**
     * Formats elements in the Offer object from byte-arrays to integers and addresses
     */
    function format(Offer memory _queries) internal pure returns (FormattedOffer memory) {
        return FormattedOffer(
            _queries.amounts.toUints(),
            _queries.adapters.toAddresses(),
            _queries.path.toAddresses(),
            _queries.recipients.toAddresses(),
            _queries.deployers.toAddresses()
        );
    }

    function getTokenOut(Offer memory _offer) internal pure returns (address tokenOut) {
        tokenOut = _offer.path.toAddress(_offer.path.length); // Last SLOT_LENGTH bytes
    }

    function getTokenIn(Offer memory _offer) internal pure returns (address tokenIn) {
        tokenIn = _offer.path.toAddress(SLOT_LENGTH); // First SLOT_LENGTH bytes
    }

    function getAmountOut(Offer memory _offer) internal pure returns (uint256 amount) {
        amount = _offer.amounts.toUint(_offer.amounts.length); // Last SLOT_LENGTH bytes
    }

    function getAmountIn(Offer memory _offer) internal pure returns (uint256 amount) {
        amount = _offer.amounts.toUint(SLOT_LENGTH); // First SLOT_LENGTH bytes
    }
}

library FormattedOfferUtils {
    using TypeConversion for address;
    using TypeConversion for uint256;
    using TypeConversion for bytes;

    /**
     * Appends new elements to the end of FormattedOffer
     */
    function addToTail(
        FormattedOffer memory offer,
        uint256 amount,
        address wrapper,
        address token,
        address recipient
    ) internal pure {
        offer.amounts = bytes.concat(abi.encodePacked(offer.amounts), amount.toBytes()).toUints();
        offer.adapters =
            bytes.concat(abi.encodePacked(offer.adapters), wrapper.toBytes()).toAddresses();
        offer.path = bytes.concat(abi.encodePacked(offer.path), token.toBytes()).toAddresses();
        offer.recipients =
            bytes.concat(abi.encodePacked(offer.recipients), recipient.toBytes()).toAddresses();
        offer.deployers =
            bytes.concat(abi.encodePacked(offer.deployers), address(0).toBytes()).toAddresses();
    }

    function addToTail(
        FormattedOffer memory offer,
        uint256 amount,
        address wrapper,
        address token,
        address recipient,
        address deployer
    ) internal pure {
        offer.amounts = bytes.concat(abi.encodePacked(offer.amounts), amount.toBytes()).toUints();
        offer.adapters =
            bytes.concat(abi.encodePacked(offer.adapters), wrapper.toBytes()).toAddresses();
        offer.path = bytes.concat(abi.encodePacked(offer.path), token.toBytes()).toAddresses();
        offer.recipients =
            bytes.concat(abi.encodePacked(offer.recipients), recipient.toBytes()).toAddresses();
        offer.deployers =
            bytes.concat(abi.encodePacked(offer.deployers), deployer.toBytes()).toAddresses();
    }

    /**
     * Appends new elements to the beginning of FormattedOffer
     */
    function addToHead(
        FormattedOffer memory offer,
        uint256 amount,
        address wrapper,
        address token,
        address recipient
    ) internal pure {
        offer.amounts = bytes.concat(amount.toBytes(), abi.encodePacked(offer.amounts)).toUints();
        offer.adapters =
            bytes.concat(wrapper.toBytes(), abi.encodePacked(offer.adapters)).toAddresses();
        offer.path = bytes.concat(token.toBytes(), abi.encodePacked(offer.path)).toAddresses();
        offer.recipients =
            bytes.concat(recipient.toBytes(), abi.encodePacked(offer.recipients)).toAddresses();
        offer.deployers =
            bytes.concat(address(0).toBytes(), abi.encodePacked(offer.deployers)).toAddresses();
    }

    function addToHead(
        FormattedOffer memory offer,
        uint256 amount,
        address wrapper,
        address token,
        address recipient,
        address deployer
    ) internal pure {
        offer.amounts = bytes.concat(amount.toBytes(), abi.encodePacked(offer.amounts)).toUints();
        offer.adapters =
            bytes.concat(wrapper.toBytes(), abi.encodePacked(offer.adapters)).toAddresses();
        offer.path = bytes.concat(token.toBytes(), abi.encodePacked(offer.path)).toAddresses();
        offer.recipients =
            bytes.concat(recipient.toBytes(), abi.encodePacked(offer.recipients)).toAddresses();
        offer.deployers =
            bytes.concat(deployer.toBytes(), abi.encodePacked(offer.deployers)).toAddresses();
    }

    function getAmountOut(FormattedOffer memory offer) internal pure returns (uint256) {
        return offer.amounts[offer.amounts.length - 1];
    }
}

Settings
{
  "evmVersion": "prague",
  "optimizer": {
    "enabled": true,
    "runs": 1000000,
    "details": {
      "yulDetails": {
        "optimizerSteps": "u"
      }
    }
  },
  "viaIR": true,
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address[]","name":"_adapters","type":"address[]"},{"internalType":"address[]","name":"_trustedTokens","type":"address[]"},{"internalType":"address","name":"_feeClaimer","type":"address"},{"internalType":"address","name":"_wrapped_native","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"uint256","name":"_currentTime","type":"uint256"}],"name":"DeadlinePassed","type":"error"},{"inputs":[],"name":"ETHTransferFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_maxFee","type":"uint256"}],"name":"FeeAboveMaximum","type":"error"},{"inputs":[{"internalType":"uint256","name":"_sentAmount","type":"uint256"},{"internalType":"uint256","name":"_expectedAmount","type":"uint256"}],"name":"IncorrectETHAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_minFee","type":"uint256"}],"name":"InsufficientFee","type":"error"},{"inputs":[{"internalType":"uint256","name":"_amountOut","type":"uint256"},{"internalType":"uint256","name":"_minAmountOut","type":"uint256"}],"name":"InsufficientOutputAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"_maxSteps","type":"uint256"}],"name":"InvalidMaxSteps","type":"error"},{"inputs":[],"name":"NothingToRecover","type":"error"},{"inputs":[],"name":"OnlyMaintainer","type":"error"},{"inputs":[{"internalType":"address","name":"_tokenIn","type":"address"}],"name":"PathDoesNotBeginWithWETH","type":"error"},{"inputs":[{"internalType":"address","name":"_tokenOut","type":"address"}],"name":"PathDoesNotEndWithWETH","type":"error"},{"inputs":[],"name":"TradeLengthDoesNotMatch","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_newAdapters","type":"address[]"}],"name":"UpdatedAdapters","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldFeeClaimer","type":"address"},{"indexed":false,"internalType":"address","name":"_newFeeClaimer","type":"address"}],"name":"UpdatedFeeClaimer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_oldMinFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newMinFee","type":"uint256"}],"name":"UpdatedMinFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_newTrustedTokens","type":"address[]"}],"name":"UpdatedTrustedTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_tokenIn","type":"address"},{"indexed":true,"internalType":"address","name":"_tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountOut","type":"uint256"}],"name":"YakSwap","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ADAPTERS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_CLAIMER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAINTAINER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NATIVE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SLOT_LENGTH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"TRUSTED_TOKENS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WNATIVE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adaptersCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addedMaintainer","type":"address"}],"name":"addMaintainer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"address[]","name":"_trustedTokens","type":"address[]"},{"internalType":"uint256","name":"_maxSteps","type":"uint256"},{"internalType":"bool","name":"_exactIn","type":"bool"}],"name":"findBestPath","outputs":[{"components":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"address[]","name":"deployers","type":"address[]"}],"internalType":"struct FormattedOffer","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"uint8","name":"_index","type":"uint8"},{"internalType":"bool","name":"_exactIn","type":"bool"}],"name":"queryAdapter","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"uint8[]","name":"_options","type":"uint8[]"},{"internalType":"bool","name":"_exactIn","type":"bool"}],"name":"queryNoSplit","outputs":[{"components":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Query","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"bool","name":"_exactIn","type":"bool"}],"name":"queryNoSplit","outputs":[{"components":[{"internalType":"address","name":"adapter","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Query","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"recoverNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"removedMaintainer","type":"address"}],"name":"removeMaintainer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_adapters","type":"address[]"}],"name":"setAdapters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_claimer","type":"address"}],"name":"setFeeClaimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setMinFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_trustedTokens","type":"address[]"}],"name":"setTrustedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"address[]","name":"deployers","type":"address[]"}],"internalType":"struct Trade","name":"_trade","type":"tuple"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"swapNoSplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"address[]","name":"deployers","type":"address[]"}],"internalType":"struct Trade","name":"_trade","type":"tuple"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"swapNoSplitFromETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"address[]","name":"deployers","type":"address[]"}],"internalType":"struct Trade","name":"_trade","type":"tuple"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"swapNoSplitToETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"address[]","name":"deployers","type":"address[]"}],"internalType":"struct Trade","name":"_trade","type":"tuple"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"swapNoSplitToETHWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"address[]","name":"deployers","type":"address[]"}],"internalType":"struct Trade","name":"_trade","type":"tuple"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"swapNoSplitWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"trustedTokensCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a0604052346100535761001d6100146101b9565b9291909161022a565b6040516142516107408239608051818181610cd401528181611ac9015281816120d801528181613130015261324a015261425190f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b90601f01601f191681019081106001600160401b0382111761008c57604052565b610057565b906100a561009e60405190565b928361006b565b565b6001600160401b03811161008c5760208091020190565b6001600160a01b031690565b90565b6001600160a01b0381160361005357565b905051906100a5826100cd565b909291926101006100fb826100a7565b610091565b938185526020808601920283019281841161005357915b8383106101245750505050565b6020809161013284866100de565b815201920191610117565b9080601f830112156100535781516100ca926020016100eb565b6080818303126100535780516001600160401b038111610053578261017d91830161013d565b60208201519093906001600160401b038111610053576101a2846100ca92850161013d565b9360606101b282604087016100de565b94016100de565b6101d76149b1803803806101cc81610091565b928339810190610157565b90919293565b905f19905b9181191691161790565b6100ca6100ca6100ca9290565b906102096100ca610210926101ec565b82546101dd565b9055565b6100be6100ca6100ca9290565b6100ca90610214565b906102336102b5565b61023e5f60016101f9565b6102475f610221565b6001600160a01b0381166001600160a01b03851614908115610299575b50610288576102839261027961027e92610521565b610608565b610655565b608052565b639fabe1c160e01b5f908152600490fd5b6001600160a01b031690506001600160a01b038516145f610264565b6100a56100a56102c9565b6100ca5f6101ec565b6100a5336102de816102d96102c0565b61065e565b5f5160206149915f395f51905f5261065e565b61031061030c335b5f5160206149915f395f51905f52610680565b1590565b61031d576100a5906104e0565b6301c42f3160e61b5f908152600490fd5b915f1960089290920291821b911b6101e2565b91906103526100ca610210936101ec565b90835461032e565b6100a5915f91610341565b818110610370575050565b8061037d5f60019361035a565b01610365565b91909182821061039257505050565b6100a592916103a590915f5260205f2090565b9182019101610365565b9068010000000000000000811161008c57816103cc6100a5935490565b90828155610383565b6100ca906100be906001600160a01b031682565b6100ca906103d5565b6100ca906103e9565b8151916001600160401b03831161008c5761042f61042560019261041f86866103af565b60200190565b925f5260205f2090565b9204915f5b8381106104415750505050565b600190602061045a6100ca86516001600160a01b031690565b9401938184015501610434565b906100a5916103fb565b9061049161048a610480845190565b8084529260200190565b9260200190565b905f5b8181106104a15750505090565b9091926104c76104c060019286516001600160a01b0316815260200190565b9460200190565b929101610494565b60208082526100ca92910190610471565b61051c7f658ff1688002926d8f426cb10c052ec29003f50042df9652d8613484c1a5864791610510816003610467565b604051918291826104cf565b0390a1565b6100a5906102f1565b61053661030c336102f9565b61031d576100a590610596565b6100ca906100be565b6100ca9054610543565b906001600160a01b03906101e2565b906105756100ca610210926103f2565b8254610556565b6001600160a01b0391821681529116602082015260400190565b6105a26100be5f610221565b6001600160a01b03821614610288577fb2c853ac4d80d18d058c43d8018d077a036e542a79acae1647f5ad2a8c76f4e2906105e86105e0600261054c565b916002610565565b6105f2600261054c565b9061051c6105ff60405190565b9283928361057c565b6100a59061052a565b61061d61030c336102f9565b61031d576100a59061051c7febf7325f48e05e5e38809c69f8b02a7c907ed31d8768e6c2d841b1296a9225fe91610510816004610467565b6100a590610611565b906100a5916106c7565b905b5f5260205260405f2090565b9061066a906103f2565b6100ca915f61069a6106a0936106935f90565b5082610668565b01610676565b5460ff1690565b9060ff906101e2565b906106c06100ca61021092151590565b82546106a7565b6106d461030c8383610680565b6106dc575050565b6106f560016106f0845f61069a8682610668565b6106b0565b61070f610709610703339390565b936103f2565b916103f2565b917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d61073a60405190565b5f90a456fe6080604052600436101561001a575b3615610018575f80fd5b005b5f3560e01c8062b99e361461027857806301ffc9a714610273578063243f8bfc1461026e578063248a9ca3146102695780632f2ff15d1461026457806331ac99201461025f57806336568abe1461025a5780633a9a40811461025557806352a52ab01461025057806356ca76b41461024b578063645d916b146102465780636b453c1f1461024157806375c834b51461023c57806376c7a3c71461023757806376ebe69c1461023257806377d007f71461022d5780637c7a561b146102285780638980f11f146102235780638bb9c5bf1461021e57806391d1485414610219578063952e901214610214578063974584601461020f578063a0cf0aea1461020a578063a217fddf14610205578063a3f4df7e14610200578063aede3693146101fb578063b0873e5e146101f6578063b381cf40146101f1578063b5ce5649146101ec578063c3accd48146101e7578063c8a3a5c6146101e2578063cc299d4f146101dd578063d547741f146101d8578063d73792a9146101d3578063d8baf7cf146101ce578063db6a5897146101c9578063f2fde38b146101c45763f87422540361000e57610f88565b610f70565b610f42565b610daa565b610d8f565b610d63565b610d47565b610d2f565b610d17565b610cf8565b610cbf565b610ca4565b610c7a565b610c53565b610b2e565b610af5565b610aaf565b610a22565b6109ee565b6109d6565b6109bd565b61097e565b610967565b61094b565b610930565b6108f3565b610885565b610852565b6107eb565b610756565b6106ce565b610584565b61056c565b61054e565b6104ff565b6104bd565b61035c565b6102e5565b5f91031261028757565b5f80fd5b6102ad916008021c5b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b906102ad915461028b565b6102ad5f60026102b0565b6102cf90610294565b9052565b6020810192916102e391906102c6565b565b34610287576102f536600461027d565b61030c6103006102bb565b604051918291826102d3565b0390f35b7fffffffff0000000000000000000000000000000000000000000000000000000081165b0361028757565b905035906102e382610310565b90602082820312610287576102ad9161033b565b346102875761030c610377610372366004610348565b610fc1565b60405191829182901515815260200190565b80610334565b905035906102e382610389565b61033481610294565b905035906102e38261039c565b909182601f830112156102875781359167ffffffffffffffff831161028757602001926020830284011161028757565b801515610334565b905035906102e3826103e2565b9160a0838303126102875761040c828461038f565b9261041a83602083016103a5565b9261042881604084016103a5565b92606083013567ffffffffffffffff8111610287578261044f6080946102ad9387016103b2565b949095016103ea565b906080806102e3936104705f8201515f8601906102c6565b610482602082015160208601906102c6565b610494604082015160408601906102c6565b6104a6606082015160608601906102c6565b0151910152565b60a0810192916102e39190610458565b346102875761030c6104df6104d33660046103f7565b949390939291926111d6565b604051918291826104ad565b90602082820312610287576102ad9161038f565b346102875761030c61051a6105153660046104eb565b6113d1565b6040515b9182918290815260200190565b9190604083820312610287576102ad906020610547828661038f565b94016103a5565b346102875761056761056136600461052b565b9061140e565b604051005b346102875761056761057f3660046104eb565b6115db565b346102875761056761059736600461052b565b90611675565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810190811067ffffffffffffffff82111761060a57604052565b61059d565b906102e361061c60405190565b92836105ca565b67ffffffffffffffff811161060a5760208091020190565b9092919261065061064b82610623565b61060f565b938185526020808601920283019281841161028757915b8383106106745750505050565b6020809161068284866103a5565b815201920191610667565b9080601f83011215610287578160206102ad9335910161063b565b9060208282031261028757813567ffffffffffffffff8111610287576102ad920161068d565b34610287576105676106e13660046106a8565b611897565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b80548210156107335761072b6001915f5260205f2090565b910201905f90565b6106e6565b600354811015610287576107506102ad916003610713565b906102b0565b346102875761030c61030061076c3660046104eb565b610738565b60ff8116610334565b905035906102e382610771565b919060a0838203126102875761079d818461038f565b926107ab82602083016103a5565b926102ad6107bc84604085016103a5565b9360806107cc826060870161077a565b94016103ea565b9081526040810192916102e3916020905b01906102c6565b34610287576108076107fe366004610787565b939290926118a0565b9061030c61081460405190565b928392836107d3565b60808183031261028757610831828261038f565b926102ad61084284602085016103a5565b9360606107cc82604087016103a5565b346102875761030c6104df61086836600461081d565b9291909161195a565b90602082820312610287576102ad916103a5565b3461028757610567610898366004610871565b611973565b908160c09103126102875790565b60808183031261028757803567ffffffffffffffff811161028757826108d291830161089d565b926102ad6108e3846020850161038f565b936060610547826040870161038f565b34610287576105676109063660046108ab565b929190916119f5565b6102ad916008021c81565b906102ad915461090f565b6102ad5f600161091a565b346102875761094036600461027d565b61030c61051a610925565b346102875761095b36600461027d565b61030c61051a60035490565b6105676109753660046108ab565b92919091611ba3565b346102875761098e36600461027d565b61030c61051a60045490565b9190604083820312610287576102ad9060206109b682866103a5565b940161038f565b34610287576105676109d036600461099a565b90611c48565b34610287576105676109e93660046104eb565b611c52565b346102875761030c610377610a0436600461052b565b90611c79565b600454811015610287576107506102ad916004610713565b346102875761030c610300610a383660046104eb565b610a0a565b60e08183031261028757803567ffffffffffffffff81116102875782610a6491830161089d565b92610a72836020840161038f565b92610a80816040850161038f565b92610a8e82606083016103a5565b926102ad610a9f846080850161077a565b9360c06109b68260a0870161038f565b3461028757610567610ac2366004610a3d565b95949094939193611ce1565b6102946102ad6102ad9290565b6102ad90610ace565b6102ad5f610adb565b6102ad610ae4565b3461028757610b0536600461027d565b61030c610300610aed565b6102ad6102ad6102ad9290565b6102ad5f610b10565b6102ad610b1d565b3461028757610b3e36600461027d565b61030c61051a610b26565b67ffffffffffffffff811161060a57602090601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b0190565b90610b9561064b83610b49565b918252565b610ba46010610b88565b7f43616d656c6f7459616b526f7574657200000000000000000000000000000000602082015290565b6102ad610b9a565b6102ad610bcd565b6102ad610bd5565b90825f9392825e0152565b610c11610c1a602093610b8493610c05815190565b80835293849260200190565b95869101610be5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690565b60208082526102ad92910190610bf0565b3461028757610c6336600461027d565b61030c610c6e610bdd565b60405191829182610c42565b3461028757610567610c8d3660046104eb565b611e7a565b6102ad6020610b10565b6102ad610c92565b3461028757610cb436600461027d565b61030c61051a610c9c565b3461028757610ccf36600461027d565b61030c7f0000000000000000000000000000000000000000000000000000000000000000610300565b3461028757610567610d0b366004610a3d565b95949094939193611e83565b3461028757610567610d2a366004610871565b61201b565b3461028757610567610d423660046106a8565b612068565b3461028757610567610d5a3660046108ab565b92919091612185565b3461028757610567610d7636600461052b565b906121ac565b6102ad612710610b10565b6102ad610d7c565b3461028757610d9f36600461027d565b61030c61051a610d87565b3461028757610567610dbd366004610871565b6121b6565b909160c08284031261028757610dd8838361038f565b92610de681602085016103a5565b92610df482604083016103a5565b92606082013567ffffffffffffffff811161028757610e18846102ad92850161068d565b9360a06107cc826080870161038f565b90610e48610e41610e37845190565b8084529260200190565b9260200190565b905f5b818110610e585750505090565b909192610e75610e6e6001928651815260200190565b9460200190565b929101610e4b565b90610b84816020936102c6565b90610e99610e41610e37845190565b905f5b818110610ea95750505090565b909192610ebc610e6e6001928651610e7d565b929101610e9c565b6102ad916080610f20610f0e610efc610eea60a086015f8801518782035f890152610e28565b60208701518682036020880152610e8a565b60408601518582036040870152610e8a565b60608501518482036060860152610e8a565b920151906080818403910152610e8a565b60208082526102ad92910190610ec4565b346102875761030c610f64610f58366004610dc2565b94939093929192612296565b60405191829182610f31565b3461028757610567610f83366004610871565b612432565b3461028757610f9836600461027d565b61030c7f339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab9561051a565b7f7965db0b000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821614908115611011575090565b6102ad91507fffffffff00000000000000000000000000000000000000000000000000000000167f01ffc9a7000000000000000000000000000000000000000000000000000000001490565b6102ad60a061060f565b61106f61105d565b905f825260208080808086015f8152015f8152015f8152015f905250565b6102ad611067565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b60ff1660ff81146110d35760010190565b611095565b6102ad6102ad6102ad9260ff1690565b9190811015610733576020020190565b356102ad81610771565b6102946102ad6102ad9273ffffffffffffffffffffffffffffffffffffffff1690565b6102ad90611102565b6102ad90611125565b905051906102e382610389565b905051906102e38261039c565b9190604083820312610287576102ad90602061116d8286611137565b9401611144565b6111a66102e39461119c606094989795611192608086019a5f870152565b60208501906102c6565b60408301906102c6565b019015159052565b6040513d5f823e3d90fd5b6111c66102ad6102ad9290565b60ff1690565b906102cf90610294565b93959491926111e361108d565b506111ec61108d565b965f5b816111f9826110d8565b10156113a95760048660408661122c61075061122661122161121a896110d8565b8a8c6110e8565b6110f8565b86610713565b938b61123f61123a8761112e565b61112e565b9161127f8c61124d60405190565b978896879586957f409d653d000000000000000000000000000000000000000000000000000000005b87528601611174565b03915afa5f9182919081611376575b506112a5575050506112a0905b6110c2565b6111ef565b8b6112af5f6111b9565b60ff861614801561136f575b1561132b576080015182115b6112d8575b5050506112a0906110c2565b6112a0939b5090611301611322926112f86112f161105d565b95866111cc565b602085016111cc565b61130e87604085016111cc565b61131b89606085016111cc565b6080830152565b98905f806112cc565b506113355f610b10565b82141580156112c7575060808c0180516113556113515f610b10565b9190565b14908115611364575b506112c7565b51905082105f61135e565b50886112bb565b90925061139a915060403d81116113a2575b61139281836105ca565b810190611151565b90915f61128e565b503d611388565b50505050505050565b905b5f5260205260405f2090565b6102ad9081565b6102ad90546113c0565b60016113e86102ad926113e15f90565b505f6113b2565b016113c7565b906102e3916114046113ff826113d1565b61244f565b906102e39161247a565b906102e3916113ee565b61144a611446335b7f339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab95611c79565b1590565b611457576102e39061151f565b7f710bcc40000000000000000000000000000000000000000000000000000000005f90815260045b035ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b81156114ba570490565b611483565b9081526040810192916102e39160200152565b0152565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905b9181191691161790565b906115146102ad61151b92610b10565b82546114d6565b9055565b611527610d7c565b61153d6102ad611537600a610b10565b836114b0565b821161159557507f4bb8a6184424e4bb853a4836042f5a726e4e710873989bfc6abdab19966f5b709061157060016113c7565b61157b826001611504565b61159061158760405190565b928392836114bf565b0390a1565b5f916115ae61147f926115a8600a610b10565b906114b0565b907f38c7b9ea000000000000000000000000000000000000000000000000000000008452600484016114bf565b6102e390611418565b156115eb57565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c660000000000000000000000000000000000606482015280608481015b0390fd5b6102e3919061169a61168b33610294565b610294565b61169484610294565b146115e4565b6124f2565b6116ab61144633611420565b611457576102e39061185b565b818102929181159184041417156110d357565b919060086114fa9102916116fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841b90565b921b90565b91906117146102ad61151b93610b10565b9083546116cb565b6102e3915f91611703565b818110611732575050565b8061173f5f60019361171c565b01611727565b91909182821061175457505050565b6102e3929161176790915f5260205f2090565b9182019101611727565b9068010000000000000000811161060a578161178e6102e3935490565b90828155611745565b6102ad9051610294565b81519167ffffffffffffffff831161060a576117d66117cc6001926117c68686611771565b60200190565b925f5260205f2090565b9204915f5b8381106117e85750505050565b60019060206117f96102ad86611797565b94019381840155016117db565b906102e3916117a1565b9061181f610e41610e37845190565b905f5b81811061182f5750505090565b909192611842610e6e6001928651610e7d565b929101611822565b60208082526102ad92910190611810565b6115907febf7325f48e05e5e38809c69f8b02a7c907ed31d8768e6c2d841b1296a9225fe9161188b816004611806565b6040519182918261184a565b6102e39061169f565b90949293916040915f966118bd61123a806107505f9a6004610713565b916118fd6118ca60405190565b968795869485947f409d653d00000000000000000000000000000000000000000000000000000000865260048601611174565b03915afa805f80939092611937575b50611931575050600161191b57565b9150506119275f610adb565b906113515f610b10565b93509150565b909250611953915060403d6040116113a25761139281836105ca565b915f61190c565b9061196f93929161196961108d565b50612579565b5090565b6102e3907f339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab9561140e565b919392914285106119b457906102e39394916119e5565b7f83f2ba20000000000000000000000000000000000000000000000000000000005f90815261147f428760046114bf565b6119f29392503390612910565b50565b906102e393929161199d565b919392914285106119b457906102e3939491611a81565b356102ad81610389565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13682900301821215610287570180359067ffffffffffffffff82116102875760200191602082023603831361028757565b356102ad8161039c565b9392915083611a926102ad82611a18565b3403611b69575060408401611ac1611abc611aad8388611a22565b611ab65f610b10565b916110e8565b611a77565b611af3611aed7f0000000000000000000000000000000000000000000000000000000000000000610294565b91610294565b03611b20575083611b11611b0c5f6119f2969701611a18565b613128565b611b1a3061112e565b90612910565b61147f611b3d611abc611b345f9489611a22565b611ab686610b10565b7f1c4f4020000000000000000000000000000000000000000000000000000000008352600483016102d3565b61147f611b765f92611a18565b7f1791902400000000000000000000000000000000000000000000000000000000835234600484016114bf565b906102e3939291611a01565b90611bbc61144633611420565b611457576102e391611bcd5f610b10565b8214611c1e57611c19611c0f8261123a85611c087f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa289661112e565b33906131fa565b9261051e60405190565b0390a2565b7faba3a548000000000000000000000000000000000000000000000000000000005f908152600490fd5b906102e391611baf565b6102e39033610597565b906113b49061112e565b6102ad906111c6565b6102ad9054611c66565b6102ad915f611c93611c9993611c8c5f90565b50826113b2565b01611c5c565b611c6f565b949290979695939160e08601985f8701611cb7916102c6565b60208601611cc4916102c6565b6040850152606084015260ff16608083015260a082015260c00152565b95909294939193611d0161123a61123a611abc611aad60408c018c611a22565b611d0a3061112e565b92611d1489611a18565b90823b15610287575f94611d6286928a96611d2e60405190565b998a98899788967fd505accf0000000000000000000000000000000000000000000000000000000088523360048901611c9e565b03925af19081611d98575b50611d8f57611d7d565b50505050565b611d8693612185565b5f808080611d77565b6102e393612185565b611daf905f611da781836105ca565b81019061027d565b5f611d6d565b611dc161144633611420565b611457576102e390611dec565b3d15611de757611ddd3d610b88565b903d5f602084013e565b606090565b611df55f610b10565b8114611c1e57611e1c5f80611e0960405190565b5f9085335af1611e17611dce565b501590565b611e50577f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa28611c19611c0f61123a5f610adb565b7fb12d13eb000000000000000000000000000000000000000000000000000000005f908152600490fd5b6102e390611db5565b95909294939193611ea361123a61123a611abc611aad60408c018c611a22565b611eac3061112e565b92611eb689611a18565b90823b15610287575f94611ed086928a96611d2e60405190565b03925af19081611ef2575b50611ee957611d86936119f5565b6102e3936119f5565b611f01905f611da781836105ca565b5f611edb565b611f1361144633611420565b611457576102e390611f80565b6102ad90610294565b6102ad9054611f20565b9073ffffffffffffffffffffffffffffffffffffffff906114fa565b90611f5f6102ad61151b9261112e565b8254611f33565b9160206102e39294936107e460408201965f8301906102c6565b611f8c6116865f610adb565b611f9582610294565b14611ff1577fb2c853ac4d80d18d058c43d8018d077a036e542a79acae1647f5ad2a8c76f4e290611fd1611fc96002611f29565b916002611f4f565b611fdb6002611f29565b90611590611fe860405190565b92839283611f66565b7f9fabe1c1000000000000000000000000000000000000000000000000000000005f908152600490fd5b6102e390611f07565b61203061144633611420565b611457576102e3906115907f658ff1688002926d8f426cb10c052ec29003f50042df9652d8613484c1a586479161188b816003611806565b6102e390612024565b919392914285106119b457906102e3939491612095565b919082039182116110d357565b9150604082016120d0611abc6120ab8386611a22565b611ab66120c06120bb878a611a22565b905090565b6120ca6001610b10565b90612088565b6120fc611aed7f0000000000000000000000000000000000000000000000000000000000000000610294565b0361213257506102e3929161211c916121143061112e565b913390612910565b61212581613242565b61212d610ae4565b6132be565b61147f612159611abc8584611ab66120c06120bb6121515f9986611a22565b949095611a22565b7f4f157e96000000000000000000000000000000000000000000000000000000008352600483016102d3565b906102e3939291612071565b906102e3916121a26113ff826113d1565b906102e3916124f2565b906102e391612191565b6102e3907f339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab956121ac565b6121e861105d565b906060825260208080808086016060815201606081520160608152016060905250565b6102ad6121e0565b919082018092116110d357565b90610b9561064b83610623565b369037565b906102e361224861224284612220565b93610623565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0016020840161222d565b9061227c825190565b811015610733576020809102010190565b6102ad5f610b88565b9594929093946122a461220b565b506122ae5f610b10565b8614801561241f575b6123f05783156123e7576122cc855b8861333a565b946003976122d960035490565b956122f36122ee6122e8875190565b89612213565b612232565b975f998a5b8981101561233057806123258c8f8361231a61075061232b9761232094610713565b92612273565b6111cc565b60010190565b6122f8565b50919395979a50919395985b6123476102ad895190565b81101561238157806123258c6123206123768461237061236b8f9861237c99612273565b611797565b93612213565b8d612273565b61233c565b5091939690929498506123ac95506102ad9761239c5f610b10565b976123a689612232565b96613360565b906123bc6102ad60208401515190565b03613792576123d06123cc61228d565b8252565b6123e26123db61228d565b6040830152565b613792565b6122cc836122c6565b7f73f24386000000000000000000000000000000000000000000000000000000005f9081526004879052602490fd5b5061242a6005610b10565b8610156122b7565b6102e390339061244a612443610b1d565b918261140e565b611675565b6102e39033906138a0565b9060ff906114fa565b906124736102ad61151b92151590565b825461245a565b6124876114468383611c79565b61248f575050565b6124a860016124a3845f611c9386826113b2565b612463565b6124c26124bc6124b6339390565b9361112e565b9161112e565b917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d6124ed60405190565b5f90a4565b6124fc8282611c79565b612504575050565b6125175f6124a38482611c9386826113b2565b6125256124bc6124b6339390565b917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b6124ed60405190565b9091606082840312610287576102ad6125698484611137565b93604061116d8260208701611144565b9190939261258561108d565b945f956004612592815490565b965f5b885b6125a0826110d8565b101561285657866125b46107508386610713565b6060876125c361123a8461112e565b8c886125ff8d6125d260405190565b988996879586957ffe6cd70d00000000000000000000000000000000000000000000000000000000611276565b03915afa5f928391829181612823575b5061275a57505090508760408761262861123a8561112e565b8c886126378d61124d60405190565b03915afa5f9182919081612737575b506126675750505061266061259791600161129b576110c2565b9050612595565b6126705f6111b9565b60ff8516148015612730575b156126f15761268f6102ad608089015190565b82115b6126a6575b505050612660612597916110c2565b6126d9939c506126bf91929496506112f86112f161105d565b6126cc86604085016111cc565b61131b88606085016111cc565b916125976126606126e95f610adb565b9a915f612697565b6126fa5f610b10565b821415801561269257506080870180516127166113515f610b10565b14908115612725575b50612692565b51905082105f61271f565b508861267c565b909250612752915060403d81116113a25761139281836105ca565b90915f612646565b6127635f6111b9565b60ff861614801561281c575b156127dd576127826102ad60808a015190565b82115b61279a575b50505050612660612597916110c2565b612597949d50612660939750906127b96127d3926112f86112f161105d565b6127c689604085016111cc565b61131b8b606085016111cc565b949a915f8061278a565b6127e65f610b10565b821415801561278557506080880180516128026113515f610b10565b14908115612811575b50612785565b51905082105f61280b565b508961276f565b91509350612848915060603d811161284f575b61284081836105ca565b810190612550565b935f61260f565b503d612836565b5096505093505050509190565b90602082820312610287576102ad91611137565b6102cf90610b10565b909594926102e3946128b76107e4926128ad6080966128a360a088019c5f890152565b6020870190612877565b60408501906102c6565b60608301906102c6565b91946129066107e4929897956128fc60a0966128f26102e39a6128e860c08a019e5f8b0152565b6020890190612877565b60408701906102c6565b60608501906102c6565b60808301906102c6565b92939291905f9461292a6120c06120bb6040870187611a22565b6129406113516102ad6120bb6060890189611a22565b141580156130fa575b80156130a1575b6130775761295d84611a18565b926129675f610b10565b8111801561305a575b613003575b506129d461299261123a61123a611abc611aad60408a018a611a22565b926370a0823194602060808801956129b0611abc611aad898c611a22565b906129ba60405190565b80809781946129c98d60e01b90565b8352600483016102d3565b03915afa908115612cfc57612a60935f92612fe0575b508192612a1c9250612a05611abc611aad60408c018c611a22565b90612a16611abc611aad8a8d611a22565b916139b2565b6020612a3761123a61123a611abc611aad60408c018c611a22565b612a47611abc611aad888b611a22565b90612a5160405190565b80809681946129c98c60e01b90565b03915afa8015612cfc57612a7b925f91612fc7575b50612088565b95612aa2611abc612a8f6040880188611a22565b611ab66120c06120bb60408c018c611a22565b95612ab36120bb6060880188611a22565b91975b82891015612f055786908986612ad86102ad612ad26001610b10565b88612088565b821015612eea5761123a611abc612b3493611ab6612b21612b15611abc612b0261123a988c611a22565b611ab6612b0f6001610b10565b88612213565b985b6040810190611a22565b919092612b2e6001610b10565b90612213565b916020612b4060405190565b8094612b4c8b60e01b90565b82528180612b5d86600483016102d3565b03915afa928315612cfc575f93612eca575b508a60a08a0192612b836120bb858d611a22565b612b8f6113515f610b10565b1182858d83612e9e575b5050505f14612e1a578a612bfb611abc612bc961123a61123a611abc88612bc36060890189611a22565b906110e8565b94612bc3612be1611abc83612bc36040890189611a22565b98612bf5611abc612b026040890189611a22565b95611a22565b90833b1561028757848f96945f928f968490612c4e612c1960405190565b978896879586947f230f11c7000000000000000000000000000000000000000000000000000000008652868d600488016128c1565b03925af19081612e05575b50612de557612d01565b5090612c8861123a61123a611abc612ca296611ab6612b216020986040810190611a22565b6129c98a612c9560405190565b9586948593849360e01b90565b03915afa918215612cfc57612cc892612cc1925f91612cce5750612088565b9860010190565b97612ab6565b612cef915060203d8111612cf5575b612ce781836105ca565b810190612863565b5f612a75565b503d612cdd565b6111ae565b90612d48611abc612d2261123a61123a611abc89612bc36060890189611a22565b95611ab6612b21612d3d611abc84612bc360408a018a611a22565b956040810190611a22565b93803b15610287575f808094612d9487612d6160405190565b998a97889687957feab90da600000000000000000000000000000000000000000000000000000000875260048701612880565b03925af1908115612cfc57612c8861123a61123a611abc8f611ab68f612b2190612ca29a60209a612dd0575b5097985050509650505050612c63565b612ddf905f611da781836105ca565b5f612dc0565b5090612c8861123a61123a611abc612ca296611ab6612b21602098612b17565b612e14905f611da781836105ca565b5f612c59565b90925089612e3e611abc612d2261123a61123a611abc89612bc36060890189611a22565b93803b15610287575f808094612e5787612d6160405190565b03925af1908115612cfc57612c8861123a61123a611abc8f611ab68f612b2190612ca29a60209a612e89575b50612b17565b612e98905f611da781836105ca565b5f612e83565b612eb19350611abc9291612bc391611a22565b612ec0611aed6116865f610adb565b141582858d612b99565b612ee391935060203d8111612cf557612ce781836105ca565b915f612b6f565b5061123a61123a611abc612b3493611ab6612b218b98612b17565b969450965050505060208201612f1d6102ad82611a18565b8410612f8c57507f9fc8352e52998db4087d5e6e1c1aafa38788e749e5d7a24f5cb230f73795440283612f796124b6612f735f612f6c611abc612f6360408b018b611a22565b611ab685610b10565b9701611a18565b9561112e565b93612f8661158760405190565b0390a390565b61147f84612f9a5f93611a18565b907fd28d3eb5000000000000000000000000000000000000000000000000000000008452600484016114bf565b612cef915060203d602011612cf557612ce781836105ca565b612a1c9250612ffd9060203d602011612cf557612ce781836105ca565b916129ea565b8493506130549061301c9061301786611a18565b61393e565b80948461304e613035611abc611aad60408c018c611a22565b936130496130436002611f29565b94611a18565b612088565b926139b2565b5f612975565b5061306560016113c7565b6130716113515f610b10565b11612970565b7f454b6c9a000000000000000000000000000000000000000000000000000000005f908152600490fd5b5060a084016130b36120bb8287611a22565b6130bf6113515f610b10565b1190816130cd575b50612950565b6130dc91506120bb9086611a22565b6130f26113516102ad6120bb6060890189611a22565b14155f6130c7565b5061310b6120bb6080860186611a22565b6131216113516102ad6120bb6060890189611a22565b1415612949565b61315461123a7f000000000000000000000000000000000000000000000000000000000000000061112e565b63d0e30db0813b15610287575f9161317a9161316f60405190565b948593849260e01b90565b825281600481015b03925af18015612cfc576131935750565b6102e3905f611da781836105ca565b6131bb6131b56102ad9263ffffffff1690565b60e01b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b9160206102e39294936114d260408201965f8301906102c6565b61323d60049261322e6102e39561321463a9059cbb6131a2565b9261321e60405190565b96879460208601908152016131e0565b602082018103825203836105ca565b613b04565b61326e61123a7f000000000000000000000000000000000000000000000000000000000000000061112e565b803b15610287576131825f9291839261328660405190565b94859384928391907f2e1a7d4d0000000000000000000000000000000000000000000000000000000083526004830190815260200190565b6132c73061112e565b6132d3611aed85610294565b036132dd57505050565b6132e8611686610ae4565b6132f182610294565b0361332857505f809161330961123a6133229561112e565b9061331360405190565b90818003925af1611e17611dce565b611e5057565b916133356102e39361112e565b6131fa565b9190604061335b61334961220b565b926133548496613b78565b8452613b78565b910152565b949290939695919561337061220b565b5061337a84613b93565b9384986133845f90565b9789888a9861339589848487612579565b91908860808201956133a5875190565b6133b16113515f610b10565b116136e9575b505050505050506001986133cb6001610b10565b8311806136b1575b6133e6575b505050505050505050505090565b808a5b6133f4575b506133d8565b6133ff6102ad885190565b8110156136ac57858a89878f8b8991845f1461368d5761342561168661236b8a85612273565b61342e87610294565b145b61367c5761343c61108d565b5084156136515761345b8561345461236b8b86612273565b888d612579565b9790613468608082015190565b915b6134735f610b10565b831461363d578715613615576134a18a8c8b61349b61236b61349488611797565b938b612273565b91613c04565b965b6134ad8289613c59565b613600576134bd6134c591613b93565b918298613db7565b9888156135b757505061351598816134e15f6135029401611797565b906134fa60606134f360208401611797565b9201611797565b91858a613d47565b61350f61236b8a85612273565b90613360565b881561359c578c61352b611aed61168684613e51565b145b61353d575b506001015b8a6133e9565b881561358e5761354c81613e74565b6135555f610b10565b8b148015613577575b613569575b50613532565b909d5098506135375f613563565b508915613586578a811161355e565b8a811061355e565b61359781613e68565b61354c565b6135a861168682613e34565b6135b18c610294565b1461352d565b6135fb9a9197509293506135e8916135ce81611797565b906135e060406134f360208401611797565b91868a613cd8565b6135f561236b8a85612273565b91613360565b613515565b50505050505050505050506135379060010190565b6136378a858d61363161236b61362a88611797565b928b612273565b90613c04565b966134a3565b505050505050505050506135379060010190565b613669858261366361236b8c87612273565b8d612579565b9790613676608082015190565b9161346a565b505050505050506135379060010190565b61369d61168661236b8a85612273565b6136a682610294565b14613430565b6133ee565b506136c96136c160208601515190565b6115a8610c92565b6136e26113516102ad6136dc6002610b10565b87612088565b11156133d3565b61370c9161370786611446935f8701996137028b611797565b613c04565b613c59565b61371b575b8c9350888f6133b7565b613760959b508a5f1461376b5761375c9361373d613737875190565b91611797565b90613756606061374f60208601611797565b9401611797565b93613d47565b5190565b955f80808080613711565b61375c9361377a613737875190565b9061378c604061374f60208601611797565b93613cd8565b61379a61220b565b506102ad6137aa5f830151613e80565b9161131b6137bb6020830151613f03565b916138086137cc6040830151613f03565b6138016137e960806137e16060870151613f03565b950151613f03565b956137fa6137f561105d565b998a52565b6020890152565b6040870152565b6060850152565b6102ad90610b10565b610b846138309260209261382a815190565b94859290565b93849101610be5565b6138776102ad9392613871613871937f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260170190565b90613818565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000815260110190565b906138ae6114468284611c79565b6138b6575050565b6139006102ad611671936138e86138d86138d261390c96613f8e565b9261380f565b6138e26020610b10565b90614080565b906138f260405190565b938492602084019283613839565b908103825203826105ca565b6040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260048301610c42565b61394b6102ad60016113c7565b821061397657906139716102ad9261396b613964610d7c565b9384612088565b906116b8565b6114b0565b5f61147f8361398560016113c7565b907fa458261b000000000000000000000000000000000000000000000000000000008452600484016114bf565b9291906139c16116863061112e565b6139ca82610294565b146139e0576139db6102e39461112e565b6141e5565b506133356102e39361112e565b156139f457565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815280611671600482016020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b905051906102e3826103e2565b90602082820312610287576102ad91613a57565b15613a7f57565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608490fd5b5f91613b10839261112e565b82602083519301915af1613b2b613b25611dce565b916139ed565b8051613b396113515f610b10565b11613b415750565b613b5d816020613b526102e3945190565b818301019101613a64565b613a78565b906102e3612248613b7284610b88565b93610b49565b90613b8b613b866020610b10565b613b62565b916020830152565b613b9b61220b565b506102ad5f8201519161131b602082015191613808604082015161380160806060850151940151956137fa6137f561105d565b613bda6102cf91610294565b60601b90565b92613bfc6014610b8494613bfc8288613bfc829b9a8399613bce565b018092613bce565b613c399291613c1283610294565b613c1b82610294565b1015613c4f576139009092945b604051958694602086019485613be0565b613c4b613c44825190565b9160200190565b2090565b9361390090613c28565b613c625f610b10565b613c6d6102ad845190565b811015613c9757613c8161375c8285612273565b8214613c8f57600101613c62565b505050600190565b5050505f90565b613871906102ad9392613818565b613ccc92916102e391613cbe60405190565b948592602084019283613c9e565b908103825203836105ca565b9490608093613d1e613d2c92613d0e613d3c96613cf76102cf9a613b78565b613d0760408d0191825190613cac565b9052613b78565b613d0760208b0191825190613cac565b87518891613d079190613cac565b613d076060870191825190613cac565b920191825190613cac565b9190608093916102cf95613d6a6040860191613d64835191613b78565b90613cac565b9052613d7f6020850191613d64835191613b78565b9052613d935f840191613d64835191613b78565b9052613da86060830191613d64835191613b78565b90520191613d64835191613b78565b815191613dcd6122ee600194612b2e6001610b10565b9280613dd85f610b10565b905b613df9575b505090613df6613df06102ad935190565b84612273565b52565b613e046102ad845190565b811015613e2f5780612325613e1f61375c613e299487612273565b613df68389612273565b81613dda565b613ddf565b60406102ad91613e415f90565b500151613e4c610c92565b015190565b60406102ad91613e5e5f90565b5001518051015190565b5f6102ad91613e415f90565b5f6102ad91613e5e5f90565b90815191613e966122ee6020946115a886610b10565b9283613ea15f610b10565b613eb86102ad613eaf865190565b6115a886610b10565b811015613efc57613ef790612325613eed613ee7613ede613ed888610b10565b856116b8565b612b2e88610b10565b87015190565b613df6838a612273565b613ea1565b5093505050565b90815191613f196122ee6020946115a886610b10565b9283613f245f610b10565b613f326102ad613eaf865190565b811015613efc57613f5c90612325613f52613ee7613ede613ed888610b10565b612320838a612273565b613f24565b6102ad6102ad6102ad9273ffffffffffffffffffffffffffffffffffffffff1690565b6102ad60146111b9565b613fab613fa66102ad92613fa0606090565b50611125565b613f61565b6138e2613fb6613f84565b6110d8565b90613fc4825190565b811015610733570160200190565b80156110d3577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b6102ad906140116113516102ad9460ff1690565b901c90565b1561401d57565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815280611671600482016020808252818101527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604082015260600190565b6140a3613b86614099846140946002610b10565b6116b8565b612b2e6002610b10565b9160306140b86140b25f610b10565b85613fbb565b537f780000000000000000000000000000000000000000000000000000000000000061410d61410460019384935f1a6140f96140f386610b10565b89613fbb565b536140946002610b10565b612b2e83610b10565b915b614130575b5050906102ad6102ad9261412a6113515f610b10565b14614016565b909161413b82610b10565b8311156141b6577f303132333435363738396162636465660000000000000000000000000000000061416d600f610b10565b821690601082101561073357839261418d6141aa926141b0941a60f81b90565b5f1a6141998789613fbb565b536141a460046111b9565b90613ffd565b93613fd2565b9161410f565b91614114565b6040906114d26102e394969593966141db60608401985f8501906102c6565b60208301906102c6565b9061323d9061322e6102e3956004956142016323b872dd6131a2565b9361420b60405190565b97889560208701908152016141bc56fea2646970667358221220575e7d2416d18ff2bf2ce8b3cf20997bf0c80ed664a436fc3bfbf02d1b43b0c164736f6c634300081e0033339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab950000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000001bb7b44cc398aaa2b76ac6253f0f5634279db9d00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab10000000000000000000000000000000000000000000000000000000000000003000000000000000000000000abc401a16595077881964683450ad63ab3688dab0000000000000000000000001486882e02e6a1037da6afc324b2f911f4c893a000000000000000000000000064c0861a141a7f80ac0b3a32312acad3c62947f3000000000000000000000000000000000000000000000000000000000000000700000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab10000000000000000000000002f2a2543b76a4166549f7aab2e75bef0aefc5b0f000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000912ce59144191c1204e64559fe8253a0e49e6548000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9000000000000000000000000da10009cbd5d07dd0cecc66161fc93d7c9000da1

Deployed Bytecode

0x6080604052600436101561001a575b3615610018575f80fd5b005b5f3560e01c8062b99e361461027857806301ffc9a714610273578063243f8bfc1461026e578063248a9ca3146102695780632f2ff15d1461026457806331ac99201461025f57806336568abe1461025a5780633a9a40811461025557806352a52ab01461025057806356ca76b41461024b578063645d916b146102465780636b453c1f1461024157806375c834b51461023c57806376c7a3c71461023757806376ebe69c1461023257806377d007f71461022d5780637c7a561b146102285780638980f11f146102235780638bb9c5bf1461021e57806391d1485414610219578063952e901214610214578063974584601461020f578063a0cf0aea1461020a578063a217fddf14610205578063a3f4df7e14610200578063aede3693146101fb578063b0873e5e146101f6578063b381cf40146101f1578063b5ce5649146101ec578063c3accd48146101e7578063c8a3a5c6146101e2578063cc299d4f146101dd578063d547741f146101d8578063d73792a9146101d3578063d8baf7cf146101ce578063db6a5897146101c9578063f2fde38b146101c45763f87422540361000e57610f88565b610f70565b610f42565b610daa565b610d8f565b610d63565b610d47565b610d2f565b610d17565b610cf8565b610cbf565b610ca4565b610c7a565b610c53565b610b2e565b610af5565b610aaf565b610a22565b6109ee565b6109d6565b6109bd565b61097e565b610967565b61094b565b610930565b6108f3565b610885565b610852565b6107eb565b610756565b6106ce565b610584565b61056c565b61054e565b6104ff565b6104bd565b61035c565b6102e5565b5f91031261028757565b5f80fd5b6102ad916008021c5b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b906102ad915461028b565b6102ad5f60026102b0565b6102cf90610294565b9052565b6020810192916102e391906102c6565b565b34610287576102f536600461027d565b61030c6103006102bb565b604051918291826102d3565b0390f35b7fffffffff0000000000000000000000000000000000000000000000000000000081165b0361028757565b905035906102e382610310565b90602082820312610287576102ad9161033b565b346102875761030c610377610372366004610348565b610fc1565b60405191829182901515815260200190565b80610334565b905035906102e382610389565b61033481610294565b905035906102e38261039c565b909182601f830112156102875781359167ffffffffffffffff831161028757602001926020830284011161028757565b801515610334565b905035906102e3826103e2565b9160a0838303126102875761040c828461038f565b9261041a83602083016103a5565b9261042881604084016103a5565b92606083013567ffffffffffffffff8111610287578261044f6080946102ad9387016103b2565b949095016103ea565b906080806102e3936104705f8201515f8601906102c6565b610482602082015160208601906102c6565b610494604082015160408601906102c6565b6104a6606082015160608601906102c6565b0151910152565b60a0810192916102e39190610458565b346102875761030c6104df6104d33660046103f7565b949390939291926111d6565b604051918291826104ad565b90602082820312610287576102ad9161038f565b346102875761030c61051a6105153660046104eb565b6113d1565b6040515b9182918290815260200190565b9190604083820312610287576102ad906020610547828661038f565b94016103a5565b346102875761056761056136600461052b565b9061140e565b604051005b346102875761056761057f3660046104eb565b6115db565b346102875761056761059736600461052b565b90611675565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810190811067ffffffffffffffff82111761060a57604052565b61059d565b906102e361061c60405190565b92836105ca565b67ffffffffffffffff811161060a5760208091020190565b9092919261065061064b82610623565b61060f565b938185526020808601920283019281841161028757915b8383106106745750505050565b6020809161068284866103a5565b815201920191610667565b9080601f83011215610287578160206102ad9335910161063b565b9060208282031261028757813567ffffffffffffffff8111610287576102ad920161068d565b34610287576105676106e13660046106a8565b611897565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b80548210156107335761072b6001915f5260205f2090565b910201905f90565b6106e6565b600354811015610287576107506102ad916003610713565b906102b0565b346102875761030c61030061076c3660046104eb565b610738565b60ff8116610334565b905035906102e382610771565b919060a0838203126102875761079d818461038f565b926107ab82602083016103a5565b926102ad6107bc84604085016103a5565b9360806107cc826060870161077a565b94016103ea565b9081526040810192916102e3916020905b01906102c6565b34610287576108076107fe366004610787565b939290926118a0565b9061030c61081460405190565b928392836107d3565b60808183031261028757610831828261038f565b926102ad61084284602085016103a5565b9360606107cc82604087016103a5565b346102875761030c6104df61086836600461081d565b9291909161195a565b90602082820312610287576102ad916103a5565b3461028757610567610898366004610871565b611973565b908160c09103126102875790565b60808183031261028757803567ffffffffffffffff811161028757826108d291830161089d565b926102ad6108e3846020850161038f565b936060610547826040870161038f565b34610287576105676109063660046108ab565b929190916119f5565b6102ad916008021c81565b906102ad915461090f565b6102ad5f600161091a565b346102875761094036600461027d565b61030c61051a610925565b346102875761095b36600461027d565b61030c61051a60035490565b6105676109753660046108ab565b92919091611ba3565b346102875761098e36600461027d565b61030c61051a60045490565b9190604083820312610287576102ad9060206109b682866103a5565b940161038f565b34610287576105676109d036600461099a565b90611c48565b34610287576105676109e93660046104eb565b611c52565b346102875761030c610377610a0436600461052b565b90611c79565b600454811015610287576107506102ad916004610713565b346102875761030c610300610a383660046104eb565b610a0a565b60e08183031261028757803567ffffffffffffffff81116102875782610a6491830161089d565b92610a72836020840161038f565b92610a80816040850161038f565b92610a8e82606083016103a5565b926102ad610a9f846080850161077a565b9360c06109b68260a0870161038f565b3461028757610567610ac2366004610a3d565b95949094939193611ce1565b6102946102ad6102ad9290565b6102ad90610ace565b6102ad5f610adb565b6102ad610ae4565b3461028757610b0536600461027d565b61030c610300610aed565b6102ad6102ad6102ad9290565b6102ad5f610b10565b6102ad610b1d565b3461028757610b3e36600461027d565b61030c61051a610b26565b67ffffffffffffffff811161060a57602090601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b0190565b90610b9561064b83610b49565b918252565b610ba46010610b88565b7f43616d656c6f7459616b526f7574657200000000000000000000000000000000602082015290565b6102ad610b9a565b6102ad610bcd565b6102ad610bd5565b90825f9392825e0152565b610c11610c1a602093610b8493610c05815190565b80835293849260200190565b95869101610be5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690565b60208082526102ad92910190610bf0565b3461028757610c6336600461027d565b61030c610c6e610bdd565b60405191829182610c42565b3461028757610567610c8d3660046104eb565b611e7a565b6102ad6020610b10565b6102ad610c92565b3461028757610cb436600461027d565b61030c61051a610c9c565b3461028757610ccf36600461027d565b61030c7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1610300565b3461028757610567610d0b366004610a3d565b95949094939193611e83565b3461028757610567610d2a366004610871565b61201b565b3461028757610567610d423660046106a8565b612068565b3461028757610567610d5a3660046108ab565b92919091612185565b3461028757610567610d7636600461052b565b906121ac565b6102ad612710610b10565b6102ad610d7c565b3461028757610d9f36600461027d565b61030c61051a610d87565b3461028757610567610dbd366004610871565b6121b6565b909160c08284031261028757610dd8838361038f565b92610de681602085016103a5565b92610df482604083016103a5565b92606082013567ffffffffffffffff811161028757610e18846102ad92850161068d565b9360a06107cc826080870161038f565b90610e48610e41610e37845190565b8084529260200190565b9260200190565b905f5b818110610e585750505090565b909192610e75610e6e6001928651815260200190565b9460200190565b929101610e4b565b90610b84816020936102c6565b90610e99610e41610e37845190565b905f5b818110610ea95750505090565b909192610ebc610e6e6001928651610e7d565b929101610e9c565b6102ad916080610f20610f0e610efc610eea60a086015f8801518782035f890152610e28565b60208701518682036020880152610e8a565b60408601518582036040870152610e8a565b60608501518482036060860152610e8a565b920151906080818403910152610e8a565b60208082526102ad92910190610ec4565b346102875761030c610f64610f58366004610dc2565b94939093929192612296565b60405191829182610f31565b3461028757610567610f83366004610871565b612432565b3461028757610f9836600461027d565b61030c7f339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab9561051a565b7f7965db0b000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821614908115611011575090565b6102ad91507fffffffff00000000000000000000000000000000000000000000000000000000167f01ffc9a7000000000000000000000000000000000000000000000000000000001490565b6102ad60a061060f565b61106f61105d565b905f825260208080808086015f8152015f8152015f8152015f905250565b6102ad611067565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b60ff1660ff81146110d35760010190565b611095565b6102ad6102ad6102ad9260ff1690565b9190811015610733576020020190565b356102ad81610771565b6102946102ad6102ad9273ffffffffffffffffffffffffffffffffffffffff1690565b6102ad90611102565b6102ad90611125565b905051906102e382610389565b905051906102e38261039c565b9190604083820312610287576102ad90602061116d8286611137565b9401611144565b6111a66102e39461119c606094989795611192608086019a5f870152565b60208501906102c6565b60408301906102c6565b019015159052565b6040513d5f823e3d90fd5b6111c66102ad6102ad9290565b60ff1690565b906102cf90610294565b93959491926111e361108d565b506111ec61108d565b965f5b816111f9826110d8565b10156113a95760048660408661122c61075061122661122161121a896110d8565b8a8c6110e8565b6110f8565b86610713565b938b61123f61123a8761112e565b61112e565b9161127f8c61124d60405190565b978896879586957f409d653d000000000000000000000000000000000000000000000000000000005b87528601611174565b03915afa5f9182919081611376575b506112a5575050506112a0905b6110c2565b6111ef565b8b6112af5f6111b9565b60ff861614801561136f575b1561132b576080015182115b6112d8575b5050506112a0906110c2565b6112a0939b5090611301611322926112f86112f161105d565b95866111cc565b602085016111cc565b61130e87604085016111cc565b61131b89606085016111cc565b6080830152565b98905f806112cc565b506113355f610b10565b82141580156112c7575060808c0180516113556113515f610b10565b9190565b14908115611364575b506112c7565b51905082105f61135e565b50886112bb565b90925061139a915060403d81116113a2575b61139281836105ca565b810190611151565b90915f61128e565b503d611388565b50505050505050565b905b5f5260205260405f2090565b6102ad9081565b6102ad90546113c0565b60016113e86102ad926113e15f90565b505f6113b2565b016113c7565b906102e3916114046113ff826113d1565b61244f565b906102e39161247a565b906102e3916113ee565b61144a611446335b7f339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab95611c79565b1590565b611457576102e39061151f565b7f710bcc40000000000000000000000000000000000000000000000000000000005f90815260045b035ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b81156114ba570490565b611483565b9081526040810192916102e39160200152565b0152565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905b9181191691161790565b906115146102ad61151b92610b10565b82546114d6565b9055565b611527610d7c565b61153d6102ad611537600a610b10565b836114b0565b821161159557507f4bb8a6184424e4bb853a4836042f5a726e4e710873989bfc6abdab19966f5b709061157060016113c7565b61157b826001611504565b61159061158760405190565b928392836114bf565b0390a1565b5f916115ae61147f926115a8600a610b10565b906114b0565b907f38c7b9ea000000000000000000000000000000000000000000000000000000008452600484016114bf565b6102e390611418565b156115eb57565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c660000000000000000000000000000000000606482015280608481015b0390fd5b6102e3919061169a61168b33610294565b610294565b61169484610294565b146115e4565b6124f2565b6116ab61144633611420565b611457576102e39061185b565b818102929181159184041417156110d357565b919060086114fa9102916116fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841b90565b921b90565b91906117146102ad61151b93610b10565b9083546116cb565b6102e3915f91611703565b818110611732575050565b8061173f5f60019361171c565b01611727565b91909182821061175457505050565b6102e3929161176790915f5260205f2090565b9182019101611727565b9068010000000000000000811161060a578161178e6102e3935490565b90828155611745565b6102ad9051610294565b81519167ffffffffffffffff831161060a576117d66117cc6001926117c68686611771565b60200190565b925f5260205f2090565b9204915f5b8381106117e85750505050565b60019060206117f96102ad86611797565b94019381840155016117db565b906102e3916117a1565b9061181f610e41610e37845190565b905f5b81811061182f5750505090565b909192611842610e6e6001928651610e7d565b929101611822565b60208082526102ad92910190611810565b6115907febf7325f48e05e5e38809c69f8b02a7c907ed31d8768e6c2d841b1296a9225fe9161188b816004611806565b6040519182918261184a565b6102e39061169f565b90949293916040915f966118bd61123a806107505f9a6004610713565b916118fd6118ca60405190565b968795869485947f409d653d00000000000000000000000000000000000000000000000000000000865260048601611174565b03915afa805f80939092611937575b50611931575050600161191b57565b9150506119275f610adb565b906113515f610b10565b93509150565b909250611953915060403d6040116113a25761139281836105ca565b915f61190c565b9061196f93929161196961108d565b50612579565b5090565b6102e3907f339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab9561140e565b919392914285106119b457906102e39394916119e5565b7f83f2ba20000000000000000000000000000000000000000000000000000000005f90815261147f428760046114bf565b6119f29392503390612910565b50565b906102e393929161199d565b919392914285106119b457906102e3939491611a81565b356102ad81610389565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13682900301821215610287570180359067ffffffffffffffff82116102875760200191602082023603831361028757565b356102ad8161039c565b9392915083611a926102ad82611a18565b3403611b69575060408401611ac1611abc611aad8388611a22565b611ab65f610b10565b916110e8565b611a77565b611af3611aed7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1610294565b91610294565b03611b20575083611b11611b0c5f6119f2969701611a18565b613128565b611b1a3061112e565b90612910565b61147f611b3d611abc611b345f9489611a22565b611ab686610b10565b7f1c4f4020000000000000000000000000000000000000000000000000000000008352600483016102d3565b61147f611b765f92611a18565b7f1791902400000000000000000000000000000000000000000000000000000000835234600484016114bf565b906102e3939291611a01565b90611bbc61144633611420565b611457576102e391611bcd5f610b10565b8214611c1e57611c19611c0f8261123a85611c087f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa289661112e565b33906131fa565b9261051e60405190565b0390a2565b7faba3a548000000000000000000000000000000000000000000000000000000005f908152600490fd5b906102e391611baf565b6102e39033610597565b906113b49061112e565b6102ad906111c6565b6102ad9054611c66565b6102ad915f611c93611c9993611c8c5f90565b50826113b2565b01611c5c565b611c6f565b949290979695939160e08601985f8701611cb7916102c6565b60208601611cc4916102c6565b6040850152606084015260ff16608083015260a082015260c00152565b95909294939193611d0161123a61123a611abc611aad60408c018c611a22565b611d0a3061112e565b92611d1489611a18565b90823b15610287575f94611d6286928a96611d2e60405190565b998a98899788967fd505accf0000000000000000000000000000000000000000000000000000000088523360048901611c9e565b03925af19081611d98575b50611d8f57611d7d565b50505050565b611d8693612185565b5f808080611d77565b6102e393612185565b611daf905f611da781836105ca565b81019061027d565b5f611d6d565b611dc161144633611420565b611457576102e390611dec565b3d15611de757611ddd3d610b88565b903d5f602084013e565b606090565b611df55f610b10565b8114611c1e57611e1c5f80611e0960405190565b5f9085335af1611e17611dce565b501590565b611e50577f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa28611c19611c0f61123a5f610adb565b7fb12d13eb000000000000000000000000000000000000000000000000000000005f908152600490fd5b6102e390611db5565b95909294939193611ea361123a61123a611abc611aad60408c018c611a22565b611eac3061112e565b92611eb689611a18565b90823b15610287575f94611ed086928a96611d2e60405190565b03925af19081611ef2575b50611ee957611d86936119f5565b6102e3936119f5565b611f01905f611da781836105ca565b5f611edb565b611f1361144633611420565b611457576102e390611f80565b6102ad90610294565b6102ad9054611f20565b9073ffffffffffffffffffffffffffffffffffffffff906114fa565b90611f5f6102ad61151b9261112e565b8254611f33565b9160206102e39294936107e460408201965f8301906102c6565b611f8c6116865f610adb565b611f9582610294565b14611ff1577fb2c853ac4d80d18d058c43d8018d077a036e542a79acae1647f5ad2a8c76f4e290611fd1611fc96002611f29565b916002611f4f565b611fdb6002611f29565b90611590611fe860405190565b92839283611f66565b7f9fabe1c1000000000000000000000000000000000000000000000000000000005f908152600490fd5b6102e390611f07565b61203061144633611420565b611457576102e3906115907f658ff1688002926d8f426cb10c052ec29003f50042df9652d8613484c1a586479161188b816003611806565b6102e390612024565b919392914285106119b457906102e3939491612095565b919082039182116110d357565b9150604082016120d0611abc6120ab8386611a22565b611ab66120c06120bb878a611a22565b905090565b6120ca6001610b10565b90612088565b6120fc611aed7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1610294565b0361213257506102e3929161211c916121143061112e565b913390612910565b61212581613242565b61212d610ae4565b6132be565b61147f612159611abc8584611ab66120c06120bb6121515f9986611a22565b949095611a22565b7f4f157e96000000000000000000000000000000000000000000000000000000008352600483016102d3565b906102e3939291612071565b906102e3916121a26113ff826113d1565b906102e3916124f2565b906102e391612191565b6102e3907f339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab956121ac565b6121e861105d565b906060825260208080808086016060815201606081520160608152016060905250565b6102ad6121e0565b919082018092116110d357565b90610b9561064b83610623565b369037565b906102e361224861224284612220565b93610623565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0016020840161222d565b9061227c825190565b811015610733576020809102010190565b6102ad5f610b88565b9594929093946122a461220b565b506122ae5f610b10565b8614801561241f575b6123f05783156123e7576122cc855b8861333a565b946003976122d960035490565b956122f36122ee6122e8875190565b89612213565b612232565b975f998a5b8981101561233057806123258c8f8361231a61075061232b9761232094610713565b92612273565b6111cc565b60010190565b6122f8565b50919395979a50919395985b6123476102ad895190565b81101561238157806123258c6123206123768461237061236b8f9861237c99612273565b611797565b93612213565b8d612273565b61233c565b5091939690929498506123ac95506102ad9761239c5f610b10565b976123a689612232565b96613360565b906123bc6102ad60208401515190565b03613792576123d06123cc61228d565b8252565b6123e26123db61228d565b6040830152565b613792565b6122cc836122c6565b7f73f24386000000000000000000000000000000000000000000000000000000005f9081526004879052602490fd5b5061242a6005610b10565b8610156122b7565b6102e390339061244a612443610b1d565b918261140e565b611675565b6102e39033906138a0565b9060ff906114fa565b906124736102ad61151b92151590565b825461245a565b6124876114468383611c79565b61248f575050565b6124a860016124a3845f611c9386826113b2565b612463565b6124c26124bc6124b6339390565b9361112e565b9161112e565b917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d6124ed60405190565b5f90a4565b6124fc8282611c79565b612504575050565b6125175f6124a38482611c9386826113b2565b6125256124bc6124b6339390565b917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b6124ed60405190565b9091606082840312610287576102ad6125698484611137565b93604061116d8260208701611144565b9190939261258561108d565b945f956004612592815490565b965f5b885b6125a0826110d8565b101561285657866125b46107508386610713565b6060876125c361123a8461112e565b8c886125ff8d6125d260405190565b988996879586957ffe6cd70d00000000000000000000000000000000000000000000000000000000611276565b03915afa5f928391829181612823575b5061275a57505090508760408761262861123a8561112e565b8c886126378d61124d60405190565b03915afa5f9182919081612737575b506126675750505061266061259791600161129b576110c2565b9050612595565b6126705f6111b9565b60ff8516148015612730575b156126f15761268f6102ad608089015190565b82115b6126a6575b505050612660612597916110c2565b6126d9939c506126bf91929496506112f86112f161105d565b6126cc86604085016111cc565b61131b88606085016111cc565b916125976126606126e95f610adb565b9a915f612697565b6126fa5f610b10565b821415801561269257506080870180516127166113515f610b10565b14908115612725575b50612692565b51905082105f61271f565b508861267c565b909250612752915060403d81116113a25761139281836105ca565b90915f612646565b6127635f6111b9565b60ff861614801561281c575b156127dd576127826102ad60808a015190565b82115b61279a575b50505050612660612597916110c2565b612597949d50612660939750906127b96127d3926112f86112f161105d565b6127c689604085016111cc565b61131b8b606085016111cc565b949a915f8061278a565b6127e65f610b10565b821415801561278557506080880180516128026113515f610b10565b14908115612811575b50612785565b51905082105f61280b565b508961276f565b91509350612848915060603d811161284f575b61284081836105ca565b810190612550565b935f61260f565b503d612836565b5096505093505050509190565b90602082820312610287576102ad91611137565b6102cf90610b10565b909594926102e3946128b76107e4926128ad6080966128a360a088019c5f890152565b6020870190612877565b60408501906102c6565b60608301906102c6565b91946129066107e4929897956128fc60a0966128f26102e39a6128e860c08a019e5f8b0152565b6020890190612877565b60408701906102c6565b60608501906102c6565b60808301906102c6565b92939291905f9461292a6120c06120bb6040870187611a22565b6129406113516102ad6120bb6060890189611a22565b141580156130fa575b80156130a1575b6130775761295d84611a18565b926129675f610b10565b8111801561305a575b613003575b506129d461299261123a61123a611abc611aad60408a018a611a22565b926370a0823194602060808801956129b0611abc611aad898c611a22565b906129ba60405190565b80809781946129c98d60e01b90565b8352600483016102d3565b03915afa908115612cfc57612a60935f92612fe0575b508192612a1c9250612a05611abc611aad60408c018c611a22565b90612a16611abc611aad8a8d611a22565b916139b2565b6020612a3761123a61123a611abc611aad60408c018c611a22565b612a47611abc611aad888b611a22565b90612a5160405190565b80809681946129c98c60e01b90565b03915afa8015612cfc57612a7b925f91612fc7575b50612088565b95612aa2611abc612a8f6040880188611a22565b611ab66120c06120bb60408c018c611a22565b95612ab36120bb6060880188611a22565b91975b82891015612f055786908986612ad86102ad612ad26001610b10565b88612088565b821015612eea5761123a611abc612b3493611ab6612b21612b15611abc612b0261123a988c611a22565b611ab6612b0f6001610b10565b88612213565b985b6040810190611a22565b919092612b2e6001610b10565b90612213565b916020612b4060405190565b8094612b4c8b60e01b90565b82528180612b5d86600483016102d3565b03915afa928315612cfc575f93612eca575b508a60a08a0192612b836120bb858d611a22565b612b8f6113515f610b10565b1182858d83612e9e575b5050505f14612e1a578a612bfb611abc612bc961123a61123a611abc88612bc36060890189611a22565b906110e8565b94612bc3612be1611abc83612bc36040890189611a22565b98612bf5611abc612b026040890189611a22565b95611a22565b90833b1561028757848f96945f928f968490612c4e612c1960405190565b978896879586947f230f11c7000000000000000000000000000000000000000000000000000000008652868d600488016128c1565b03925af19081612e05575b50612de557612d01565b5090612c8861123a61123a611abc612ca296611ab6612b216020986040810190611a22565b6129c98a612c9560405190565b9586948593849360e01b90565b03915afa918215612cfc57612cc892612cc1925f91612cce5750612088565b9860010190565b97612ab6565b612cef915060203d8111612cf5575b612ce781836105ca565b810190612863565b5f612a75565b503d612cdd565b6111ae565b90612d48611abc612d2261123a61123a611abc89612bc36060890189611a22565b95611ab6612b21612d3d611abc84612bc360408a018a611a22565b956040810190611a22565b93803b15610287575f808094612d9487612d6160405190565b998a97889687957feab90da600000000000000000000000000000000000000000000000000000000875260048701612880565b03925af1908115612cfc57612c8861123a61123a611abc8f611ab68f612b2190612ca29a60209a612dd0575b5097985050509650505050612c63565b612ddf905f611da781836105ca565b5f612dc0565b5090612c8861123a61123a611abc612ca296611ab6612b21602098612b17565b612e14905f611da781836105ca565b5f612c59565b90925089612e3e611abc612d2261123a61123a611abc89612bc36060890189611a22565b93803b15610287575f808094612e5787612d6160405190565b03925af1908115612cfc57612c8861123a61123a611abc8f611ab68f612b2190612ca29a60209a612e89575b50612b17565b612e98905f611da781836105ca565b5f612e83565b612eb19350611abc9291612bc391611a22565b612ec0611aed6116865f610adb565b141582858d612b99565b612ee391935060203d8111612cf557612ce781836105ca565b915f612b6f565b5061123a61123a611abc612b3493611ab6612b218b98612b17565b969450965050505060208201612f1d6102ad82611a18565b8410612f8c57507f9fc8352e52998db4087d5e6e1c1aafa38788e749e5d7a24f5cb230f73795440283612f796124b6612f735f612f6c611abc612f6360408b018b611a22565b611ab685610b10565b9701611a18565b9561112e565b93612f8661158760405190565b0390a390565b61147f84612f9a5f93611a18565b907fd28d3eb5000000000000000000000000000000000000000000000000000000008452600484016114bf565b612cef915060203d602011612cf557612ce781836105ca565b612a1c9250612ffd9060203d602011612cf557612ce781836105ca565b916129ea565b8493506130549061301c9061301786611a18565b61393e565b80948461304e613035611abc611aad60408c018c611a22565b936130496130436002611f29565b94611a18565b612088565b926139b2565b5f612975565b5061306560016113c7565b6130716113515f610b10565b11612970565b7f454b6c9a000000000000000000000000000000000000000000000000000000005f908152600490fd5b5060a084016130b36120bb8287611a22565b6130bf6113515f610b10565b1190816130cd575b50612950565b6130dc91506120bb9086611a22565b6130f26113516102ad6120bb6060890189611a22565b14155f6130c7565b5061310b6120bb6080860186611a22565b6131216113516102ad6120bb6060890189611a22565b1415612949565b61315461123a7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab161112e565b63d0e30db0813b15610287575f9161317a9161316f60405190565b948593849260e01b90565b825281600481015b03925af18015612cfc576131935750565b6102e3905f611da781836105ca565b6131bb6131b56102ad9263ffffffff1690565b60e01b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b9160206102e39294936114d260408201965f8301906102c6565b61323d60049261322e6102e39561321463a9059cbb6131a2565b9261321e60405190565b96879460208601908152016131e0565b602082018103825203836105ca565b613b04565b61326e61123a7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab161112e565b803b15610287576131825f9291839261328660405190565b94859384928391907f2e1a7d4d0000000000000000000000000000000000000000000000000000000083526004830190815260200190565b6132c73061112e565b6132d3611aed85610294565b036132dd57505050565b6132e8611686610ae4565b6132f182610294565b0361332857505f809161330961123a6133229561112e565b9061331360405190565b90818003925af1611e17611dce565b611e5057565b916133356102e39361112e565b6131fa565b9190604061335b61334961220b565b926133548496613b78565b8452613b78565b910152565b949290939695919561337061220b565b5061337a84613b93565b9384986133845f90565b9789888a9861339589848487612579565b91908860808201956133a5875190565b6133b16113515f610b10565b116136e9575b505050505050506001986133cb6001610b10565b8311806136b1575b6133e6575b505050505050505050505090565b808a5b6133f4575b506133d8565b6133ff6102ad885190565b8110156136ac57858a89878f8b8991845f1461368d5761342561168661236b8a85612273565b61342e87610294565b145b61367c5761343c61108d565b5084156136515761345b8561345461236b8b86612273565b888d612579565b9790613468608082015190565b915b6134735f610b10565b831461363d578715613615576134a18a8c8b61349b61236b61349488611797565b938b612273565b91613c04565b965b6134ad8289613c59565b613600576134bd6134c591613b93565b918298613db7565b9888156135b757505061351598816134e15f6135029401611797565b906134fa60606134f360208401611797565b9201611797565b91858a613d47565b61350f61236b8a85612273565b90613360565b881561359c578c61352b611aed61168684613e51565b145b61353d575b506001015b8a6133e9565b881561358e5761354c81613e74565b6135555f610b10565b8b148015613577575b613569575b50613532565b909d5098506135375f613563565b508915613586578a811161355e565b8a811061355e565b61359781613e68565b61354c565b6135a861168682613e34565b6135b18c610294565b1461352d565b6135fb9a9197509293506135e8916135ce81611797565b906135e060406134f360208401611797565b91868a613cd8565b6135f561236b8a85612273565b91613360565b613515565b50505050505050505050506135379060010190565b6136378a858d61363161236b61362a88611797565b928b612273565b90613c04565b966134a3565b505050505050505050506135379060010190565b613669858261366361236b8c87612273565b8d612579565b9790613676608082015190565b9161346a565b505050505050506135379060010190565b61369d61168661236b8a85612273565b6136a682610294565b14613430565b6133ee565b506136c96136c160208601515190565b6115a8610c92565b6136e26113516102ad6136dc6002610b10565b87612088565b11156133d3565b61370c9161370786611446935f8701996137028b611797565b613c04565b613c59565b61371b575b8c9350888f6133b7565b613760959b508a5f1461376b5761375c9361373d613737875190565b91611797565b90613756606061374f60208601611797565b9401611797565b93613d47565b5190565b955f80808080613711565b61375c9361377a613737875190565b9061378c604061374f60208601611797565b93613cd8565b61379a61220b565b506102ad6137aa5f830151613e80565b9161131b6137bb6020830151613f03565b916138086137cc6040830151613f03565b6138016137e960806137e16060870151613f03565b950151613f03565b956137fa6137f561105d565b998a52565b6020890152565b6040870152565b6060850152565b6102ad90610b10565b610b846138309260209261382a815190565b94859290565b93849101610be5565b6138776102ad9392613871613871937f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260170190565b90613818565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000815260110190565b906138ae6114468284611c79565b6138b6575050565b6139006102ad611671936138e86138d86138d261390c96613f8e565b9261380f565b6138e26020610b10565b90614080565b906138f260405190565b938492602084019283613839565b908103825203826105ca565b6040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260048301610c42565b61394b6102ad60016113c7565b821061397657906139716102ad9261396b613964610d7c565b9384612088565b906116b8565b6114b0565b5f61147f8361398560016113c7565b907fa458261b000000000000000000000000000000000000000000000000000000008452600484016114bf565b9291906139c16116863061112e565b6139ca82610294565b146139e0576139db6102e39461112e565b6141e5565b506133356102e39361112e565b156139f457565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815280611671600482016020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b905051906102e3826103e2565b90602082820312610287576102ad91613a57565b15613a7f57565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608490fd5b5f91613b10839261112e565b82602083519301915af1613b2b613b25611dce565b916139ed565b8051613b396113515f610b10565b11613b415750565b613b5d816020613b526102e3945190565b818301019101613a64565b613a78565b906102e3612248613b7284610b88565b93610b49565b90613b8b613b866020610b10565b613b62565b916020830152565b613b9b61220b565b506102ad5f8201519161131b602082015191613808604082015161380160806060850151940151956137fa6137f561105d565b613bda6102cf91610294565b60601b90565b92613bfc6014610b8494613bfc8288613bfc829b9a8399613bce565b018092613bce565b613c399291613c1283610294565b613c1b82610294565b1015613c4f576139009092945b604051958694602086019485613be0565b613c4b613c44825190565b9160200190565b2090565b9361390090613c28565b613c625f610b10565b613c6d6102ad845190565b811015613c9757613c8161375c8285612273565b8214613c8f57600101613c62565b505050600190565b5050505f90565b613871906102ad9392613818565b613ccc92916102e391613cbe60405190565b948592602084019283613c9e565b908103825203836105ca565b9490608093613d1e613d2c92613d0e613d3c96613cf76102cf9a613b78565b613d0760408d0191825190613cac565b9052613b78565b613d0760208b0191825190613cac565b87518891613d079190613cac565b613d076060870191825190613cac565b920191825190613cac565b9190608093916102cf95613d6a6040860191613d64835191613b78565b90613cac565b9052613d7f6020850191613d64835191613b78565b9052613d935f840191613d64835191613b78565b9052613da86060830191613d64835191613b78565b90520191613d64835191613b78565b815191613dcd6122ee600194612b2e6001610b10565b9280613dd85f610b10565b905b613df9575b505090613df6613df06102ad935190565b84612273565b52565b613e046102ad845190565b811015613e2f5780612325613e1f61375c613e299487612273565b613df68389612273565b81613dda565b613ddf565b60406102ad91613e415f90565b500151613e4c610c92565b015190565b60406102ad91613e5e5f90565b5001518051015190565b5f6102ad91613e415f90565b5f6102ad91613e5e5f90565b90815191613e966122ee6020946115a886610b10565b9283613ea15f610b10565b613eb86102ad613eaf865190565b6115a886610b10565b811015613efc57613ef790612325613eed613ee7613ede613ed888610b10565b856116b8565b612b2e88610b10565b87015190565b613df6838a612273565b613ea1565b5093505050565b90815191613f196122ee6020946115a886610b10565b9283613f245f610b10565b613f326102ad613eaf865190565b811015613efc57613f5c90612325613f52613ee7613ede613ed888610b10565b612320838a612273565b613f24565b6102ad6102ad6102ad9273ffffffffffffffffffffffffffffffffffffffff1690565b6102ad60146111b9565b613fab613fa66102ad92613fa0606090565b50611125565b613f61565b6138e2613fb6613f84565b6110d8565b90613fc4825190565b811015610733570160200190565b80156110d3577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b6102ad906140116113516102ad9460ff1690565b901c90565b1561401d57565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815280611671600482016020808252818101527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604082015260600190565b6140a3613b86614099846140946002610b10565b6116b8565b612b2e6002610b10565b9160306140b86140b25f610b10565b85613fbb565b537f780000000000000000000000000000000000000000000000000000000000000061410d61410460019384935f1a6140f96140f386610b10565b89613fbb565b536140946002610b10565b612b2e83610b10565b915b614130575b5050906102ad6102ad9261412a6113515f610b10565b14614016565b909161413b82610b10565b8311156141b6577f303132333435363738396162636465660000000000000000000000000000000061416d600f610b10565b821690601082101561073357839261418d6141aa926141b0941a60f81b90565b5f1a6141998789613fbb565b536141a460046111b9565b90613ffd565b93613fd2565b9161410f565b91614114565b6040906114d26102e394969593966141db60608401985f8501906102c6565b60208301906102c6565b9061323d9061322e6102e3956004956142016323b872dd6131a2565b9361420b60405190565b97889560208701908152016141bc56fea2646970667358221220575e7d2416d18ff2bf2ce8b3cf20997bf0c80ed664a436fc3bfbf02d1b43b0c164736f6c634300081e0033

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

0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000001bb7b44cc398aaa2b76ac6253f0f5634279db9d00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab10000000000000000000000000000000000000000000000000000000000000003000000000000000000000000abc401a16595077881964683450ad63ab3688dab0000000000000000000000001486882e02e6a1037da6afc324b2f911f4c893a000000000000000000000000064c0861a141a7f80ac0b3a32312acad3c62947f3000000000000000000000000000000000000000000000000000000000000000700000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab10000000000000000000000002f2a2543b76a4166549f7aab2e75bef0aefc5b0f000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000912ce59144191c1204e64559fe8253a0e49e6548000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9000000000000000000000000da10009cbd5d07dd0cecc66161fc93d7c9000da1

-----Decoded View---------------
Arg [0] : _adapters (address[]): 0xaBC401a16595077881964683450ad63aB3688DAb,0x1486882E02E6A1037DA6AfC324b2F911f4c893A0,0x64C0861A141a7F80Ac0B3a32312acAD3c62947f3
Arg [1] : _trustedTokens (address[]): 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1,0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f,0xaf88d065e77c8cC2239327C5EDb3A432268e5831,0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8,0x912CE59144191C1204E64559FE8253a0e49E6548,0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9,0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1
Arg [2] : _feeClaimer (address): 0x01Bb7B44cc398AaA2b76Ac6253F0F5634279Db9D
Arg [3] : _wrapped_native (address): 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1

-----Encoded View---------------
16 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [2] : 00000000000000000000000001bb7b44cc398aaa2b76ac6253f0f5634279db9d
Arg [3] : 00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [5] : 000000000000000000000000abc401a16595077881964683450ad63ab3688dab
Arg [6] : 0000000000000000000000001486882e02e6a1037da6afc324b2f911f4c893a0
Arg [7] : 00000000000000000000000064c0861a141a7f80ac0b3a32312acad3c62947f3
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [9] : 00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1
Arg [10] : 0000000000000000000000002f2a2543b76a4166549f7aab2e75bef0aefc5b0f
Arg [11] : 000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831
Arg [12] : 000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8
Arg [13] : 000000000000000000000000912ce59144191c1204e64559fe8253a0e49e6548
Arg [14] : 000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9
Arg [15] : 000000000000000000000000da10009cbd5d07dd0cecc66161fc93d7c9000da1


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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