ETH Price: $1,785.99 (+13.18%)

Contract

0xa062aE8A9c5e11aaA026fc2670B0D65cCc8B2858
Transaction Hash
Method
Block
From
To
Swap Exact Token...1348918312025-04-23 4:27:193 mins ago1745382439IN
Velodrome Finance: Router V2
0 ETH0.0000021010110.00181506
Swap Exact Token...1348916532025-04-23 4:21:239 mins ago1745382083IN
Velodrome Finance: Router V2
0 ETH0.0000021366890.00159712
Swap Exact Token...1348915072025-04-23 4:16:3114 mins ago1745381791IN
Velodrome Finance: Router V2
0 ETH0.0000023296990.00147168
Swap Exact Token...1348913462025-04-23 4:11:0919 mins ago1745381469IN
Velodrome Finance: Router V2
0 ETH0.0000020255610.00136974
Swap Exact Token...1348911922025-04-23 4:06:0125 mins ago1745381161IN
Velodrome Finance: Router V2
0 ETH0.0000032553780.00128524
Swap Exact Token...1348910642025-04-23 4:01:4529 mins ago1745380905IN
Velodrome Finance: Router V2
0 ETH0.0000036362370.00125225
Swap Exact Token...1348909922025-04-23 3:59:2131 mins ago1745380761IN
Velodrome Finance: Router V2
0 ETH0.0000025531840.00121245
Remove Liquidity1348909882025-04-23 3:59:1331 mins ago1745380753IN
Velodrome Finance: Router V2
0 ETH0.0000023652420.00121129
Swap Exact Token...1348907512025-04-23 3:51:1939 mins ago1745380279IN
Velodrome Finance: Router V2
0 ETH0.0000011530390.0011159
Swap Exact Token...1348907482025-04-23 3:51:1339 mins ago1745380273IN
Velodrome Finance: Router V2
0 ETH0.0000010139450.00105868
Swap Exact Token...1348906212025-04-23 3:46:5944 mins ago1745380019IN
Velodrome Finance: Router V2
0 ETH0.0000013761260.00108332
Swap Exact Token...1348904622025-04-23 3:41:4149 mins ago1745379701IN
Velodrome Finance: Router V2
0 ETH0.0000036348660.00105368
Swap Exact Token...1348903122025-04-23 3:36:4154 mins ago1745379401IN
Velodrome Finance: Router V2
0 ETH0.000002260060.0010383
Remove Liquidity1348902062025-04-23 3:33:0957 mins ago1745379189IN
Velodrome Finance: Router V2
0 ETH0.000003937380.00103371
Swap Exact Token...1348901682025-04-23 3:31:531 hrs ago1745379113IN
Velodrome Finance: Router V2
0 ETH0.0000041128980.00103652
Add Liquidity1348896662025-04-23 3:15:091 hr ago1745378109IN
Velodrome Finance: Router V2
0 ETH0.0000057347740.00018907
Add Liquidity1348896172025-04-23 3:13:311 hr ago1745378011IN
Velodrome Finance: Router V2
0 ETH0.0000044322870.00019952
Swap Exact Token...1348895572025-04-23 3:11:311 hr ago1745377891IN
Velodrome Finance: Router V2
0 ETH0.0000040783810.00111389
Remove Liquidity1348893272025-04-23 3:03:511 hr ago1745377431IN
Velodrome Finance: Router V2
0 ETH0.0000042641660.00027934
Swap Exact Token...1348892712025-04-23 3:01:591 hr ago1745377319IN
Velodrome Finance: Router V2
0 ETH0.0000046066670.00120183
Swap Exact Token...1348891842025-04-23 2:59:051 hr ago1745377145IN
Velodrome Finance: Router V2
0 ETH0.0000043403650.00122752
Add Liquidity1348890212025-04-23 2:53:391 hr ago1745376819IN
Velodrome Finance: Router V2
0 ETH0.0000053196170.00055191
Remove Liquidity1348889272025-04-23 2:50:311 hr ago1745376631IN
Velodrome Finance: Router V2
0 ETH0.0000056689520.00040888
Add Liquidity1348886192025-04-23 2:40:151 hr ago1745376015IN
Velodrome Finance: Router V2
0 ETH0.0000043032170.00156952
Swap Exact Token...1348882222025-04-23 2:27:012 hrs ago1745375221IN
Velodrome Finance: Router V2
0 ETH0.0000030794340.00197007
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
1348753392025-04-22 19:17:359 hrs ago1745349455
Velodrome Finance: Router V2
0.0009975 ETH
1348753392025-04-22 19:17:359 hrs ago1745349455
Velodrome Finance: Router V2
0.0009975 ETH
1348695272025-04-22 16:03:5112 hrs ago1745337831
Velodrome Finance: Router V2
0.000000000367175 ETH
1348695272025-04-22 16:03:5112 hrs ago1745337831
Velodrome Finance: Router V2
0.010599999632824 ETH
1348682412025-04-22 15:20:5913 hrs ago1745335259
Velodrome Finance: Router V2
0.061207276964681 ETH
1348584432025-04-22 9:54:2318 hrs ago1745315663
Velodrome Finance: Router V2
1.857438368207062 ETH
1348550582025-04-22 8:01:3320 hrs ago1745308893
Velodrome Finance: Router V2
0.00000000016763 ETH
1348550582025-04-22 8:01:3320 hrs ago1745308893
Velodrome Finance: Router V2
0.002761308504194 ETH
1348448612025-04-22 2:21:3926 hrs ago1745288499
Velodrome Finance: Router V2
0.007014246565236 ETH
1348448612025-04-22 2:21:3926 hrs ago1745288499
Velodrome Finance: Router V2
0.007014246565236 ETH
1348447922025-04-22 2:19:2126 hrs ago1745288361
Velodrome Finance: Router V2
0.676858701544741 ETH
1348418122025-04-22 0:40:0127 hrs ago1745282401
Velodrome Finance: Router V2
0.000025971084718 ETH
1348418122025-04-22 0:40:0127 hrs ago1745282401
Velodrome Finance: Router V2
0.032974028915281 ETH
1348417612025-04-22 0:38:1927 hrs ago1745282299
Velodrome Finance: Router V2
0.472205440699682 ETH
1348343472025-04-21 20:31:1131 hrs ago1745267471
Velodrome Finance: Router V2
13.801208133362586 ETH
1348207492025-04-21 12:57:5539 hrs ago1745240275
Velodrome Finance: Router V2
0.000697989659819 ETH
1348152912025-04-21 9:55:5942 hrs ago1745229359
Velodrome Finance: Router V2
0.000498547405201 ETH
1348094552025-04-21 6:41:2745 hrs ago1745217687
Velodrome Finance: Router V2
0.202029899843997 ETH
1348087762025-04-21 6:18:4946 hrs ago1745216329
Velodrome Finance: Router V2
0.044417578918365 ETH
1347808112025-04-20 14:46:392 days ago1745160399
Velodrome Finance: Router V2
1 wei
1347808112025-04-20 14:46:392 days ago1745160399
Velodrome Finance: Router V2
0.002299999999999 ETH
1347785012025-04-20 13:29:392 days ago1745155779
Velodrome Finance: Router V2
0.000000000632639 ETH
1347769482025-04-20 12:37:532 days ago1745152673
Velodrome Finance: Router V2
1 wei
1347769482025-04-20 12:37:532 days ago1745152673
Velodrome Finance: Router V2
6.999999999999999 ETH
1347715062025-04-20 9:36:292 days ago1745141789
Velodrome Finance: Router V2
0.002085561614778 ETH
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Router

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 18 : Router.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IPool} from "./interfaces/IPool.sol";
import {IPoolFactory} from "./interfaces/factories/IPoolFactory.sol";
import {IPairFactoryV1} from "./interfaces/v1/IPairFactoryV1.sol";
import {IRouter} from "./interfaces/IRouter.sol";
import {IVoter} from "./interfaces/IVoter.sol";
import {IGauge} from "./interfaces/IGauge.sol";
import {IFactoryRegistry} from "./interfaces/factories/IFactoryRegistry.sol";
import {IWETH} from "./interfaces/IWETH.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";

