ETH Price: $1,975.69 (+0.68%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Amount:Between 1-100
Reset Filter

Transaction Hash
Method
Block
From
To

There are no matching entries

Update your filters to view other transactions

Amount:Between 1-100
Reset Filter

Advanced mode:
Parent Transaction Hash Method Block
From
To

There are no matching entries

Update your filters to view other transactions

View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
EnhancedSwapRouter

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./LiquidityPoolETH.sol";
import "./interfaces/ISwapRouter.sol";
import "./interfaces/ICurvePool.sol";
import "./interfaces/IAggregationRouter.sol";
import "./interfaces/IDodoexRouter.sol";
import "./interfaces/IBalancerVault.sol";
import "./interfaces/IWETH.sol";

/**
 * @title EnhancedSwapRouter
 * @notice Multi-protocol swap router with intelligent routing and decision logic
 * @dev Supports Uniswap V3, Curve, Dodoex PMM, Balancer, and 1inch aggregation
 */
contract EnhancedSwapRouter is AccessControl, ReentrancyGuard {
    using SafeERC20 for IERC20;
    
    bytes32 public constant COORDINATOR_ROLE = keccak256("COORDINATOR_ROLE");
    bytes32 public constant ROUTING_MANAGER_ROLE = keccak256("ROUTING_MANAGER_ROLE");

    enum SwapProvider {
        UniswapV3,
        Curve,
        Dodoex,
        Balancer,
        OneInch
    }

    // Protocol addresses
    address public immutable uniswapV3Router;
    address public immutable curve3Pool;
    address public immutable dodoexRouter;
    address public immutable balancerVault;
    address public immutable oneInchRouter;
    
    // Token addresses
    address public immutable weth;
    address public immutable usdt;
    address public immutable usdc;
    address public immutable dai;

    // Routing configuration
    struct RoutingConfig {
        SwapProvider[] providers; // Ordered list of providers to try
        uint256[] sizeThresholds; // Size thresholds in wei
        bool enabled;
    }

    mapping(SwapProvider => bool) public providerEnabled;
    mapping(uint256 => RoutingConfig) public sizeBasedRouting; // size category => config
    uint256 public constant SMALL_SWAP_THRESHOLD = 10_000 * 1e18; // $10k
    uint256 public constant MEDIUM_SWAP_THRESHOLD = 100_000 * 1e18; // $100k

    // Uniswap V3 fee tiers
    uint24 public constant FEE_TIER_LOW = 500;
    uint24 public constant FEE_TIER_MEDIUM = 3000;
    uint24 public constant FEE_TIER_HIGH = 10000;

    // Balancer pool IDs (example - would be set via admin)
    mapping(address => mapping(address => bytes32)) public balancerPoolIds; // tokenIn => tokenOut => poolId

    // Dodoex PMM pool addresses (tokenIn => tokenOut => PMM pool address)
    mapping(address => mapping(address => address)) public dodoPoolAddresses;

    /// @dev Uniswap V3 Quoter for on-chain quotes; set via setUniswapQuoter when deployed on 138/651940
    address public uniswapQuoter;

    event SwapExecuted(
        SwapProvider indexed provider,
        LiquidityPoolETH.AssetType indexed inputAsset,
        address indexed tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOut,
        uint256 gasUsed
    );

    event RoutingConfigUpdated(uint256 sizeCategory, SwapProvider[] providers);
    event ProviderToggled(SwapProvider provider, bool enabled);

    error ZeroAddress();
    error ZeroAmount();
    error SwapFailed();
    error InvalidProvider();
    error ProviderDisabled();
    error InsufficientOutput();
    error InvalidRoutingConfig();

    /**
     * @notice Constructor
     * @param _uniswapV3Router Uniswap V3 SwapRouter address
     * @param _curve3Pool Curve 3pool address
     * @param _dodoexRouter Dodoex Router address
     * @param _balancerVault Balancer Vault address
     * @param _oneInchRouter 1inch Router address (can be address(0))
     * @param _weth WETH address
     * @param _usdt USDT address
     * @param _usdc USDC address
     * @param _dai DAI address
     */
    constructor(
        address _uniswapV3Router,
        address _curve3Pool,
        address _dodoexRouter,
        address _balancerVault,
        address _oneInchRouter,
        address _weth,
        address _usdt,
        address _usdc,
        address _dai
    ) {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        
        if (_uniswapV3Router == address(0) || _curve3Pool == address(0) || 
            _dodoexRouter == address(0) || _balancerVault == address(0) ||
            _weth == address(0) || _usdt == address(0) || _usdc == address(0) || _dai == address(0)) {
            revert ZeroAddress();
        }

        uniswapV3Router = _uniswapV3Router;
        curve3Pool = _curve3Pool;
        dodoexRouter = _dodoexRouter;
        balancerVault = _balancerVault;
        oneInchRouter = _oneInchRouter;
        weth = _weth;
        usdt = _usdt;
        usdc = _usdc;
        dai = _dai;

        // Enable all providers by default
        providerEnabled[SwapProvider.UniswapV3] = true;
        providerEnabled[SwapProvider.Curve] = true;
        providerEnabled[SwapProvider.Dodoex] = true;
        providerEnabled[SwapProvider.Balancer] = true;
        if (_oneInchRouter != address(0)) {
            providerEnabled[SwapProvider.OneInch] = true;
        }

        // Initialize default routing configs
        _initializeDefaultRouting();
    }

    /**
     * @notice Swap to stablecoin using intelligent routing
     * @param inputAsset Input asset type (ETH or WETH)
     * @param stablecoinToken Target stablecoin
     * @param amountIn Input amount
     * @param amountOutMin Minimum output (slippage protection)
     * @param preferredProvider Optional preferred provider (0 = auto-select)
     * @return amountOut Output amount
     * @return providerUsed Provider that executed the swap
     */
    function swapToStablecoin(
        LiquidityPoolETH.AssetType inputAsset,
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin,
        SwapProvider preferredProvider
    ) external payable nonReentrant returns (uint256 amountOut, SwapProvider providerUsed) {
        if (amountIn == 0) revert ZeroAmount();
        if (stablecoinToken == address(0)) revert ZeroAddress();
        if (!_isValidStablecoin(stablecoinToken)) revert("EnhancedSwapRouter: invalid stablecoin");

        // Convert ETH to WETH if needed
        if (inputAsset == LiquidityPoolETH.AssetType.ETH) {
            require(msg.value == amountIn, "EnhancedSwapRouter: ETH amount mismatch");
            IWETH(weth).deposit{value: amountIn}();
        }

        // Get routing providers based on swap size
        SwapProvider[] memory providers = _getRoutingProviders(amountIn, preferredProvider);

        // Try each provider in order
        for (uint256 i = 0; i < providers.length; i++) {
            if (!providerEnabled[providers[i]]) continue;

            try this._executeSwap(
                providers[i],
                stablecoinToken,
                amountIn,
                amountOutMin
            ) returns (uint256 output) {
                if (output >= amountOutMin) {
                    // Transfer output to caller
                    IERC20(stablecoinToken).safeTransfer(msg.sender, output);
                    
                    emit SwapExecuted(
                        providers[i],
                        inputAsset,
                        weth,
                        stablecoinToken,
                        amountIn,
                        output,
                        gasleft()
                    );
                    
                    return (output, providers[i]);
                }
            } catch {
                // Try next provider
                continue;
            }
        }

        revert SwapFailed();
    }

    /**
     * @notice Get quote from all enabled providers
     * @param stablecoinToken Target stablecoin
     * @param amountIn Input amount
     * @return providers Array of providers that returned quotes
     * @return amounts Array of output amounts for each provider
     */
    function getQuotes(
        address stablecoinToken,
        uint256 amountIn
    ) external view returns (SwapProvider[] memory providers, uint256[] memory amounts) {
        SwapProvider[] memory enabledProviders = new SwapProvider[](5);
        uint256[] memory quotes = new uint256[](5);
        uint256 count = 0;

        // Query each enabled provider
        if (providerEnabled[SwapProvider.UniswapV3]) {
            try this._getUniswapV3Quote(stablecoinToken, amountIn) returns (uint256 quote) {
                enabledProviders[count] = SwapProvider.UniswapV3;
                quotes[count] = quote;
                count++;
            } catch {}
        }

        if (providerEnabled[SwapProvider.Dodoex]) {
            try this._getDodoexQuote(stablecoinToken, amountIn) returns (uint256 quote) {
                enabledProviders[count] = SwapProvider.Dodoex;
                quotes[count] = quote;
                count++;
            } catch {}
        }

        if (providerEnabled[SwapProvider.Balancer]) {
            try this._getBalancerQuote(stablecoinToken, amountIn) returns (uint256 quote) {
                enabledProviders[count] = SwapProvider.Balancer;
                quotes[count] = quote;
                count++;
            } catch {}
        }

        // Resize arrays
        SwapProvider[] memory resultProviders = new SwapProvider[](count);
        uint256[] memory resultQuotes = new uint256[](count);
        for (uint256 i = 0; i < count; i++) {
            resultProviders[i] = enabledProviders[i];
            resultQuotes[i] = quotes[i];
        }

        return (resultProviders, resultQuotes);
    }

    /**
     * @notice Set routing configuration for a size category
     * @param sizeCategory 0 = small, 1 = medium, 2 = large
     * @param providers Ordered list of providers to try
     */
    function setRoutingConfig(
        uint256 sizeCategory,
        SwapProvider[] calldata providers
    ) external onlyRole(ROUTING_MANAGER_ROLE) {
        require(sizeCategory < 3, "EnhancedSwapRouter: invalid size category");
        require(providers.length > 0, "EnhancedSwapRouter: empty providers");

        sizeBasedRouting[sizeCategory] = RoutingConfig({
            providers: providers,
            sizeThresholds: new uint256[](0),
            enabled: true
        });

        emit RoutingConfigUpdated(sizeCategory, providers);
    }

    /**
     * @notice Toggle provider on/off
     * @param provider Provider to toggle
     * @param enabled Whether to enable
     */
    function setProviderEnabled(
        SwapProvider provider,
        bool enabled
    ) external onlyRole(ROUTING_MANAGER_ROLE) {
        providerEnabled[provider] = enabled;
        emit ProviderToggled(provider, enabled);
    }

    /**
     * @notice Set Balancer pool ID for a token pair
     * @param tokenIn Input token
     * @param tokenOut Output token
     * @param poolId Balancer pool ID
     */
    function setBalancerPoolId(
        address tokenIn,
        address tokenOut,
        bytes32 poolId
    ) external onlyRole(ROUTING_MANAGER_ROLE) {
        balancerPoolIds[tokenIn][tokenOut] = poolId;
    }

    /**
     * @notice Set Dodoex PMM pool address for a token pair
     * @param tokenIn Input token
     * @param tokenOut Output token
     * @param poolAddress Dodo PMM pool address
     */
    function setDodoPoolAddress(
        address tokenIn,
        address tokenOut,
        address poolAddress
    ) external onlyRole(ROUTING_MANAGER_ROLE) {
        dodoPoolAddresses[tokenIn][tokenOut] = poolAddress;
    }

    /**
     * @notice Set Uniswap V3 Quoter address for on-chain quotes
     * @param _quoter Quoter contract address (address(0) to use 0.5% slippage estimate)
     */
    function setUniswapQuoter(address _quoter) external onlyRole(ROUTING_MANAGER_ROLE) {
        uniswapQuoter = _quoter;
    }

    /**
     * @notice Swap arbitrary token pair via Dodoex when pool is configured
     * @param tokenIn Input token
     * @param tokenOut Output token
     * @param amountIn Input amount
     * @param amountOutMin Minimum output (slippage protection)
     * @return amountOut Output amount
     */
    function swapTokenToToken(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOutMin
    ) external nonReentrant returns (uint256 amountOut) {
        if (amountIn == 0) revert ZeroAmount();
        if (tokenIn == address(0) || tokenOut == address(0)) revert ZeroAddress();
        address pool = dodoPoolAddresses[tokenIn][tokenOut];
        require(pool != address(0), "EnhancedSwapRouter: Dodoex pool not configured");

        IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
        IERC20(tokenIn).approve(dodoexRouter, amountIn);

        address[] memory dodoPairs = new address[](1);
        dodoPairs[0] = pool;

        IDodoexRouter.DodoSwapParams memory params = IDodoexRouter.DodoSwapParams({
            fromToken: tokenIn,
            toToken: tokenOut,
            fromTokenAmount: amountIn,
            minReturnAmount: amountOutMin,
            dodoPairs: dodoPairs,
            directions: 0,
            isIncentive: false,
            deadLine: block.timestamp + 300
        });

        amountOut = IDodoexRouter(dodoexRouter).dodoSwapV2TokenToToken(params);
        require(amountOut >= amountOutMin, "EnhancedSwapRouter: insufficient output");
        IERC20(tokenOut).safeTransfer(msg.sender, amountOut);
        return amountOut;
    }

    // ============ Internal Functions ============

    /**
     * @notice Execute swap via specified provider
     */
    function _executeSwap(
        SwapProvider provider,
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin
    ) external returns (uint256) {
        require(msg.sender == address(this), "EnhancedSwapRouter: internal only");

        if (provider == SwapProvider.UniswapV3) {
            return _executeUniswapV3Swap(stablecoinToken, amountIn, amountOutMin);
        } else if (provider == SwapProvider.Dodoex) {
            return _executeDodoexSwap(stablecoinToken, amountIn, amountOutMin);
        } else if (provider == SwapProvider.Balancer) {
            return _executeBalancerSwap(stablecoinToken, amountIn, amountOutMin);
        } else if (provider == SwapProvider.Curve) {
            return _executeCurveSwap(stablecoinToken, amountIn, amountOutMin);
        } else if (provider == SwapProvider.OneInch && oneInchRouter != address(0)) {
            return _execute1inchSwap(stablecoinToken, amountIn, amountOutMin);
        }

        revert InvalidProvider();
    }

    /**
     * @notice Get routing providers based on swap size
     */
    function _getRoutingProviders(
        uint256 amountIn,
        SwapProvider preferredProvider
    ) internal view returns (SwapProvider[] memory) {
        // If preferred provider is specified and enabled, use it first
        if (preferredProvider != SwapProvider.UniswapV3 && providerEnabled[preferredProvider]) {
            SwapProvider[] memory providers = new SwapProvider[](1);
            providers[0] = preferredProvider;
            return providers;
        }

        // Determine size category
        uint256 category;
        if (amountIn < SMALL_SWAP_THRESHOLD) {
            category = 0; // Small
        } else if (amountIn < MEDIUM_SWAP_THRESHOLD) {
            category = 1; // Medium
        } else {
            category = 2; // Large
        }

        RoutingConfig memory config = sizeBasedRouting[category];
        if (config.enabled && config.providers.length > 0) {
            return config.providers;
        }

        // Default fallback routing
        SwapProvider[] memory defaultProviders = new SwapProvider[](5);
        defaultProviders[0] = SwapProvider.Dodoex;
        defaultProviders[1] = SwapProvider.UniswapV3;
        defaultProviders[2] = SwapProvider.Balancer;
        defaultProviders[3] = SwapProvider.Curve;
        defaultProviders[4] = SwapProvider.OneInch;
        return defaultProviders;
    }

    /**
     * @notice Execute Uniswap V3 swap
     */
    function _executeUniswapV3Swap(
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin
    ) internal returns (uint256) {
        // Approve for swap
        IERC20 wethToken = IERC20(weth);
        wethToken.approve(uniswapV3Router, amountIn);

        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
            tokenIn: weth,
            tokenOut: stablecoinToken,
            fee: FEE_TIER_MEDIUM,
            recipient: address(this),
            deadline: block.timestamp + 300,
            amountIn: amountIn,
            amountOutMinimum: amountOutMin,
            sqrtPriceLimitX96: 0
        });

        return ISwapRouter(uniswapV3Router).exactInputSingle(params);
    }

    /**
     * @notice Execute Dodoex PMM swap
     */
    function _executeDodoexSwap(
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin
    ) internal returns (uint256) {
        address pool = dodoPoolAddresses[weth][stablecoinToken];
        require(pool != address(0), "EnhancedSwapRouter: Dodoex pool not configured");

        IERC20 wethToken = IERC20(weth);
        wethToken.approve(dodoexRouter, amountIn);

        address[] memory dodoPairs = new address[](1);
        dodoPairs[0] = pool;

        IDodoexRouter.DodoSwapParams memory params = IDodoexRouter.DodoSwapParams({
            fromToken: weth,
            toToken: stablecoinToken,
            fromTokenAmount: amountIn,
            minReturnAmount: amountOutMin,
            dodoPairs: dodoPairs,
            directions: 0,
            isIncentive: false,
            deadLine: block.timestamp + 300
        });

        return IDodoexRouter(dodoexRouter).dodoSwapV2TokenToToken(params);
    }

    /**
     * @notice Execute Balancer swap
     */
    function _executeBalancerSwap(
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin
    ) internal returns (uint256) {
        bytes32 poolId = balancerPoolIds[weth][stablecoinToken];
        require(poolId != bytes32(0), "EnhancedSwapRouter: pool not configured");

        IERC20 wethToken = IERC20(weth);
        wethToken.approve(balancerVault, amountIn);

        IBalancerVault.SingleSwap memory singleSwap = IBalancerVault.SingleSwap({
            poolId: poolId,
            kind: IBalancerVault.SwapKind.GIVEN_IN,
            assetIn: weth,
            assetOut: stablecoinToken,
            amount: amountIn,
            userData: ""
        });

        IBalancerVault.FundManagement memory funds = IBalancerVault.FundManagement({
            sender: address(this),
            fromInternalBalance: false,
            recipient: payable(address(this)),
            toInternalBalance: false
        });

        return IBalancerVault(balancerVault).swap(
            singleSwap,
            funds,
            amountOutMin,
            block.timestamp + 300
        );
    }

    /**
     * @notice Execute Curve swap
     */
    function _executeCurveSwap(
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin
    ) internal returns (uint256) {
        // Curve 3pool doesn't support WETH directly
        // Would need intermediate swap or different pool
        revert("EnhancedSwapRouter: Curve direct swap not supported");
    }

    /**
     * @notice Execute 1inch swap
     */
    function _execute1inchSwap(
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin
    ) internal returns (uint256) {
        if (oneInchRouter == address(0)) revert ProviderDisabled();
        
        IERC20 wethToken = IERC20(weth);
        wethToken.approve(oneInchRouter, amountIn);
        
        // 1inch: requires route data from 1inch API (e.g. /swap/v5.2/138/swap). Use 1inch SDK or API to get calldata and execute separately.
        revert("EnhancedSwapRouter: 1inch requires route calldata from API; use 1inch aggregator SDK");
    }

    /**
     * @notice Get Uniswap V3 quote (view)
     * When UNISWAP_QUOTER_ADDRESS is configured, queries quoter. Otherwise returns
     * estimate via amountIn * 9950/10000 (0.5% slippage) for stablecoin pairs.
     */
    function _getUniswapV3Quote(
        address stablecoinToken,
        uint256 amountIn
    ) external view returns (uint256) {
        if (uniswapQuoter != address(0) && _isValidStablecoin(stablecoinToken)) {
            // IQuoter.quoteExactInputSingle(tokenIn, tokenOut, fee, amountIn, sqrtPriceLimitX96)
            (bool ok, bytes memory data) = uniswapQuoter.staticcall(
                abi.encodeWithSignature(
                    "quoteExactInputSingle(address,address,uint24,uint256,uint160)",
                    weth,
                    stablecoinToken,
                    FEE_TIER_MEDIUM,
                    amountIn,
                    uint160(0)
                )
            );
            if (ok && data.length >= 32) {
                uint256 quoted = abi.decode(data, (uint256));
                if (quoted > 0) return quoted;
            }
        }
        if (_isValidStablecoin(stablecoinToken)) {
            return (amountIn * 9950) / 10000; // 0.5% slippage estimate for WETH->stable
        }
        return 0;
    }

    /**
     * @notice Get Dodoex quote (view)
     */
    function _getDodoexQuote(
        address stablecoinToken,
        uint256 amountIn
    ) external view returns (uint256) {
        return IDodoexRouter(dodoexRouter).getDodoSwapQuote(weth, stablecoinToken, amountIn);
    }

    /**
     * @notice Get Balancer quote (view)
     * When poolId configured, would query Balancer. Otherwise estimate for stablecoins.
     */
    function _getBalancerQuote(
        address stablecoinToken,
        uint256 amountIn
    ) external view returns (uint256) {
        bytes32 poolId = balancerPoolIds[weth][stablecoinToken];
        if (poolId != bytes32(0)) {
            (address[] memory tokens, uint256[] memory balances,) =
                IBalancerVault(balancerVault).getPoolTokens(poolId);
            if (tokens.length >= 2 && balances.length >= 2) {
                uint256 wethIdx = type(uint256).max;
                uint256 stableIdx = type(uint256).max;
                for (uint256 i = 0; i < tokens.length; i++) {
                    if (tokens[i] == weth) wethIdx = i;
                    if (tokens[i] == stablecoinToken) stableIdx = i;
                }
                if (wethIdx != type(uint256).max && stableIdx != type(uint256).max && balances[wethIdx] > 0) {
                    uint256 amountOut = (amountIn * balances[stableIdx]) / balances[wethIdx];
                    return (amountOut * 9950) / 10000; // 0.5% slippage
                }
            }
        }
        if (_isValidStablecoin(stablecoinToken)) {
            return (amountIn * 9950) / 10000; // 0.5% slippage estimate when pool not configured
        }
        return 0;
    }

    /**
     * @notice Initialize default routing configurations
     */
    function _initializeDefaultRouting() internal {
        // Small swaps (< $10k): Uniswap V3, Dodoex
        SwapProvider[] memory smallProviders = new SwapProvider[](2);
        smallProviders[0] = SwapProvider.UniswapV3;
        smallProviders[1] = SwapProvider.Dodoex;
        sizeBasedRouting[0] = RoutingConfig({
            providers: smallProviders,
            sizeThresholds: new uint256[](0),
            enabled: true
        });

        // Medium swaps ($10k-$100k): Dodoex, Balancer, Uniswap V3
        SwapProvider[] memory mediumProviders = new SwapProvider[](3);
        mediumProviders[0] = SwapProvider.Dodoex;
        mediumProviders[1] = SwapProvider.Balancer;
        mediumProviders[2] = SwapProvider.UniswapV3;
        sizeBasedRouting[1] = RoutingConfig({
            providers: mediumProviders,
            sizeThresholds: new uint256[](0),
            enabled: true
        });

        // Large swaps (> $100k): Dodoex, Curve, Balancer
        SwapProvider[] memory largeProviders = new SwapProvider[](3);
        largeProviders[0] = SwapProvider.Dodoex;
        largeProviders[1] = SwapProvider.Curve;
        largeProviders[2] = SwapProvider.Balancer;
        sizeBasedRouting[2] = RoutingConfig({
            providers: largeProviders,
            sizeThresholds: new uint256[](0),
            enabled: true
        });
    }

    /**
     * @notice Check if token is valid stablecoin
     */
    function _isValidStablecoin(address token) internal view returns (bool) {
        return token == usdt || token == usdc || token == dai;
    }

    // Allow contract to receive ETH
    receive() external payable {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

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

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

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

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

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

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../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 account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    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 returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @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 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 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 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 `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @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 Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

/**
 * @title LiquidityPoolETH
 * @notice Liquidity pool for ETH and WETH with fee model and minimum liquidity ratio enforcement
 * @dev Supports separate pools for native ETH and WETH (ERC-20)
 */
contract LiquidityPoolETH is ReentrancyGuard {
    using SafeERC20 for IERC20;

    enum AssetType {
        ETH,   // Native ETH
        WETH   // Wrapped ETH (ERC-20)
    }

    // Pool configuration
    uint256 public immutable lpFeeBps; // Liquidity provider fee in basis points (default: 5 = 0.05%)
    uint256 public immutable minLiquidityRatioBps; // Minimum liquidity ratio in basis points (default: 11000 = 110%)
    address public immutable weth; // WETH token address
    
    // WETH getter for external access
    function getWeth() external view returns (address) {
        return weth;
    }

    // Pool state
    struct PoolState {
        uint256 totalLiquidity;
        uint256 pendingClaims; // Total amount of pending claims to be released
        mapping(address => uint256) lpShares; // LP address => amount provided
    }

    mapping(AssetType => PoolState) public pools;
    mapping(address => bool) public authorizedRelease; // Contracts authorized to release funds

    event LiquidityProvided(
        AssetType indexed assetType,
        address indexed provider,
        uint256 amount
    );

    event LiquidityWithdrawn(
        AssetType indexed assetType,
        address indexed provider,
        uint256 amount
    );

    event FundsReleased(
        AssetType indexed assetType,
        uint256 indexed depositId,
        address indexed recipient,
        uint256 amount,
        uint256 feeAmount
    );

    event PendingClaimAdded(
        AssetType indexed assetType,
        uint256 amount
    );

    event PendingClaimRemoved(
        AssetType indexed assetType,
        uint256 amount
    );

    error ZeroAmount();
    error ZeroAddress();
    error InsufficientLiquidity();
    error WithdrawalBlockedByLiquidityRatio();
    error UnauthorizedRelease();
    error InvalidAssetType();

    /**
     * @notice Constructor
     * @param _weth WETH token address
     * @param _lpFeeBps LP fee in basis points (5 = 0.05%)
     * @param _minLiquidityRatioBps Minimum liquidity ratio in basis points (11000 = 110%)
     */
    constructor(
        address _weth,
        uint256 _lpFeeBps,
        uint256 _minLiquidityRatioBps
    ) {
        require(_weth != address(0), "LiquidityPoolETH: zero WETH address");
        require(_lpFeeBps <= 10000, "LiquidityPoolETH: fee exceeds 100%");
        require(_minLiquidityRatioBps >= 10000, "LiquidityPoolETH: min ratio must be >= 100%");
        
        weth = _weth;
        lpFeeBps = _lpFeeBps;
        minLiquidityRatioBps = _minLiquidityRatioBps;
    }

    /**
     * @notice Authorize a contract to release funds (called during deployment)
     * @param releaser Address authorized to release funds
     */
    function authorizeRelease(address releaser) external {
        require(releaser != address(0), "LiquidityPoolETH: zero address");
        authorizedRelease[releaser] = true;
    }

    /**
     * @notice Provide liquidity to the pool
     * @param assetType Type of asset (ETH or WETH)
     */
    function provideLiquidity(AssetType assetType) external payable nonReentrant {
        uint256 amount;
        
        if (assetType == AssetType.ETH) {
            if (msg.value == 0) revert ZeroAmount();
            amount = msg.value;
        } else if (assetType == AssetType.WETH) {
            if (msg.value != 0) revert("LiquidityPoolETH: WETH deposits must use depositWETH()");
            revert("LiquidityPoolETH: use depositWETH() for WETH deposits");
        } else {
            revert InvalidAssetType();
        }

        pools[assetType].totalLiquidity += amount;
        pools[assetType].lpShares[msg.sender] += amount;

        emit LiquidityProvided(assetType, msg.sender, amount);
    }

    /**
     * @notice Provide WETH liquidity to the pool
     * @param amount Amount of WETH to deposit
     */
    function depositWETH(uint256 amount) external nonReentrant {
        if (amount == 0) revert ZeroAmount();

        IERC20(weth).safeTransferFrom(msg.sender, address(this), amount);

        pools[AssetType.WETH].totalLiquidity += amount;
        pools[AssetType.WETH].lpShares[msg.sender] += amount;

        emit LiquidityProvided(AssetType.WETH, msg.sender, amount);
    }

    /**
     * @notice Withdraw liquidity from the pool
     * @param amount Amount to withdraw
     * @param assetType Type of asset (ETH or WETH)
     */
    function withdrawLiquidity(
        uint256 amount,
        AssetType assetType
    ) external nonReentrant {
        if (amount == 0) revert ZeroAmount();
        if (pools[assetType].lpShares[msg.sender] < amount) revert InsufficientLiquidity();

        // Check minimum liquidity ratio
        uint256 availableLiquidity = pools[assetType].totalLiquidity - pools[assetType].pendingClaims;
        uint256 newAvailableLiquidity = availableLiquidity - amount;
        uint256 minRequired = (pools[assetType].pendingClaims * minLiquidityRatioBps) / 10000;

        if (newAvailableLiquidity < minRequired) {
            revert WithdrawalBlockedByLiquidityRatio();
        }

        pools[assetType].totalLiquidity -= amount;
        pools[assetType].lpShares[msg.sender] -= amount;

        if (assetType == AssetType.ETH) {
            (bool success, ) = payable(msg.sender).call{value: amount}("");
            require(success, "LiquidityPoolETH: ETH transfer failed");
        } else {
            IERC20(weth).safeTransfer(msg.sender, amount);
        }

        emit LiquidityWithdrawn(assetType, msg.sender, amount);
    }

    /**
     * @notice Release funds to recipient (only authorized contracts)
     * @param depositId Deposit ID (for event tracking)
     * @param recipient Recipient address
     * @param amount Amount to release (before fees)
     * @param assetType Type of asset (ETH or WETH)
     */
    function releaseToRecipient(
        uint256 depositId,
        address recipient,
        uint256 amount,
        AssetType assetType
    ) external nonReentrant {
        if (!authorizedRelease[msg.sender]) revert UnauthorizedRelease();
        if (amount == 0) revert ZeroAmount();
        if (recipient == address(0)) revert ZeroAddress();

        // Calculate fee
        uint256 feeAmount = (amount * lpFeeBps) / 10000;
        uint256 releaseAmount = amount - feeAmount;

        // Check available liquidity
        PoolState storage pool = pools[assetType];
        uint256 availableLiquidity = pool.totalLiquidity - pool.pendingClaims;
        
        if (availableLiquidity < releaseAmount) {
            revert InsufficientLiquidity();
        }

        // Reduce pending claims
        pool.pendingClaims -= amount;

        // Release funds to recipient
        if (assetType == AssetType.ETH) {
            (bool success, ) = payable(recipient).call{value: releaseAmount}("");
            require(success, "LiquidityPoolETH: ETH transfer failed");
        } else {
            IERC20(weth).safeTransfer(recipient, releaseAmount);
        }

        // Fee remains in pool (increases totalLiquidity effectively by reducing pendingClaims)

        emit FundsReleased(assetType, depositId, recipient, releaseAmount, feeAmount);
    }

    /**
     * @notice Add pending claim (called when claim is submitted)
     * @param amount Amount of pending claim
     * @param assetType Type of asset
     */
    function addPendingClaim(uint256 amount, AssetType assetType) external {
        if (!authorizedRelease[msg.sender]) revert UnauthorizedRelease();
        pools[assetType].pendingClaims += amount;
        emit PendingClaimAdded(assetType, amount);
    }

    /**
     * @notice Remove pending claim (called when claim is challenged/slashed)
     * @param amount Amount of pending claim to remove
     * @param assetType Type of asset
     */
    function removePendingClaim(uint256 amount, AssetType assetType) external {
        if (!authorizedRelease[msg.sender]) revert UnauthorizedRelease();
        pools[assetType].pendingClaims -= amount;
        emit PendingClaimRemoved(assetType, amount);
    }

    /**
     * @notice Get available liquidity for an asset type
     * @param assetType Type of asset
     * @return Available liquidity (total - pending claims)
     */
    function getAvailableLiquidity(AssetType assetType) external view returns (uint256) {
        PoolState storage pool = pools[assetType];
        uint256 pending = pool.pendingClaims;
        if (pool.totalLiquidity <= pending) {
            return 0;
        }
        return pool.totalLiquidity - pending;
    }

    /**
     * @notice Get LP share for a provider
     * @param provider LP provider address
     * @param assetType Type of asset
     * @return LP share amount
     */
    function getLpShare(address provider, AssetType assetType) external view returns (uint256) {
        return pools[assetType].lpShares[provider];
    }

    /**
     * @notice Get pool statistics
     * @param assetType Type of asset
     * @return totalLiquidity Total liquidity in pool
     * @return pendingClaims Total pending claims
     * @return availableLiquidity Available liquidity (total - pending)
     */
    function getPoolStats(
        AssetType assetType
    ) external view returns (
        uint256 totalLiquidity,
        uint256 pendingClaims,
        uint256 availableLiquidity
    ) {
        PoolState storage pool = pools[assetType];
        totalLiquidity = pool.totalLiquidity;
        pendingClaims = pool.pendingClaims;
        if (totalLiquidity > pendingClaims) {
            availableLiquidity = totalLiquidity - pendingClaims;
        } else {
            availableLiquidity = 0;
        }
    }

    // Allow contract to receive ETH
    receive() external payable {}
}

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

/**
 * @title ISwapRouter - Uniswap V3 SwapRouter Interface
 * @notice Minimal interface for Uniswap V3 SwapRouter
 * @dev Based on Uniswap V3 SwapRouter02
 */
interface ISwapRouter {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    function exactInputSingle(ExactInputSingleParams calldata params)
        external
        payable
        returns (uint256 amountOut);

    function exactInput(ExactInputParams calldata params)
        external
        payable
        returns (uint256 amountOut);
}

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

/**
 * @title ICurvePool - Curve Pool Interface
 * @notice Minimal interface for Curve stable pools (e.g., 3pool)
 * @dev Based on Curve StableSwap pools
 */
interface ICurvePool {
    function exchange(
        int128 i,
        int128 j,
        uint256 dx,
        uint256 min_dy
    ) external payable returns (uint256);

    function exchange_underlying(
        int128 i,
        int128 j,
        uint256 dx,
        uint256 min_dy
    ) external payable returns (uint256);

    function get_dy(
        int128 i,
        int128 j,
        uint256 dx
    ) external view returns (uint256);

    function coins(uint256 i) external view returns (address);
}

File 9 of 18 : IAggregationRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/**
 * @title IAggregationRouter - 1inch AggregationRouter Interface
 * @notice Minimal interface for 1inch AggregationRouter
 * @dev Based on 1inch V5 Router
 */
interface IAggregationRouter {
    struct SwapDescription {
        address srcToken;
        address dstToken;
        address srcReceiver;
        address dstReceiver;
        uint256 amount;
        uint256 minReturnAmount;
        uint256 flags;
        bytes permit;
    }

    function swap(
        address executor,
        SwapDescription calldata desc,
        bytes calldata permit,
        bytes calldata data
    ) external payable returns (uint256 returnAmount, uint256 spentAmount);
}

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

/**
 * @title IDodoexRouter
 * @notice Interface for Dodoex PMM (Proactive Market Maker) Router
 * @dev Dodoex uses PMM which provides better price discovery and lower slippage
 */
interface IDodoexRouter {
    struct DodoSwapParams {
        address fromToken;
        address toToken;
        uint256 fromTokenAmount;
        uint256 minReturnAmount;
        address[] dodoPairs; // Dodo PMM pool addresses
        uint256 directions; // 0 = base to quote, 1 = quote to base
        bool isIncentive; // Whether to use incentive mechanism
        uint256 deadLine;
    }

    /**
     * @notice Swap tokens via Dodoex PMM
     * @param params Swap parameters
     * @return receivedAmount Amount received after swap
     */
    function dodoSwapV2TokenToToken(
        DodoSwapParams calldata params
    ) external returns (uint256 receivedAmount);

    /**
     * @notice Get quote for swap (view function)
     * @param fromToken Source token
     * @param toToken Destination token
     * @param fromTokenAmount Amount to swap
     * @return toTokenAmount Expected output amount
     */
    function getDodoSwapQuote(
        address fromToken,
        address toToken,
        uint256 fromTokenAmount
    ) external view returns (uint256 toTokenAmount);
}

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

/**
 * @title IBalancerVault
 * @notice Interface for Balancer V2 Vault
 * @dev Balancer provides weighted pools and better stablecoin swaps
 */
interface IBalancerVault {
    struct SingleSwap {
        bytes32 poolId;
        SwapKind kind;
        address assetIn;
        address assetOut;
        uint256 amount;
        bytes userData;
    }

    struct FundManagement {
        address sender;
        bool fromInternalBalance;
        address payable recipient;
        bool toInternalBalance;
    }

    enum SwapKind {
        GIVEN_IN,  // Amount in is known
        GIVEN_OUT  // Amount out is known
    }

    /**
     * @notice Execute a single swap
     * @param singleSwap Swap parameters
     * @param funds Fund management parameters
     * @param limit Maximum amount to swap (slippage protection)
     * @param deadline Deadline for swap
     * @return amountCalculated Amount calculated for swap
     */
    function swap(
        SingleSwap memory singleSwap,
        FundManagement memory funds,
        uint256 limit,
        uint256 deadline
    ) external payable returns (uint256 amountCalculated);

    /**
     * @notice Get pool information
     * @param poolId Pool identifier
     * @return poolAddress Pool address
     * @return specialization Pool specialization type
     */
    function getPool(bytes32 poolId) external view returns (address poolAddress, uint8 specialization);

    /**
     * @notice Query batch swap for quotes
     * @param kind Swap kind
     * @param swaps Array of swaps to query
     * @param assets Array of assets involved
     * @return assetDeltas Asset deltas for each asset
     */
    function queryBatchSwap(
        SwapKind kind,
        SingleSwap[] memory swaps,
        address[] memory assets
    ) external view returns (int256[] memory assetDeltas);

    /**
     * @notice Get pool tokens and balances
     * @param poolId Pool identifier
     * @return tokens Token addresses in the pool
     * @return balances Token balances in the pool
     * @return lastChangeBlock Last block number that changed balances
     */
    function getPoolTokens(bytes32 poolId)
        external
        view
        returns (
            address[] memory tokens,
            uint256[] memory balances,
            uint256 lastChangeBlock
        );
}

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

/**
 * @title IWETH
 * @notice Minimal WETH interface for bridge contracts
 */
interface IWETH {
    function deposit() external payable;
    function withdraw(uint256) external;
    function transfer(address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

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

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @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.
     */
    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 `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "./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);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

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

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "forge-std/=lib/forge-std/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "@emoney/=contracts/emoney/",
    "@emoney-scripts/=script/emoney/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "dodo-contractV2/=lib/dodo-contractV2/",
    "hardhat/=lib/dodo-contractV2/node_modules/hardhat/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_uniswapV3Router","type":"address"},{"internalType":"address","name":"_curve3Pool","type":"address"},{"internalType":"address","name":"_dodoexRouter","type":"address"},{"internalType":"address","name":"_balancerVault","type":"address"},{"internalType":"address","name":"_oneInchRouter","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_usdt","type":"address"},{"internalType":"address","name":"_usdc","type":"address"},{"internalType":"address","name":"_dai","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientOutput","type":"error"},{"inputs":[],"name":"InvalidProvider","type":"error"},{"inputs":[],"name":"InvalidRoutingConfig","type":"error"},{"inputs":[],"name":"ProviderDisabled","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SwapFailed","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum EnhancedSwapRouter.SwapProvider","name":"provider","type":"uint8"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"ProviderToggled","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":"uint256","name":"sizeCategory","type":"uint256"},{"indexed":false,"internalType":"enum EnhancedSwapRouter.SwapProvider[]","name":"providers","type":"uint8[]"}],"name":"RoutingConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"enum EnhancedSwapRouter.SwapProvider","name":"provider","type":"uint8"},{"indexed":true,"internalType":"enum LiquidityPoolETH.AssetType","name":"inputAsset","type":"uint8"},{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasUsed","type":"uint256"}],"name":"SwapExecuted","type":"event"},{"inputs":[],"name":"COORDINATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_TIER_HIGH","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_TIER_LOW","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_TIER_MEDIUM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MEDIUM_SWAP_THRESHOLD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROUTING_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SMALL_SWAP_THRESHOLD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum EnhancedSwapRouter.SwapProvider","name":"provider","type":"uint8"},{"internalType":"address","name":"stablecoinToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"_executeSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoinToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"_getBalancerQuote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoinToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"_getDodoexQuote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoinToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"_getUniswapV3Quote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"balancerPoolIds","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balancerVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"curve3Pool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dai","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"dodoPoolAddresses","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dodoexRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoinToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"getQuotes","outputs":[{"internalType":"enum EnhancedSwapRouter.SwapProvider[]","name":"providers","type":"uint8[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"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":[],"name":"oneInchRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum EnhancedSwapRouter.SwapProvider","name":"","type":"uint8"}],"name":"providerEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"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":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"bytes32","name":"poolId","type":"bytes32"}],"name":"setBalancerPoolId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"address","name":"poolAddress","type":"address"}],"name":"setDodoPoolAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum EnhancedSwapRouter.SwapProvider","name":"provider","type":"uint8"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setProviderEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"sizeCategory","type":"uint256"},{"internalType":"enum EnhancedSwapRouter.SwapProvider[]","name":"providers","type":"uint8[]"}],"name":"setRoutingConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_quoter","type":"address"}],"name":"setUniswapQuoter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"sizeBasedRouting","outputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum LiquidityPoolETH.AssetType","name":"inputAsset","type":"uint8"},{"internalType":"address","name":"stablecoinToken","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"enum EnhancedSwapRouter.SwapProvider","name":"preferredProvider","type":"uint8"}],"name":"swapToStablecoin","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"enum EnhancedSwapRouter.SwapProvider","name":"providerUsed","type":"uint8"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"swapTokenToToken","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapQuoter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniswapV3Router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usdc","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usdt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

3462000ba55762004390388190036101a0601f8201601f19168101906001600160401b0382119082101762000780576101209282916040526101a0391262000ba5576200004e6101a062000c0a565b6200005b6101c062000c0a565b620000686101e062000c0a565b916200007661020062000c0a565b6200008361022062000c0a565b906200009161024062000c0a565b906200009f61026062000c0a565b95620000ad61028062000c0a565b95620000bb6102a062000c0a565b9560018055620000cb3362000c7e565b506001600160a01b03811615801562000b93575b801562000b81575b801562000b6f575b801562000b5d575b801562000b4b575b801562000b39575b801562000b27575b62000b155760805260a05260c05260e0526101008290526101205261014093845261016092835261018091825260026020527fac33ff75c19e70fe83507db0d683fd3465c996598dc972688b7ace676c89077b8054600160ff1991821681179092557fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e080548216831790557f679795a0195a1b76cdebb7c51d74e058aee92919b8c3389af86ef24535e8a28c805482168317905560036000527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c380548216909217909155906001600160a01b031662000afb575b506200020e62000baa565b6002815260403660208301376000620002278262000c1f565b526002620002358262000c43565b526200024062000bca565b600081526200024e62000baa565b918252602080830191909152600160408301526000805260039052805180517f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff92916001600160401b03821162000780576801000000000000000082116200078057602090845483865580841062000ac5575b5001908360005260206000209060005b8160051c811062000a735750601f19811680820362000a17575b50505050602081015180519060018401906001600160401b038311620007805768010000000000000000831162000780576020908254848455808510620009f7575b500190600052602060002060005b838110620009e25750505050906002604062000368930151151591019060ff801983541691151516179055565b6200037262000bea565b60038152606036602083013760026200038b8262000c1f565b526003620003998262000c43565b526000620003a78262000c54565b52620003b262000bca565b60008152620003c062000baa565b91825260208083019190915260016040830181905260005260039052805180517fa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c92916001600160401b03821162000780576801000000000000000082116200078057602090845483865580841062000989575b5001908360005260206000209060005b8160051c8110620009375750601f198116808203620008db575b50505050602081015180519060018401906001600160401b038311620007805768010000000000000000831162000780576020908254848455808510620008bb575b500190600052602060002060005b838110620008a657505050509060026040620004db930151151591019060ff801983541691151516179055565b620004e562000bea565b6003815260603660208301376002620004fe8262000c1f565b5260016200050c8262000c43565b5260036200051a8262000c54565b526200052562000bca565b600081526200053362000baa565b918252602080830191825260016040840190815260026000526003909152915180517fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d9392916001600160401b03821162000780576801000000000000000082116200078057602090855483875580841062000855575b5001908460005260206000208160051c9160005b838110620008035750601f19811690038062000796575b50509151805192506001850191506001600160401b03831162000780576801000000000000000083116200078057602090825484845580851062000760575b500190600052602060002060005b8381106200074b57505050509060026200064c9251151591019060ff801983541691151516179055565b60405191613680938462000d1085396080518481816114c101528181611c6b0152611dbc015260a05184610a47015260c051848181610eec01528181610f980152818161119e0152611eb2015260e05184818161159f0152818161208801528181612249015261304c0152610100518481816107350152818161239f01526124e2015261012051848181610f50015281816110a9015281816118ee0152818161197d01528181611ca501528181611cfd01528181611e550152818161202c015281816123fa01528181612ec60152612fa101525183818161143d01526132f20152518281816113b1015261335201525181818161021a015261332a0152f35b60019060208451940193818401550162000622565b6200077990846000528584600020918201910162000c65565b3862000614565b634e487b7160e01b600052604160045260246000fd5b9260009360005b818110620007b457505050015538808080620005d5565b90919485516005811015620007ed57620007e2602091846001949060ff809160031b9316831b921b19161790565b96019291016200079d565b634e487b7160e01b600052602160045260246000fd5b6000805b602081106200081e575083820155600101620005be565b959081516005811015620007ed576200084b602091896001949060ff809160031b9316831b921b19161790565b9201960162000807565b62000884908760005283600020601f80870160051c820192818816806200088b575b500160051c019062000c65565b38620005aa565b60001990818601918254918a0360031b1c1690553862000877565b600190602084519401938184015501620004ae565b620008d490846000528584600020918201910162000c65565b38620004a0565b9260009360005b8184038110620008fe5750505060051c0155388080806200045e565b90919485516005811015620007ed576200092c602091846001949060ff809160031b9316831b921b19161790565b9601929101620008e2565b6000805b602081106200095257508382015560010162000444565b949081516005811015620007ed576200097f602091886001949060ff809160031b9316831b921b19161790565b920195016200093b565b620009b99086600052601f846000209181871680620009c0575b500160051c810190601f860160051c0162000c65565b3862000434565b6000199081848a0160051c86010191825491890360031b1c16905538620009a3565b6001906020845194019381840155016200033b565b62000a1090846000528584600020918201910162000c65565b386200032d565b9260009360005b818403811062000a3a5750505060051c015538808080620002eb565b90919485516005811015620007ed5762000a68602091846001949060ff809160031b9316831b921b19161790565b960192910162000a1e565b6000805b6020811062000a8e575083820155600101620002d1565b949081516005811015620007ed5762000abb602091886001949060ff809160031b9316831b921b19161790565b9201950162000a77565b62000af49086600052601f846000209181871680620009c057500160051c810190601f860160051c0162000c65565b38620002c1565b600460005260016040600020918254161790553862000203565b60405163d92e233d60e01b8152600490fd5b506001600160a01b038716156200010f565b506001600160a01b0388161562000107565b506001600160a01b03891615620000ff565b506001600160a01b03851615620000f7565b506001600160a01b03841615620000ef565b506001600160a01b03831615620000e7565b506001600160a01b03821615620000df565b600080fd5b60405190606082016001600160401b038111838210176200078057604052565b60405190602082016001600160401b038111838210176200078057604052565b60405190608082016001600160401b038111838210176200078057604052565b51906001600160a01b038216820362000ba557565b80511562000c2d5760200190565b634e487b7160e01b600052603260045260246000fd5b80516001101562000c2d5760400190565b80516002101562000c2d5760600190565b81811062000c71575050565b6000815560010162000c65565b6001600160a01b031660008181527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205490919060ff1662000d0b57818052816020526040822081835260205260408220600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b509056fe608080604052600436101561001d575b50361561001b57600080fd5b005b60003560e01c90816301ffc9a7146115ce57508063158274a5146115895780631c28242a14611562578063248a9ca31461153357806326158136146114f05780632c76d7a6146114ab5780632f2ff15d1461146c5780632f48ab7d1461142757806336568abe146113e05780633e413bee1461139b5780633ee2afc1146110d85780633fc8cef3146110935780634db4a3521461106a5780634f214fd31461101957806352e25bce14610f1b57806355470d8714610ed65780635559294314610eb157806359fbc2d214610a7657806363a6b1da14610a3157806365a5292e146109f65780636b366cb5146109bb5780636c04a824146109325780637574d9a0146109155780637c47ddd01461089c57806381cfa7a01461086657806382cb9ae8146108415780638f40e8f51461082457806391d14854146107d757806395e42ded14610780578063a217fddf14610764578063ac3af2081461071f578063b8ad530e146106e6578063c5b35f63146106b7578063cb29ac5c14610652578063d547741f14610613578063d713da52146105df578063de5663141461058f578063e785f45b14610249578063f4b9fa75146102045763f6b9ec7c146101e2573861000f565b346101ff5760003660031901126101ff5760206040516101f48152f35b600080fd5b346101ff5760003660031901126101ff576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101ff5760403660031901126101ff57610262611621565b60405160243591610272826116ea565b6005825260209260a036858501376040519161028d836116ea565b6005835260a0368685013784600092600080526002825260ff604060002054166104fa575b60026000526002825260ff60406000205416610465575b60036000526002825260ff604060002054166103d4575b5050506102ec81611aca565b916102f682611aca565b9360005b83811061038c5786858760405192839260408401604085528151809152826060860192019060005b818110610368575050508381038285015281808451928381520193019160005b82811061035157505050500390f35b835185528695509381019392810192600101610342565b919495509183808261037d6001948951611666565b01950191019186959492610322565b806103b46103a56103a06103cf9486611758565b61176c565b6103af8389611758565b611afc565b6103be8185611758565b516103c98289611758565b52611726565b6102fa565b60405163c5b35f6360e01b81526001600160a01b03939093166004840152602483015281604481305afa60009181610436575b50610414575b84816102e0565b906104309160036104258387611758565b526103c98285611758565b8461040d565b9091508581813d831161045e575b61044e8183611705565b810103126101ff57519086610407565b503d610444565b6040516329712de760e11b81526001600160a01b0384166004820152602481018290528281604481305afa600091816104c9575b506104a5575b506102c9565b936104c2919460026104b7838a611758565b526103c98288611758565b928761049f565b8481959293503d83116104f3575b6104e18183611705565b810103126101ff578792519089610499565b503d6104d7565b604051630e14121560e11b81526001600160a01b0384166004820152602481018290528281604481305afa6000918161055e575b5061053a575b506102b2565b91509250600061054986611735565b5261055384611735565b528460019287610534565b8481959293503d8311610588575b6105768183611705565b810103126101ff57879251908961052e565b503d61056c565b346101ff5760403660031901126101ff5760206105aa611621565b6105b2611637565b60018060a01b03809216600052600583528160406000209116600052825260406000205416604051908152f35b346101ff5760203660031901126101ff576004356000526003602052602060ff600260406000200154166040519015158152f35b346101ff5760403660031901126101ff5761001b600435610632611637565b9080600052600060205261064d6001604060002001546133f6565b61349a565b60a03660031901126101ff5760043560028110156101ff57610672611637565b6084359160058310156101ff576040926106b59261069f92610692613627565b6064359160443591611779565b6001809392935583519283526020830190611666565bf35b346101ff5760403660031901126101ff5760206106de6106d5611621565b60243590612f95565b604051908152f35b346101ff5760803660031901126101ff5760043560058110156101ff576106de602091610711611637565b906064359160443591611c3a565b346101ff5760003660031901126101ff576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101ff5760003660031901126101ff57602060405160008152f35b346101ff5760603660031901126101ff57610799611621565b6107a1611637565b906107aa61337c565b60018060a01b03809116600052600460205260406000209116600052602052604435604060002055600080f35b346101ff5760403660031901126101ff576107f0611637565b600435600052600060205260406000209060018060a01b0316600052602052602060ff604060002054166040519015158152f35b346101ff5760003660031901126101ff576020604051610bb88152f35b346101ff5760003660031901126101ff57602060405169152d02c7e14af68000008152f35b346101ff5760203660031901126101ff5760043560058110156101ff5760ff61089060209261164d565b54166040519015158152f35b346101ff5760603660031901126101ff576108b5611621565b6108bd611637565b6001600160a01b0360443581811693908490036101ff5781906108de61337c565b166000526005602052604060002091166000526020526040600020906bffffffffffffffffffffffff60a01b825416179055600080f35b346101ff5760003660031901126101ff5760206040516127108152f35b346101ff5760403660031901126101ff5760043560058110156101ff57602435801515918282036101ff577f6be7b5903863679a655e520a2395f264a654b5663f6ceb97c8a878f8983534cb926109a760409361098d61337c565b6109968461164d565b9060ff801983541691151516179055565b6109b383518093611666565b6020820152a1005b346101ff5760003660031901126101ff5760206040517f2e8b98eef02e8df3bd27d1270ded3bea3d14db99c5234c7b14001a7fff957bcc8152f35b346101ff5760003660031901126101ff5760206040517fa842d0039ede86e50de3e76d5357314d987325eb17d7eb32afe0fdec2be0031e8152f35b346101ff5760003660031901126101ff576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101ff5760403660031901126101ff576024356001600160401b0381116101ff57366023820112156101ff576001600160401b038160040135116101ff576024816004013560051b820101903682116101ff57610ad261337c565b60036004351015610e5a57806004013515610e0957604051610af3816116cf565b6000815260405190610b0482611699565b610b118360040135611ab3565b610b1e6040519182611705565b6004840135815260248401948590602083015b818310610df0575050508252602082015260016040820152600435600052600360205260406000209080518051906001600160401b038211610cd457600160401b8211610cd4576020908454838655808410610da4575b5001908360005260206000209060005b8160051c8110610d575750601f198116808203610cea575b50505050600182016020820151908151916001600160401b038311610cd457600160401b8311610cd4576020908254848455808510610cb7575b500190600052602060002060005b838110610ca3578787610c2188600260408a0151151591019060ff801983541691151516179055565b6040519060408201600435835260406020840152816004013590526060820192906000905b80600401358210610c79577f266370fe8933b3766025a9b1de987798164b818824e57c1057eeb8f9ae71ce3e84860385a1005b90919384359060058210156101ff57602081610c988293600195611666565b019501920190610c46565b600190602084519401938184015501610bf8565b610cce908460005285846000209182019101611b08565b88610bea565b634e487b7160e01b600052604160045260246000fd5b9260009360005b8184038110610d0b5750505060051c015584808080610bb0565b90919485516005811015610d4157610d37602091846001949060ff809160031b9316831b921b19161790565b9601929101610cf1565b634e487b7160e01b600052602160045260246000fd5b6000805b60208110610d70575083820155600101610b98565b949081516005811015610d4157610d9b602091886001949060ff809160031b9316831b921b19161790565b92019501610d5b565b610dd0908660005283600020601f80870160051c82019281881680610dd6575b500160051c0190611b08565b87610b88565b60001990818601918254918a0360031b1c1690558c610dc4565b823560058110156101ff57815260209283019201610b31565b60405162461bcd60e51b815260206004820152602360248201527f456e68616e63656453776170526f757465723a20656d7074792070726f76696460448201526265727360e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602960248201527f456e68616e63656453776170526f757465723a20696e76616c69642073697a656044820152682063617465676f727960b81b6064820152608490fd5b346101ff5760003660031901126101ff57602060405169021e19e0c9bab24000008152f35b346101ff5760003660031901126101ff576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101ff5760403660031901126101ff57610f946020610f39611621565b60405163137dbaf360e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015290911660248083019190915235604482015291829081906064820190565b03817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa801561100d57600090610fdb575b602090604051908152f35b506020813d8211611005575b81610ff460209383611705565b810103126101ff5760209051610fd0565b3d9150610fe7565b6040513d6000823e3d90fd5b346101ff5760403660031901126101ff57611032611621565b61103a611637565b9060018060a01b038091166000526004602052604060002091166000526020526020604060002054604051908152f35b346101ff5760003660031901126101ff576006546040516001600160a01b039091168152602090f35b346101ff5760003660031901126101ff576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101ff5760803660031901126101ff576110f1611621565b6110f9611637565b9060443591606435611109613627565b8315611389576001600160a01b039283168015801561137f575b61136d578060005260209460058652846040600020941693846000528652846040600020541691611155831515611b1f565b6040516323b872dd60e01b888201523360248201523060448201526064808201849052815261118f90611189608482611705565b82613552565b60405163095ea7b360e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038116600483015260248201849052919088816044816000865af1801561100d57611340575b50604051936111f8856116b4565b6001855288368187013761120b85611735565b5261012c420180421161132a578894611273946040519361122b8561167d565b8452888785015260408401528660608401526080830152600060a0830152600060c083015260e08201526000604051978880958194630b5e801360e01b835260048301611b9a565b0393165af192831561100d576000936112fb575b5082106112a6578161129a91339061350f565b60018055604051908152f35b60405162461bcd60e51b815260048101849052602760248201527f456e68616e63656453776170526f757465723a20696e73756666696369656e74604482015266081bdd5d1c1d5d60ca1b6064820152608490fd5b9092508381813d8311611323575b6113138183611705565b810103126101ff57519184611287565b503d611309565b634e487b7160e01b600052601160045260246000fd5b61135f90893d8b11611366575b6113578183611705565b810190611b82565b50886111ea565b503d61134d565b60405163d92e233d60e01b8152600490fd5b5083831615611123565b604051631f2a200560e01b8152600490fd5b346101ff5760003660031901126101ff576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101ff5760403660031901126101ff576113f9611637565b336001600160a01b038216036114155761001b9060043561349a565b60405163334bd91960e11b8152600490fd5b346101ff5760003660031901126101ff576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101ff5760403660031901126101ff5761001b60043561148b611637565b908060005260006020526114a66001604060002001546133f6565b61341c565b346101ff5760003660031901126101ff576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101ff5760203660031901126101ff57611509611621565b61151161337c565b600680546001600160a01b0319166001600160a01b0392909216919091179055005b346101ff5760203660031901126101ff5760043560005260006020526020600160406000200154604051908152f35b346101ff5760403660031901126101ff5760206106de611580611621565b60243590612e63565b346101ff5760003660031901126101ff576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346101ff5760203660031901126101ff576004359063ffffffff60e01b82168092036101ff57602091637965db0b60e01b8114908115611610575b5015158152f35b6301ffc9a760e01b14905083611609565b600435906001600160a01b03821682036101ff57565b602435906001600160a01b03821682036101ff57565b6005811015610d41576000526002602052604060002090565b906005821015610d415752565b60021115610d4157565b61010081019081106001600160401b03821117610cd457604052565b606081019081106001600160401b03821117610cd457604052565b604081019081106001600160401b03821117610cd457604052565b602081019081106001600160401b03821117610cd457604052565b60c081019081106001600160401b03821117610cd457604052565b90601f801991011681019081106001600160401b03821117610cd457604052565b600019811461132a5760010190565b8051156117425760200190565b634e487b7160e01b600052603260045260246000fd5b80518210156117425760209160051b010190565b516005811015610d415790565b94929493919360008515611389576001600160a01b039383851693841561136d576117a3906132e5565b15611a5f576117b183611673565b8215611972575b6117c29087612560565b93815b85518110156119605760ff6117e56117e06103a0848a611758565b61164d565b541615611957576117f96103a08288611758565b60408051635c56a98760e11b815291600491906118199083850190611666565b602488818501528b60448501528c606485015260209081856084818b305af1889581611928575b5061185857505050505061185390611726565b6117c5565b8d85101561186e57505050505061185390611726565b9091939c506118838d9c93959b9c338b61350f565b6118906103a08c8c611758565b965a9560058910156119185750505093611915999795938c7f471477f254242ea7aad26ea1f3bd0deca99d48aa097615152bf8f64ec41f3bf2946080948a986118dc6103a09e9c611673565b82519a8b528a015288015260608701527f00000000000000000000000000000000000000000000000000000000000000001694a4611758565b90565b634e487b7160e01b825260219052fd5b9095508281813d8311611950575b6119408183611705565b810103126101ff57519438611840565b503d611936565b61185390611726565b60405163081ceff360e41b8152600490fd5b90863403611a0a57847f000000000000000000000000000000000000000000000000000000000000000016803b15611a0657818891600460405180948193630d0e30db60e41b83525af180156119fb576119ce575b50906117b8565b6001600160401b0381116119e7576040526117c26119c7565b634e487b7160e01b82526041600452602482fd5b6040513d84823e3d90fd5b5080fd5b60405162461bcd60e51b815260206004820152602760248201527f456e68616e63656453776170526f757465723a2045544820616d6f756e74206d6044820152660d2e6dac2e8c6d60cb1b6064820152608490fd5b60405162461bcd60e51b815260206004820152602660248201527f456e68616e63656453776170526f757465723a20696e76616c696420737461626044820152653632b1b7b4b760d11b6064820152608490fd5b6001600160401b038111610cd45760051b60200190565b90611ad482611ab3565b611ae16040519182611705565b8281528092611af2601f1991611ab3565b0190602036910137565b6005821015610d415752565b818110611b13575050565b60008155600101611b08565b15611b2657565b60405162461bcd60e51b815260206004820152602e60248201527f456e68616e63656453776170526f757465723a20446f646f657820706f6f6c2060448201526d1b9bdd0818dbdb999a59dd5c995960921b6064820152608490fd5b908160209103126101ff575180151581036101ff5790565b602080825261012082019260018060a01b03808251168385015280838301511660408501526040820151606085015260608201516080850152608082015194610100938460a087015286518092528061014087019701926000905b838210611c215750505050508060a060e092015160c085015260c0810151151582850152015191015290565b8451811689529782019793820193600190910190611bf5565b92919092303303612511576005811015610d415780611e3e575060405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166004820152602481018290526020818060448101038160007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af1801561100d57611e1f575b5061012c4201421161132a5760405192611cf38461167d565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811685529081166020808601918252610bb86040808801918252306060890190815261012c420160808a0190815260a08a0197885260c08a01988952600060e08b01818152935163414bf38960e01b81529a51881660048c01529551871660248b0152925162ffffff1660448a0152518516606489015290516084880152935160a4870152935160c48601529151811660e48501528391610104918391907f0000000000000000000000000000000000000000000000000000000000000000165af190811561100d57600091611df0575090565b90506020813d602011611e17575b81611e0b60209383611705565b810103126101ff575190565b3d9150611dfe565b611e379060203d602011611366576113578183611705565b5038611cda565b91929160028103612017575060018060a01b0391827f000000000000000000000000000000000000000000000000000000000000000016916000928084526020926005845260409686888720921691828752855286888720541690611ea4821515611b1f565b885163095ea7b360e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038116600483015260248201879052959087816044818c8a5af1801561200d57611ff0575b50895192611f0a846116b4565b60018452873681860137611f1d84611735565b5261012c420193844211611fdc5791611f83959391889795938c5195611f428761167d565b8652888601528b850152606084015260808301528660a08301528660c083015260e0820152858851978880958194630b5e801360e01b835260048301611b9a565b0393165af1938415611fd157508193611f9d575b50505090565b9091809350813d8311611fca575b611fb58183611705565b81010312611fc7575051388080611f97565b80fd5b503d611fab565b51913d9150823e3d90fd5b634e487b7160e01b89526011600452602489fd5b61200690883d8a11611366576113578183611705565b5038611efd565b8b513d8b823e3d90fd5b9092906003810361230a575060018060a01b037f0000000000000000000000000000000000000000000000000000000000000000169283600052600460205260406000209260018060a01b031692836000526020526040600020549283156122b55760405163095ea7b360e01b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031660048201526024810183905260208160448160008a5af1801561100d57612296575b50604051946001600160401b039460c0870186811188821017610cd457604052865260006020870152604086015260608501526080840152604051612116816116cf565b6000815260a0840152604051916080830190811183821017610cd457604052308252600060208301523060408301526000606083015261012c4201421161132a579160a0926040519384926352bbbe2960e01b845260e06004850152805160e4850152602081015161218781611673565b61010485015260408101516000196001841b019081166101248601526060820151166101448501526080810151610164850152015160c061018484015280516101a48401819052919060005b83811061227d57505060006101c4838501810182905285516001600160a01b039081166024870152602080880151151560448801526040880151821660648801526060909701511515608487015260a486019390935261012c420160c4860152601f909301601f191684018490039092019183917f0000000000000000000000000000000000000000000000000000000000000000165af190811561100d57600091611df0575090565b60208282018101516101c48984010152879550016121d3565b6122ae9060203d602011611366576113578183611705565b50386120d2565b60405162461bcd60e51b815260206004820152602760248201527f456e68616e63656453776170526f757465723a20706f6f6c206e6f7420636f6e604482015266199a59dd5c995960ca1b6064820152608490fd5b8390600181036123755760405162461bcd60e51b815260206004820152603360248201527f456e68616e63656453776170526f757465723a20437572766520646972656374604482015272081cddd85c081b9bdd081cdd5c1c1bdc9d1959606a1b6064820152608490fd5b600414806124df575b61239457604051633b136dc160e11b8152600490fd5b6001600160a01b03907f000000000000000000000000000000000000000000000000000000000000000090828216156124cd5760405163095ea7b360e01b81526001600160a01b03929092166004830152602482015290602090829060449082906000907f0000000000000000000000000000000000000000000000000000000000000000165af1801561100d576124af575b60405162461bcd60e51b815260206004820152605460248201527f456e68616e63656453776170526f757465723a2031696e63682072657175697260448201527f657320726f7574652063616c6c646174612066726f6d204150493b207573652060648201527331696e63682061676772656761746f722053444b60601b608482015260a490fd5b6124c69060203d8111611366576113578183611705565b5080612427565b604051631368c7cb60e21b8152600490fd5b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316151561237e565b60405162461bcd60e51b815260206004820152602160248201527f456e68616e63656453776170526f757465723a20696e7465726e616c206f6e6c6044820152607960f81b6064820152608490fd5b906005811015610d415780151580612e11575b612deb575069021e19e0c9bab2400000811015612dcc575060005b6000526020600381526040806000209080516125a981611699565b815180818680875493848152018760005281600020936000905b80601f830110612b58576126df95549184828210612b3e575b828210612b21575b828210612b04575b828210612ae7575b828210612acb575b828210612aae575b828210612a91575b828210612a74575b8a838310612a57575b50828210612a3a575b828210612a1d575b828210612a00575b8282106129e3575b8282106129c6575b8282106129a9575b82821061298c575b82821061296f575b828210612952575b828210612935575b828210612918575b8282106128fb575b8282106128de575b8282106128c1575b8282106128a4575b828210612887575b82821061286a575b82821061284d575b828210612830575b828210612813575b8282106127f6575b8282106127d9575b50106127c3575b5090500382611705565b815260019283810183519081878254918281520191600052876000209060005b888a8383106127b1575050505050918161271f60029360ff950382611705565b8785015201541615158083830152806127a6575b61279e575060a0815193612746856116ea565b600585523690850137600261275a84611735565b52825182101561174257600090830152815160021015611742576003606083015281516003101561174257608082015280516004101561174257600460a082015290565b925050505190565b508051511515612733565b845486529094019392830192016126ff565b816127d09160f81c611666565b018790386126d5565b846127ed6001939660ff8760f01c16611666565b019301846126ce565b8461280a6001939660ff8760e81c16611666565b019301846126c6565b846128276001939660ff8760e01c16611666565b019301846126be565b846128446001939660ff8760d81c16611666565b019301846126b6565b846128616001939660ff8760d01c16611666565b019301846126ae565b8461287e6001939660ff8760c81c16611666565b019301846126a6565b8461289b6001939660ff8760c01c16611666565b0193018461269e565b846128b86001939660ff8760b81c16611666565b01930184612696565b846128d56001939660ff8760b01c16611666565b0193018461268e565b846128f26001939660ff8760a81c16611666565b01930184612686565b8461290f6001939660ff8760a01c16611666565b0193018461267e565b8461292c6001939660ff8760981c16611666565b01930184612676565b846129496001939660ff8760901c16611666565b0193018461266e565b846129666001939660ff8760881c16611666565b01930184612666565b846129836001939660ff8760801c16611666565b0193018461265e565b846129a06001939660ff8760781c16611666565b01930184612656565b846129bd6001939660ff8760701c16611666565b0193018461264e565b846129da6001939660ff8760681c16611666565b01930184612646565b846129f76001939660ff8760601c16611666565b0193018461263e565b84612a146001939660ff8760581c16611666565b01930184612636565b84612a316001939660ff8760501c16611666565b0193018461262e565b84612a4e6001939660ff8760481c16611666565b01930184612626565b94612a6a8160ff87600196991c16611666565b019301848a61261d565b84612a886001939660ff8760381c16611666565b01930184612614565b84612aa56001939660ff8760301c16611666565b0193018461260c565b84612ac26001939660ff8760281c16611666565b01930184612604565b84612ade6001939660ff87851c16611666565b019301846125fc565b84612afb6001939660ff8760181c16611666565b019301846125f4565b84612b186001939660ff8760101c16611666565b019301846125ec565b84612b356001939660ff8760081c16611666565b019301846125e4565b84612b4f6001939660ff8716611666565b019301846125dc565b9291600191945061040090612dbc8a612dae86612d8b612c1a612d438d5495612cfb89612cb360ff97612b8d838a8d16611666565b612b9e8184018a8d60081c16611666565b612baf8584018a8d60101c16611666565b888b610200606095612bc8878201858560181c16611666565b612bdb6080958585888501921c16611666565b612c7160a097612bf2898401878760281c16611666565b612c2960c09b612c098d8601898960301c16611666565b60e09e8f8601898960381c16611666565b87876101008701921c16611666565b612c3c6101208401878760481c16611666565b612c4f6101408401878760501c16611666565b612c626101608401878760581c16611666565b85856101808501921c16611666565b612c846101a08201858560681c16611666565b612c976101c08201858560701c16611666565b612caa6101e08201858560781c16611666565b01921c16611666565b612cc66102208c01888b60881c16611666565b612cd96102408c01888b60901c16611666565b612cec6102608c01888b60981c16611666565b86896102808d01921c16611666565b612d0e6102a08a01868960a81c16611666565b612d216102c08a01868960b01c16611666565b612d346102e08a01868960b81c16611666565b84876103008b01921c16611666565b612d566103208801848760c81c16611666565b612d696103408801848760d01c16611666565b612d7c6103608801848760d81c16611666565b82856103808901921c16611666565b612d9e6103a08601828560e81c16611666565b6103c08501908360f01c16611666565b6103e083019060f81c611666565b01940192019284929389926125c3565b69152d02c7e14af68000001115612de457600161258e565b600261258e565b905061191560405191612dfd836116b4565b60018352602036818501376103af83611735565b5060ff612e1d8261164d565b5416612573565b3d15612e5e573d906001600160401b038211610cd45760405191612e52601f8201601f191660200184611705565b82523d6000602084013e565b606090565b6006546001600160a01b0390811680151580612f86575b612eb0575b5050612e8a906132e5565b612e945750600090565b6126de9081810291818304149015171561132a57612710900490565b60405191602083019063f7729d4360e01b8252807f000000000000000000000000000000000000000000000000000000000000000016602485015284166044840152610bb860648401528460848401526000928360a482015260a4815260e081018181106001600160401b03821117612f72576040525183928392905afa90612f37612e24565b9180612f66575b15612e7f57602082805181010312611fc757506020015180612f605780612e7f565b91505090565b50602082511015612f3e565b634e487b7160e01b85526041600452602485fd5b50612f90836132e5565b612e7a565b9060018060a01b0391827f0000000000000000000000000000000000000000000000000000000000000000166000938185526020916004835260409182872091818616928389528552838820549384613032575b505050505050612ff8906132e5565b613000575090565b906126de9182810292818404149015171561301e5750612710900490565b634e487b7160e01b81526011600452602490fd5b805194631f29a8cd60e31b865260048601528885602481867f0000000000000000000000000000000000000000000000000000000000000000165afa9485156132db5789968a966131c5575b505050600285511015806131b9575b613098575b80612fe9565b9397929691959094600019908198845b8b518110156130fa5789898d8a826130c08684611758565b5116146130f0575b836130d291611758565b5116146130e8575b6130e390611726565b6130a8565b9950896130da565b92955085926130c8565b5094995094509450945094600019841415806131ad575b8061319a575b61312357808394613092565b8161312f929350611758565b5192838102938185041490151715613186579061314b91611758565b519081156131725704906126de9182810292818404149015171561301e5750612710900490565b634e487b7160e01b83526012600452602483fd5b634e487b7160e01b84526011600452602484fd5b506131a58483611758565b511515613117565b50600019811415613111565b5060028451101561308d565b9196509194503d808a843e6131da8184611705565b82016060838203126132d75782516001600160401b03908181116132af5784019382601f860112156132af5784519461321286611ab3565b9561321f8b519788611705565b808752858088019160051b830101918583116132d3578601905b8282106132b757505050838101519182116132af570181601f820112156132b35780519061327261326983611ab3565b9951998a611705565b81895283808a019260051b8201019283116132af578301905b8282106132a05750505050939238808061307e565b8151815290830190830161328b565b8b80fd5b8a80fd5b815189811681036132cf578152908601908601613239565b8f80fd5b8e80fd5b8980fd5b81513d8b823e3d90fd5b6001600160a01b039081167f000000000000000000000000000000000000000000000000000000000000000082168114918215613350575b821561332857505090565b7f00000000000000000000000000000000000000000000000000000000000000001614919050565b7f000000000000000000000000000000000000000000000000000000000000000081168214925061331d565b3360009081527f17e306466f0d09a5f30cdc36b7cbca18c119dc1a4639b791678f67eca9a1571360205260409020547fa842d0039ede86e50de3e76d5357314d987325eb17d7eb32afe0fdec2be0031e9060ff16156133d85750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b80600052600060205260406000203360005260205260ff60406000205416156133d85750565b9060009180835282602052604083209160018060a01b03169182845260205260ff6040842054161560001461349557808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b9060009180835282602052604083209160018060a01b03169182845260205260ff6040842054166000146134955780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526135509161354b606483611705565b613552565b565b60008061357b9260018060a01b03169360208151910182865af1613574612e24565b90836135c4565b80519081151591826135a9575b50506135915750565b60249060405190635274afe760e01b82526004820152fd5b6135bc9250602080918301019101611b82565b153880613588565b906135eb57508051156135d957805190602001fd5b604051630a12f52160e11b8152600490fd5b8151158061361e575b6135fc575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156135f4565b600260015414613638576002600155565b604051633ee5aeb560e01b8152600490fdfea264697066735822122046a67042375eaf85066a73901de0c3e794f11bf746a06101cf6e17fbf9b9758a64736f6c6343000814003300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000a356867fdcea8e71aeaf87805808803806231fdc000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c80000000000000000000000001111111254eeb25477b68fb85ed929f73a960582000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006b175474e89094c44da98b954eedeac495271d0f

Deployed Bytecode

0x608080604052600436101561001d575b50361561001b57600080fd5b005b60003560e01c90816301ffc9a7146115ce57508063158274a5146115895780631c28242a14611562578063248a9ca31461153357806326158136146114f05780632c76d7a6146114ab5780632f2ff15d1461146c5780632f48ab7d1461142757806336568abe146113e05780633e413bee1461139b5780633ee2afc1146110d85780633fc8cef3146110935780634db4a3521461106a5780634f214fd31461101957806352e25bce14610f1b57806355470d8714610ed65780635559294314610eb157806359fbc2d214610a7657806363a6b1da14610a3157806365a5292e146109f65780636b366cb5146109bb5780636c04a824146109325780637574d9a0146109155780637c47ddd01461089c57806381cfa7a01461086657806382cb9ae8146108415780638f40e8f51461082457806391d14854146107d757806395e42ded14610780578063a217fddf14610764578063ac3af2081461071f578063b8ad530e146106e6578063c5b35f63146106b7578063cb29ac5c14610652578063d547741f14610613578063d713da52146105df578063de5663141461058f578063e785f45b14610249578063f4b9fa75146102045763f6b9ec7c146101e2573861000f565b346101ff5760003660031901126101ff5760206040516101f48152f35b600080fd5b346101ff5760003660031901126101ff576040517f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f6001600160a01b03168152602090f35b346101ff5760403660031901126101ff57610262611621565b60405160243591610272826116ea565b6005825260209260a036858501376040519161028d836116ea565b6005835260a0368685013784600092600080526002825260ff604060002054166104fa575b60026000526002825260ff60406000205416610465575b60036000526002825260ff604060002054166103d4575b5050506102ec81611aca565b916102f682611aca565b9360005b83811061038c5786858760405192839260408401604085528151809152826060860192019060005b818110610368575050508381038285015281808451928381520193019160005b82811061035157505050500390f35b835185528695509381019392810192600101610342565b919495509183808261037d6001948951611666565b01950191019186959492610322565b806103b46103a56103a06103cf9486611758565b61176c565b6103af8389611758565b611afc565b6103be8185611758565b516103c98289611758565b52611726565b6102fa565b60405163c5b35f6360e01b81526001600160a01b03939093166004840152602483015281604481305afa60009181610436575b50610414575b84816102e0565b906104309160036104258387611758565b526103c98285611758565b8461040d565b9091508581813d831161045e575b61044e8183611705565b810103126101ff57519086610407565b503d610444565b6040516329712de760e11b81526001600160a01b0384166004820152602481018290528281604481305afa600091816104c9575b506104a5575b506102c9565b936104c2919460026104b7838a611758565b526103c98288611758565b928761049f565b8481959293503d83116104f3575b6104e18183611705565b810103126101ff578792519089610499565b503d6104d7565b604051630e14121560e11b81526001600160a01b0384166004820152602481018290528281604481305afa6000918161055e575b5061053a575b506102b2565b91509250600061054986611735565b5261055384611735565b528460019287610534565b8481959293503d8311610588575b6105768183611705565b810103126101ff57879251908961052e565b503d61056c565b346101ff5760403660031901126101ff5760206105aa611621565b6105b2611637565b60018060a01b03809216600052600583528160406000209116600052825260406000205416604051908152f35b346101ff5760203660031901126101ff576004356000526003602052602060ff600260406000200154166040519015158152f35b346101ff5760403660031901126101ff5761001b600435610632611637565b9080600052600060205261064d6001604060002001546133f6565b61349a565b60a03660031901126101ff5760043560028110156101ff57610672611637565b6084359160058310156101ff576040926106b59261069f92610692613627565b6064359160443591611779565b6001809392935583519283526020830190611666565bf35b346101ff5760403660031901126101ff5760206106de6106d5611621565b60243590612f95565b604051908152f35b346101ff5760803660031901126101ff5760043560058110156101ff576106de602091610711611637565b906064359160443591611c3a565b346101ff5760003660031901126101ff576040517f0000000000000000000000001111111254eeb25477b68fb85ed929f73a9605826001600160a01b03168152602090f35b346101ff5760003660031901126101ff57602060405160008152f35b346101ff5760603660031901126101ff57610799611621565b6107a1611637565b906107aa61337c565b60018060a01b03809116600052600460205260406000209116600052602052604435604060002055600080f35b346101ff5760403660031901126101ff576107f0611637565b600435600052600060205260406000209060018060a01b0316600052602052602060ff604060002054166040519015158152f35b346101ff5760003660031901126101ff576020604051610bb88152f35b346101ff5760003660031901126101ff57602060405169152d02c7e14af68000008152f35b346101ff5760203660031901126101ff5760043560058110156101ff5760ff61089060209261164d565b54166040519015158152f35b346101ff5760603660031901126101ff576108b5611621565b6108bd611637565b6001600160a01b0360443581811693908490036101ff5781906108de61337c565b166000526005602052604060002091166000526020526040600020906bffffffffffffffffffffffff60a01b825416179055600080f35b346101ff5760003660031901126101ff5760206040516127108152f35b346101ff5760403660031901126101ff5760043560058110156101ff57602435801515918282036101ff577f6be7b5903863679a655e520a2395f264a654b5663f6ceb97c8a878f8983534cb926109a760409361098d61337c565b6109968461164d565b9060ff801983541691151516179055565b6109b383518093611666565b6020820152a1005b346101ff5760003660031901126101ff5760206040517f2e8b98eef02e8df3bd27d1270ded3bea3d14db99c5234c7b14001a7fff957bcc8152f35b346101ff5760003660031901126101ff5760206040517fa842d0039ede86e50de3e76d5357314d987325eb17d7eb32afe0fdec2be0031e8152f35b346101ff5760003660031901126101ff576040517f000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c76001600160a01b03168152602090f35b346101ff5760403660031901126101ff576024356001600160401b0381116101ff57366023820112156101ff576001600160401b038160040135116101ff576024816004013560051b820101903682116101ff57610ad261337c565b60036004351015610e5a57806004013515610e0957604051610af3816116cf565b6000815260405190610b0482611699565b610b118360040135611ab3565b610b1e6040519182611705565b6004840135815260248401948590602083015b818310610df0575050508252602082015260016040820152600435600052600360205260406000209080518051906001600160401b038211610cd457600160401b8211610cd4576020908454838655808410610da4575b5001908360005260206000209060005b8160051c8110610d575750601f198116808203610cea575b50505050600182016020820151908151916001600160401b038311610cd457600160401b8311610cd4576020908254848455808510610cb7575b500190600052602060002060005b838110610ca3578787610c2188600260408a0151151591019060ff801983541691151516179055565b6040519060408201600435835260406020840152816004013590526060820192906000905b80600401358210610c79577f266370fe8933b3766025a9b1de987798164b818824e57c1057eeb8f9ae71ce3e84860385a1005b90919384359060058210156101ff57602081610c988293600195611666565b019501920190610c46565b600190602084519401938184015501610bf8565b610cce908460005285846000209182019101611b08565b88610bea565b634e487b7160e01b600052604160045260246000fd5b9260009360005b8184038110610d0b5750505060051c015584808080610bb0565b90919485516005811015610d4157610d37602091846001949060ff809160031b9316831b921b19161790565b9601929101610cf1565b634e487b7160e01b600052602160045260246000fd5b6000805b60208110610d70575083820155600101610b98565b949081516005811015610d4157610d9b602091886001949060ff809160031b9316831b921b19161790565b92019501610d5b565b610dd0908660005283600020601f80870160051c82019281881680610dd6575b500160051c0190611b08565b87610b88565b60001990818601918254918a0360031b1c1690558c610dc4565b823560058110156101ff57815260209283019201610b31565b60405162461bcd60e51b815260206004820152602360248201527f456e68616e63656453776170526f757465723a20656d7074792070726f76696460448201526265727360e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602960248201527f456e68616e63656453776170526f757465723a20696e76616c69642073697a656044820152682063617465676f727960b81b6064820152608490fd5b346101ff5760003660031901126101ff57602060405169021e19e0c9bab24000008152f35b346101ff5760003660031901126101ff576040517f000000000000000000000000a356867fdcea8e71aeaf87805808803806231fdc6001600160a01b03168152602090f35b346101ff5760403660031901126101ff57610f946020610f39611621565b60405163137dbaf360e01b81526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28116600483015290911660248083019190915235604482015291829081906064820190565b03817f000000000000000000000000a356867fdcea8e71aeaf87805808803806231fdc6001600160a01b03165afa801561100d57600090610fdb575b602090604051908152f35b506020813d8211611005575b81610ff460209383611705565b810103126101ff5760209051610fd0565b3d9150610fe7565b6040513d6000823e3d90fd5b346101ff5760403660031901126101ff57611032611621565b61103a611637565b9060018060a01b038091166000526004602052604060002091166000526020526020604060002054604051908152f35b346101ff5760003660031901126101ff576006546040516001600160a01b039091168152602090f35b346101ff5760003660031901126101ff576040517f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b03168152602090f35b346101ff5760803660031901126101ff576110f1611621565b6110f9611637565b9060443591606435611109613627565b8315611389576001600160a01b039283168015801561137f575b61136d578060005260209460058652846040600020941693846000528652846040600020541691611155831515611b1f565b6040516323b872dd60e01b888201523360248201523060448201526064808201849052815261118f90611189608482611705565b82613552565b60405163095ea7b360e01b81527f000000000000000000000000a356867fdcea8e71aeaf87805808803806231fdc6001600160a01b038116600483015260248201849052919088816044816000865af1801561100d57611340575b50604051936111f8856116b4565b6001855288368187013761120b85611735565b5261012c420180421161132a578894611273946040519361122b8561167d565b8452888785015260408401528660608401526080830152600060a0830152600060c083015260e08201526000604051978880958194630b5e801360e01b835260048301611b9a565b0393165af192831561100d576000936112fb575b5082106112a6578161129a91339061350f565b60018055604051908152f35b60405162461bcd60e51b815260048101849052602760248201527f456e68616e63656453776170526f757465723a20696e73756666696369656e74604482015266081bdd5d1c1d5d60ca1b6064820152608490fd5b9092508381813d8311611323575b6113138183611705565b810103126101ff57519184611287565b503d611309565b634e487b7160e01b600052601160045260246000fd5b61135f90893d8b11611366575b6113578183611705565b810190611b82565b50886111ea565b503d61134d565b60405163d92e233d60e01b8152600490fd5b5083831615611123565b604051631f2a200560e01b8152600490fd5b346101ff5760003660031901126101ff576040517f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03168152602090f35b346101ff5760403660031901126101ff576113f9611637565b336001600160a01b038216036114155761001b9060043561349a565b60405163334bd91960e11b8152600490fd5b346101ff5760003660031901126101ff576040517f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec76001600160a01b03168152602090f35b346101ff5760403660031901126101ff5761001b60043561148b611637565b908060005260006020526114a66001604060002001546133f6565b61341c565b346101ff5760003660031901126101ff576040517f00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc456001600160a01b03168152602090f35b346101ff5760203660031901126101ff57611509611621565b61151161337c565b600680546001600160a01b0319166001600160a01b0392909216919091179055005b346101ff5760203660031901126101ff5760043560005260006020526020600160406000200154604051908152f35b346101ff5760403660031901126101ff5760206106de611580611621565b60243590612e63565b346101ff5760003660031901126101ff576040517f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c86001600160a01b03168152602090f35b346101ff5760203660031901126101ff576004359063ffffffff60e01b82168092036101ff57602091637965db0b60e01b8114908115611610575b5015158152f35b6301ffc9a760e01b14905083611609565b600435906001600160a01b03821682036101ff57565b602435906001600160a01b03821682036101ff57565b6005811015610d41576000526002602052604060002090565b906005821015610d415752565b60021115610d4157565b61010081019081106001600160401b03821117610cd457604052565b606081019081106001600160401b03821117610cd457604052565b604081019081106001600160401b03821117610cd457604052565b602081019081106001600160401b03821117610cd457604052565b60c081019081106001600160401b03821117610cd457604052565b90601f801991011681019081106001600160401b03821117610cd457604052565b600019811461132a5760010190565b8051156117425760200190565b634e487b7160e01b600052603260045260246000fd5b80518210156117425760209160051b010190565b516005811015610d415790565b94929493919360008515611389576001600160a01b039383851693841561136d576117a3906132e5565b15611a5f576117b183611673565b8215611972575b6117c29087612560565b93815b85518110156119605760ff6117e56117e06103a0848a611758565b61164d565b541615611957576117f96103a08288611758565b60408051635c56a98760e11b815291600491906118199083850190611666565b602488818501528b60448501528c606485015260209081856084818b305af1889581611928575b5061185857505050505061185390611726565b6117c5565b8d85101561186e57505050505061185390611726565b9091939c506118838d9c93959b9c338b61350f565b6118906103a08c8c611758565b965a9560058910156119185750505093611915999795938c7f471477f254242ea7aad26ea1f3bd0deca99d48aa097615152bf8f64ec41f3bf2946080948a986118dc6103a09e9c611673565b82519a8b528a015288015260608701527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21694a4611758565b90565b634e487b7160e01b825260219052fd5b9095508281813d8311611950575b6119408183611705565b810103126101ff57519438611840565b503d611936565b61185390611726565b60405163081ceff360e41b8152600490fd5b90863403611a0a57847f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216803b15611a0657818891600460405180948193630d0e30db60e41b83525af180156119fb576119ce575b50906117b8565b6001600160401b0381116119e7576040526117c26119c7565b634e487b7160e01b82526041600452602482fd5b6040513d84823e3d90fd5b5080fd5b60405162461bcd60e51b815260206004820152602760248201527f456e68616e63656453776170526f757465723a2045544820616d6f756e74206d6044820152660d2e6dac2e8c6d60cb1b6064820152608490fd5b60405162461bcd60e51b815260206004820152602660248201527f456e68616e63656453776170526f757465723a20696e76616c696420737461626044820152653632b1b7b4b760d11b6064820152608490fd5b6001600160401b038111610cd45760051b60200190565b90611ad482611ab3565b611ae16040519182611705565b8281528092611af2601f1991611ab3565b0190602036910137565b6005821015610d415752565b818110611b13575050565b60008155600101611b08565b15611b2657565b60405162461bcd60e51b815260206004820152602e60248201527f456e68616e63656453776170526f757465723a20446f646f657820706f6f6c2060448201526d1b9bdd0818dbdb999a59dd5c995960921b6064820152608490fd5b908160209103126101ff575180151581036101ff5790565b602080825261012082019260018060a01b03808251168385015280838301511660408501526040820151606085015260608201516080850152608082015194610100938460a087015286518092528061014087019701926000905b838210611c215750505050508060a060e092015160c085015260c0810151151582850152015191015290565b8451811689529782019793820193600190910190611bf5565b92919092303303612511576005811015610d415780611e3e575060405163095ea7b360e01b81526001600160a01b037f00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45166004820152602481018290526020818060448101038160007f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b03165af1801561100d57611e1f575b5061012c4201421161132a5760405192611cf38461167d565b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2811685529081166020808601918252610bb86040808801918252306060890190815261012c420160808a0190815260a08a0197885260c08a01988952600060e08b01818152935163414bf38960e01b81529a51881660048c01529551871660248b0152925162ffffff1660448a0152518516606489015290516084880152935160a4870152935160c48601529151811660e48501528391610104918391907f00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45165af190811561100d57600091611df0575090565b90506020813d602011611e17575b81611e0b60209383611705565b810103126101ff575190565b3d9150611dfe565b611e379060203d602011611366576113578183611705565b5038611cda565b91929160028103612017575060018060a01b0391827f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216916000928084526020926005845260409686888720921691828752855286888720541690611ea4821515611b1f565b885163095ea7b360e01b81527f000000000000000000000000a356867fdcea8e71aeaf87805808803806231fdc6001600160a01b038116600483015260248201879052959087816044818c8a5af1801561200d57611ff0575b50895192611f0a846116b4565b60018452873681860137611f1d84611735565b5261012c420193844211611fdc5791611f83959391889795938c5195611f428761167d565b8652888601528b850152606084015260808301528660a08301528660c083015260e0820152858851978880958194630b5e801360e01b835260048301611b9a565b0393165af1938415611fd157508193611f9d575b50505090565b9091809350813d8311611fca575b611fb58183611705565b81010312611fc7575051388080611f97565b80fd5b503d611fab565b51913d9150823e3d90fd5b634e487b7160e01b89526011600452602489fd5b61200690883d8a11611366576113578183611705565b5038611efd565b8b513d8b823e3d90fd5b9092906003810361230a575060018060a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169283600052600460205260406000209260018060a01b031692836000526020526040600020549283156122b55760405163095ea7b360e01b81527f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c86001600160a01b031660048201526024810183905260208160448160008a5af1801561100d57612296575b50604051946001600160401b039460c0870186811188821017610cd457604052865260006020870152604086015260608501526080840152604051612116816116cf565b6000815260a0840152604051916080830190811183821017610cd457604052308252600060208301523060408301526000606083015261012c4201421161132a579160a0926040519384926352bbbe2960e01b845260e06004850152805160e4850152602081015161218781611673565b61010485015260408101516000196001841b019081166101248601526060820151166101448501526080810151610164850152015160c061018484015280516101a48401819052919060005b83811061227d57505060006101c4838501810182905285516001600160a01b039081166024870152602080880151151560448801526040880151821660648801526060909701511515608487015260a486019390935261012c420160c4860152601f909301601f191684018490039092019183917f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8165af190811561100d57600091611df0575090565b60208282018101516101c48984010152879550016121d3565b6122ae9060203d602011611366576113578183611705565b50386120d2565b60405162461bcd60e51b815260206004820152602760248201527f456e68616e63656453776170526f757465723a20706f6f6c206e6f7420636f6e604482015266199a59dd5c995960ca1b6064820152608490fd5b8390600181036123755760405162461bcd60e51b815260206004820152603360248201527f456e68616e63656453776170526f757465723a20437572766520646972656374604482015272081cddd85c081b9bdd081cdd5c1c1bdc9d1959606a1b6064820152608490fd5b600414806124df575b61239457604051633b136dc160e11b8152600490fd5b6001600160a01b03907f0000000000000000000000001111111254eeb25477b68fb85ed929f73a96058290828216156124cd5760405163095ea7b360e01b81526001600160a01b03929092166004830152602482015290602090829060449082906000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2165af1801561100d576124af575b60405162461bcd60e51b815260206004820152605460248201527f456e68616e63656453776170526f757465723a2031696e63682072657175697260448201527f657320726f7574652063616c6c646174612066726f6d204150493b207573652060648201527331696e63682061676772656761746f722053444b60601b608482015260a490fd5b6124c69060203d8111611366576113578183611705565b5080612427565b604051631368c7cb60e21b8152600490fd5b507f0000000000000000000000001111111254eeb25477b68fb85ed929f73a9605826001600160a01b0316151561237e565b60405162461bcd60e51b815260206004820152602160248201527f456e68616e63656453776170526f757465723a20696e7465726e616c206f6e6c6044820152607960f81b6064820152608490fd5b906005811015610d415780151580612e11575b612deb575069021e19e0c9bab2400000811015612dcc575060005b6000526020600381526040806000209080516125a981611699565b815180818680875493848152018760005281600020936000905b80601f830110612b58576126df95549184828210612b3e575b828210612b21575b828210612b04575b828210612ae7575b828210612acb575b828210612aae575b828210612a91575b828210612a74575b8a838310612a57575b50828210612a3a575b828210612a1d575b828210612a00575b8282106129e3575b8282106129c6575b8282106129a9575b82821061298c575b82821061296f575b828210612952575b828210612935575b828210612918575b8282106128fb575b8282106128de575b8282106128c1575b8282106128a4575b828210612887575b82821061286a575b82821061284d575b828210612830575b828210612813575b8282106127f6575b8282106127d9575b50106127c3575b5090500382611705565b815260019283810183519081878254918281520191600052876000209060005b888a8383106127b1575050505050918161271f60029360ff950382611705565b8785015201541615158083830152806127a6575b61279e575060a0815193612746856116ea565b600585523690850137600261275a84611735565b52825182101561174257600090830152815160021015611742576003606083015281516003101561174257608082015280516004101561174257600460a082015290565b925050505190565b508051511515612733565b845486529094019392830192016126ff565b816127d09160f81c611666565b018790386126d5565b846127ed6001939660ff8760f01c16611666565b019301846126ce565b8461280a6001939660ff8760e81c16611666565b019301846126c6565b846128276001939660ff8760e01c16611666565b019301846126be565b846128446001939660ff8760d81c16611666565b019301846126b6565b846128616001939660ff8760d01c16611666565b019301846126ae565b8461287e6001939660ff8760c81c16611666565b019301846126a6565b8461289b6001939660ff8760c01c16611666565b0193018461269e565b846128b86001939660ff8760b81c16611666565b01930184612696565b846128d56001939660ff8760b01c16611666565b0193018461268e565b846128f26001939660ff8760a81c16611666565b01930184612686565b8461290f6001939660ff8760a01c16611666565b0193018461267e565b8461292c6001939660ff8760981c16611666565b01930184612676565b846129496001939660ff8760901c16611666565b0193018461266e565b846129666001939660ff8760881c16611666565b01930184612666565b846129836001939660ff8760801c16611666565b0193018461265e565b846129a06001939660ff8760781c16611666565b01930184612656565b846129bd6001939660ff8760701c16611666565b0193018461264e565b846129da6001939660ff8760681c16611666565b01930184612646565b846129f76001939660ff8760601c16611666565b0193018461263e565b84612a146001939660ff8760581c16611666565b01930184612636565b84612a316001939660ff8760501c16611666565b0193018461262e565b84612a4e6001939660ff8760481c16611666565b01930184612626565b94612a6a8160ff87600196991c16611666565b019301848a61261d565b84612a886001939660ff8760381c16611666565b01930184612614565b84612aa56001939660ff8760301c16611666565b0193018461260c565b84612ac26001939660ff8760281c16611666565b01930184612604565b84612ade6001939660ff87851c16611666565b019301846125fc565b84612afb6001939660ff8760181c16611666565b019301846125f4565b84612b186001939660ff8760101c16611666565b019301846125ec565b84612b356001939660ff8760081c16611666565b019301846125e4565b84612b4f6001939660ff8716611666565b019301846125dc565b9291600191945061040090612dbc8a612dae86612d8b612c1a612d438d5495612cfb89612cb360ff97612b8d838a8d16611666565b612b9e8184018a8d60081c16611666565b612baf8584018a8d60101c16611666565b888b610200606095612bc8878201858560181c16611666565b612bdb6080958585888501921c16611666565b612c7160a097612bf2898401878760281c16611666565b612c2960c09b612c098d8601898960301c16611666565b60e09e8f8601898960381c16611666565b87876101008701921c16611666565b612c3c6101208401878760481c16611666565b612c4f6101408401878760501c16611666565b612c626101608401878760581c16611666565b85856101808501921c16611666565b612c846101a08201858560681c16611666565b612c976101c08201858560701c16611666565b612caa6101e08201858560781c16611666565b01921c16611666565b612cc66102208c01888b60881c16611666565b612cd96102408c01888b60901c16611666565b612cec6102608c01888b60981c16611666565b86896102808d01921c16611666565b612d0e6102a08a01868960a81c16611666565b612d216102c08a01868960b01c16611666565b612d346102e08a01868960b81c16611666565b84876103008b01921c16611666565b612d566103208801848760c81c16611666565b612d696103408801848760d01c16611666565b612d7c6103608801848760d81c16611666565b82856103808901921c16611666565b612d9e6103a08601828560e81c16611666565b6103c08501908360f01c16611666565b6103e083019060f81c611666565b01940192019284929389926125c3565b69152d02c7e14af68000001115612de457600161258e565b600261258e565b905061191560405191612dfd836116b4565b60018352602036818501376103af83611735565b5060ff612e1d8261164d565b5416612573565b3d15612e5e573d906001600160401b038211610cd45760405191612e52601f8201601f191660200184611705565b82523d6000602084013e565b606090565b6006546001600160a01b0390811680151580612f86575b612eb0575b5050612e8a906132e5565b612e945750600090565b6126de9081810291818304149015171561132a57612710900490565b60405191602083019063f7729d4360e01b8252807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216602485015284166044840152610bb860648401528460848401526000928360a482015260a4815260e081018181106001600160401b03821117612f72576040525183928392905afa90612f37612e24565b9180612f66575b15612e7f57602082805181010312611fc757506020015180612f605780612e7f565b91505090565b50602082511015612f3e565b634e487b7160e01b85526041600452602485fd5b50612f90836132e5565b612e7a565b9060018060a01b0391827f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2166000938185526020916004835260409182872091818616928389528552838820549384613032575b505050505050612ff8906132e5565b613000575090565b906126de9182810292818404149015171561301e5750612710900490565b634e487b7160e01b81526011600452602490fd5b805194631f29a8cd60e31b865260048601528885602481867f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8165afa9485156132db5789968a966131c5575b505050600285511015806131b9575b613098575b80612fe9565b9397929691959094600019908198845b8b518110156130fa5789898d8a826130c08684611758565b5116146130f0575b836130d291611758565b5116146130e8575b6130e390611726565b6130a8565b9950896130da565b92955085926130c8565b5094995094509450945094600019841415806131ad575b8061319a575b61312357808394613092565b8161312f929350611758565b5192838102938185041490151715613186579061314b91611758565b519081156131725704906126de9182810292818404149015171561301e5750612710900490565b634e487b7160e01b83526012600452602483fd5b634e487b7160e01b84526011600452602484fd5b506131a58483611758565b511515613117565b50600019811415613111565b5060028451101561308d565b9196509194503d808a843e6131da8184611705565b82016060838203126132d75782516001600160401b03908181116132af5784019382601f860112156132af5784519461321286611ab3565b9561321f8b519788611705565b808752858088019160051b830101918583116132d3578601905b8282106132b757505050838101519182116132af570181601f820112156132b35780519061327261326983611ab3565b9951998a611705565b81895283808a019260051b8201019283116132af578301905b8282106132a05750505050939238808061307e565b8151815290830190830161328b565b8b80fd5b8a80fd5b815189811681036132cf578152908601908601613239565b8f80fd5b8e80fd5b8980fd5b81513d8b823e3d90fd5b6001600160a01b039081167f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec782168114918215613350575b821561332857505090565b7f0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f1614919050565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881168214925061331d565b3360009081527f17e306466f0d09a5f30cdc36b7cbca18c119dc1a4639b791678f67eca9a1571360205260409020547fa842d0039ede86e50de3e76d5357314d987325eb17d7eb32afe0fdec2be0031e9060ff16156133d85750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b80600052600060205260406000203360005260205260ff60406000205416156133d85750565b9060009180835282602052604083209160018060a01b03169182845260205260ff6040842054161560001461349557808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b9060009180835282602052604083209160018060a01b03169182845260205260ff6040842054166000146134955780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526135509161354b606483611705565b613552565b565b60008061357b9260018060a01b03169360208151910182865af1613574612e24565b90836135c4565b80519081151591826135a9575b50506135915750565b60249060405190635274afe760e01b82526004820152fd5b6135bc9250602080918301019101611b82565b153880613588565b906135eb57508051156135d957805190602001fd5b604051630a12f52160e11b8152600490fd5b8151158061361e575b6135fc575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156135f4565b600260015414613638576002600155565b604051633ee5aeb560e01b8152600490fdfea264697066735822122046a67042375eaf85066a73901de0c3e794f11bf746a06101cf6e17fbf9b9758a64736f6c63430008140033

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

00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000a356867fdcea8e71aeaf87805808803806231fdc000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c80000000000000000000000001111111254eeb25477b68fb85ed929f73a960582000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006b175474e89094c44da98b954eedeac495271d0f

-----Decoded View---------------
Arg [0] : _uniswapV3Router (address): 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
Arg [1] : _curve3Pool (address): 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7
Arg [2] : _dodoexRouter (address): 0xa356867fDCEa8e71AEaF87805808803806231FdC
Arg [3] : _balancerVault (address): 0xBA12222222228d8Ba445958a75a0704d566BF2C8
Arg [4] : _oneInchRouter (address): 0x1111111254EEB25477B68fb85Ed929f73A960582
Arg [5] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [6] : _usdt (address): 0xdAC17F958D2ee523a2206206994597C13D831ec7
Arg [7] : _usdc (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [8] : _dai (address): 0x6B175474E89094C44Da98b954EedeAC495271d0F

-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45
Arg [1] : 000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7
Arg [2] : 000000000000000000000000a356867fdcea8e71aeaf87805808803806231fdc
Arg [3] : 000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
Arg [4] : 0000000000000000000000001111111254eeb25477b68fb85ed929f73a960582
Arg [5] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [6] : 000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7
Arg [7] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [8] : 0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f


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

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.