/// @title Velodrome V2 Router
/// @author velodrome.finance, @pegahcarter
/// @notice Router allows routes through any pools created by any factory adhering to univ2 interface.
/// @dev Zapping and swapping support both v1 and v2. Adding liquidity supports v2 only.
contract Router is IRouter, ERC2771Context {
    using SafeERC20 for IERC20;

    address public immutable factoryRegistry;
    address public immutable v1Factory;
    /// @dev v2 default pair factory
    address public immutable defaultFactory;
    address public immutable voter;
    IWETH public immutable weth;
    uint256 internal constant MINIMUM_LIQUIDITY = 10 ** 3;
    /// @dev Represents Ether. Used by zapper to determine whether to return assets as ETH/WETH.
    address public constant ETHER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    modifier ensure(uint256 deadline) {
        _ensureDeadline(deadline);
        _;
    }

    function _ensureDeadline(uint256 deadline) internal view {
        if (deadline < block.timestamp) revert Expired();
    }

    constructor(
        address _forwarder,
        address _factoryRegistry,
        address _v1Factory,
        address _factory,
        address _voter,
        address _weth
    ) ERC2771Context(_forwarder) {
        factoryRegistry = _factoryRegistry;
        v1Factory = _v1Factory;
        defaultFactory = _factory;
        voter = _voter;
        weth = IWETH(_weth);
    }

    receive() external payable {
        if (msg.sender != address(weth)) revert OnlyWETH();
    }

    /// @inheritdoc IRouter
    function sortTokens(address tokenA, address tokenB) public pure returns (address token0, address token1) {
        if (tokenA == tokenB) revert SameAddresses();
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        if (token0 == address(0)) revert ZeroAddress();
    }

    /// @inheritdoc IRouter
    function pairFor(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory
    ) external view returns (address pool) {
        return poolFor(tokenA, tokenB, stable, _factory);
    }

    /// @inheritdoc IRouter
    function poolFor(address tokenA, address tokenB, bool stable, address _factory) public view returns (address pool) {
        address _defaultFactory = defaultFactory;
        address factory = _factory == address(0) ? _defaultFactory : _factory;
        if (!IFactoryRegistry(factoryRegistry).isPoolFactoryApproved(factory)) revert PoolFactoryDoesNotExist();
        address velo = IPoolFactory(_defaultFactory).velo();
        address veloV2 = IPoolFactory(_defaultFactory).veloV2();
        // Disable routing v2 -> v1 velo
        if ((tokenA == veloV2) && (tokenB == velo)) revert ConversionFromV2ToV1VeloProhibited();
        // Override for sink converter
        if ((tokenA == velo) && (tokenB == veloV2)) {
            return IPoolFactory(_defaultFactory).sinkConverter();
        }

        (address token0, address token1) = sortTokens(tokenA, tokenB);
        if (factory != v1Factory) {
            bytes32 salt = keccak256(abi.encodePacked(token0, token1, stable));
            pool = Clones.predictDeterministicAddress(IPoolFactory(factory).implementation(), salt, factory);
        } else {
            // backwards compatible with v1
            bytes32 pairCodeHash = IPairFactoryV1(factory).pairCodeHash();
            pool = address(
                uint160(
                    uint256(
                        keccak256(
                            abi.encodePacked(
                                hex"ff",
                                factory,
                                keccak256(abi.encodePacked(token0, token1, stable)),
                                pairCodeHash // init code hash
                            )
                        )
                    )
                )
            );
        }
    }

    /// @dev given some amount of an asset and pool reserves, returns an equivalent amount of the other asset
    /// @dev this only accounts for volatile pools and may return insufficient liquidity for stable pools
    function quoteLiquidity(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) internal pure returns (uint256 amountB) {
        if (amountA == 0) revert InsufficientAmount();
        if (reserveA == 0 || reserveB == 0) revert InsufficientLiquidity();
        amountB = (amountA * reserveB) / reserveA;
    }

    /// @inheritdoc IRouter
    function getReserves(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory
    ) public view returns (uint256 reserveA, uint256 reserveB) {
        (address token0, ) = sortTokens(tokenA, tokenB);
        (uint256 reserve0, uint256 reserve1, ) = IPool(poolFor(tokenA, tokenB, stable, _factory)).getReserves();
        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    }

    /// @inheritdoc IRouter
    function getAmountsOut(uint256 amountIn, Route[] memory routes) public view returns (uint256[] memory amounts) {
        if (routes.length < 1) revert InvalidPath();
        amounts = new uint256[](routes.length + 1);
        amounts[0] = amountIn;
        uint256 _length = routes.length;
        for (uint256 i = 0; i < _length; i++) {
            address factory = routes[i].factory == address(0) ? defaultFactory : routes[i].factory; // default to v2
            address pool = poolFor(routes[i].from, routes[i].to, routes[i].stable, factory);
            if (IPoolFactory(factory).isPair(pool)) {
                amounts[i + 1] = IPool(pool).getAmountOut(amounts[i], routes[i].from);
            }
        }
    }

    /// @inheritdoc IRouter
    function quoteAddLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory,
        uint256 amountADesired,
        uint256 amountBDesired
    ) public view returns (uint256 amountA, uint256 amountB, uint256 liquidity) {
        address _pool = IPoolFactory(_factory).getPair(tokenA, tokenB, stable);
        (uint256 reserveA, uint256 reserveB) = (0, 0);
        uint256 _totalSupply = 0;
        if (_pool != address(0)) {
            _totalSupply = IERC20(_pool).totalSupply();
            (reserveA, reserveB) = getReserves(tokenA, tokenB, stable, _factory);
        }
        if (reserveA == 0 && reserveB == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
            liquidity = Math.sqrt(amountA * amountB) - MINIMUM_LIQUIDITY;
        } else {
            uint256 amountBOptimal = quoteLiquidity(amountADesired, reserveA, reserveB);
            if (amountBOptimal <= amountBDesired) {
                (amountA, amountB) = (amountADesired, amountBOptimal);
                liquidity = Math.min((amountA * _totalSupply) / reserveA, (amountB * _totalSupply) / reserveB);
            } else {
                uint256 amountAOptimal = quoteLiquidity(amountBDesired, reserveB, reserveA);
                (amountA, amountB) = (amountAOptimal, amountBDesired);
                liquidity = Math.min((amountA * _totalSupply) / reserveA, (amountB * _totalSupply) / reserveB);
            }
        }
    }

    /// @inheritdoc IRouter
    function quoteRemoveLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory,
        uint256 liquidity
    ) public view returns (uint256 amountA, uint256 amountB) {
        address _pool = IPoolFactory(_factory).getPair(tokenA, tokenB, stable);

        if (_pool == address(0)) {
            return (0, 0);
        }

        (uint256 reserveA, uint256 reserveB) = getReserves(tokenA, tokenB, stable, _factory);
        uint256 _totalSupply = IERC20(_pool).totalSupply();

        amountA = (liquidity * reserveA) / _totalSupply; // using balances ensures pro-rata distribution
        amountB = (liquidity * reserveB) / _totalSupply; // using balances ensures pro-rata distribution
    }

    function _addLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin
    ) internal returns (uint256 amountA, uint256 amountB) {
        if (amountADesired < amountAMin) revert InsufficientAmountADesired();
        if (amountBDesired < amountBMin) revert InsufficientAmountBDesired();
        // create the pool if it doesn't exist yet
        address _pool = IPoolFactory(defaultFactory).getPair(tokenA, tokenB, stable);
        if (_pool == address(0)) {
            _pool = IPoolFactory(defaultFactory).createPair(tokenA, tokenB, stable);
        }
        (uint256 reserveA, uint256 reserveB) = getReserves(tokenA, tokenB, stable, defaultFactory);
        if (reserveA == 0 && reserveB == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
        } else {
            uint256 amountBOptimal = quoteLiquidity(amountADesired, reserveA, reserveB);
            if (amountBOptimal <= amountBDesired) {
                if (amountBOptimal < amountBMin) revert InsufficientAmountB();
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } else {
                uint256 amountAOptimal = quoteLiquidity(amountBDesired, reserveB, reserveA);
                assert(amountAOptimal <= amountADesired);
                if (amountAOptimal < amountAMin) revert InsufficientAmountA();
                (amountA, amountB) = (amountAOptimal, amountBDesired);
            }
        }
    }

    /// @inheritdoc IRouter
    function addLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) public ensure(deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity) {
        (amountA, amountB) = _addLiquidity(
            tokenA,
            tokenB,
            stable,
            amountADesired,
            amountBDesired,
            amountAMin,
            amountBMin
        );
        address pool = poolFor(tokenA, tokenB, stable, defaultFactory);
        _safeTransferFrom(tokenA, _msgSender(), pool, amountA);
        _safeTransferFrom(tokenB, _msgSender(), pool, amountB);
        liquidity = IPool(pool).mint(to);
    }

    /// @inheritdoc IRouter
    function addLiquidityETH(
        address token,
        bool stable,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external payable ensure(deadline) returns (uint256 amountToken, uint256 amountETH, uint256 liquidity) {
        (amountToken, amountETH) = _addLiquidity(
            token,
            address(weth),
            stable,
            amountTokenDesired,
            msg.value,
            amountTokenMin,
            amountETHMin
        );
        address pool = poolFor(token, address(weth), stable, defaultFactory);
        _safeTransferFrom(token, _msgSender(), pool, amountToken);
        weth.deposit{value: amountETH}();
        assert(weth.transfer(pool, amountETH));
        liquidity = IPool(pool).mint(to);
        // refund dust eth, if any
        if (msg.value > amountETH) _safeTransferETH(_msgSender(), msg.value - amountETH);
    }

    // **** REMOVE LIQUIDITY ****

    /// @inheritdoc IRouter
    function removeLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) public ensure(deadline) returns (uint256 amountA, uint256 amountB) {
        address pool = poolFor(tokenA, tokenB, stable, defaultFactory);
        IERC20(pool).safeTransferFrom(_msgSender(), pool, liquidity);
        (uint256 amount0, uint256 amount1) = IPool(pool).burn(to);
        (address token0, ) = sortTokens(tokenA, tokenB);
        (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
        if (amountA < amountAMin) revert InsufficientAmountA();
        if (amountB < amountBMin) revert InsufficientAmountB();
    }

    /// @inheritdoc IRouter
    function removeLiquidityETH(
        address token,
        bool stable,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) public ensure(deadline) returns (uint256 amountToken, uint256 amountETH) {
        (amountToken, amountETH) = removeLiquidity(
            token,
            address(weth),
            stable,
            liquidity,
            amountTokenMin,
            amountETHMin,
            address(this),
            deadline
        );
        _safeTransfer(token, to, amountToken);
        weth.withdraw(amountETH);
        _safeTransferETH(to, amountETH);
    }

    // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) ****
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        bool stable,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) public ensure(deadline) returns (uint256 amountETH) {
        (, amountETH) = removeLiquidity(
            token,
            address(weth),
            stable,
            liquidity,
            amountTokenMin,
            amountETHMin,
            address(this),
            deadline
        );
        _safeTransfer(token, to, IERC20(token).balanceOf(address(this)));
        weth.withdraw(amountETH);
        _safeTransferETH(to, amountETH);
    }

    // **** SWAP ****
    /// @dev requires the initial amount to have already been sent to the first pool
    function _swap(uint256[] memory amounts, Route[] memory routes, address _to) internal virtual {
        uint256 _length = routes.length;
        for (uint256 i = 0; i < _length; i++) {
            (address token0, ) = sortTokens(routes[i].from, routes[i].to);
            uint256 amountOut = amounts[i + 1];
            (uint256 amount0Out, uint256 amount1Out) = routes[i].from == token0
                ? (uint256(0), amountOut)
                : (amountOut, uint256(0));
            address to = i < routes.length - 1
                ? poolFor(routes[i + 1].from, routes[i + 1].to, routes[i + 1].stable, routes[i + 1].factory)
                : _to;
            IPool(poolFor(routes[i].from, routes[i].to, routes[i].stable, routes[i].factory)).swap(
                amount0Out,
                amount1Out,
                to,
                new bytes(0)
            );
        }
    }

    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external ensure(deadline) returns (uint256[] memory amounts) {
        amounts = getAmountsOut(amountIn, routes);
        if (amounts[amounts.length - 1] < amountOutMin) revert InsufficientOutputAmount();
        _safeTransferFrom(
            routes[0].from,
            _msgSender(),
            poolFor(routes[0].from, routes[0].to, routes[0].stable, routes[0].factory),
            amounts[0]
        );
        _swap(amounts, routes, to);
    }

    function swapExactETHForTokens(
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external payable ensure(deadline) returns (uint256[] memory amounts) {
        if (routes[0].from != address(weth)) revert InvalidPath();
        amounts = getAmountsOut(msg.value, routes);
        if (amounts[amounts.length - 1] < amountOutMin) revert InsufficientOutputAmount();
        weth.deposit{value: amounts[0]}();
        assert(weth.transfer(poolFor(routes[0].from, routes[0].to, routes[0].stable, routes[0].factory), amounts[0]));
        _swap(amounts, routes, to);
    }

    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external ensure(deadline) returns (uint256[] memory amounts) {
        if (routes[routes.length - 1].to != address(weth)) revert InvalidPath();
        amounts = getAmountsOut(amountIn, routes);
        if (amounts[amounts.length - 1] < amountOutMin) revert InsufficientOutputAmount();
        _safeTransferFrom(
            routes[0].from,
            _msgSender(),
            poolFor(routes[0].from, routes[0].to, routes[0].stable, routes[0].factory),
            amounts[0]
        );
        _swap(amounts, routes, address(this));
        weth.withdraw(amounts[amounts.length - 1]);
        _safeTransferETH(to, amounts[amounts.length - 1]);
    }

    function UNSAFE_swapExactTokensForTokens(
        uint256[] memory amounts,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external ensure(deadline) returns (uint256[] memory) {
        _safeTransferFrom(
            routes[0].from,
            _msgSender(),
            poolFor(routes[0].from, routes[0].to, routes[0].stable, routes[0].factory),
            amounts[0]
        );
        _swap(amounts, routes, to);
        return amounts;
    }

    // **** SWAP (supporting fee-on-transfer tokens) ****
    /// @dev requires the initial amount to have already been sent to the first pool
    function _swapSupportingFeeOnTransferTokens(Route[] memory routes, address _to) internal virtual {
        uint256 _length = routes.length;
        for (uint256 i; i < _length; i++) {
            (address token0, ) = sortTokens(routes[i].from, routes[i].to);
            address pool = poolFor(routes[i].from, routes[i].to, routes[i].stable, routes[i].factory);
            uint256 amountInput;
            uint256 amountOutput;
            {
                // stack too deep
                (uint256 reserveA, ) = getReserves(routes[i].from, routes[i].to, routes[i].stable, routes[i].factory); // getReserves sorts it for us i.e. reserveA is always for from
                amountInput = IERC20(routes[i].from).balanceOf(pool) - reserveA;
            }
            amountOutput = IPool(pool).getAmountOut(amountInput, routes[i].from);
            (uint256 amount0Out, uint256 amount1Out) = routes[i].from == token0
                ? (uint256(0), amountOutput)
                : (amountOutput, uint256(0));
            address to = i < routes.length - 1
                ? poolFor(routes[i + 1].from, routes[i + 1].to, routes[i + 1].stable, routes[i + 1].factory)
                : _to;
            IPool(pool).swap(amount0Out, amount1Out, to, new bytes(0));
        }
    }

    /// @inheritdoc IRouter
    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external ensure(deadline) {
        _safeTransferFrom(
            routes[0].from,
            _msgSender(),
            poolFor(routes[0].from, routes[0].to, routes[0].stable, routes[0].factory),
            amountIn
        );
        uint256 _length = routes.length - 1;
        uint256 balanceBefore = IERC20(routes[_length].to).balanceOf(to);
        _swapSupportingFeeOnTransferTokens(routes, to);
        if (IERC20(routes[_length].to).balanceOf(to) - balanceBefore < amountOutMin) revert InsufficientOutputAmount();
    }

    /// @inheritdoc IRouter
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external payable ensure(deadline) {
        if (routes[0].from != address(weth)) revert InvalidPath();
        uint256 amountIn = msg.value;
        weth.deposit{value: amountIn}();
        assert(weth.transfer(poolFor(routes[0].from, routes[0].to, routes[0].stable, routes[0].factory), amountIn));
        uint256 _length = routes.length - 1;
        uint256 balanceBefore = IERC20(routes[_length].to).balanceOf(to);
        _swapSupportingFeeOnTransferTokens(routes, to);
        if (IERC20(routes[_length].to).balanceOf(to) - balanceBefore < amountOutMin) revert InsufficientOutputAmount();
    }

    /// @inheritdoc IRouter
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external ensure(deadline) {
        if (routes[routes.length - 1].to != address(weth)) revert InvalidPath();
        _safeTransferFrom(
            routes[0].from,
            _msgSender(),
            poolFor(routes[0].from, routes[0].to, routes[0].stable, routes[0].factory),
            amountIn
        );
        _swapSupportingFeeOnTransferTokens(routes, address(this));
        uint256 amountOut = weth.balanceOf(address(this));
        if (amountOut < amountOutMin) revert InsufficientOutputAmount();
        weth.withdraw(amountOut);
        _safeTransferETH(to, amountOut);
    }

    /// @inheritdoc IRouter
    function zapIn(
        address tokenIn,
        uint256 amountInA,
        uint256 amountInB,
        Zap calldata zapInPool,
        Route[] calldata routesA,
        Route[] calldata routesB,
        address to,
        bool stake
    ) external payable returns (uint256 liquidity) {
        uint256 amountIn = amountInA + amountInB;
        address _tokenIn = tokenIn;
        uint256 value = msg.value;
        if (tokenIn == ETHER) {
            if (amountIn != value) revert InvalidAmountInForETHDeposit();
            _tokenIn = address(weth);
            weth.deposit{value: value}();
        } else {
            if (value != 0) revert InvalidTokenInForETHDeposit();
            _safeTransferFrom(_tokenIn, _msgSender(), address(this), amountIn);
        }

        _zapSwap(_tokenIn, amountInA, amountInB, zapInPool, routesA, routesB);
        _zapInLiquidity(zapInPool);
        address pool = poolFor(zapInPool.tokenA, zapInPool.tokenB, zapInPool.stable, zapInPool.factory);

        if (stake) {
            liquidity = IPool(pool).mint(address(this));
            address gauge = IVoter(voter).gauges(pool);
            IERC20(pool).safeApprove(address(gauge), liquidity);
            IGauge(gauge).deposit(liquidity, to);
            IERC20(pool).safeApprove(address(gauge), 0);
        } else {
            liquidity = IPool(pool).mint(to);
        }

        _returnAssets(tokenIn);
        _returnAssets(zapInPool.tokenA);
        _returnAssets(zapInPool.tokenB);
    }

    /// @dev Handles swap leg of zap in (i.e. convert tokenIn into tokenA and tokenB).
    function _zapSwap(
        address tokenIn,
        uint256 amountInA,
        uint256 amountInB,
        Zap calldata zapInPool,
        Route[] calldata routesA,
        Route[] calldata routesB
    ) internal {
        address tokenA = zapInPool.tokenA;
        address tokenB = zapInPool.tokenB;
        bool stable = zapInPool.stable;
        address factory = zapInPool.factory;
        address pool = poolFor(tokenA, tokenB, stable, factory);

        {
            (uint256 reserve0, uint256 reserve1, ) = IPool(pool).getReserves();
            if (reserve0 <= MINIMUM_LIQUIDITY || reserve1 <= MINIMUM_LIQUIDITY) revert PoolDoesNotExist();
        }

        if (tokenIn != tokenA) {
            if (routesA[routesA.length - 1].to != tokenA) revert InvalidRouteA();
            _internalSwap(tokenIn, amountInA, zapInPool.amountOutMinA, routesA);
        }
        if (tokenIn != tokenB) {
            if (routesB[routesB.length - 1].to != tokenB) revert InvalidRouteB();
            _internalSwap(tokenIn, amountInB, zapInPool.amountOutMinB, routesB);
        }
    }

    /// @dev Handles liquidity adding component of zap in.
    function _zapInLiquidity(Zap calldata zapInPool) internal {
        address tokenA = zapInPool.tokenA;
        address tokenB = zapInPool.tokenB;
        bool stable = zapInPool.stable;
        address factory = zapInPool.factory;
        address pool = poolFor(tokenA, tokenB, stable, factory);
        (uint256 amountA, uint256 amountB) = _quoteZapLiquidity(
            tokenA,
            tokenB,
            stable,
            factory,
            IERC20(tokenA).balanceOf(address(this)),
            IERC20(tokenB).balanceOf(address(this)),
            zapInPool.amountAMin,
            zapInPool.amountBMin
        );
        _safeTransfer(tokenA, pool, amountA);
        _safeTransfer(tokenB, pool, amountB);
    }

    /// @dev Similar to _addLiquidity. Assumes a pool exists, and accepts a factory argument.
    function _quoteZapLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin
    ) internal view returns (uint256 amountA, uint256 amountB) {
        if (amountADesired < amountAMin) revert InsufficientAmountADesired();
        if (amountBDesired < amountBMin) revert InsufficientAmountBDesired();
        (uint256 reserveA, uint256 reserveB) = getReserves(tokenA, tokenB, stable, _factory);
        if (reserveA == 0 && reserveB == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
        } else {
            uint256 amountBOptimal = quoteLiquidity(amountADesired, reserveA, reserveB);
            if (amountBOptimal <= amountBDesired) {
                if (amountBOptimal < amountBMin) revert InsufficientAmountB();
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } else {
                uint256 amountAOptimal = quoteLiquidity(amountBDesired, reserveB, reserveA);
                assert(amountAOptimal <= amountADesired);
                if (amountAOptimal < amountAMin) revert InsufficientAmountA();
                (amountA, amountB) = (amountAOptimal, amountBDesired);
            }
        }
    }

    /// @dev Handles swaps internally for zaps.
    function _internalSwap(address tokenIn, uint256 amountIn, uint256 amountOutMin, Route[] memory routes) internal {
        uint256[] memory amounts = getAmountsOut(amountIn, routes);
        if (amounts[amounts.length - 1] < amountOutMin) revert InsufficientOutputAmount();
        address pool = poolFor(routes[0].from, routes[0].to, routes[0].stable, routes[0].factory);
        _safeTransfer(tokenIn, pool, amountIn);
        _swap(amounts, routes, address(this));
    }

    /// @inheritdoc IRouter
    function zapOut(
        address tokenOut,
        uint256 liquidity,
        Zap calldata zapOutPool,
        Route[] calldata routesA,
        Route[] calldata routesB
    ) external {
        address tokenA = zapOutPool.tokenA;
        address tokenB = zapOutPool.tokenB;
        address _tokenOut = (tokenOut == ETHER) ? address(weth) : tokenOut;
        _zapOutLiquidity(liquidity, zapOutPool);

        uint256 balance;
        if (tokenA != _tokenOut) {
            balance = IERC20(tokenA).balanceOf(address(this));
            if (routesA[routesA.length - 1].to != _tokenOut) revert InvalidRouteA();
            _internalSwap(tokenA, balance, zapOutPool.amountOutMinA, routesA);
        }
        if (tokenB != _tokenOut) {
            balance = IERC20(tokenB).balanceOf(address(this));
            if (routesB[routesB.length - 1].to != _tokenOut) revert InvalidRouteB();
            _internalSwap(tokenB, balance, zapOutPool.amountOutMinB, routesB);
        }

        _returnAssets(tokenOut);
    }

    /// @dev Handles liquidity removing component of zap out.
    function _zapOutLiquidity(uint256 liquidity, Zap calldata zapOutPool) internal {
        address tokenA = zapOutPool.tokenA;
        address tokenB = zapOutPool.tokenB;
        address pool = poolFor(tokenA, tokenB, zapOutPool.stable, zapOutPool.factory);
        IERC20(pool).safeTransferFrom(msg.sender, pool, liquidity);
        (address token0, ) = sortTokens(tokenA, tokenB);
        (uint256 amount0, uint256 amount1) = IPool(pool).burn(address(this));
        (uint256 amountA, uint256 amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
        if (amountA < zapOutPool.amountAMin) revert InsufficientAmountA();
        if (amountB < zapOutPool.amountBMin) revert InsufficientAmountB();
    }

    /// @inheritdoc IRouter
    function generateZapInParams(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory,
        uint256 amountInA,
        uint256 amountInB,
        Route[] calldata routesA,
        Route[] calldata routesB
    ) external view returns (uint256 amountOutMinA, uint256 amountOutMinB, uint256 amountAMin, uint256 amountBMin) {
        amountOutMinA = amountInA;
        amountOutMinB = amountInB;
        uint256[] memory amounts;
        if (routesA.length > 0) {
            amounts = getAmountsOut(amountInA, routesA);
            amountOutMinA = amounts[amounts.length - 1];
        }
        if (routesB.length > 0) {
            amounts = getAmountsOut(amountInB, routesB);
            amountOutMinB = amounts[amounts.length - 1];
        }
        (amountAMin, amountBMin, ) = quoteAddLiquidity(tokenA, tokenB, stable, _factory, amountOutMinA, amountOutMinB);
    }

    /// @inheritdoc IRouter
    function generateZapOutParams(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory,
        uint256 liquidity,
        Route[] calldata routesA,
        Route[] calldata routesB
    ) external view returns (uint256 amountOutMinA, uint256 amountOutMinB, uint256 amountAMin, uint256 amountBMin) {
        (amountAMin, amountBMin) = quoteRemoveLiquidity(tokenA, tokenB, stable, _factory, liquidity);
        amountOutMinA = amountAMin;
        amountOutMinB = amountBMin;
        uint256[] memory amounts;
        if (routesA.length > 0) {
            amounts = getAmountsOut(amountAMin, routesA);
            amountOutMinA = amounts[amounts.length - 1];
        }
        if (routesB.length > 0) {
            amounts = getAmountsOut(amountBMin, routesB);
            amountOutMinB = amounts[amounts.length - 1];
        }
    }

    /// @dev Return residual assets from zapping.
    /// @param token token to return, put `ETHER` if you want Ether back.
    function _returnAssets(address token) internal {
        address sender = _msgSender();
        uint256 balance;
        if (token == ETHER) {
            balance = IERC20(weth).balanceOf(address(this));
            if (balance > 0) {
                IWETH(weth).withdraw(balance);
                _safeTransferETH(sender, balance);
            }
        } else {
            balance = IERC20(token).balanceOf(address(this));
            if (balance > 0) {
                IERC20(token).safeTransfer(sender, balance);
            }
        }
    }

    /// @inheritdoc IRouter
    function quoteStableLiquidityRatio(
        address tokenA,
        address tokenB,
        address _factory
    ) external view returns (uint256 ratio) {
        IPool pool = IPool(poolFor(tokenA, tokenB, true, _factory));

        uint256 decimalsA = 10 ** IERC20Metadata(tokenA).decimals();
        uint256 decimalsB = 10 ** IERC20Metadata(tokenB).decimals();

        uint256 investment = decimalsA;
        uint256 out = pool.getAmountOut(investment, tokenA);
        (uint256 amountA, uint256 amountB, ) = quoteAddLiquidity(tokenA, tokenB, true, _factory, investment, out);

        amountA = (amountA * 1e18) / decimalsA;
        amountB = (amountB * 1e18) / decimalsB;
        out = (out * 1e18) / decimalsB;
        investment = (investment * 1e18) / decimalsA;

        ratio = (((out * 1e18) / investment) * amountA) / amountB;

        return (investment * 1e18) / (ratio + 1e18);
    }

    function _safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        if (!success) revert ETHTransferFailed();
    }

    function _safeTransfer(address token, address to, uint256 value) internal {
        require(token.code.length > 0);
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))));
    }

    function _safeTransferFrom(address token, address from, address to, uint256 value) internal {
        require(token.code.length > 0);
        (bool success, bytes memory data) = token.call(
            abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)
        );
        require(success && (data.length == 0 || abi.decode(data, (bool))));
    }
}

File 2 of 18 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

interface IPool {
    error DepositsNotEqual();
    error BelowMinimumK();
    error FactoryAlreadySet();
    error InsufficientLiquidity();
    error InsufficientLiquidityMinted();
    error InsufficientLiquidityBurned();
    error InsufficientOutputAmount();
    error InsufficientInputAmount();
    error IsPaused();
    error InvalidTo();
    error K();
    error NotEmergencyCouncil();

    event Fees(address indexed sender, uint256 amount0, uint256 amount1);
    event Mint(address indexed sender, uint256 amount0, uint256 amount1);
    event Burn(address indexed sender, address indexed to, uint256 amount0, uint256 amount1);
    event Swap(
        address indexed sender,
        address indexed to,
        uint256 amount0In,
        uint256 amount1In,
        uint256 amount0Out,
        uint256 amount1Out
    );
    event Sync(uint256 reserve0, uint256 reserve1);
    event Claim(address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1);

    function metadata()
        external
        view
        returns (uint256 dec0, uint256 dec1, uint256 r0, uint256 r1, bool st, address t0, address t1);

    function claimFees() external returns (uint256, uint256);

    function tokens() external view returns (address, address);

    function token0() external view returns (address);

    function token1() external view returns (address);

    function stable() external view returns (bool);

    function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;

    function burn(address to) external returns (uint256 amount0, uint256 amount1);

    function mint(address to) external returns (uint256 liquidity);

    function getReserves() external view returns (uint256 _reserve0, uint256 _reserve1, uint256 _blockTimestampLast);

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

    function skim(address to) external;

    function initialize(address _token0, address _token1, bool _stable) external;
}

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

interface IPoolFactory {
    event SetFeeManager(address feeManager);
    event SetPauser(address pauser);
    event SetPauseState(bool state);
    event SetVoter(address voter);
    event PoolCreated(address indexed token0, address indexed token1, bool indexed stable, address pool, uint256);
    event SetCustomFee(address indexed pool, uint256 fee);

    error FeeInvalid();
    error FeeTooHigh();
    error InvalidPool();
    error NotFeeManager();
    error NotPauser();
    error NotSinkConverter();
    error NotVoter();
    error PoolAlreadyExists();
    error SameAddress();
    error ZeroFee();
    error ZeroAddress();

    /// @notice returns the number of pools created from this factory
    function allPoolsLength() external view returns (uint256);

    /// @notice Is a valid pool created by this factory.
    /// @param .
    function isPool(address pool) external view returns (bool);

    /// @notice Support for Velodrome v1 which wraps around isPool(pool);
    /// @param .
    function isPair(address pool) external view returns (bool);

    /// @notice Return address of pool created by this factory
    /// @param tokenA .
    /// @param tokenB .
    /// @param stable True if stable, false if volatile
    function getPool(address tokenA, address tokenB, bool stable) external view returns (address);

    /// @notice Support for v3-style pools which wraps around getPool(tokenA,tokenB,stable)
    /// @dev fee is converted to stable boolean.
    /// @param tokenA .
    /// @param tokenB .
    /// @param fee  1 if stable, 0 if volatile, else returns address(0)
    function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address);

    /// @notice Support for Velodrome v1 pools as a "pool" was previously referenced as "pair"
    /// @notice Wraps around getPool(tokenA,tokenB,stable)
    function getPair(address tokenA, address tokenB, bool stable) external view returns (address);

    /// @dev Only called once to set to Voter.sol - Voter does not have a function
    ///      to call this contract method, so once set it's immutable.
    ///      This also follows convention of setVoterAndDistributor() in VotingEscrow.sol
    /// @param _voter .
    function setVoter(address _voter) external;

    function setSinkConverter(address _sinkConvert, address _velo, address _veloV2) external;

    function setPauser(address _pauser) external;

    function setPauseState(bool _state) external;

    function setFeeManager(address _feeManager) external;

    /// @notice Set default fee for stable and volatile pools.
    /// @dev Throws if higher than maximum fee.
    ///      Throws if fee is zero.
    /// @param _stable Stable or volatile pool.
    /// @param _fee .
    function setFee(bool _stable, uint256 _fee) external;

    /// @notice Set overriding fee for a pool from the default
    /// @dev A custom fee of zero means the default fee will be used.
    function setCustomFee(address _pool, uint256 _fee) external;

    /// @notice Returns fee for a pool, as custom fees are possible.
    function getFee(address _pool, bool _stable) external view returns (uint256);

    /// @notice Create a pool given two tokens and if they're stable/volatile
    /// @dev token order does not matter
    /// @param tokenA .
    /// @param tokenB .
    /// @param stable .
    function createPool(address tokenA, address tokenB, bool stable) external returns (address pool);

    /// @notice Support for v3-style pools which wraps around createPool(tokena,tokenB,stable)
    /// @dev fee is converted to stable boolean
    /// @dev token order does not matter
    /// @param tokenA .
    /// @param tokenB .
    /// @param fee 1 if stable, 0 if volatile, else revert
    function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool);

    /// @notice Support for Velodrome v1 which wraps around createPool(tokenA,tokenB,stable)
    function createPair(address tokenA, address tokenB, bool stable) external returns (address pool);

    function isPaused() external view returns (bool);

    function velo() external view returns (address);

    function veloV2() external view returns (address);

    function voter() external view returns (address);

    function sinkConverter() external view returns (address);

    function implementation() external view returns (address);
}

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

interface IPairFactoryV1 {
    function pairCodeHash() external view returns (bytes32);
}

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

interface IRouter {
    struct Route {
        address from;
        address to;
        bool stable;
        address factory;
    }

    error ConversionFromV2ToV1VeloProhibited();
    error ETHTransferFailed();
    error Expired();
    error InsufficientAmount();
    error InsufficientAmountA();
    error InsufficientAmountB();
    error InsufficientAmountADesired();
    error InsufficientAmountBDesired();
    error InsufficientAmountAOptimal();
    error InsufficientLiquidity();
    error InsufficientOutputAmount();
    error InvalidAmountInForETHDeposit();
    error InvalidTokenInForETHDeposit();
    error InvalidPath();
    error InvalidRouteA();
    error InvalidRouteB();
    error OnlyWETH();
    error PoolDoesNotExist();
    error PoolFactoryDoesNotExist();
    error SameAddresses();
    error ZeroAddress();

    /// @dev Struct containing information necessary to zap in and out of pools
    /// @param tokenA           .
    /// @param tokenB           .
    /// @param stable           Stable or volatile pool
    /// @param factory          factory of pool
    /// @param amountOutMinA    Minimum amount expected from swap leg of zap via routesA
    /// @param amountOutMinB    Minimum amount expected from swap leg of zap via routesB
    /// @param amountAMin       Minimum amount of tokenA expected from liquidity leg of zap
    /// @param amountBMin       Minimum amount of tokenB expected from liquidity leg of zap
    struct Zap {
        address tokenA;
        address tokenB;
        bool stable;
        address factory;
        uint256 amountOutMinA;
        uint256 amountOutMinB;
        uint256 amountAMin;
        uint256 amountBMin;
    }

    /// @notice Sort two tokens by which address value is less than the other
    /// @param tokenA   Address of token to sort
    /// @param tokenB   Address of token to sort
    /// @return token0  Lower address value between tokenA and tokenB
    /// @return token1  Higher address value between tokenA and tokenB
    function sortTokens(address tokenA, address tokenB) external pure returns (address token0, address token1);

    /// @notice Calculate the address of a pool by its' factory.
    ///         Used by all Router functions containing a `Route[]` or `_factory` argument.
    ///         Reverts if _factory is not approved by the FactoryRegistry
    /// @dev Returns a randomly generated address for a nonexistent pool
    /// @param tokenA   Address of token to query
    /// @param tokenB   Address of token to query
    /// @param stable   True if pool is stable, false if volatile
    /// @param _factory Address of factory which created the pool
    function poolFor(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory
    ) external view returns (address pool);

    /// @notice Wraps around poolFor(tokenA,tokenB,stable,_factory) for backwards compatibility to Velodrome v1
    function pairFor(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory
    ) external view returns (address pool);

    /// @notice Fetch and sort the reserves for a pool
    /// @param tokenA       .
    /// @param tokenB       .
    /// @param stable       True if pool is stable, false if volatile
    /// @param _factory     Address of PoolFactory for tokenA and tokenB
    /// @return reserveA    Amount of reserves of the sorted token A
    /// @return reserveB    Amount of reserves of the sorted token B
    function getReserves(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory
    ) external view returns (uint256 reserveA, uint256 reserveB);

    /// @notice Perform chained getAmountOut calculations on any number of pools
    function getAmountsOut(uint256 amountIn, Route[] memory routes) external view returns (uint256[] memory amounts);

    // **** ADD LIQUIDITY ****

    /// @notice Quote the amount deposited into a Pool
    /// @param tokenA           .
    /// @param tokenB           .
    /// @param stable           True if pool is stable, false if volatile
    /// @param _factory         Address of PoolFactory for tokenA and tokenB
    /// @param amountADesired   Amount of tokenA desired to deposit
    /// @param amountBDesired   Amount of tokenB desired to deposit
    /// @return amountA         Amount of tokenA to actually deposit
    /// @return amountB         Amount of tokenB to actually deposit
    /// @return liquidity       Amount of liquidity token returned from deposit
    function quoteAddLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory,
        uint256 amountADesired,
        uint256 amountBDesired
    ) external view returns (uint256 amountA, uint256 amountB, uint256 liquidity);

    /// @notice Quote the amount of liquidity removed from a Pool
    /// @param tokenA       .
    /// @param tokenB       .
    /// @param stable       True if pool is stable, false if volatile
    /// @param _factory     Address of PoolFactory for tokenA and tokenB
    /// @param liquidity    Amount of liquidity to remove
    /// @return amountA     Amount of tokenA received
    /// @return amountB     Amount of tokenB received
    function quoteRemoveLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory,
        uint256 liquidity
    ) external view returns (uint256 amountA, uint256 amountB);

    /// @notice Add liquidity of two tokens to a Pool
    /// @param tokenA           .
    /// @param tokenB           .
    /// @param stable           True if pool is stable, false if volatile
    /// @param amountADesired   Amount of tokenA desired to deposit
    /// @param amountBDesired   Amount of tokenB desired to deposit
    /// @param amountAMin       Minimum amount of tokenA to deposit
    /// @param amountBMin       Minimum amount of tokenB to deposit
    /// @param to               Recipient of liquidity token
    /// @param deadline         Deadline to receive liquidity
    /// @return amountA         Amount of tokenA to actually deposit
    /// @return amountB         Amount of tokenB to actually deposit
    /// @return liquidity       Amount of liquidity token returned from deposit
    function addLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);

    /// @notice Add liquidity of a token and WETH (transferred as ETH) to a Pool
    /// @param token                .
    /// @param stable               True if pool is stable, false if volatile
    /// @param amountTokenDesired   Amount of token desired to deposit
    /// @param amountTokenMin       Minimum amount of token to deposit
    /// @param amountETHMin         Minimum amount of ETH to deposit
    /// @param to                   Recipient of liquidity token
    /// @param deadline             Deadline to add liquidity
    /// @return amountToken         Amount of token to actually deposit
    /// @return amountETH           Amount of tokenETH to actually deposit
    /// @return liquidity           Amount of liquidity token returned from deposit
    function addLiquidityETH(
        address token,
        bool stable,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);

    // **** REMOVE LIQUIDITY ****

    /// @notice Remove liquidity of two tokens from a Pool
    /// @param tokenA       .
    /// @param tokenB       .
    /// @param stable       True if pool is stable, false if volatile
    /// @param liquidity    Amount of liquidity to remove
    /// @param amountAMin   Minimum amount of tokenA to receive
    /// @param amountBMin   Minimum amount of tokenB to receive
    /// @param to           Recipient of tokens received
    /// @param deadline     Deadline to remove liquidity
    /// @return amountA     Amount of tokenA received
    /// @return amountB     Amount of tokenB received
    function removeLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB);

    /// @notice Remove liquidity of a token and WETH (returned as ETH) from a Pool
    /// @param token            .
    /// @param stable           True if pool is stable, false if volatile
    /// @param liquidity        Amount of liquidity to remove
    /// @param amountTokenMin   Minimum amount of token to receive
    /// @param amountETHMin     Minimum amount of ETH to receive
    /// @param to               Recipient of liquidity token
    /// @param deadline         Deadline to receive liquidity
    /// @return amountToken     Amount of token received
    /// @return amountETH       Amount of ETH received
    function removeLiquidityETH(
        address token,
        bool stable,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountToken, uint256 amountETH);

    /// @notice Remove liquidity of a fee-on-transfer token and WETH (returned as ETH) from a Pool
    /// @param token            .
    /// @param stable           True if pool is stable, false if volatile
    /// @param liquidity        Amount of liquidity to remove
    /// @param amountTokenMin   Minimum amount of token to receive
    /// @param amountETHMin     Minimum amount of ETH to receive
    /// @param to               Recipient of liquidity token
    /// @param deadline         Deadline to receive liquidity
    /// @return amountETH       Amount of ETH received
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        bool stable,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountETH);

    // **** SWAP ****

    /// @notice Swap one token for another
    /// @param amountIn     Amount of token in
    /// @param amountOutMin Minimum amount of desired token received
    /// @param routes       Array of trade routes used in the swap
    /// @param to           Recipient of the tokens received
    /// @param deadline     Deadline to receive tokens
    /// @return amounts     Array of amounts returned per route
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    /// @notice Swap ETH for a token
    /// @param amountOutMin Minimum amount of desired token received
    /// @param routes       Array of trade routes used in the swap
    /// @param to           Recipient of the tokens received
    /// @param deadline     Deadline to receive tokens
    /// @return amounts     Array of amounts returned per route
    function swapExactETHForTokens(
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    /// @notice Swap a token for WETH (returned as ETH)
    /// @param amountIn     Amount of token in
    /// @param amountOutMin Minimum amount of desired ETH
    /// @param routes       Array of trade routes used in the swap
    /// @param to           Recipient of the tokens received
    /// @param deadline     Deadline to receive tokens
    /// @return amounts     Array of amounts returned per route
    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    /// @notice Swap one token for another without slippage protection
    /// @return amounts     Array of amounts to swap  per route
    /// @param routes       Array of trade routes used in the swap
    /// @param to           Recipient of the tokens received
    /// @param deadline     Deadline to receive tokens
    function UNSAFE_swapExactTokensForTokens(
        uint256[] memory amounts,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory);

    // **** SWAP (supporting fee-on-transfer tokens) ****

    /// @notice Swap one token for another supporting fee-on-transfer tokens
    /// @param amountIn     Amount of token in
    /// @param amountOutMin Minimum amount of desired token received
    /// @param routes       Array of trade routes used in the swap
    /// @param to           Recipient of the tokens received
    /// @param deadline     Deadline to receive tokens
    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external;

    /// @notice Swap ETH for a token supporting fee-on-transfer tokens
    /// @param amountOutMin Minimum amount of desired token received
    /// @param routes       Array of trade routes used in the swap
    /// @param to           Recipient of the tokens received
    /// @param deadline     Deadline to receive tokens
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external payable;

    /// @notice Swap a token for WETH (returned as ETH) supporting fee-on-transfer tokens
    /// @param amountIn     Amount of token in
    /// @param amountOutMin Minimum amount of desired ETH
    /// @param routes       Array of trade routes used in the swap
    /// @param to           Recipient of the tokens received
    /// @param deadline     Deadline to receive tokens
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        Route[] calldata routes,
        address to,
        uint256 deadline
    ) external;

    /// @notice Zap a token A into a pool (B, C). (A can be equal to B or C).
    ///         Supports standard ERC20 tokens only (i.e. not fee-on-transfer tokens etc).
    ///         Slippage is required for the initial swap.
    ///         Additional slippage may be required when adding liquidity as the
    ///         price of the token may have changed.
    /// @param tokenIn      Token you are zapping in from (i.e. input token).
    /// @param amountInA    Amount of input token you wish to send down routesA
    /// @param amountInB    Amount of input token you wish to send down routesB
    /// @param zapInPool    Contains zap struct information. See Zap struct.
    /// @param routesA      Route used to convert input token to tokenA
    /// @param routesB      Route used to convert input token to tokenB
    /// @param to           Address you wish to mint liquidity to.
    /// @param stake        Auto-stake liquidity in corresponding gauge.
    /// @return liquidity   Amount of LP tokens created from zapping in.
    function zapIn(
        address tokenIn,
        uint256 amountInA,
        uint256 amountInB,
        Zap calldata zapInPool,
        Route[] calldata routesA,
        Route[] calldata routesB,
        address to,
        bool stake
    ) external payable returns (uint256 liquidity);

    /// @notice Zap out a pool (B, C) into A.
    ///         Supports standard ERC20 tokens only (i.e. not fee-on-transfer tokens etc).
    ///         Slippage is required for the removal of liquidity.
    ///         Additional slippage may be required on the swap as the
    ///         price of the token may have changed.
    /// @param tokenOut     Token you are zapping out to (i.e. output token).
    /// @param liquidity    Amount of liquidity you wish to remove.
    /// @param zapOutPool   Contains zap struct information. See Zap struct.
    /// @param routesA      Route used to convert tokenA into output token.
    /// @param routesB      Route used to convert tokenB into output token.
    function zapOut(
        address tokenOut,
        uint256 liquidity,
        Zap calldata zapOutPool,
        Route[] calldata routesA,
        Route[] calldata routesB
    ) external;

    /// @notice Used to generate params required for zapping in.
    ///         Zap in => remove liquidity then swap.
    ///         Apply slippage to expected swap values to account for changes in reserves in between.
    /// @dev Output token refers to the token you want to zap in from.
    /// @param tokenA           .
    /// @param tokenB           .
    /// @param stable           .
    /// @param _factory         .
    /// @param amountInA        Amount of input token you wish to send down routesA
    /// @param amountInB        Amount of input token you wish to send down routesB
    /// @param routesA          Route used to convert input token to tokenA
    /// @param routesB          Route used to convert input token to tokenB
    /// @return amountOutMinA   Minimum output expected from swapping input token to tokenA.
    /// @return amountOutMinB   Minimum output expected from swapping input token to tokenB.
    /// @return amountAMin      Minimum amount of tokenA expected from depositing liquidity.
    /// @return amountBMin      Minimum amount of tokenB expected from depositing liquidity.
    function generateZapInParams(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory,
        uint256 amountInA,
        uint256 amountInB,
        Route[] calldata routesA,
        Route[] calldata routesB
    ) external view returns (uint256 amountOutMinA, uint256 amountOutMinB, uint256 amountAMin, uint256 amountBMin);

    /// @notice Used to generate params required for zapping out.
    ///         Zap out => swap then add liquidity.
    ///         Apply slippage to expected liquidity values to account for changes in reserves in between.
    /// @dev Output token refers to the token you want to zap out of.
    /// @param tokenA           .
    /// @param tokenB           .
    /// @param stable           .
    /// @param _factory         .
    /// @param liquidity        Amount of liquidity being zapped out of into a given output token.
    /// @param routesA          Route used to convert tokenA into output token.
    /// @param routesB          Route used to convert tokenB into output token.
    /// @return amountOutMinA   Minimum output expected from swapping tokenA into output token.
    /// @return amountOutMinB   Minimum output expected from swapping tokenB into output token.
    /// @return amountAMin      Minimum amount of tokenA expected from withdrawing liquidity.
    /// @return amountBMin      Minimum amount of tokenB expected from withdrawing liquidity.
    function generateZapOutParams(
        address tokenA,
        address tokenB,
        bool stable,
        address _factory,
        uint256 liquidity,
        Route[] calldata routesA,
        Route[] calldata routesB
    ) external view returns (uint256 amountOutMinA, uint256 amountOutMinB, uint256 amountAMin, uint256 amountBMin);

    /// @notice Used by zapper to determine appropriate ratio of A to B to deposit liquidity. Assumes stable pool.
    /// @dev Returns stable liquidity ratio of B to (A + B).
    ///      E.g. if ratio is 0.4, it means there is more of A than there is of B.
    ///      Therefore you should deposit more of token A than B.
    /// @param tokenA   tokenA of stable pool you are zapping into.
    /// @param tokenB   tokenB of stable pool you are zapping into.
    /// @param factory  Factory that created stable pool.
    /// @return ratio   Ratio of token0 to token1 required to deposit into zap.
    function quoteStableLiquidityRatio(
        address tokenA,
        address tokenB,
        address factory
    ) external view returns (uint256 ratio);
}

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

interface IVoter {
    error AlreadyVotedOrDeposited();
    error DistributeWindow();
    error FactoryPathNotApproved();
    error GaugeAlreadyKilled();
    error GaugeAlreadyRevived();
    error GaugeExists();
    error GaugeDoesNotExist(address _pool);
    error GaugeNotAlive(address _gauge);
    error InactiveManagedNFT();
    error MaximumVotingNumberTooLow();
    error NonZeroVotes();
    error NotAPool();
    error NotApprovedOrOwner();
    error NotGovernor();
    error NotEmergencyCouncil();
    error NotMinter();
    error NotWhitelistedNFT();
    error NotWhitelistedToken();
    error SameValue();
    error SpecialVotingWindow();
    error TooManyPools();
    error UnequalLengths();
    error ZeroBalance();
    error ZeroAddress();

    event GaugeCreated(
        address indexed poolFactory,
        address indexed votingRewardsFactory,
        address indexed gaugeFactory,
        address pool,
        address bribeVotingReward,
        address feeVotingReward,
        address gauge,
        address creator
    );
    event GaugeKilled(address indexed gauge);
    event GaugeRevived(address indexed gauge);
    event Voted(
        address indexed voter,
        address indexed pool,
        uint256 indexed tokenId,
        uint256 weight,
        uint256 totalWeight,
        uint256 timestamp
    );
    event Abstained(
        address indexed voter,
        address indexed pool,
        uint256 indexed tokenId,
        uint256 weight,
        uint256 totalWeight,
        uint256 timestamp
    );
    event NotifyReward(address indexed sender, address indexed reward, uint256 amount);
    event DistributeReward(address indexed sender, address indexed gauge, uint256 amount);
    event WhitelistToken(address indexed whitelister, address indexed token, bool indexed _bool);
    event WhitelistNFT(address indexed whitelister, uint256 indexed tokenId, bool indexed _bool);

    // mappings
    function gauges(address pool) external view returns (address);

    function poolForGauge(address gauge) external view returns (address);

    function gaugeToFees(address gauge) external view returns (address);

    function gaugeToBribe(address gauge) external view returns (address);

    function weights(address pool) external view returns (uint256);

    function votes(uint256 tokenId, address pool) external view returns (uint256);

    function usedWeights(uint256 tokenId) external view returns (uint256);

    function lastVoted(uint256 tokenId) external view returns (uint256);

    function isGauge(address) external view returns (bool);

    function isWhitelistedToken(address token) external view returns (bool);

    function isWhitelistedNFT(uint256 tokenId) external view returns (bool);

    function isAlive(address gauge) external view returns (bool);

    function ve() external view returns (address);

    function governor() external view returns (address);

    function epochGovernor() external view returns (address);

    function emergencyCouncil() external view returns (address);

    function length() external view returns (uint256);

    /// @notice Called by Minter to distribute weekly emissions rewards for disbursement amongst gauges.
    /// @dev Assumes totalWeight != 0 (Will never be zero as long as users are voting).
    ///      Throws if not called by minter.
    /// @param _amount Amount of rewards to distribute.
    function notifyRewardAmount(uint256 _amount) external;

    /// @dev Utility to distribute to gauges of pools in range _start to _finish.
    /// @param _start   Starting index of gauges to distribute to.
    /// @param _finish  Ending index of gauges to distribute to.
    function distribute(uint256 _start, uint256 _finish) external;

    /// @dev Utility to distribute to gauges of pools in array.
    /// @param _gauges Array of gauges to distribute to.
    function distribute(address[] memory _gauges) external;

    /// @notice Called by users to update voting balances in voting rewards contracts.
    /// @param _tokenId Id of veNFT whose balance you wish to update.
    function poke(uint256 _tokenId) external;

    /// @notice Called by users to vote for pools. Votes distributed proportionally based on weights.
    ///         Can only vote or deposit into a managed NFT once per epoch.
    ///         Can only vote for gauges that have not been killed.
    /// @dev Weights are distributed proportional to the sum of the weights in the array.
    ///      Throws if length of _poolVote and _weights do not match.
    /// @param _tokenId     Id of veNFT you are voting with.
    /// @param _poolVote    Array of pools you are voting for.
    /// @param _weights     Weights of pools.
    function vote(uint256 _tokenId, address[] calldata _poolVote, uint256[] calldata _weights) external;

    /// @notice Called by users to reset voting state. Required if you wish to make changes to
    ///         veNFT state (e.g. merge, split, deposit into managed etc).
    ///         Cannot reset in the same epoch that you voted in.
    ///         Can vote or deposit into a managed NFT again after reset.
    /// @param _tokenId Id of veNFT you are reseting.
    function reset(uint256 _tokenId) external;

    /// @notice Called by users to deposit into a managed NFT.
    ///         Can only vote or deposit into a managed NFT once per epoch.
    ///         Note that NFTs deposited into a managed NFT will be re-locked
    ///         to the maximum lock time on withdrawal.
    /// @dev Throws if not approved or owner.
    ///      Throws if managed NFT is inactive.
    ///      Throws if depositing within privileged window (one hour prior to epoch flip).
    function depositManaged(uint256 _tokenId, uint256 _mTokenId) external;

    /// @notice Called by users to withdraw from a managed NFT.
    ///         Cannot do it in the same epoch that you deposited into a managed NFT.
    ///         Can vote or deposit into a managed NFT again after withdrawing.
    ///         Note that the NFT withdrawn is re-locked to the maximum lock time.
    function withdrawManaged(uint256 _tokenId) external;

    /// @notice Claim emissions from gauges.
    /// @param _gauges Array of gauges to collect emissions from.
    function claimRewards(address[] memory _gauges) external;

    /// @notice Claim bribes for a given NFT.
    /// @dev Utility to help batch bribe claims.
    /// @param _bribes  Array of BribeVotingReward contracts to collect from.
    /// @param _tokens  Array of tokens that are used as bribes.
    /// @param _tokenId Id of veNFT that you wish to claim bribes for.
    function claimBribes(address[] memory _bribes, address[][] memory _tokens, uint256 _tokenId) external;

    /// @notice Claim fees for a given NFT.
    /// @dev Utility to help batch fee claims.
    /// @param _fees    Array of FeesVotingReward contracts to collect from.
    /// @param _tokens  Array of tokens that are used as fees.
    /// @param _tokenId Id of veNFT that you wish to claim fees for.
    function claimFees(address[] memory _fees, address[][] memory _tokens, uint256 _tokenId) external;

    /// @notice Set new governor.
    /// @dev Throws if not called by governor.
    /// @param _governor .
    function setGovernor(address _governor) external;

    /// @notice Set new epoch based governor.
    /// @dev Throws if not called by governor.
    /// @param _epochGovernor .
    function setEpochGovernor(address _epochGovernor) external;

    /// @notice Set new emergency council.
    /// @dev Throws if not called by emergency council.
    /// @param _emergencyCouncil .
    function setEmergencyCouncil(address _emergencyCouncil) external;

    /// @notice Whitelist (or unwhitelist) token for use in bribes.
    /// @dev Throws if not called by governor.
    /// @param _token .
    /// @param _bool .
    function whitelistToken(address _token, bool _bool) external;

    /// @notice Whitelist (or unwhitelist) token id for voting in last hour prior to epoch flip.
    /// @dev Throws if not called by governor.
    ///      Throws if already whitelisted.
    /// @param _tokenId .
    /// @param _bool .
    function whitelistNFT(uint256 _tokenId, bool _bool) external;

    /// @notice Create a new gauge (unpermissioned).
    /// @dev Governor can create a new gauge for a pool with any address.
    /// @dev V1 gauges can only be created by governor.
    /// @param _poolFactory .
    /// @param _pool .
    function createGauge(address _poolFactory, address _pool) external returns (address);

    /// @notice Kills a gauge. The gauge will not receive any new emissions and cannot be deposited into.
    ///         Can still withdraw from gauge.
    /// @dev Throws if not called by emergency council.
    ///      Throws if gauge already killed.
    /// @param _gauge .
    function killGauge(address _gauge) external;

    /// @notice Revives a killed gauge. Gauge will can receive emissions and deposits again.
    /// @dev Throws if not called by emergency council.
    ///      Throws if gauge is not killed.
    /// @param _gauge .
    function reviveGauge(address _gauge) external;

    /// @dev Update claims to emissions for an array of gauges.
    /// @param _gauges Array of gauges to update emissions for.
    function updateFor(address[] memory _gauges) external;

    /// @dev Update claims to emissions for gauges based on their pool id as stored in Voter.
    /// @param _start   Starting index of pools.
    /// @param _end     Ending index of pools.
    function updateFor(uint256 _start, uint256 _end) external;

    /// @dev Update claims to emissions for single gauge
    /// @param _gauge .
    function updateFor(address _gauge) external;
}

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

interface IGauge {
    error NotAlive();
    error NotAuthorized();
    error NotVoter();
    error RewardRateTooHigh();
    error ZeroAmount();
    error ZeroRewardRate();

    event Deposit(address indexed from, address indexed to, uint256 amount);
    event Withdraw(address indexed from, uint256 amount);
    event NotifyReward(address indexed from, uint256 amount);
    event ClaimFees(address indexed from, uint256 claimed0, uint256 claimed1);
    event ClaimRewards(address indexed from, uint256 amount);

    function rewardPerToken() external view returns (uint256 _rewardPerToken);

    /// @notice Returns the last time the reward was modified or periodFinish if the reward has ended
    function lastTimeRewardApplicable() external view returns (uint256 _time);

    /// @notice Returns accrued balance to date from last claim / first deposit.
    function earned(address _account) external view returns (uint256 _earned);

    function left() external view returns (uint256 _left);

    /// @notice Returns if gauge is linked to a legitimate Velodrome pool
    function isPool() external view returns (bool _isPool);

    function stakingToken() external view returns (address _pool);

    /// @notice Retrieve rewards for an address.
    /// @dev Throws if not called by same address or voter.
    /// @param _account .
    function getReward(address _account) external;

    /// @notice Deposit LP tokens into gauge for msg.sender
    /// @param _amount .
    function deposit(uint256 _amount) external;

    /// @notice Deposit LP tokens into gauge for any user
    /// @param _amount .
    /// @param _recipient Recipient to give balance to
    function deposit(uint256 _amount, address _recipient) external;

    /// @notice Withdraw LP tokens for user
    /// @param _amount .
    function withdraw(uint256 _amount) external;

    /// @dev Notifies gauge of gauge rewards. Assumes gauge reward tokens is 18 decimals.
    ///      If not 18 decimals, rewardRate may have rounding issues.
    function notifyRewardAmount(uint256 amount) external;
}

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

interface IFactoryRegistry {
    error FallbackFactory();
    error InvalidFactoriesToPoolFactory();
    error PathAlreadyApproved();
    error PathNotApproved();
    error SameAddress();
    error ZeroAddress();

    event Approve(address indexed poolFactory, address indexed votingRewardsFactory, address indexed gaugeFactory);
    event Unapprove(address indexed poolFactory, address indexed votingRewardsFactory, address indexed gaugeFactory);
    event SetManagedRewardsFactory(address indexed _newRewardsFactory);

    /// @notice Approve a set of factories used in Velodrome Protocol.
    ///         Router.sol is able to swap any poolFactories currently approved.
    ///         Cannot approve address(0) factories.
    ///         Cannot aprove path that is already approved.
    ///         Each poolFactory has one unique set and maintains state.  In the case a poolFactory is unapproved
    ///             and then re-approved, the same set of factories must be used.  In other words, you cannot overwrite
    ///             the factories tied to a poolFactory address.
    ///         VotingRewardsFactories and GaugeFactories may use the same address across multiple poolFactories.
    /// @dev Callable by onlyOwner
    /// @param poolFactory .
    /// @param votingRewardsFactory .
    /// @param gaugeFactory .
    function approve(address poolFactory, address votingRewardsFactory, address gaugeFactory) external;

    /// @notice Unapprove a set of factories used in Velodrome Protocol.
    ///         While a poolFactory is unapproved, Router.sol cannot swap with pools made from the corresponding factory
    ///         Can only unapprove an approved path.
    ///         Cannot unapprove the fallback path (core v2 factories).
    /// @dev Callable by onlyOwner
    /// @param poolFactory .
    function unapprove(address poolFactory) external;

    /// @notice Factory to create free and locked rewards for a managed veNFT
    function managedRewardsFactory() external view returns (address);

    /// @notice Set the rewards factory address
    /// @dev Callable by onlyOwner
    /// @param _newManagedRewardsFactory address of new managedRewardsFactory
    function setManagedRewardsFactory(address _newManagedRewardsFactory) external;

    /// @notice Get the factories correlated to a poolFactory.
    ///         Once set, this can never be modified.
    ///         Returns the correlated factories even after an approved poolFactory is unapproved.
    function factoriesToPoolFactory(
        address poolFactory
    ) external view returns (address votingRewardsFactory, address gaugeFactory);

    /// @notice Get all PoolFactories approved by the registry
    /// @dev The same PoolFactory address cannot be used twice
    /// @return Array of PoolFactory addresses
    function poolFactories() external view returns (address[] memory);

    /// @notice Check if a PoolFactory is approved within the factory registry.  Router uses this method to
    ///         ensure a pool swapped from is approved.
    /// @param poolFactory .
    /// @return True if PoolFactory is approved, else false
    function isPoolFactoryApproved(address poolFactory) external view returns (bool);

    /// @notice Get the length of the poolFactories array
    function poolFactoriesLength() external view returns (uint256);
}

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

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IWETH is IERC20 {
    function deposit() external payable;

    function withdraw(uint256) external;
}

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

pragma solidity ^0.8.0;

/**
 * @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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 13 of 18 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

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

File 14 of 18 : ERC2771Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)

pragma solidity ^0.8.9;

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

/**
 * @dev Context variant with ERC2771 support.
 */
abstract contract ERC2771Context is Context {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _trustedForwarder;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address trustedForwarder) {
        _trustedForwarder = trustedForwarder;
    }

    function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
        return forwarder == _trustedForwarder;
    }

    function _msgSender() internal view virtual override returns (address sender) {
        if (isTrustedForwarder(msg.sender)) {
            // The assembly code is more direct than the Solidity version using `abi.decode`.
            /// @solidity memory-safe-assembly
            assembly {
                sender := shr(96, calldataload(sub(calldatasize(), 20)))
            }
        } else {
            return super._msgSender();
        }
    }

    function _msgData() internal view virtual override returns (bytes calldata) {
        if (isTrustedForwarder(msg.sender)) {
            return msg.data[:msg.data.length - 20];
        } else {
            return super._msgData();
        }
    }
}

File 15 of 18 : Clones.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(0, 0x09, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(0, 0x09, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

File 16 of 18 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

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

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

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

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

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

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
        }
    }
}

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

pragma solidity ^0.8.0;

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

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

Settings
{
  "remappings": [
    "@opengsn/=lib/gsn/packages/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "@uniswap/v3-core/=lib/v3-core/",
    "concentrated-liquidity/=lib/concentrated-liquidity/",
    "ds-test/=lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "eth-gas-reporter/=node_modules/eth-gas-reporter/",
    "forge-std/=lib/forge-std/src/",
    "gsn/=lib/gsn/",
    "hardhat-deploy/=node_modules/hardhat-deploy/",
    "hardhat/=node_modules/hardhat/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "utils/=test/utils/",
    "v3-core/=lib/v3-core/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {
    "contracts/art/PerlinNoise.sol": {
      "PerlinNoise": "0x08947e304064b3f3ef2b99fca7e549c5fc3f75d4"
    },
    "contracts/art/Trig.sol": {
      "Trig": "0xbdd6f9662e904a9176aafcbdded45d076b5170ef"
    },
    "contracts/libraries/BalanceLogicLibrary.sol": {
      "BalanceLogicLibrary": "0x79bca9bcc19e157cb5f8c5a2f4d6cb951b1f8dce"
    },
    "contracts/libraries/DelegationLogicLibrary.sol": {
      "DelegationLogicLibrary": "0x73746410b0dd4526e1fa00d0854e99ba54aefd30"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_forwarder","type":"address"},{"internalType":"address","name":"_factoryRegistry","type":"address"},{"internalType":"address","name":"_v1Factory","type":"address"},{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_voter","type":"address"},{"internalType":"address","name":"_weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ConversionFromV2ToV1VeloProhibited","type":"error"},{"inputs":[],"name":"ETHTransferFailed","type":"error"},{"inputs":[],"name":"Expired","type":"error"},{"inputs":[],"name":"InsufficientAmount","type":"error"},{"inputs":[],"name":"InsufficientAmountA","type":"error"},{"inputs":[],"name":"InsufficientAmountADesired","type":"error"},{"inputs":[],"name":"InsufficientAmountAOptimal","type":"error"},{"inputs":[],"name":"InsufficientAmountB","type":"error"},{"inputs":[],"name":"InsufficientAmountBDesired","type":"error"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[],"name":"InsufficientOutputAmount","type":"error"},{"inputs":[],"name":"InvalidAmountInForETHDeposit","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidRouteA","type":"error"},{"inputs":[],"name":"InvalidRouteB","type":"error"},{"inputs":[],"name":"InvalidTokenInForETHDeposit","type":"error"},{"inputs":[],"name":"OnlyWETH","type":"error"},{"inputs":[],"name":"PoolDoesNotExist","type":"error"},{"inputs":[],"name":"PoolFactoryDoesNotExist","type":"error"},{"inputs":[],"name":"SameAddresses","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ETHER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routes","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"UNSAFE_swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"defaultFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factoryRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"_factory","type":"address"},{"internalType":"uint256","name":"amountInA","type":"uint256"},{"internalType":"uint256","name":"amountInB","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routesA","type":"tuple[]"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routesB","type":"tuple[]"}],"name":"generateZapInParams","outputs":[{"internalType":"uint256","name":"amountOutMinA","type":"uint256"},{"internalType":"uint256","name":"amountOutMinB","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"_factory","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routesA","type":"tuple[]"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routesB","type":"tuple[]"}],"name":"generateZapOutParams","outputs":[{"internalType":"uint256","name":"amountOutMinA","type":"uint256"},{"internalType":"uint256","name":"amountOutMinB","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routes","type":"tuple[]"}],"name":"getAmountsOut","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"_factory","type":"address"}],"name":"getReserves","outputs":[{"internalType":"uint256","name":"reserveA","type":"uint256"},{"internalType":"uint256","name":"reserveB","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"_factory","type":"address"}],"name":"pairFor","outputs":[{"internalType":"address","name":"pool","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"_factory","type":"address"}],"name":"poolFor","outputs":[{"internalType":"address","name":"pool","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"_factory","type":"address"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"}],"name":"quoteAddLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"_factory","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"quoteRemoveLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"address","name":"_factory","type":"address"}],"name":"quoteStableLiquidityRatio","outputs":[{"internalType":"uint256","name":"ratio","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETHSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"sortTokens","outputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routes","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routes","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routes","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routes","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETHSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routes","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routes","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"v1Factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountInA","type":"uint256"},{"internalType":"uint256","name":"amountInB","type":"uint256"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"},{"internalType":"uint256","name":"amountOutMinA","type":"uint256"},{"internalType":"uint256","name":"amountOutMinB","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"}],"internalType":"struct IRouter.Zap","name":"zapInPool","type":"tuple"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routesA","type":"tuple[]"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routesB","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stake","type":"bool"}],"name":"zapIn","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"components":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"},{"internalType":"uint256","name":"amountOutMinA","type":"uint256"},{"internalType":"uint256","name":"amountOutMinB","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"}],"internalType":"struct IRouter.Zap","name":"zapOutPool","type":"tuple"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routesA","type":"tuple[]"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"factory","type":"address"}],"internalType":"struct IRouter.Route[]","name":"routesB","type":"tuple[]"}],"name":"zapOut","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

6101406040523480156200001257600080fd5b50604051620061f5380380620061f5833981016040819052620000359162000082565b6001600160a01b0395861660805293851660a05291841660c052831660e052821661010052166101205262000103565b80516001600160a01b03811681146200007d57600080fd5b919050565b60008060008060008060c087890312156200009c57600080fd5b620000a78762000065565b9550620000b76020880162000065565b9450620000c76040880162000065565b9350620000d76060880162000065565b9250620000e76080880162000065565b9150620000f760a0880162000065565b90509295509295509295565b60805160a05160c05160e0516101005161012051615f9f62000256600039600081816101ec0152818161033c015281816109dd01528181610bb301528181610c5f01528181610ce301528181610d6201528181610dd601528181611eb001528181611fc501528181612053015281816121d6015281816124b9015281816124ef01528181612545015281816125da0152818161270c015281816128de01528181612e0a01528181612e5a015281816131b8015281816131db01528181613542015281816135fb0152818161445a01526144eb0152600081816103c5015261337f01526000818161066b0152818161088701528181611351015281816115750152818161177d0152818161251101528181613fc10152818161406201526140ec0152600081816105110152611a330152600081816102dd01526117d6015260008181610464015261369d0152615f9f6000f3fe6080604052600436106101dc5760003560e01c8063874029d911610102578063cac88ea911610095578063e4ea9d7411610064578063e4ea9d74146106ad578063f5ba53c7146106cd578063fb49bafd146106fb578063fe411f141461070e57600080fd5b8063cac88ea914610619578063ce700c2914610639578063d4b6846d14610659578063d7b0e0a51461068d57600080fd5b8063a81b9159116100d1578063a81b9159146105a6578063b7e0d4c0146105c6578063c6b7f1b6146105d9578063c92de3ec146105f957600080fd5b8063874029d91461053357806388cd821e146105535780638c0037dc14610573578063903638a41461059357600080fd5b806342cb1fbc1161017a578063572b6c0511610149578063572b6c05146104475780635a47ddc3146104a45780637539d413146104df5780638083f7bb146104ff57600080fd5b806342cb1fbc1461038b57806346c96aac146103b3578063544caa56146103e75780635509a1ac1461042757600080fd5b80633bf0c9fb116101b65780633bf0c9fb146102cb5780633da5acba146103175780633fc8cef31461032a5780634111d5971461035e57600080fd5b806307db50fa146102315780630dede6c41461027657806312bc3aca146102ab57600080fd5b3661022c57336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461022a576040516301f180c960e01b815260040160405180910390fd5b005b600080fd5b34801561023d57600080fd5b5061025161024c3660046151a5565b61072e565b6040805194855260208501939093529183015260608201526080015b60405180910390f35b34801561028257600080fd5b50610296610291366004615275565b610870565b6040805192835260208301919091520161026d565b3480156102b757600080fd5b5061022a6102c63660046152fa565b6109c9565b3480156102d757600080fd5b506102ff7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161026d565b61022a61032536600461536c565b610cd7565b34801561033657600080fd5b506102ff7f000000000000000000000000000000000000000000000000000000000000000081565b34801561036a57600080fd5b5061037e61037936600461543b565b611068565b60405161026d9190615506565b34801561039757600080fd5b506102ff73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b3480156103bf57600080fd5b506102ff7f000000000000000000000000000000000000000000000000000000000000000081565b3480156103f357600080fd5b5061040761040236600461554a565b6111c8565b604080516001600160a01b0393841681529290911660208301520161026d565b34801561043357600080fd5b5061037e61044236600461560b565b611253565b34801561045357600080fd5b506104946104623660046156b2565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b604051901515815260200161026d565b3480156104b057600080fd5b506104c46104bf3660046156cf565b611548565b6040805193845260208401929092529082015260600161026d565b3480156104eb57600080fd5b506102516104fa36600461575e565b611640565b34801561050b57600080fd5b506102ff7f000000000000000000000000000000000000000000000000000000000000000081565b34801561053f57600080fd5b506102ff61054e366004615823565b611779565b34801561055f57600080fd5b5061022a61056e3660046152fa565b611c67565b34801561057f57600080fd5b5061029661058e366004615823565b611de8565b61037e6105a136600461536c565b611ea2565b3480156105b257600080fd5b5061022a6105c1366004615898565b612183565b6104c46105d4366004615938565b6124a4565b3480156105e557600080fd5b5061037e6105f43660046152fa565b6126f6565b34801561060557600080fd5b506102966106143660046159aa565b6129bd565b34801561062557600080fd5b5061037e6106343660046152fa565b612b06565b34801561064557600080fd5b506104c4610654366004615a0e565b612c15565b34801561066557600080fd5b506102ff7f000000000000000000000000000000000000000000000000000000000000000081565b34801561069957600080fd5b506102966106a8366004615938565b612df7565b3480156106b957600080fd5b506102ff6106c8366004615823565b612ed5565b3480156106d957600080fd5b506106ed6106e8366004615a7c565b612eec565b60405190815260200161026d565b6106ed610709366004615ac7565b613160565b34801561071a57600080fd5b506106ed610729366004615938565b613530565b8585600080606087156107c3576107978b8a8a808060200260200160405190810160405280939291908181526020016000905b8282101561078d5761077e60808302860136819003810190615b98565b81526020019060010190610761565b5050505050611253565b905080600182516107a89190615bca565b815181106107b8576107b8615bdd565b602002602001015194505b85156108475761081b8a8888808060200260200160405190810160405280939291908181526020016000905b8282101561078d5761080c60808302860136819003810190615b98565b815260200190600101906107ef565b9050806001825161082c9190615bca565b8151811061083c5761083c615bdd565b602002602001015193505b6108558f8f8f8f8989612c15565b508093508194505050509a509a509a509a9650505050505050565b6000808261087d81613675565b60006108ab8c8c8c7f0000000000000000000000000000000000000000000000000000000000000000611779565b90506108ca6108b8613699565b6001600160a01b03831690838c6136dd565b60405163226bf2d160e21b81526001600160a01b03878116600483015260009182918416906389afcb449060240160408051808303816000875af1158015610916573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061093a9190615bf3565b91509150600061094a8f8f6111c8565b509050806001600160a01b03168f6001600160a01b03161461096d578183610970565b82825b90975095508a871015610996576040516323d9bb0560e21b815260040160405180910390fd5b898610156109b757604051630d32418960e21b815260040160405180910390fd5b50505050509850989650505050505050565b806109d381613675565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168585610a0a600182615bca565b818110610a1957610a19615bdd565b9050608002016020016020810190610a3191906156b2565b6001600160a01b031614610a58576040516320db826760e01b815260040160405180910390fd5b610b3f85856000818110610a6e57610a6e615bdd565b610a8492602060809092020190810191506156b2565b610a8c613699565b610b3988886000818110610aa257610aa2615bdd565b610ab892602060809092020190810191506156b2565b89896000818110610acb57610acb615bdd565b9050608002016020016020810190610ae391906156b2565b8a8a6000818110610af657610af6615bdd565b9050608002016040016020810190610b0e9190615c17565b8b8b6000818110610b2157610b21615bdd565b905060800201606001602081019061054e91906156b2565b8a61374e565b610b9b8585808060200260200160405190810160405280939291908181526020016000905b82821015610b9057610b8160808302860136819003810190615b98565b81526020019060010190610b64565b505050505030613846565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610c02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c269190615c34565b905086811015610c49576040516342301c2360e01b815260040160405180910390fd5b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015610cab57600080fd5b505af1158015610cbf573d6000803e3d6000fd5b50505050610ccd8482613c6f565b5050505050505050565b80610ce181613675565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031685856000818110610d1e57610d1e615bdd565b610d3492602060809092020190810191506156b2565b6001600160a01b031614610d5b576040516320db826760e01b815260040160405180910390fd5b60003490507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015610dbb57600080fd5b505af1158015610dcf573d6000803e3d6000fd5b50505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb610e1988886000818110610aa257610aa2615bdd565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018490526044016020604051808303816000875af1158015610e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8a9190615c4d565b610e9657610e96615c6a565b6000610ea3600187615bca565b90506000878783818110610eb957610eb9615bdd565b9050608002016020016020810190610ed191906156b2565b6040516370a0823160e01b81526001600160a01b03888116600483015291909116906370a0823190602401602060405180830381865afa158015610f19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3d9190615c34565b9050610f9b8888808060200260200160405190810160405280939291908181526020016000905b82821015610f9057610f8160808302860136819003810190615b98565b81526020019060010190610f64565b505050505087613846565b8881898985818110610faf57610faf615bdd565b9050608002016020016020810190610fc791906156b2565b6040516370a0823160e01b81526001600160a01b038a8116600483015291909116906370a08231906024015b602060405180830381865afa158015611010573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110349190615c34565b61103e9190615bca565b101561105d576040516342301c2360e01b815260040160405180910390fd5b505050505050505050565b60608161107481613675565b61115d8686600081811061108a5761108a615bdd565b6110a092602060809092020190810191506156b2565b6110a8613699565b61113d898960008181106110be576110be615bdd565b6110d492602060809092020190810191506156b2565b8a8a60008181106110e7576110e7615bdd565b90506080020160200160208101906110ff91906156b2565b8b8b600081811061111257611112615bdd565b905060800201604001602081019061112a9190615c17565b8c8c6000818110610b2157610b21615bdd565b8a60008151811061115057611150615bdd565b602002602001015161374e565b6111ba878787808060200260200160405190810160405280939291908181526020016000905b828210156111af576111a060808302860136819003810190615b98565b81526020019060010190611183565b505050505086613d02565b8691505b5095945050505050565b600080826001600160a01b0316846001600160a01b0316036111fd57604051633295f3fd60e21b815260040160405180910390fd5b826001600160a01b0316846001600160a01b03161061121d578284611220565b83835b90925090506001600160a01b03821661124c5760405163d92e233d60e01b815260040160405180910390fd5b9250929050565b6060600182511015611278576040516320db826760e01b815260040160405180910390fd5b8151611285906001615c80565b6001600160401b0381111561129c5761129c6153d2565b6040519080825280602002602001820160405280156112c5578160200160208202803683370190505b50905082816000815181106112dc576112dc615bdd565b6020908102919091010152815160005b81811015611540576000806001600160a01b031685838151811061131257611312615bdd565b6020026020010151606001516001600160a01b03161461134f5784828151811061133e5761133e615bdd565b602002602001015160600151611371565b7f00000000000000000000000000000000000000000000000000000000000000005b905060006113d886848151811061138a5761138a615bdd565b6020026020010151600001518785815181106113a8576113a8615bdd565b6020026020010151602001518886815181106113c6576113c6615bdd565b60200260200101516040015185611779565b60405163e5e31b1360e01b81526001600160a01b0380831660048301529192509083169063e5e31b1390602401602060405180830381865afa158015611422573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114469190615c4d565b1561152b57806001600160a01b031663f140a35a86858151811061146c5761146c615bdd565b602002602001015188868151811061148657611486615bdd565b6020026020010151600001516040518363ffffffff1660e01b81526004016114c19291909182526001600160a01b0316602082015260400190565b602060405180830381865afa1580156114de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115029190615c34565b8561150e856001615c80565b8151811061151e5761151e615bdd565b6020026020010181815250505b5050808061153890615c93565b9150506112ec565b505092915050565b60008060008361155781613675565b6115668d8d8d8d8d8d8d613f62565b909450925060006115998e8e8e7f0000000000000000000000000000000000000000000000000000000000000000611779565b90506115ae8e6115a7613699565b838861374e565b6115c18d6115ba613699565b838761374e565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af1158015611609573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162d9190615c34565b9250505099509950999650505050505050565b6000806000806116538d8d8d8d8d6129bd565b9094509250839150829050606087156116e4576116b8838a8a808060200260200160405190810160405280939291908181526020016000905b8282101561078d576116a960808302860136819003810190615b98565b8152602001906001019061168c565b905080600182516116c99190615bca565b815181106116d9576116d9615bdd565b602002602001015194505b85156117685761173c828888808060200260200160405190810160405280939291908181526020016000905b8282101561078d5761172d60808302860136819003810190615b98565b81526020019060010190611710565b9050806001825161174d9190615bca565b8151811061175d5761175d615bdd565b602002602001015193505b509950995099509995505050505050565b60007f0000000000000000000000000000000000000000000000000000000000000000816001600160a01b038416156117b257836117b4565b815b60405163d1ea0a1d60e01b81526001600160a01b0380831660048301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063d1ea0a1d90602401602060405180830381865afa15801561181f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118439190615c4d565b61186057604051634d39d5a360e11b815260040160405180910390fd5b6000826001600160a01b0316638c7c53ce6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118c49190615cac565b90506000836001600160a01b031663c6751c096040518163ffffffff1660e01b8152600401602060405180830381865afa158015611906573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061192a9190615cac565b9050806001600160a01b0316896001600160a01b031614801561195e5750816001600160a01b0316886001600160a01b0316145b1561197c57604051639cda859960e01b815260040160405180910390fd5b816001600160a01b0316896001600160a01b03161480156119ae5750806001600160a01b0316886001600160a01b0316145b15611a2057836001600160a01b0316638e39ee166040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a159190615cac565b945050505050611c5f565b600080611a2d8b8b6111c8565b915091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316856001600160a01b031614611b6357600082828b604051602001611a8193929190615cc9565b604051602081830303815290604052805190602001209050611b5b866001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ada573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611afe9190615cac565b604051603881018990526f5af43d82803e903d91602b57fd5bf3ff60248201526014810191909152733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018390526037600c8201206078820152605560439091012090565b975050611c58565b6000856001600160a01b0316639aab92486040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ba3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc79190615c34565b90508583838c604051602001611bdf93929190615cc9565b6040516020818303038152906040528051906020012082604051602001611c3b939291906001600160f81b0319815260609390931b6bffffffffffffffffffffffff191660018401526015830191909152603582015260550190565b6040516020818303038152906040528051906020012060001c9750505b5050505050505b949350505050565b80611c7181613675565b611c8785856000818110610a6e57610a6e615bdd565b6000611c94600186615bca565b90506000868683818110611caa57611caa615bdd565b9050608002016020016020810190611cc291906156b2565b6040516370a0823160e01b81526001600160a01b03878116600483015291909116906370a0823190602401602060405180830381865afa158015611d0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d2e9190615c34565b9050611d8c8787808060200260200160405190810160405280939291908181526020016000905b82821015611d8157611d7260808302860136819003810190615b98565b81526020019060010190611d55565b505050505086613846565b8781888885818110611da057611da0615bdd565b9050608002016020016020810190611db891906156b2565b6040516370a0823160e01b81526001600160a01b03898116600483015291909116906370a0823190602401610ff3565b6000806000611df787876111c8565b509050600080611e0989898989611779565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015611e46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6a9190615cfc565b5091509150826001600160a01b0316896001600160a01b031614611e8f578082611e92565b81815b909a909950975050505050505050565b606081611eae81613675565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031686866000818110611eeb57611eeb615bdd565b611f0192602060809092020190810191506156b2565b6001600160a01b031614611f28576040516320db826760e01b815260040160405180910390fd5b611f7a348787808060200260200160405190810160405280939291908181526020016000905b8282101561078d57611f6b60808302860136819003810190615b98565b81526020019060010190611f4e565b9150868260018451611f8c9190615bca565b81518110611f9c57611f9c615bdd565b60200260200101511015611fc3576040516342301c2360e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db08360008151811061200557612005615bdd565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561203857600080fd5b505af115801561204c573d6000803e3d6000fd5b50505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb61209688886000818110610aa257610aa2615bdd565b846000815181106120a9576120a9615bdd565b60200260200101516040518363ffffffff1660e01b81526004016120e29291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af1158015612101573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121259190615c4d565b61213157612131615c6a565b6111be828787808060200260200160405190810160405280939291908181526020016000905b828210156111af5761217460808302860136819003810190615b98565b81526020019060010190612157565b600061219260208701876156b2565b905060006121a660408801602089016156b2565b905060006001600160a01b038a1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146121d457896121f6565b7f00000000000000000000000000000000000000000000000000000000000000005b905061220289896141cb565b6000816001600160a01b0316846001600160a01b03161461234e576040516370a0823160e01b81523060048201526001600160a01b038516906370a0823190602401602060405180830381865afa158015612261573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122859190615c34565b90506001600160a01b038216888861229e600182615bca565b8181106122ad576122ad615bdd565b90506080020160200160208101906122c591906156b2565b6001600160a01b0316146122ec576040516309d41c6760e31b815260040160405180910390fd5b61234e84828b608001358b8b808060200260200160405190810160405280939291908181526020016000905b828210156123445761233560808302860136819003810190615b98565b81526020019060010190612318565b5050505050614330565b816001600160a01b0316836001600160a01b03161461248e576040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156123ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123cf9190615c34565b90506001600160a01b03821686866123e8600182615bca565b8181106123f7576123f7615bdd565b905060800201602001602081019061240f91906156b2565b6001600160a01b031614612436576040516332b2410360e21b815260040160405180910390fd5b61248e83828b60a001358989808060200260200160405190810160405280939291908181526020016000905b828210156123445761247f60808302860136819003810190615b98565b81526020019060010190612462565b6124978b614412565b5050505050505050505050565b6000806000836124b381613675565b6124e28b7f00000000000000000000000000000000000000000000000000000000000000008c8c348d8d613f62565b909450925060006125358c7f00000000000000000000000000000000000000000000000000000000000000008d7f0000000000000000000000000000000000000000000000000000000000000000611779565b90506125438c6115a7613699565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b15801561259e57600080fd5b505af11580156125b2573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b038581166004830152602482018990527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb925060440190506020604051808303816000875af1158015612627573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061264b9190615c4d565b61265757612657615c6a565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af115801561269f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126c39190615c34565b9250833411156126e7576126e76126d8613699565b6126e28634615bca565b613c6f565b50509750975097945050505050565b60608161270281613675565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168686612739600182615bca565b81811061274857612748615bdd565b905060800201602001602081019061276091906156b2565b6001600160a01b031614612787576040516320db826760e01b815260040160405180910390fd5b6127d9888787808060200260200160405190810160405280939291908181526020016000905b8282101561078d576127ca60808302860136819003810190615b98565b815260200190600101906127ad565b91508682600184516127eb9190615bca565b815181106127fb576127fb615bdd565b60200260200101511015612822576040516342301c2360e01b815260040160405180910390fd5b61287f8686600081811061283857612838615bdd565b61284e92602060809092020190810191506156b2565b612856613699565b61286c898960008181106110be576110be615bdd565b8560008151811061115057611150615bdd565b6128dc828787808060200260200160405190810160405280939291908181526020016000905b828210156128d1576128c260808302860136819003810190615b98565b815260200190600101906128a5565b505050505030613d02565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632e1a7d4d836001855161291a9190615bca565b8151811061292a5761292a615bdd565b60200260200101516040518263ffffffff1660e01b815260040161295091815260200190565b600060405180830381600087803b15801561296a57600080fd5b505af115801561297e573d6000803e3d6000fd5b505050506129b28483600185516129959190615bca565b815181106129a5576129a5615bdd565b6020026020010151613c6f565b509695505050505050565b6000806000846001600160a01b0316636801cc308989896040518463ffffffff1660e01b81526004016129f293929190615d2a565b602060405180830381865afa158015612a0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a339190615cac565b90506001600160a01b038116612a50576000809250925050612afc565b600080612a5f8a8a8a8a611de8565b915091506000836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612aa3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ac79190615c34565b905080612ad48489615d4e565b612ade9190615d7b565b955080612aeb8389615d4e565b612af59190615d7b565b9450505050505b9550959350505050565b606081612b1281613675565b612b64888787808060200260200160405190810160405280939291908181526020016000905b8282101561078d57612b5560808302860136819003810190615b98565b81526020019060010190612b38565b9150868260018451612b769190615bca565b81518110612b8657612b86615bdd565b60200260200101511015612bad576040516342301c2360e01b815260040160405180910390fd5b612bc38686600081811061283857612838615bdd565b6129b2828787808060200260200160405190810160405280939291908181526020016000905b828210156111af57612c0660808302860136819003810190615b98565b81526020019060010190612be9565b600080600080866001600160a01b0316636801cc308b8b8b6040518463ffffffff1660e01b8152600401612c4b93929190615d2a565b602060405180830381865afa158015612c68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c8c9190615cac565b9050600080806001600160a01b03841615612d1757836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d039190615c34565b9050612d118d8d8d8d611de8565b90935091505b82158015612d23575081155b15612d54578896508795506103e8612d43612d3e888a615d4e565b6145dd565b612d4d9190615bca565b9450612de7565b6000612d618a85856146cc565b9050888111612da957899750955085612da284612d7e848b615d4e565b612d889190615d7b565b84612d93858b615d4e565b612d9d9190615d7b565b61472d565b9550612de5565b6000612db68a85876146cc565b9850899750889050612de185612dcc8584615d4e565b612dd69190615d7b565b85612d93868c615d4e565b9650505b505b5050505096509650969350505050565b60008082612e0481613675565b612e348a7f00000000000000000000000000000000000000000000000000000000000000008b8b8b8b308b610870565b9093509150612e448a8685614747565b604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015612ea657600080fd5b505af1158015612eba573d6000803e3d6000fd5b50505050612ec88583613c6f565b5097509795505050505050565b6000612ee385858585611779565b95945050505050565b600080612efc8585600186611779565b90506000856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f629190615d9d565b612f6d90600a615ea4565b90506000856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612faf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fd39190615d9d565b612fde90600a615ea4565b6040516378a051ad60e11b8152600481018490526001600160a01b03898116602483015291925083916000919086169063f140a35a90604401602060405180830381865afa158015613034573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130589190615c34565b905060008061306c8b8b60018c8888612c15565b5090925090508561308583670de0b6b3a7640000615d4e565b61308f9190615d7b565b9150846130a482670de0b6b3a7640000615d4e565b6130ae9190615d7b565b9050846130c384670de0b6b3a7640000615d4e565b6130cd9190615d7b565b9250856130e285670de0b6b3a7640000615d4e565b6130ec9190615d7b565b935080828561310386670de0b6b3a7640000615d4e565b61310d9190615d7b565b6131179190615d4e565b6131219190615d7b565b975061313588670de0b6b3a7640000615c80565b61314785670de0b6b3a7640000615d4e565b6131519190615d7b565b9b9a5050505050505050505050565b60008061316d8a8c615c80565b90508b3473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03831601613252578083146131b657604051633851fdc960e11b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561323457600080fd5b505af1158015613248573d6000803e3d6000fd5b5050505050613284565b80156132715760405163ae6d566f60e01b815260040160405180910390fd5b6132848261327d613699565b308661374e565b613294828e8e8e8e8e8e8e61482f565b61329d8b614ae4565b60006132e86132af60208e018e6156b2565b8d60200160208101906132c291906156b2565b8e60400160208101906132d59190615c17565b8f606001602081019061054e91906156b2565b9050851561347e576040516335313c2160e11b81523060048201526001600160a01b03821690636a627842906024016020604051808303816000875af1158015613336573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061335a9190615c34565b60405163b9a09fd560e01b81526001600160a01b0383811660048301529196506000917f0000000000000000000000000000000000000000000000000000000000000000169063b9a09fd590602401602060405180830381865afa1580156133c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ea9190615cac565b90506134006001600160a01b0383168288614c4c565b604051636e553f6560e01b8152600481018790526001600160a01b038981166024830152821690636e553f6590604401600060405180830381600087803b15801561344a57600080fd5b505af115801561345e573d6000803e3d6000fd5b50613478925050506001600160a01b038316826000614c4c565b506134ed565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af11580156134c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134ea9190615c34565b94505b6134f68f614412565b61350b61350660208e018e6156b2565b614412565b61351e61350660408e0160208f016156b2565b505050509a9950505050505050505050565b60008161353c81613675565b61356c897f00000000000000000000000000000000000000000000000000000000000000008a8a8a8a308a610870565b6040516370a0823160e01b81523060048201529093506135e591508a9086906001600160a01b038316906370a0823190602401602060405180830381865afa1580156135bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135e09190615c34565b614747565b604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561364757600080fd5b505af115801561365b573d6000803e3d6000fd5b505050506136698483613c6f565b50979650505050505050565b4281101561369657604051630407b05b60e31b815260040160405180910390fd5b50565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036136d8575060131936013560601c90565b503390565b6040516001600160a01b03808516602483015283166044820152606481018290526137489085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614d66565b50505050565b6000846001600160a01b03163b1161376557600080fd5b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17905291516000928392908816916137c99190615ed7565b6000604051808303816000865af19150503d8060008114613806576040519150601f19603f3d011682016040523d82523d6000602084013e61380b565b606091505b50915091508180156138355750805115806138355750808060200190518101906138359190615c4d565b61383e57600080fd5b505050505050565b815160005b8181101561374857600061389985838151811061386a5761386a615bdd565b60200260200101516000015186848151811061388857613888615bdd565b6020026020010151602001516111c8565b509050600061391e8684815181106138b3576138b3615bdd565b6020026020010151600001518785815181106138d1576138d1615bdd565b6020026020010151602001518886815181106138ef576138ef615bdd565b60200260200101516040015189878151811061390d5761390d615bdd565b602002602001015160600151611779565b905060008060006139a589878151811061393a5761393a615bdd565b6020026020010151600001518a888151811061395857613958615bdd565b6020026020010151602001518b898151811061397657613976615bdd565b6020026020010151604001518c8a8151811061399457613994615bdd565b602002602001015160600151611de8565b509050808987815181106139bb576139bb615bdd565b6020908102919091010151516040516370a0823160e01b81526001600160a01b038781166004830152909116906370a0823190602401602060405180830381865afa158015613a0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a329190615c34565b613a3c9190615bca565b925050826001600160a01b031663f140a35a838a8881518110613a6157613a61615bdd565b6020026020010151600001516040518363ffffffff1660e01b8152600401613a9c9291909182526001600160a01b0316602082015260400190565b602060405180830381865afa158015613ab9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613add9190615c34565b9050600080856001600160a01b03168a8881518110613afe57613afe615bdd565b6020026020010151600001516001600160a01b031614613b2057826000613b24565b6000835b91509150600060018b51613b389190615bca565b8810613b445789613be1565b613be18b613b538a6001615c80565b81518110613b6357613b63615bdd565b6020026020010151600001518c8a6001613b7d9190615c80565b81518110613b8d57613b8d615bdd565b6020026020010151602001518d8b6001613ba79190615c80565b81518110613bb757613bb7615bdd565b6020026020010151604001518e8c6001613bd19190615c80565b8151811061390d5761390d615bdd565b6040805160008152602081019182905263022c0d9f60e01b9091529091506001600160a01b0387169063022c0d9f90613c239086908690869060248101615f1f565b600060405180830381600087803b158015613c3d57600080fd5b505af1158015613c51573d6000803e3d6000fd5b50505050505050505050508080613c6790615c93565b91505061384b565b604080516000808252602082019092526001600160a01b038416908390604051613c999190615ed7565b60006040518083038185875af1925050503d8060008114613cd6576040519150601f19603f3d011682016040523d82523d6000602084013e613cdb565b606091505b5050905080613cfd5760405163b12d13eb60e01b815260040160405180910390fd5b505050565b815160005b81811015613f5b576000613d2685838151811061386a5761386a615bdd565b509050600086613d37846001615c80565b81518110613d4757613d47615bdd565b60200260200101519050600080836001600160a01b0316888681518110613d7057613d70615bdd565b6020026020010151600001516001600160a01b031614613d9257826000613d96565b6000835b91509150600060018951613daa9190615bca565b8610613db65787613e43565b613e4389613dc5886001615c80565b81518110613dd557613dd5615bdd565b6020026020010151600001518a886001613def9190615c80565b81518110613dff57613dff615bdd565b6020026020010151602001518b896001613e199190615c80565b81518110613e2957613e29615bdd565b6020026020010151604001518c8a6001613bd19190615c80565b9050613eb4898781518110613e5a57613e5a615bdd565b6020026020010151600001518a8881518110613e7857613e78615bdd565b6020026020010151602001518b8981518110613e9657613e96615bdd565b6020026020010151604001518c8a8151811061390d5761390d615bdd565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f191660200182016040528015613ef1576020820181803683370190505b506040518563ffffffff1660e01b8152600401613f119493929190615f1f565b600060405180830381600087803b158015613f2b57600080fd5b505af1158015613f3f573d6000803e3d6000fd5b5050505050505050508080613f5390615c93565b915050613d07565b5050505050565b60008083861015613f8657604051636e35977960e11b815260040160405180910390fd5b82851015613fa75760405163acee051360e01b815260040160405180910390fd5b6040516306801cc360e41b81526000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636801cc3090613ffa908d908d908d90600401615d2a565b602060405180830381865afa158015614017573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061403b9190615cac565b90506001600160a01b0381166140e1576040516320b7f73960e21b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906382dfdce49061409b908d908d908d90600401615d2a565b6020604051808303816000875af11580156140ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140de9190615cac565b90505b6000806141108c8c8c7f0000000000000000000000000000000000000000000000000000000000000000611de8565b91509150816000148015614122575080155b15614132578894508793506141bc565b600061413f8a84846146cc565b9050888111614174578681101561416957604051630d32418960e21b815260040160405180910390fd5b8995509350836141ba565b60006141818a84866146cc565b90508a81111561419357614193615c6a565b888110156141b4576040516323d9bb0560e21b815260040160405180910390fd5b95508894505b505b50505097509795505050505050565b60006141da60208301836156b2565b905060006141ee60408401602085016156b2565b9050600061421783836142076060880160408901615c17565b61054e6080890160608a016156b2565b905061422e6001600160a01b0382163383886136dd565b600061423a84846111c8565b5060405163226bf2d160e21b815230600482015290915060009081906001600160a01b038516906389afcb449060240160408051808303816000875af1158015614288573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142ac9190615bf3565b91509150600080846001600160a01b0316886001600160a01b0316146142d35782846142d6565b83835b915091508860c001358210156142ff576040516323d9bb0560e21b815260040160405180910390fd5b8860e0013581101561432457604051630d32418960e21b815260040160405180910390fd5b50505050505050505050565b600061433c8483611253565b905082816001835161434e9190615bca565b8151811061435e5761435e615bdd565b60200260200101511015614385576040516342301c2360e01b815260040160405180910390fd5b60006143fa8360008151811061439d5761439d615bdd565b602002602001015160000151846000815181106143bc576143bc615bdd565b602002602001015160200151856000815181106143db576143db615bdd565b6020026020010151604001518660008151811061390d5761390d615bdd565b9050614407868287614747565b61383e828430613d02565b600061441c613699565b9050600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601614559576040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156144a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144cd9190615c34565b90508015613cfd57604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561453757600080fd5b505af115801561454b573d6000803e3d6000fd5b50505050613cfd8282613c6f565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa15801561459d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145c19190615c34565b90508015613cfd57613cfd6001600160a01b0384168383614e38565b6000816000036145ef57506000919050565b600060016145fc84614e68565b901c6001901b9050600181848161461557614615615d65565b048201901c9050600181848161462d5761462d615d65565b048201901c9050600181848161464557614645615d65565b048201901c9050600181848161465d5761465d615d65565b048201901c9050600181848161467557614675615d65565b048201901c9050600181848161468d5761468d615d65565b048201901c905060018184816146a5576146a5615d65565b048201901c90506146c5818285816146bf576146bf615d65565b0461472d565b9392505050565b6000836000036146ef57604051632ca2f52b60e11b815260040160405180910390fd5b8215806146fa575081155b156147185760405163bb55fd2760e01b815260040160405180910390fd5b826147238386615d4e565b611c5f9190615d7b565b600081831061473c578161473e565b825b90505b92915050565b6000836001600160a01b03163b1161475e57600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916147ba9190615ed7565b6000604051808303816000865af19150503d80600081146147f7576040519150601f19603f3d011682016040523d82523d6000602084013e6147fc565b606091505b50915091508180156148265750805115806148265750808060200190518101906148269190615c4d565b613f5b57600080fd5b600061483e60208701876156b2565b9050600061485260408801602089016156b2565b905060006148666060890160408a01615c17565b9050600061487a60808a0160608b016156b2565b9050600061488a85858585611779565b9050600080826001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa1580156148cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148f19190615cfc565b50915091506103e88211158061490957506103e88111155b15614927576040516302721e1f60e61b815260040160405180910390fd5b5050846001600160a01b03168d6001600160a01b0316146149ff576001600160a01b0385168989614959600182615bca565b81811061496857614968615bdd565b905060800201602001602081019061498091906156b2565b6001600160a01b0316146149a7576040516309d41c6760e31b815260040160405180910390fd5b6149ff8d8d8c608001358c8c808060200260200160405190810160405280939291908181526020016000905b82821015612344576149f060808302860136819003810190615b98565b815260200190600101906149d3565b836001600160a01b03168d6001600160a01b031614614ad5576001600160a01b0384168787614a2f600182615bca565b818110614a3e57614a3e615bdd565b9050608002016020016020810190614a5691906156b2565b6001600160a01b031614614a7d576040516332b2410360e21b815260040160405180910390fd5b614ad58d8c8c60a001358a8a808060200260200160405190810160405280939291908181526020016000905b8282101561234457614ac660808302860136819003810190615b98565b81526020019060010190614aa9565b50505050505050505050505050565b6000614af360208301836156b2565b90506000614b0760408401602085016156b2565b90506000614b1b6060850160408601615c17565b90506000614b2f60808601606087016156b2565b90506000614b3f85858585611779565b6040516370a0823160e01b81523060048201529091506000908190614c329088908890889088906001600160a01b038516906370a0823190602401602060405180830381865afa158015614b97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bbb9190615c34565b6040516370a0823160e01b81523060048201526001600160a01b038d16906370a0823190602401602060405180830381865afa158015614bff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c239190615c34565b8e60c001358f60e00135614efc565b91509150614c41878484614747565b610ccd868483614747565b801580614cc65750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015614ca0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cc49190615c34565b155b614d365760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084015b60405180910390fd5b6040516001600160a01b038316602482015260448101829052613cfd90849063095ea7b360e01b90606401613711565b6000614dbb826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661500b9092919063ffffffff16565b805190915015613cfd5780806020019051810190614dd99190615c4d565b613cfd5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401614d2d565b6040516001600160a01b038316602482015260448101829052613cfd90849063a9059cbb60e01b90606401613711565b600080608083901c15614e7d57608092831c92015b604083901c15614e8f57604092831c92015b602083901c15614ea157602092831c92015b601083901c15614eb357601092831c92015b600883901c15614ec557600892831c92015b600483901c15614ed757600492831c92015b600283901c15614ee957600292831c92015b600183901c156147415760010192915050565b60008083861015614f2057604051636e35977960e11b815260040160405180910390fd5b82851015614f415760405163acee051360e01b815260040160405180910390fd5b600080614f508c8c8c8c611de8565b91509150816000148015614f62575080155b15614f7257879350869250614ffc565b6000614f7f8984846146cc565b9050878111614fb45785811015614fa957604051630d32418960e21b815260040160405180910390fd5b889450925082614ffa565b6000614fc18984866146cc565b905089811115614fd357614fd3615c6a565b87811015614ff4576040516323d9bb0560e21b815260040160405180910390fd5b94508793505b505b50509850989650505050505050565b6060611c5f848460008585600080866001600160a01b031685876040516150329190615ed7565b60006040518083038185875af1925050503d806000811461506f576040519150601f19603f3d011682016040523d82523d6000602084013e615074565b606091505b509150915061508587838387615090565b979650505050505050565b606083156150ff5782516000036150f8576001600160a01b0385163b6150f85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401614d2d565b5081611c5f565b611c5f83838151156151145781518083602001fd5b8060405162461bcd60e51b8152600401614d2d9190615f56565b6001600160a01b038116811461369657600080fd5b803561514e8161512e565b919050565b801515811461369657600080fd5b60008083601f84011261517357600080fd5b5081356001600160401b0381111561518a57600080fd5b6020830191508360208260071b850101111561124c57600080fd5b6000806000806000806000806000806101008b8d0312156151c557600080fd5b8a356151d08161512e565b995060208b01356151e08161512e565b985060408b01356151f081615153565b975060608b01356152008161512e565b965060808b0135955060a08b0135945060c08b01356001600160401b038082111561522a57600080fd5b6152368e838f01615161565b909650945060e08d013591508082111561524f57600080fd5b5061525c8d828e01615161565b915080935050809150509295989b9194979a5092959850565b600080600080600080600080610100898b03121561529257600080fd5b883561529d8161512e565b975060208901356152ad8161512e565b965060408901356152bd81615153565b9550606089013594506080890135935060a0890135925060c08901356152e28161512e565b8092505060e089013590509295985092959890939650565b60008060008060008060a0878903121561531357600080fd5b863595506020870135945060408701356001600160401b0381111561533757600080fd5b61534389828a01615161565b90955093505060608701356153578161512e565b80925050608087013590509295509295509295565b60008060008060006080868803121561538457600080fd5b8535945060208601356001600160401b038111156153a157600080fd5b6153ad88828901615161565b90955093505060408601356153c18161512e565b949793965091946060013592915050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715615410576154106153d2565b604052919050565b60006001600160401b03821115615431576154316153d2565b5060051b60200190565b60008060008060006080868803121561545357600080fd5b85356001600160401b038082111561546a57600080fd5b818801915088601f83011261547e57600080fd5b8135602061549361548e83615418565b6153e8565b82815260059290921b8401810191818101908c8411156154b257600080fd5b948201945b838610156154d0578535825294820194908201906154b7565b995050890135925050808211156154e657600080fd5b506154f388828901615161565b90955093506153c1905060408701615143565b6020808252825182820181905260009190848201906040850190845b8181101561553e57835183529284019291840191600101615522565b50909695505050505050565b6000806040838503121561555d57600080fd5b82356155688161512e565b915060208301356155788161512e565b809150509250929050565b60006080828403121561559557600080fd5b604051608081018181106001600160401b03821117156155b7576155b76153d2565b60405290508082356155c88161512e565b815260208301356155d88161512e565b602082015260408301356155eb81615153565b604082015260608301356155fe8161512e565b6060919091015292915050565b6000806040838503121561561e57600080fd5b823591506020808401356001600160401b0381111561563c57600080fd5b8401601f8101861361564d57600080fd5b803561565b61548e82615418565b81815260079190911b8201830190838101908883111561567a57600080fd5b928401925b828410156156a3576156918985615583565b8252848201915060808401935061567f565b80955050505050509250929050565b6000602082840312156156c457600080fd5b81356146c58161512e565b60008060008060008060008060006101208a8c0312156156ee57600080fd5b89356156f98161512e565b985060208a01356157098161512e565b975060408a013561571981615153565b965060608a0135955060808a0135945060a08a0135935060c08a0135925060e08a01356157458161512e565b809250506101008a013590509295985092959850929598565b600080600080600080600080600060e08a8c03121561577c57600080fd5b89356157878161512e565b985060208a01356157978161512e565b975060408a01356157a781615153565b965060608a01356157b78161512e565b955060808a0135945060a08a01356001600160401b03808211156157da57600080fd5b6157e68d838e01615161565b909650945060c08c01359150808211156157ff57600080fd5b5061580c8c828d01615161565b915080935050809150509295985092959850929598565b6000806000806080858703121561583957600080fd5b84356158448161512e565b935060208501356158548161512e565b9250604085013561586481615153565b915060608501356158748161512e565b939692955090935050565b6000610100828403121561589257600080fd5b50919050565b6000806000806000806000610180888a0312156158b457600080fd5b87356158bf8161512e565b9650602088013595506158d58960408a0161587f565b94506101408801356001600160401b03808211156158f257600080fd5b6158fe8b838c01615161565b90965094506101608a013591508082111561591857600080fd5b506159258a828b01615161565b989b979a50959850939692959293505050565b600080600080600080600060e0888a03121561595357600080fd5b873561595e8161512e565b9650602088013561596e81615153565b955060408801359450606088013593506080880135925060a08801356159938161512e565b8092505060c0880135905092959891949750929550565b600080600080600060a086880312156159c257600080fd5b85356159cd8161512e565b945060208601356159dd8161512e565b935060408601356159ed81615153565b925060608601356159fd8161512e565b949793965091946080013592915050565b60008060008060008060c08789031215615a2757600080fd5b8635615a328161512e565b95506020870135615a428161512e565b94506040870135615a5281615153565b93506060870135615a628161512e565b9598949750929560808101359460a0909101359350915050565b600080600060608486031215615a9157600080fd5b8335615a9c8161512e565b92506020840135615aac8161512e565b91506040840135615abc8161512e565b809150509250925092565b6000806000806000806000806000806101e08b8d031215615ae757600080fd5b8a35615af28161512e565b995060208b0135985060408b01359750615b0f8c60608d0161587f565b96506101608b01356001600160401b0380821115615b2c57600080fd5b615b388e838f01615161565b90985096506101808d0135915080821115615b5257600080fd5b50615b5f8d828e01615161565b9095509350506101a08b0135615b748161512e565b91506101c08b0135615b8581615153565b809150509295989b9194979a5092959850565b600060808284031215615baa57600080fd5b61473e8383615583565b634e487b7160e01b600052601160045260246000fd5b8181038181111561474157614741615bb4565b634e487b7160e01b600052603260045260246000fd5b60008060408385031215615c0657600080fd5b505080516020909101519092909150565b600060208284031215615c2957600080fd5b81356146c581615153565b600060208284031215615c4657600080fd5b5051919050565b600060208284031215615c5f57600080fd5b81516146c581615153565b634e487b7160e01b600052600160045260246000fd5b8082018082111561474157614741615bb4565b600060018201615ca557615ca5615bb4565b5060010190565b600060208284031215615cbe57600080fd5b81516146c58161512e565b6bffffffffffffffffffffffff19606094851b811682529290931b9091166014830152151560f81b602882015260290190565b600080600060608486031215615d1157600080fd5b8351925060208401519150604084015190509250925092565b6001600160a01b039384168152919092166020820152901515604082015260600190565b808202811582820484141761474157614741615bb4565b634e487b7160e01b600052601260045260246000fd5b600082615d9857634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615daf57600080fd5b815160ff811681146146c557600080fd5b600181815b80851115615dfb578160001904821115615de157615de1615bb4565b80851615615dee57918102915b93841c9390800290615dc5565b509250929050565b600082615e1257506001614741565b81615e1f57506000614741565b8160018114615e355760028114615e3f57615e5b565b6001915050614741565b60ff841115615e5057615e50615bb4565b50506001821b614741565b5060208310610133831016604e8410600b8410161715615e7e575081810a614741565b615e888383615dc0565b8060001904821115615e9c57615e9c615bb4565b029392505050565b600061473e60ff841683615e03565b60005b83811015615ece578181015183820152602001615eb6565b50506000910152565b60008251615ee9818460208701615eb3565b9190910192915050565b60008151808452615f0b816020860160208601615eb3565b601f01601f19169290920160200192915050565b84815283602082015260018060a01b0383166040820152608060608201526000615f4c6080830184615ef3565b9695505050505050565b60208152600061473e6020830184615ef356fea2646970667358221220b1099ee7ddb5dc2ea1ec270042b192b9761fe430654308578bf270f4f8bb35cd64736f6c6343000813003300000000000000000000000006824df38d1d77eadeb6bafcb03904e27429ab74000000000000000000000000f4c67cdeaab8360370f41514d06e32ccd8aa1d7b00000000000000000000000025cbddb98b35ab1ff77413456b31ec81a6b6b746000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a00000000000000000000000041c914ee0c7e1a5edcd0295623e6dc557b5abf3c0000000000000000000000004200000000000000000000000000000000000006

Deployed Bytecode

0x6080604052600436106101dc5760003560e01c8063874029d911610102578063cac88ea911610095578063e4ea9d7411610064578063e4ea9d74146106ad578063f5ba53c7146106cd578063fb49bafd146106fb578063fe411f141461070e57600080fd5b8063cac88ea914610619578063ce700c2914610639578063d4b6846d14610659578063d7b0e0a51461068d57600080fd5b8063a81b9159116100d1578063a81b9159146105a6578063b7e0d4c0146105c6578063c6b7f1b6146105d9578063c92de3ec146105f957600080fd5b8063874029d91461053357806388cd821e146105535780638c0037dc14610573578063903638a41461059357600080fd5b806342cb1fbc1161017a578063572b6c0511610149578063572b6c05146104475780635a47ddc3146104a45780637539d413146104df5780638083f7bb146104ff57600080fd5b806342cb1fbc1461038b57806346c96aac146103b3578063544caa56146103e75780635509a1ac1461042757600080fd5b80633bf0c9fb116101b65780633bf0c9fb146102cb5780633da5acba146103175780633fc8cef31461032a5780634111d5971461035e57600080fd5b806307db50fa146102315780630dede6c41461027657806312bc3aca146102ab57600080fd5b3661022c57336001600160a01b037f0000000000000000000000004200000000000000000000000000000000000006161461022a576040516301f180c960e01b815260040160405180910390fd5b005b600080fd5b34801561023d57600080fd5b5061025161024c3660046151a5565b61072e565b6040805194855260208501939093529183015260608201526080015b60405180910390f35b34801561028257600080fd5b50610296610291366004615275565b610870565b6040805192835260208301919091520161026d565b3480156102b757600080fd5b5061022a6102c63660046152fa565b6109c9565b3480156102d757600080fd5b506102ff7f000000000000000000000000f4c67cdeaab8360370f41514d06e32ccd8aa1d7b81565b6040516001600160a01b03909116815260200161026d565b61022a61032536600461536c565b610cd7565b34801561033657600080fd5b506102ff7f000000000000000000000000420000000000000000000000000000000000000681565b34801561036a57600080fd5b5061037e61037936600461543b565b611068565b60405161026d9190615506565b34801561039757600080fd5b506102ff73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b3480156103bf57600080fd5b506102ff7f00000000000000000000000041c914ee0c7e1a5edcd0295623e6dc557b5abf3c81565b3480156103f357600080fd5b5061040761040236600461554a565b6111c8565b604080516001600160a01b0393841681529290911660208301520161026d565b34801561043357600080fd5b5061037e61044236600461560b565b611253565b34801561045357600080fd5b506104946104623660046156b2565b7f00000000000000000000000006824df38d1d77eadeb6bafcb03904e27429ab746001600160a01b0390811691161490565b604051901515815260200161026d565b3480156104b057600080fd5b506104c46104bf3660046156cf565b611548565b6040805193845260208401929092529082015260600161026d565b3480156104eb57600080fd5b506102516104fa36600461575e565b611640565b34801561050b57600080fd5b506102ff7f00000000000000000000000025cbddb98b35ab1ff77413456b31ec81a6b6b74681565b34801561053f57600080fd5b506102ff61054e366004615823565b611779565b34801561055f57600080fd5b5061022a61056e3660046152fa565b611c67565b34801561057f57600080fd5b5061029661058e366004615823565b611de8565b61037e6105a136600461536c565b611ea2565b3480156105b257600080fd5b5061022a6105c1366004615898565b612183565b6104c46105d4366004615938565b6124a4565b3480156105e557600080fd5b5061037e6105f43660046152fa565b6126f6565b34801561060557600080fd5b506102966106143660046159aa565b6129bd565b34801561062557600080fd5b5061037e6106343660046152fa565b612b06565b34801561064557600080fd5b506104c4610654366004615a0e565b612c15565b34801561066557600080fd5b506102ff7f000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a81565b34801561069957600080fd5b506102966106a8366004615938565b612df7565b3480156106b957600080fd5b506102ff6106c8366004615823565b612ed5565b3480156106d957600080fd5b506106ed6106e8366004615a7c565b612eec565b60405190815260200161026d565b6106ed610709366004615ac7565b613160565b34801561071a57600080fd5b506106ed610729366004615938565b613530565b8585600080606087156107c3576107978b8a8a808060200260200160405190810160405280939291908181526020016000905b8282101561078d5761077e60808302860136819003810190615b98565b81526020019060010190610761565b5050505050611253565b905080600182516107a89190615bca565b815181106107b8576107b8615bdd565b602002602001015194505b85156108475761081b8a8888808060200260200160405190810160405280939291908181526020016000905b8282101561078d5761080c60808302860136819003810190615b98565b815260200190600101906107ef565b9050806001825161082c9190615bca565b8151811061083c5761083c615bdd565b602002602001015193505b6108558f8f8f8f8989612c15565b508093508194505050509a509a509a509a9650505050505050565b6000808261087d81613675565b60006108ab8c8c8c7f000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a611779565b90506108ca6108b8613699565b6001600160a01b03831690838c6136dd565b60405163226bf2d160e21b81526001600160a01b03878116600483015260009182918416906389afcb449060240160408051808303816000875af1158015610916573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061093a9190615bf3565b91509150600061094a8f8f6111c8565b509050806001600160a01b03168f6001600160a01b03161461096d578183610970565b82825b90975095508a871015610996576040516323d9bb0560e21b815260040160405180910390fd5b898610156109b757604051630d32418960e21b815260040160405180910390fd5b50505050509850989650505050505050565b806109d381613675565b6001600160a01b037f0000000000000000000000004200000000000000000000000000000000000006168585610a0a600182615bca565b818110610a1957610a19615bdd565b9050608002016020016020810190610a3191906156b2565b6001600160a01b031614610a58576040516320db826760e01b815260040160405180910390fd5b610b3f85856000818110610a6e57610a6e615bdd565b610a8492602060809092020190810191506156b2565b610a8c613699565b610b3988886000818110610aa257610aa2615bdd565b610ab892602060809092020190810191506156b2565b89896000818110610acb57610acb615bdd565b9050608002016020016020810190610ae391906156b2565b8a8a6000818110610af657610af6615bdd565b9050608002016040016020810190610b0e9190615c17565b8b8b6000818110610b2157610b21615bdd565b905060800201606001602081019061054e91906156b2565b8a61374e565b610b9b8585808060200260200160405190810160405280939291908181526020016000905b82821015610b9057610b8160808302860136819003810190615b98565b81526020019060010190610b64565b505050505030613846565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000042000000000000000000000000000000000000066001600160a01b0316906370a0823190602401602060405180830381865afa158015610c02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c269190615c34565b905086811015610c49576040516342301c2360e01b815260040160405180910390fd5b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015610cab57600080fd5b505af1158015610cbf573d6000803e3d6000fd5b50505050610ccd8482613c6f565b5050505050505050565b80610ce181613675565b7f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031685856000818110610d1e57610d1e615bdd565b610d3492602060809092020190810191506156b2565b6001600160a01b031614610d5b576040516320db826760e01b815260040160405180910390fd5b60003490507f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015610dbb57600080fd5b505af1158015610dcf573d6000803e3d6000fd5b50505050507f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031663a9059cbb610e1988886000818110610aa257610aa2615bdd565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018490526044016020604051808303816000875af1158015610e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8a9190615c4d565b610e9657610e96615c6a565b6000610ea3600187615bca565b90506000878783818110610eb957610eb9615bdd565b9050608002016020016020810190610ed191906156b2565b6040516370a0823160e01b81526001600160a01b03888116600483015291909116906370a0823190602401602060405180830381865afa158015610f19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3d9190615c34565b9050610f9b8888808060200260200160405190810160405280939291908181526020016000905b82821015610f9057610f8160808302860136819003810190615b98565b81526020019060010190610f64565b505050505087613846565b8881898985818110610faf57610faf615bdd565b9050608002016020016020810190610fc791906156b2565b6040516370a0823160e01b81526001600160a01b038a8116600483015291909116906370a08231906024015b602060405180830381865afa158015611010573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110349190615c34565b61103e9190615bca565b101561105d576040516342301c2360e01b815260040160405180910390fd5b505050505050505050565b60608161107481613675565b61115d8686600081811061108a5761108a615bdd565b6110a092602060809092020190810191506156b2565b6110a8613699565b61113d898960008181106110be576110be615bdd565b6110d492602060809092020190810191506156b2565b8a8a60008181106110e7576110e7615bdd565b90506080020160200160208101906110ff91906156b2565b8b8b600081811061111257611112615bdd565b905060800201604001602081019061112a9190615c17565b8c8c6000818110610b2157610b21615bdd565b8a60008151811061115057611150615bdd565b602002602001015161374e565b6111ba878787808060200260200160405190810160405280939291908181526020016000905b828210156111af576111a060808302860136819003810190615b98565b81526020019060010190611183565b505050505086613d02565b8691505b5095945050505050565b600080826001600160a01b0316846001600160a01b0316036111fd57604051633295f3fd60e21b815260040160405180910390fd5b826001600160a01b0316846001600160a01b03161061121d578284611220565b83835b90925090506001600160a01b03821661124c5760405163d92e233d60e01b815260040160405180910390fd5b9250929050565b6060600182511015611278576040516320db826760e01b815260040160405180910390fd5b8151611285906001615c80565b6001600160401b0381111561129c5761129c6153d2565b6040519080825280602002602001820160405280156112c5578160200160208202803683370190505b50905082816000815181106112dc576112dc615bdd565b6020908102919091010152815160005b81811015611540576000806001600160a01b031685838151811061131257611312615bdd565b6020026020010151606001516001600160a01b03161461134f5784828151811061133e5761133e615bdd565b602002602001015160600151611371565b7f000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a5b905060006113d886848151811061138a5761138a615bdd565b6020026020010151600001518785815181106113a8576113a8615bdd565b6020026020010151602001518886815181106113c6576113c6615bdd565b60200260200101516040015185611779565b60405163e5e31b1360e01b81526001600160a01b0380831660048301529192509083169063e5e31b1390602401602060405180830381865afa158015611422573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114469190615c4d565b1561152b57806001600160a01b031663f140a35a86858151811061146c5761146c615bdd565b602002602001015188868151811061148657611486615bdd565b6020026020010151600001516040518363ffffffff1660e01b81526004016114c19291909182526001600160a01b0316602082015260400190565b602060405180830381865afa1580156114de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115029190615c34565b8561150e856001615c80565b8151811061151e5761151e615bdd565b6020026020010181815250505b5050808061153890615c93565b9150506112ec565b505092915050565b60008060008361155781613675565b6115668d8d8d8d8d8d8d613f62565b909450925060006115998e8e8e7f000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a611779565b90506115ae8e6115a7613699565b838861374e565b6115c18d6115ba613699565b838761374e565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af1158015611609573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162d9190615c34565b9250505099509950999650505050505050565b6000806000806116538d8d8d8d8d6129bd565b9094509250839150829050606087156116e4576116b8838a8a808060200260200160405190810160405280939291908181526020016000905b8282101561078d576116a960808302860136819003810190615b98565b8152602001906001019061168c565b905080600182516116c99190615bca565b815181106116d9576116d9615bdd565b602002602001015194505b85156117685761173c828888808060200260200160405190810160405280939291908181526020016000905b8282101561078d5761172d60808302860136819003810190615b98565b81526020019060010190611710565b9050806001825161174d9190615bca565b8151811061175d5761175d615bdd565b602002602001015193505b509950995099509995505050505050565b60007f000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a816001600160a01b038416156117b257836117b4565b815b60405163d1ea0a1d60e01b81526001600160a01b0380831660048301529192507f000000000000000000000000f4c67cdeaab8360370f41514d06e32ccd8aa1d7b9091169063d1ea0a1d90602401602060405180830381865afa15801561181f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118439190615c4d565b61186057604051634d39d5a360e11b815260040160405180910390fd5b6000826001600160a01b0316638c7c53ce6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118c49190615cac565b90506000836001600160a01b031663c6751c096040518163ffffffff1660e01b8152600401602060405180830381865afa158015611906573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061192a9190615cac565b9050806001600160a01b0316896001600160a01b031614801561195e5750816001600160a01b0316886001600160a01b0316145b1561197c57604051639cda859960e01b815260040160405180910390fd5b816001600160a01b0316896001600160a01b03161480156119ae5750806001600160a01b0316886001600160a01b0316145b15611a2057836001600160a01b0316638e39ee166040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a159190615cac565b945050505050611c5f565b600080611a2d8b8b6111c8565b915091507f00000000000000000000000025cbddb98b35ab1ff77413456b31ec81a6b6b7466001600160a01b0316856001600160a01b031614611b6357600082828b604051602001611a8193929190615cc9565b604051602081830303815290604052805190602001209050611b5b866001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ada573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611afe9190615cac565b604051603881018990526f5af43d82803e903d91602b57fd5bf3ff60248201526014810191909152733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018390526037600c8201206078820152605560439091012090565b975050611c58565b6000856001600160a01b0316639aab92486040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ba3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc79190615c34565b90508583838c604051602001611bdf93929190615cc9565b6040516020818303038152906040528051906020012082604051602001611c3b939291906001600160f81b0319815260609390931b6bffffffffffffffffffffffff191660018401526015830191909152603582015260550190565b6040516020818303038152906040528051906020012060001c9750505b5050505050505b949350505050565b80611c7181613675565b611c8785856000818110610a6e57610a6e615bdd565b6000611c94600186615bca565b90506000868683818110611caa57611caa615bdd565b9050608002016020016020810190611cc291906156b2565b6040516370a0823160e01b81526001600160a01b03878116600483015291909116906370a0823190602401602060405180830381865afa158015611d0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d2e9190615c34565b9050611d8c8787808060200260200160405190810160405280939291908181526020016000905b82821015611d8157611d7260808302860136819003810190615b98565b81526020019060010190611d55565b505050505086613846565b8781888885818110611da057611da0615bdd565b9050608002016020016020810190611db891906156b2565b6040516370a0823160e01b81526001600160a01b03898116600483015291909116906370a0823190602401610ff3565b6000806000611df787876111c8565b509050600080611e0989898989611779565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015611e46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6a9190615cfc565b5091509150826001600160a01b0316896001600160a01b031614611e8f578082611e92565b81815b909a909950975050505050505050565b606081611eae81613675565b7f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031686866000818110611eeb57611eeb615bdd565b611f0192602060809092020190810191506156b2565b6001600160a01b031614611f28576040516320db826760e01b815260040160405180910390fd5b611f7a348787808060200260200160405190810160405280939291908181526020016000905b8282101561078d57611f6b60808302860136819003810190615b98565b81526020019060010190611f4e565b9150868260018451611f8c9190615bca565b81518110611f9c57611f9c615bdd565b60200260200101511015611fc3576040516342301c2360e01b815260040160405180910390fd5b7f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031663d0e30db08360008151811061200557612005615bdd565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561203857600080fd5b505af115801561204c573d6000803e3d6000fd5b50505050507f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031663a9059cbb61209688886000818110610aa257610aa2615bdd565b846000815181106120a9576120a9615bdd565b60200260200101516040518363ffffffff1660e01b81526004016120e29291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af1158015612101573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121259190615c4d565b61213157612131615c6a565b6111be828787808060200260200160405190810160405280939291908181526020016000905b828210156111af5761217460808302860136819003810190615b98565b81526020019060010190612157565b600061219260208701876156b2565b905060006121a660408801602089016156b2565b905060006001600160a01b038a1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146121d457896121f6565b7f00000000000000000000000042000000000000000000000000000000000000065b905061220289896141cb565b6000816001600160a01b0316846001600160a01b03161461234e576040516370a0823160e01b81523060048201526001600160a01b038516906370a0823190602401602060405180830381865afa158015612261573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122859190615c34565b90506001600160a01b038216888861229e600182615bca565b8181106122ad576122ad615bdd565b90506080020160200160208101906122c591906156b2565b6001600160a01b0316146122ec576040516309d41c6760e31b815260040160405180910390fd5b61234e84828b608001358b8b808060200260200160405190810160405280939291908181526020016000905b828210156123445761233560808302860136819003810190615b98565b81526020019060010190612318565b5050505050614330565b816001600160a01b0316836001600160a01b03161461248e576040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156123ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123cf9190615c34565b90506001600160a01b03821686866123e8600182615bca565b8181106123f7576123f7615bdd565b905060800201602001602081019061240f91906156b2565b6001600160a01b031614612436576040516332b2410360e21b815260040160405180910390fd5b61248e83828b60a001358989808060200260200160405190810160405280939291908181526020016000905b828210156123445761247f60808302860136819003810190615b98565b81526020019060010190612462565b6124978b614412565b5050505050505050505050565b6000806000836124b381613675565b6124e28b7f00000000000000000000000042000000000000000000000000000000000000068c8c348d8d613f62565b909450925060006125358c7f00000000000000000000000042000000000000000000000000000000000000068d7f000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a611779565b90506125438c6115a7613699565b7f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b15801561259e57600080fd5b505af11580156125b2573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b038581166004830152602482018990527f000000000000000000000000420000000000000000000000000000000000000616935063a9059cbb925060440190506020604051808303816000875af1158015612627573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061264b9190615c4d565b61265757612657615c6a565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af115801561269f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126c39190615c34565b9250833411156126e7576126e76126d8613699565b6126e28634615bca565b613c6f565b50509750975097945050505050565b60608161270281613675565b6001600160a01b037f0000000000000000000000004200000000000000000000000000000000000006168686612739600182615bca565b81811061274857612748615bdd565b905060800201602001602081019061276091906156b2565b6001600160a01b031614612787576040516320db826760e01b815260040160405180910390fd5b6127d9888787808060200260200160405190810160405280939291908181526020016000905b8282101561078d576127ca60808302860136819003810190615b98565b815260200190600101906127ad565b91508682600184516127eb9190615bca565b815181106127fb576127fb615bdd565b60200260200101511015612822576040516342301c2360e01b815260040160405180910390fd5b61287f8686600081811061283857612838615bdd565b61284e92602060809092020190810191506156b2565b612856613699565b61286c898960008181106110be576110be615bdd565b8560008151811061115057611150615bdd565b6128dc828787808060200260200160405190810160405280939291908181526020016000905b828210156128d1576128c260808302860136819003810190615b98565b815260200190600101906128a5565b505050505030613d02565b7f00000000000000000000000042000000000000000000000000000000000000066001600160a01b0316632e1a7d4d836001855161291a9190615bca565b8151811061292a5761292a615bdd565b60200260200101516040518263ffffffff1660e01b815260040161295091815260200190565b600060405180830381600087803b15801561296a57600080fd5b505af115801561297e573d6000803e3d6000fd5b505050506129b28483600185516129959190615bca565b815181106129a5576129a5615bdd565b6020026020010151613c6f565b509695505050505050565b6000806000846001600160a01b0316636801cc308989896040518463ffffffff1660e01b81526004016129f293929190615d2a565b602060405180830381865afa158015612a0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a339190615cac565b90506001600160a01b038116612a50576000809250925050612afc565b600080612a5f8a8a8a8a611de8565b915091506000836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612aa3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ac79190615c34565b905080612ad48489615d4e565b612ade9190615d7b565b955080612aeb8389615d4e565b612af59190615d7b565b9450505050505b9550959350505050565b606081612b1281613675565b612b64888787808060200260200160405190810160405280939291908181526020016000905b8282101561078d57612b5560808302860136819003810190615b98565b81526020019060010190612b38565b9150868260018451612b769190615bca565b81518110612b8657612b86615bdd565b60200260200101511015612bad576040516342301c2360e01b815260040160405180910390fd5b612bc38686600081811061283857612838615bdd565b6129b2828787808060200260200160405190810160405280939291908181526020016000905b828210156111af57612c0660808302860136819003810190615b98565b81526020019060010190612be9565b600080600080866001600160a01b0316636801cc308b8b8b6040518463ffffffff1660e01b8152600401612c4b93929190615d2a565b602060405180830381865afa158015612c68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c8c9190615cac565b9050600080806001600160a01b03841615612d1757836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d039190615c34565b9050612d118d8d8d8d611de8565b90935091505b82158015612d23575081155b15612d54578896508795506103e8612d43612d3e888a615d4e565b6145dd565b612d4d9190615bca565b9450612de7565b6000612d618a85856146cc565b9050888111612da957899750955085612da284612d7e848b615d4e565b612d889190615d7b565b84612d93858b615d4e565b612d9d9190615d7b565b61472d565b9550612de5565b6000612db68a85876146cc565b9850899750889050612de185612dcc8584615d4e565b612dd69190615d7b565b85612d93868c615d4e565b9650505b505b5050505096509650969350505050565b60008082612e0481613675565b612e348a7f00000000000000000000000042000000000000000000000000000000000000068b8b8b8b308b610870565b9093509150612e448a8685614747565b604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015612ea657600080fd5b505af1158015612eba573d6000803e3d6000fd5b50505050612ec88583613c6f565b5097509795505050505050565b6000612ee385858585611779565b95945050505050565b600080612efc8585600186611779565b90506000856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f629190615d9d565b612f6d90600a615ea4565b90506000856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612faf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fd39190615d9d565b612fde90600a615ea4565b6040516378a051ad60e11b8152600481018490526001600160a01b03898116602483015291925083916000919086169063f140a35a90604401602060405180830381865afa158015613034573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130589190615c34565b905060008061306c8b8b60018c8888612c15565b5090925090508561308583670de0b6b3a7640000615d4e565b61308f9190615d7b565b9150846130a482670de0b6b3a7640000615d4e565b6130ae9190615d7b565b9050846130c384670de0b6b3a7640000615d4e565b6130cd9190615d7b565b9250856130e285670de0b6b3a7640000615d4e565b6130ec9190615d7b565b935080828561310386670de0b6b3a7640000615d4e565b61310d9190615d7b565b6131179190615d4e565b6131219190615d7b565b975061313588670de0b6b3a7640000615c80565b61314785670de0b6b3a7640000615d4e565b6131519190615d7b565b9b9a5050505050505050505050565b60008061316d8a8c615c80565b90508b3473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03831601613252578083146131b657604051633851fdc960e11b815260040160405180910390fd5b7f000000000000000000000000420000000000000000000000000000000000000691507f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561323457600080fd5b505af1158015613248573d6000803e3d6000fd5b5050505050613284565b80156132715760405163ae6d566f60e01b815260040160405180910390fd5b6132848261327d613699565b308661374e565b613294828e8e8e8e8e8e8e61482f565b61329d8b614ae4565b60006132e86132af60208e018e6156b2565b8d60200160208101906132c291906156b2565b8e60400160208101906132d59190615c17565b8f606001602081019061054e91906156b2565b9050851561347e576040516335313c2160e11b81523060048201526001600160a01b03821690636a627842906024016020604051808303816000875af1158015613336573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061335a9190615c34565b60405163b9a09fd560e01b81526001600160a01b0383811660048301529196506000917f00000000000000000000000041c914ee0c7e1a5edcd0295623e6dc557b5abf3c169063b9a09fd590602401602060405180830381865afa1580156133c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ea9190615cac565b90506134006001600160a01b0383168288614c4c565b604051636e553f6560e01b8152600481018790526001600160a01b038981166024830152821690636e553f6590604401600060405180830381600087803b15801561344a57600080fd5b505af115801561345e573d6000803e3d6000fd5b50613478925050506001600160a01b038316826000614c4c565b506134ed565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af11580156134c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134ea9190615c34565b94505b6134f68f614412565b61350b61350660208e018e6156b2565b614412565b61351e61350660408e0160208f016156b2565b505050509a9950505050505050505050565b60008161353c81613675565b61356c897f00000000000000000000000042000000000000000000000000000000000000068a8a8a8a308a610870565b6040516370a0823160e01b81523060048201529093506135e591508a9086906001600160a01b038316906370a0823190602401602060405180830381865afa1580156135bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135e09190615c34565b614747565b604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561364757600080fd5b505af115801561365b573d6000803e3d6000fd5b505050506136698483613c6f565b50979650505050505050565b4281101561369657604051630407b05b60e31b815260040160405180910390fd5b50565b60007f00000000000000000000000006824df38d1d77eadeb6bafcb03904e27429ab746001600160a01b031633036136d8575060131936013560601c90565b503390565b6040516001600160a01b03808516602483015283166044820152606481018290526137489085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614d66565b50505050565b6000846001600160a01b03163b1161376557600080fd5b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17905291516000928392908816916137c99190615ed7565b6000604051808303816000865af19150503d8060008114613806576040519150601f19603f3d011682016040523d82523d6000602084013e61380b565b606091505b50915091508180156138355750805115806138355750808060200190518101906138359190615c4d565b61383e57600080fd5b505050505050565b815160005b8181101561374857600061389985838151811061386a5761386a615bdd565b60200260200101516000015186848151811061388857613888615bdd565b6020026020010151602001516111c8565b509050600061391e8684815181106138b3576138b3615bdd565b6020026020010151600001518785815181106138d1576138d1615bdd565b6020026020010151602001518886815181106138ef576138ef615bdd565b60200260200101516040015189878151811061390d5761390d615bdd565b602002602001015160600151611779565b905060008060006139a589878151811061393a5761393a615bdd565b6020026020010151600001518a888151811061395857613958615bdd565b6020026020010151602001518b898151811061397657613976615bdd565b6020026020010151604001518c8a8151811061399457613994615bdd565b602002602001015160600151611de8565b509050808987815181106139bb576139bb615bdd565b6020908102919091010151516040516370a0823160e01b81526001600160a01b038781166004830152909116906370a0823190602401602060405180830381865afa158015613a0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a329190615c34565b613a3c9190615bca565b925050826001600160a01b031663f140a35a838a8881518110613a6157613a61615bdd565b6020026020010151600001516040518363ffffffff1660e01b8152600401613a9c9291909182526001600160a01b0316602082015260400190565b602060405180830381865afa158015613ab9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613add9190615c34565b9050600080856001600160a01b03168a8881518110613afe57613afe615bdd565b6020026020010151600001516001600160a01b031614613b2057826000613b24565b6000835b91509150600060018b51613b389190615bca565b8810613b445789613be1565b613be18b613b538a6001615c80565b81518110613b6357613b63615bdd565b6020026020010151600001518c8a6001613b7d9190615c80565b81518110613b8d57613b8d615bdd565b6020026020010151602001518d8b6001613ba79190615c80565b81518110613bb757613bb7615bdd565b6020026020010151604001518e8c6001613bd19190615c80565b8151811061390d5761390d615bdd565b6040805160008152602081019182905263022c0d9f60e01b9091529091506001600160a01b0387169063022c0d9f90613c239086908690869060248101615f1f565b600060405180830381600087803b158015613c3d57600080fd5b505af1158015613c51573d6000803e3d6000fd5b50505050505050505050508080613c6790615c93565b91505061384b565b604080516000808252602082019092526001600160a01b038416908390604051613c999190615ed7565b60006040518083038185875af1925050503d8060008114613cd6576040519150601f19603f3d011682016040523d82523d6000602084013e613cdb565b606091505b5050905080613cfd5760405163b12d13eb60e01b815260040160405180910390fd5b505050565b815160005b81811015613f5b576000613d2685838151811061386a5761386a615bdd565b509050600086613d37846001615c80565b81518110613d4757613d47615bdd565b60200260200101519050600080836001600160a01b0316888681518110613d7057613d70615bdd565b6020026020010151600001516001600160a01b031614613d9257826000613d96565b6000835b91509150600060018951613daa9190615bca565b8610613db65787613e43565b613e4389613dc5886001615c80565b81518110613dd557613dd5615bdd565b6020026020010151600001518a886001613def9190615c80565b81518110613dff57613dff615bdd565b6020026020010151602001518b896001613e199190615c80565b81518110613e2957613e29615bdd565b6020026020010151604001518c8a6001613bd19190615c80565b9050613eb4898781518110613e5a57613e5a615bdd565b6020026020010151600001518a8881518110613e7857613e78615bdd565b6020026020010151602001518b8981518110613e9657613e96615bdd565b6020026020010151604001518c8a8151811061390d5761390d615bdd565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f191660200182016040528015613ef1576020820181803683370190505b506040518563ffffffff1660e01b8152600401613f119493929190615f1f565b600060405180830381600087803b158015613f2b57600080fd5b505af1158015613f3f573d6000803e3d6000fd5b5050505050505050508080613f5390615c93565b915050613d07565b5050505050565b60008083861015613f8657604051636e35977960e11b815260040160405180910390fd5b82851015613fa75760405163acee051360e01b815260040160405180910390fd5b6040516306801cc360e41b81526000906001600160a01b037f000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a1690636801cc3090613ffa908d908d908d90600401615d2a565b602060405180830381865afa158015614017573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061403b9190615cac565b90506001600160a01b0381166140e1576040516320b7f73960e21b81526001600160a01b037f000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a16906382dfdce49061409b908d908d908d90600401615d2a565b6020604051808303816000875af11580156140ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140de9190615cac565b90505b6000806141108c8c8c7f000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a611de8565b91509150816000148015614122575080155b15614132578894508793506141bc565b600061413f8a84846146cc565b9050888111614174578681101561416957604051630d32418960e21b815260040160405180910390fd5b8995509350836141ba565b60006141818a84866146cc565b90508a81111561419357614193615c6a565b888110156141b4576040516323d9bb0560e21b815260040160405180910390fd5b95508894505b505b50505097509795505050505050565b60006141da60208301836156b2565b905060006141ee60408401602085016156b2565b9050600061421783836142076060880160408901615c17565b61054e6080890160608a016156b2565b905061422e6001600160a01b0382163383886136dd565b600061423a84846111c8565b5060405163226bf2d160e21b815230600482015290915060009081906001600160a01b038516906389afcb449060240160408051808303816000875af1158015614288573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142ac9190615bf3565b91509150600080846001600160a01b0316886001600160a01b0316146142d35782846142d6565b83835b915091508860c001358210156142ff576040516323d9bb0560e21b815260040160405180910390fd5b8860e0013581101561432457604051630d32418960e21b815260040160405180910390fd5b50505050505050505050565b600061433c8483611253565b905082816001835161434e9190615bca565b8151811061435e5761435e615bdd565b60200260200101511015614385576040516342301c2360e01b815260040160405180910390fd5b60006143fa8360008151811061439d5761439d615bdd565b602002602001015160000151846000815181106143bc576143bc615bdd565b602002602001015160200151856000815181106143db576143db615bdd565b6020026020010151604001518660008151811061390d5761390d615bdd565b9050614407868287614747565b61383e828430613d02565b600061441c613699565b9050600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601614559576040516370a0823160e01b81523060048201527f00000000000000000000000042000000000000000000000000000000000000066001600160a01b0316906370a0823190602401602060405180830381865afa1580156144a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144cd9190615c34565b90508015613cfd57604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561453757600080fd5b505af115801561454b573d6000803e3d6000fd5b50505050613cfd8282613c6f565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa15801561459d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145c19190615c34565b90508015613cfd57613cfd6001600160a01b0384168383614e38565b6000816000036145ef57506000919050565b600060016145fc84614e68565b901c6001901b9050600181848161461557614615615d65565b048201901c9050600181848161462d5761462d615d65565b048201901c9050600181848161464557614645615d65565b048201901c9050600181848161465d5761465d615d65565b048201901c9050600181848161467557614675615d65565b048201901c9050600181848161468d5761468d615d65565b048201901c905060018184816146a5576146a5615d65565b048201901c90506146c5818285816146bf576146bf615d65565b0461472d565b9392505050565b6000836000036146ef57604051632ca2f52b60e11b815260040160405180910390fd5b8215806146fa575081155b156147185760405163bb55fd2760e01b815260040160405180910390fd5b826147238386615d4e565b611c5f9190615d7b565b600081831061473c578161473e565b825b90505b92915050565b6000836001600160a01b03163b1161475e57600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916147ba9190615ed7565b6000604051808303816000865af19150503d80600081146147f7576040519150601f19603f3d011682016040523d82523d6000602084013e6147fc565b606091505b50915091508180156148265750805115806148265750808060200190518101906148269190615c4d565b613f5b57600080fd5b600061483e60208701876156b2565b9050600061485260408801602089016156b2565b905060006148666060890160408a01615c17565b9050600061487a60808a0160608b016156b2565b9050600061488a85858585611779565b9050600080826001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa1580156148cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148f19190615cfc565b50915091506103e88211158061490957506103e88111155b15614927576040516302721e1f60e61b815260040160405180910390fd5b5050846001600160a01b03168d6001600160a01b0316146149ff576001600160a01b0385168989614959600182615bca565b81811061496857614968615bdd565b905060800201602001602081019061498091906156b2565b6001600160a01b0316146149a7576040516309d41c6760e31b815260040160405180910390fd5b6149ff8d8d8c608001358c8c808060200260200160405190810160405280939291908181526020016000905b82821015612344576149f060808302860136819003810190615b98565b815260200190600101906149d3565b836001600160a01b03168d6001600160a01b031614614ad5576001600160a01b0384168787614a2f600182615bca565b818110614a3e57614a3e615bdd565b9050608002016020016020810190614a5691906156b2565b6001600160a01b031614614a7d576040516332b2410360e21b815260040160405180910390fd5b614ad58d8c8c60a001358a8a808060200260200160405190810160405280939291908181526020016000905b8282101561234457614ac660808302860136819003810190615b98565b81526020019060010190614aa9565b50505050505050505050505050565b6000614af360208301836156b2565b90506000614b0760408401602085016156b2565b90506000614b1b6060850160408601615c17565b90506000614b2f60808601606087016156b2565b90506000614b3f85858585611779565b6040516370a0823160e01b81523060048201529091506000908190614c329088908890889088906001600160a01b038516906370a0823190602401602060405180830381865afa158015614b97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bbb9190615c34565b6040516370a0823160e01b81523060048201526001600160a01b038d16906370a0823190602401602060405180830381865afa158015614bff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c239190615c34565b8e60c001358f60e00135614efc565b91509150614c41878484614747565b610ccd868483614747565b801580614cc65750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015614ca0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cc49190615c34565b155b614d365760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084015b60405180910390fd5b6040516001600160a01b038316602482015260448101829052613cfd90849063095ea7b360e01b90606401613711565b6000614dbb826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661500b9092919063ffffffff16565b805190915015613cfd5780806020019051810190614dd99190615c4d565b613cfd5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401614d2d565b6040516001600160a01b038316602482015260448101829052613cfd90849063a9059cbb60e01b90606401613711565b600080608083901c15614e7d57608092831c92015b604083901c15614e8f57604092831c92015b602083901c15614ea157602092831c92015b601083901c15614eb357601092831c92015b600883901c15614ec557600892831c92015b600483901c15614ed757600492831c92015b600283901c15614ee957600292831c92015b600183901c156147415760010192915050565b60008083861015614f2057604051636e35977960e11b815260040160405180910390fd5b82851015614f415760405163acee051360e01b815260040160405180910390fd5b600080614f508c8c8c8c611de8565b91509150816000148015614f62575080155b15614f7257879350869250614ffc565b6000614f7f8984846146cc565b9050878111614fb45785811015614fa957604051630d32418960e21b815260040160405180910390fd5b889450925082614ffa565b6000614fc18984866146cc565b905089811115614fd357614fd3615c6a565b87811015614ff4576040516323d9bb0560e21b815260040160405180910390fd5b94508793505b505b50509850989650505050505050565b6060611c5f848460008585600080866001600160a01b031685876040516150329190615ed7565b60006040518083038185875af1925050503d806000811461506f576040519150601f19603f3d011682016040523d82523d6000602084013e615074565b606091505b509150915061508587838387615090565b979650505050505050565b606083156150ff5782516000036150f8576001600160a01b0385163b6150f85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401614d2d565b5081611c5f565b611c5f83838151156151145781518083602001fd5b8060405162461bcd60e51b8152600401614d2d9190615f56565b6001600160a01b038116811461369657600080fd5b803561514e8161512e565b919050565b801515811461369657600080fd5b60008083601f84011261517357600080fd5b5081356001600160401b0381111561518a57600080fd5b6020830191508360208260071b850101111561124c57600080fd5b6000806000806000806000806000806101008b8d0312156151c557600080fd5b8a356151d08161512e565b995060208b01356151e08161512e565b985060408b01356151f081615153565b975060608b01356152008161512e565b965060808b0135955060a08b0135945060c08b01356001600160401b038082111561522a57600080fd5b6152368e838f01615161565b909650945060e08d013591508082111561524f57600080fd5b5061525c8d828e01615161565b915080935050809150509295989b9194979a5092959850565b600080600080600080600080610100898b03121561529257600080fd5b883561529d8161512e565b975060208901356152ad8161512e565b965060408901356152bd81615153565b9550606089013594506080890135935060a0890135925060c08901356152e28161512e565b8092505060e089013590509295985092959890939650565b60008060008060008060a0878903121561531357600080fd5b863595506020870135945060408701356001600160401b0381111561533757600080fd5b61534389828a01615161565b90955093505060608701356153578161512e565b80925050608087013590509295509295509295565b60008060008060006080868803121561538457600080fd5b8535945060208601356001600160401b038111156153a157600080fd5b6153ad88828901615161565b90955093505060408601356153c18161512e565b949793965091946060013592915050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715615410576154106153d2565b604052919050565b60006001600160401b03821115615431576154316153d2565b5060051b60200190565b60008060008060006080868803121561545357600080fd5b85356001600160401b038082111561546a57600080fd5b818801915088601f83011261547e57600080fd5b8135602061549361548e83615418565b6153e8565b82815260059290921b8401810191818101908c8411156154b257600080fd5b948201945b838610156154d0578535825294820194908201906154b7565b995050890135925050808211156154e657600080fd5b506154f388828901615161565b90955093506153c1905060408701615143565b6020808252825182820181905260009190848201906040850190845b8181101561553e57835183529284019291840191600101615522565b50909695505050505050565b6000806040838503121561555d57600080fd5b82356155688161512e565b915060208301356155788161512e565b809150509250929050565b60006080828403121561559557600080fd5b604051608081018181106001600160401b03821117156155b7576155b76153d2565b60405290508082356155c88161512e565b815260208301356155d88161512e565b602082015260408301356155eb81615153565b604082015260608301356155fe8161512e565b6060919091015292915050565b6000806040838503121561561e57600080fd5b823591506020808401356001600160401b0381111561563c57600080fd5b8401601f8101861361564d57600080fd5b803561565b61548e82615418565b81815260079190911b8201830190838101908883111561567a57600080fd5b928401925b828410156156a3576156918985615583565b8252848201915060808401935061567f565b80955050505050509250929050565b6000602082840312156156c457600080fd5b81356146c58161512e565b60008060008060008060008060006101208a8c0312156156ee57600080fd5b89356156f98161512e565b985060208a01356157098161512e565b975060408a013561571981615153565b965060608a0135955060808a0135945060a08a0135935060c08a0135925060e08a01356157458161512e565b809250506101008a013590509295985092959850929598565b600080600080600080600080600060e08a8c03121561577c57600080fd5b89356157878161512e565b985060208a01356157978161512e565b975060408a01356157a781615153565b965060608a01356157b78161512e565b955060808a0135945060a08a01356001600160401b03808211156157da57600080fd5b6157e68d838e01615161565b909650945060c08c01359150808211156157ff57600080fd5b5061580c8c828d01615161565b915080935050809150509295985092959850929598565b6000806000806080858703121561583957600080fd5b84356158448161512e565b935060208501356158548161512e565b9250604085013561586481615153565b915060608501356158748161512e565b939692955090935050565b6000610100828403121561589257600080fd5b50919050565b6000806000806000806000610180888a0312156158b457600080fd5b87356158bf8161512e565b9650602088013595506158d58960408a0161587f565b94506101408801356001600160401b03808211156158f257600080fd5b6158fe8b838c01615161565b90965094506101608a013591508082111561591857600080fd5b506159258a828b01615161565b989b979a50959850939692959293505050565b600080600080600080600060e0888a03121561595357600080fd5b873561595e8161512e565b9650602088013561596e81615153565b955060408801359450606088013593506080880135925060a08801356159938161512e565b8092505060c0880135905092959891949750929550565b600080600080600060a086880312156159c257600080fd5b85356159cd8161512e565b945060208601356159dd8161512e565b935060408601356159ed81615153565b925060608601356159fd8161512e565b949793965091946080013592915050565b60008060008060008060c08789031215615a2757600080fd5b8635615a328161512e565b95506020870135615a428161512e565b94506040870135615a5281615153565b93506060870135615a628161512e565b9598949750929560808101359460a0909101359350915050565b600080600060608486031215615a9157600080fd5b8335615a9c8161512e565b92506020840135615aac8161512e565b91506040840135615abc8161512e565b809150509250925092565b6000806000806000806000806000806101e08b8d031215615ae757600080fd5b8a35615af28161512e565b995060208b0135985060408b01359750615b0f8c60608d0161587f565b96506101608b01356001600160401b0380821115615b2c57600080fd5b615b388e838f01615161565b90985096506101808d0135915080821115615b5257600080fd5b50615b5f8d828e01615161565b9095509350506101a08b0135615b748161512e565b91506101c08b0135615b8581615153565b809150509295989b9194979a5092959850565b600060808284031215615baa57600080fd5b61473e8383615583565b634e487b7160e01b600052601160045260246000fd5b8181038181111561474157614741615bb4565b634e487b7160e01b600052603260045260246000fd5b60008060408385031215615c0657600080fd5b505080516020909101519092909150565b600060208284031215615c2957600080fd5b81356146c581615153565b600060208284031215615c4657600080fd5b5051919050565b600060208284031215615c5f57600080fd5b81516146c581615153565b634e487b7160e01b600052600160045260246000fd5b8082018082111561474157614741615bb4565b600060018201615ca557615ca5615bb4565b5060010190565b600060208284031215615cbe57600080fd5b81516146c58161512e565b6bffffffffffffffffffffffff19606094851b811682529290931b9091166014830152151560f81b602882015260290190565b600080600060608486031215615d1157600080fd5b8351925060208401519150604084015190509250925092565b6001600160a01b039384168152919092166020820152901515604082015260600190565b808202811582820484141761474157614741615bb4565b634e487b7160e01b600052601260045260246000fd5b600082615d9857634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615daf57600080fd5b815160ff811681146146c557600080fd5b600181815b80851115615dfb578160001904821115615de157615de1615bb4565b80851615615dee57918102915b93841c9390800290615dc5565b509250929050565b600082615e1257506001614741565b81615e1f57506000614741565b8160018114615e355760028114615e3f57615e5b565b6001915050614741565b60ff841115615e5057615e50615bb4565b50506001821b614741565b5060208310610133831016604e8410600b8410161715615e7e575081810a614741565b615e888383615dc0565b8060001904821115615e9c57615e9c615bb4565b029392505050565b600061473e60ff841683615e03565b60005b83811015615ece578181015183820152602001615eb6565b50506000910152565b60008251615ee9818460208701615eb3565b9190910192915050565b60008151808452615f0b816020860160208601615eb3565b601f01601f19169290920160200192915050565b84815283602082015260018060a01b0383166040820152608060608201526000615f4c6080830184615ef3565b9695505050505050565b60208152600061473e6020830184615ef356fea2646970667358221220b1099ee7ddb5dc2ea1ec270042b192b9761fe430654308578bf270f4f8bb35cd64736f6c63430008130033

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

00000000000000000000000006824df38d1d77eadeb6bafcb03904e27429ab74000000000000000000000000f4c67cdeaab8360370f41514d06e32ccd8aa1d7b00000000000000000000000025cbddb98b35ab1ff77413456b31ec81a6b6b746000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a00000000000000000000000041c914ee0c7e1a5edcd0295623e6dc557b5abf3c0000000000000000000000004200000000000000000000000000000000000006

-----Decoded View---------------
Arg [0] : _forwarder (address): 0x06824df38D1D77eADEB6baFCB03904E27429Ab74
Arg [1] : _factoryRegistry (address): 0xF4c67CdEAaB8360370F41514d06e32CcD8aA1d7B
Arg [2] : _v1Factory (address): 0x25CbdDb98b35ab1FF77413456B31EC81A6B6B746
Arg [3] : _factory (address): 0xF1046053aa5682b4F9a81b5481394DA16BE5FF5a
Arg [4] : _voter (address): 0x41C914ee0c7E1A5edCD0295623e6dC557B5aBf3C
Arg [5] : _weth (address): 0x4200000000000000000000000000000000000006

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 00000000000000000000000006824df38d1d77eadeb6bafcb03904e27429ab74
Arg [1] : 000000000000000000000000f4c67cdeaab8360370f41514d06e32ccd8aa1d7b
Arg [2] : 00000000000000000000000025cbddb98b35ab1ff77413456b31ec81a6b6b746
Arg [3] : 000000000000000000000000f1046053aa5682b4f9a81b5481394da16be5ff5a
Arg [4] : 00000000000000000000000041c914ee0c7e1a5edcd0295623e6dc557b5abf3c
Arg [5] : 0000000000000000000000004200000000000000000000000000000000000006


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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