ETH Price: $2,291.49 (+9.41%)

Contract

0xbFAe8E87053309fDe07ab3cA5f4B5345f8e3058f
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Swap Exact Token...246708822026-03-16 15:13:355 mins ago1773674015IN
0xbFAe8E87...5f8e3058f
0 ETH0.00035690.41038457
Swap Exact Token...246708722026-03-16 15:11:357 mins ago1773673895IN
0xbFAe8E87...5f8e3058f
0 ETH0.000385870.40016699
Swap Exact Token...246708502026-03-16 15:07:1112 mins ago1773673631IN
0xbFAe8E87...5f8e3058f
0 ETH0.000470740.49747811
Swap Exact Token...246708402026-03-16 15:05:1114 mins ago1773673511IN
0xbFAe8E87...5f8e3058f
0 ETH0.000120090.51574233
Swap Exact Token...246708262026-03-16 15:02:2317 mins ago1773673343IN
0xbFAe8E87...5f8e3058f
0 ETH0.000473350.56503681
Swap Exact Token...246708112026-03-16 14:59:2320 mins ago1773673163IN
0xbFAe8E87...5f8e3058f
0 ETH0.000153530.5236555
Swap Exact Token...246707952026-03-16 14:56:1123 mins ago1773672971IN
0xbFAe8E87...5f8e3058f
0 ETH0.000613580.70831407
Add Liquidity246707572026-03-16 14:48:3530 mins ago1773672515IN
0xbFAe8E87...5f8e3058f
0 ETH0.000239851.19460031
Remove Liquidity246707382026-03-16 14:44:4734 mins ago1773672287IN
0xbFAe8E87...5f8e3058f
0 ETH0.000202971.20918528
Swap Exact Token...246707372026-03-16 14:44:3534 mins ago1773672275IN
0xbFAe8E87...5f8e3058f
0 ETH0.001915821.46587897
Remove Liquidity246707342026-03-16 14:43:5935 mins ago1773672239IN
0xbFAe8E87...5f8e3058f
0 ETH0.00028271.68416962
Swap Exact Token...246707282026-03-16 14:42:4736 mins ago1773672167IN
0xbFAe8E87...5f8e3058f
0 ETH0.000888180.69529358
Remove Liquidity246707012026-03-16 14:37:2342 mins ago1773671843IN
0xbFAe8E87...5f8e3058f
0 ETH0.000195850.78391964
Swap Exact Token...246706212026-03-16 14:20:5958 mins ago1773670859IN
0xbFAe8E87...5f8e3058f
0 ETH0.000849190.67322121
Swap Exact Token...246705892026-03-16 14:14:351 hr ago1773670475IN
0xbFAe8E87...5f8e3058f
0 ETH0.000792150.77383984
Swap Exact Token...246705492026-03-16 14:06:351 hr ago1773669995IN
0xbFAe8E87...5f8e3058f
0 ETH0.000458551.54823828
Swap Exact Token...246705452026-03-16 14:05:471 hr ago1773669947IN
0xbFAe8E87...5f8e3058f
0 ETH0.000365840.65676949
Swap Exact Token...246705282026-03-16 14:02:231 hr ago1773669743IN
0xbFAe8E87...5f8e3058f
0 ETH0.000626890.56230542
Swap Exact Token...246705202026-03-16 14:00:471 hr ago1773669647IN
0xbFAe8E87...5f8e3058f
0 ETH0.000235060.41547549
Swap Exact ETH F...246705002026-03-16 13:56:471 hr ago1773669407IN
0xbFAe8E87...5f8e3058f
0.010197 ETH0.000213870.47622823
Add Liquidity246704752026-03-16 13:51:471 hr ago1773669107IN
0xbFAe8E87...5f8e3058f
0 ETH0.000079260.39807238
Swap Exact Token...246704702026-03-16 13:50:351 hr ago1773669035IN
0xbFAe8E87...5f8e3058f
0 ETH0.000095420.43257622
Swap Exact Token...246703722026-03-16 13:30:591 hr ago1773667859IN
0xbFAe8E87...5f8e3058f
0 ETH0.000207240.26928573
Swap Exact Token...246703412026-03-16 13:24:351 hr ago1773667475IN
0xbFAe8E87...5f8e3058f
0 ETH0.000095890.20918178
Swap Exact Token...246703052026-03-16 13:17:232 hrs ago1773667043IN
0xbFAe8E87...5f8e3058f
0 ETH0.000084960.23722657
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Deposit246705002026-03-16 13:56:471 hr ago1773669407
0xbFAe8E87...5f8e3058f
0.010197 ETH
Deposit246703272026-03-16 13:21:471 hr ago1773667307
0xbFAe8E87...5f8e3058f
0.01088083 ETH
Swap Exact ETH F...246703272026-03-16 13:21:471 hr ago1773667307
0xbFAe8E87...5f8e3058f
0.01088083 ETH
Transfer246698052026-03-16 11:36:593 hrs ago1773661019
0xbFAe8E87...5f8e3058f
0.00470824 ETH
Transfer246698052026-03-16 11:36:593 hrs ago1773661019
0xbFAe8E87...5f8e3058f
0.00470824 ETH
Deposit246697892026-03-16 11:33:473 hrs ago1773660827
0xbFAe8E87...5f8e3058f
0.9269 ETH
Transfer246695502026-03-16 10:45:234 hrs ago1773657923
0xbFAe8E87...5f8e3058f
0.00072863 ETH
Transfer246695502026-03-16 10:45:234 hrs ago1773657923
0xbFAe8E87...5f8e3058f
0.00072863 ETH
Transfer246693222026-03-16 9:59:355 hrs ago1773655175
0xbFAe8E87...5f8e3058f
0.00644983 ETH
Transfer246693222026-03-16 9:59:355 hrs ago1773655175
0xbFAe8E87...5f8e3058f
0.00644983 ETH
Transfer246692452026-03-16 9:43:595 hrs ago1773654239
0xbFAe8E87...5f8e3058f
0.00063937 ETH
Transfer246692452026-03-16 9:43:595 hrs ago1773654239
0xbFAe8E87...5f8e3058f
0.00063937 ETH
Deposit246689992026-03-16 8:54:476 hrs ago1773651287
0xbFAe8E87...5f8e3058f
0.00249025 ETH
Transfer246686052026-03-16 7:35:237 hrs ago1773646523
0xbFAe8E87...5f8e3058f
0.00243312 ETH
Transfer246686052026-03-16 7:35:237 hrs ago1773646523
0xbFAe8E87...5f8e3058f
0.00243312 ETH
Transfer246686002026-03-16 7:34:237 hrs ago1773646463
0xbFAe8E87...5f8e3058f
0.00987021 ETH
Transfer246686002026-03-16 7:34:237 hrs ago1773646463
0xbFAe8E87...5f8e3058f
0.00987021 ETH
Deposit246682772026-03-16 6:28:598 hrs ago1773642539
0xbFAe8E87...5f8e3058f
0.051 ETH
Transfer246682712026-03-16 6:27:478 hrs ago1773642467
0xbFAe8E87...5f8e3058f
0.26408672 ETH
Transfer246682712026-03-16 6:27:478 hrs ago1773642467
0xbFAe8E87...5f8e3058f
0.26408672 ETH
Transfer246681122026-03-16 5:55:599 hrs ago1773640559
0xbFAe8E87...5f8e3058f
0.1756657 ETH
Transfer246681122026-03-16 5:55:599 hrs ago1773640559
0xbFAe8E87...5f8e3058f
0.1756657 ETH
Transfer246680102026-03-16 5:35:359 hrs ago1773639335
0xbFAe8E87...5f8e3058f
0.0883909 ETH
Transfer246680102026-03-16 5:35:359 hrs ago1773639335
0xbFAe8E87...5f8e3058f
0.0883909 ETH
Transfer246679802026-03-16 5:29:359 hrs ago1773638975
0xbFAe8E87...5f8e3058f
0.01822792 ETH
View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
RouterV2

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
// SPDX-License-Identifier: MIT OR GPL-3.0-or-later
pragma solidity 0.8.13;

import './interfaces/IAlgebraCLFactory.sol';
import './interfaces/IAlgebraPoolAPIStorage.sol';

import '@cryptoalgebra/integral-periphery/contracts/interfaces/ISwapRouter.sol';
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import './interfaces/IPair.sol';
import './interfaces/IRouterHelper.sol';
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./interfaces/IRouter.sol";
import "./interfaces/IPairFactory.sol";
import "./libraries/Math.sol";
import "./interfaces/IWETH.sol";

contract RouterV2 is ReentrancyGuard {

	using SafeERC20 for IERC20;

    struct PairSwapMetadata {
        uint decimals0;
        uint decimals1;
        uint reserve0;
        uint reserve1;
        bool stable;
        address token0;
        address token1;
        uint balanceA;
        uint balanceB;
        uint reserveA;
        uint reserveB;
        uint decimalsA;
        uint decimalsB;
    }

    struct PermitParams {
        uint value;
        uint deadline;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    struct LiquidityParams {
        address tokenA;
        address tokenB;
        bool stable;
        uint liquidity;
        uint amountAMin;
        uint amountBMin;
        address to;
        uint deadline;
    }

    address public immutable factory;
    IWETH public immutable wETH;
    uint internal constant MINIMUM_LIQUIDITY = 10**3;

    address public immutable swapRouter;
    IAlgebraPoolAPIStorage public immutable algebraPoolAPIStorage;
    address public immutable routerHelper;

    // swap event for the rebate system
    event Swap(address indexed sender,uint amount0In, uint amount0Out,address _tokenIn, address indexed to, bool stable);

    modifier ensure(uint deadline) {
        require(deadline >= block.timestamp, 'EXP');
        _;
    }

    constructor(address _factory, address _wETH, address _swapRouter, address _algebraPoolAPIStorage, address _routerHelper) {
        factory = _factory;
        wETH = IWETH(_wETH);
        swapRouter = _swapRouter;
        algebraPoolAPIStorage = IAlgebraPoolAPIStorage(_algebraPoolAPIStorage);
        routerHelper = _routerHelper;
    }

    receive() external payable {
        assert(msg.sender == address(wETH)); // only accept ETH via fallback from the WETH contract
    }

    function _k(uint x, uint y, uint decimals0, uint decimals1, bool stable) internal pure returns (uint) {
        if (stable) {
            uint _x = x * 1e18 / decimals0;
            uint _y = y * 1e18 / decimals1;
            uint _a = (_x * _y) / 1e18;
            uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18);
            return _a * _b / 1e18;  // x3y+y3x >= k
        } else {
            return x * y; // xy >= k
        }
    }

    function sortTokens(address tokenA, address tokenB) public pure returns (address token0, address token1) {
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0) && token0 != token1, 'IA');
    }

    // calculates the CREATE2 address for a pair without making any external calls
    function pairFor(address tokenA, address tokenB, bool stable) public view returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        bytes32 salt = keccak256(abi.encodePacked(token0, token1, stable));
        bytes32 initCodeHash = IPairFactory(factory).pairCodeHash();
        address pairGenerator = IPairFactory(factory).pairGenerator();
        pair = address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            hex"ff",
                            pairGenerator,
                            salt,
                            initCodeHash
                        )
                    )
                )
            )
        );
    }

    // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
    function quoteLiquidity(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
        require(amountA > 0 && reserveA > 0 && reserveB > 0, 'INL');
        amountB = amountA * reserveB / reserveA;
    }

    // fetches and sorts the reserves for a pair
    function getReserves(address tokenA, address tokenB, bool stable) public view returns (uint reserveA, uint reserveB) {
        (address token0,) = sortTokens(tokenA, tokenB);
        (uint reserve0, uint reserve1,) = IPair(pairFor(tokenA, tokenB, stable)).getReserves();
        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    }

    function getPoolAmountOut(uint amountIn, address tokenIn, address pair) public view returns (uint amount) {
        
        uint amountOut = 0;

        try IPair(pair).getAmountOut(amountIn, tokenIn) returns (uint outAmt) {
            amountOut = outAmt;
        } catch {
            amountOut = 0;
        }
        
        bool swapPossible = _swapRatio(amountIn, tokenIn, pair, amountOut);

        if(swapPossible){
            return amountOut;
        }

        return 0;
    }

    function _swapRatio(uint amountIn, address tokenIn, address pair, uint amountOut) internal view returns (bool){

        PairSwapMetadata memory pairSwapMetaData;
    
        (pairSwapMetaData.decimals0, pairSwapMetaData.decimals1, pairSwapMetaData.reserve0, 
        pairSwapMetaData.reserve1, pairSwapMetaData.stable, pairSwapMetaData.token0, pairSwapMetaData.token1)
         = IPair(pair).metadata();
        uint _balance0 = IERC20(pairSwapMetaData.token0).balanceOf(address(pair));
        uint _balance1 = IERC20(pairSwapMetaData.token1).balanceOf(address(pair));

        (pairSwapMetaData.balanceA, pairSwapMetaData.balanceB) = tokenIn == pairSwapMetaData.token0 ? (_balance0, _balance1) : (_balance1, _balance0);
        (pairSwapMetaData.reserveA, pairSwapMetaData.reserveB) = tokenIn == pairSwapMetaData.token0 ? (pairSwapMetaData.reserve0, pairSwapMetaData.reserve1) : (pairSwapMetaData.reserve1, pairSwapMetaData.reserve0);
        (pairSwapMetaData.decimalsA, pairSwapMetaData.decimalsB) = tokenIn == pairSwapMetaData.token0 ? (pairSwapMetaData.decimals0, pairSwapMetaData.decimals1) : (pairSwapMetaData.decimals1, pairSwapMetaData.decimals0);

        uint actualAmountIn = amountIn + (pairSwapMetaData.balanceA - pairSwapMetaData.reserveA);
        uint feeAmount = actualAmountIn * IPairFactory(factory).getFee(pair, pairSwapMetaData.stable) / 10000;
        pairSwapMetaData.balanceA = pairSwapMetaData.balanceA + amountIn - feeAmount;
        pairSwapMetaData.balanceB -= amountOut;

        return _k(pairSwapMetaData.balanceA, pairSwapMetaData.balanceB, pairSwapMetaData.decimalsA, pairSwapMetaData.decimalsB, pairSwapMetaData.stable) >= _k(pairSwapMetaData.reserveA, pairSwapMetaData.reserveB, pairSwapMetaData.decimalsA, pairSwapMetaData.decimalsB, pairSwapMetaData.stable);
    }

    
    function quoteAddLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        uint amountADesired,
        uint amountBDesired
    ) external view returns (uint amountA, uint amountB, uint liquidity) {
        address _pair = IPairFactory(factory).getPair(tokenA, tokenB, stable);
        (uint reserveA, uint reserveB) = (0,0);
        uint _totalSupply = 0;
        if (_pair != address(0)) {
            _totalSupply = IERC20(_pair).totalSupply();
            (reserveA, reserveB) = getReserves(tokenA, tokenB, stable);
        }
        if (_totalSupply == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
            liquidity = Math.sqrt(amountA * amountB) - MINIMUM_LIQUIDITY;
        } else {

            uint amountBOptimal = quoteLiquidity(amountADesired, reserveA, reserveB);
            if (amountBOptimal <= amountBDesired) {
                (amountA, amountB) = (amountADesired, amountBOptimal);
                liquidity = Math.min(amountA * _totalSupply / reserveA, amountB * _totalSupply / reserveB);
            } else {
                uint amountAOptimal = quoteLiquidity(amountBDesired, reserveB, reserveA);
                (amountA, amountB) = (amountAOptimal, amountBDesired);
                liquidity = Math.min(amountA * _totalSupply / reserveA, amountB * _totalSupply / reserveB);
            }
        }
    }

    function quoteRemoveLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        uint liquidity
    ) external view returns (uint amountA, uint amountB) {
        address _pair = IPairFactory(factory).getPair(tokenA, tokenB, stable);

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

        (uint reserveA, uint reserveB) = getReserves(tokenA, tokenB, stable);
        uint _totalSupply = IERC20(_pair).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,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin
    ) internal returns (uint amountA, uint amountB) {
        require(amountADesired >= amountAMin && amountBDesired >= amountBMin, "DLMA");
        // create the pair if it doesn't exist yet
        address _pair = IPairFactory(factory).getPair(tokenA, tokenB, stable);
        if (_pair == address(0)) {
            _pair = IPairFactory(factory).createPair(tokenA, tokenB, stable);
        }
        (uint reserveA, uint reserveB) = getReserves(tokenA, tokenB, stable);
        uint _totalSupply = IERC20(_pair).totalSupply();
        if (_totalSupply == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
        } else {
            uint amountBOptimal = quoteLiquidity(amountADesired, reserveA, reserveB);
            if (amountBOptimal <= amountBDesired) {
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } else {
                uint amountAOptimal = quoteLiquidity(amountBDesired, reserveB, reserveA);
                assert(amountAOptimal <= amountADesired);
                (amountA, amountB) = (amountAOptimal, amountBDesired);
            }
        }
        require(amountA >= amountAMin && amountB >= amountBMin, "IAA");
    }

    function addLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external ensure(deadline) nonReentrant returns (uint amountA, uint amountB, uint liquidity) {
        (amountA, amountB) = _addLiquidity(tokenA, tokenB, stable, amountADesired, amountBDesired, amountAMin, amountBMin);
        address pair = pairFor(tokenA, tokenB, stable);
        _safeTransferFrom(tokenA, msg.sender, pair, amountA);
        _safeTransferFrom(tokenB, msg.sender, pair, amountB);
        liquidity = IPair(pair).mint(to);

        // Additional check: ensure we received liquidity tokens
        require(liquidity > 0, 'Zero liquidity minted');
    }

    function addLiquidityETH(
        address token,
        bool stable,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable ensure(deadline) nonReentrant returns (uint amountToken, uint amountETH, uint liquidity) {
        (amountToken, amountETH) = _addLiquidity(
            token,
            address(wETH),
            stable,
            amountTokenDesired,
            msg.value,
            amountTokenMin,
            amountETHMin
        );
        address pair = pairFor(token, address(wETH), stable);
        _safeTransferFrom(token, msg.sender, pair, amountToken);
        wETH.deposit{value: amountETH}();
        assert(wETH.transfer(pair, amountETH));
        liquidity = IPair(pair).mint(to);

        // Additional check: ensure we received liquidity tokens
        require(liquidity > 0, 'Zero liquidity minted');

        // refund dust ETH, if any
        if (msg.value > amountETH) _safeTransferETH(msg.sender, msg.value - amountETH);
    }

    // **** REMOVE LIQUIDITY ****
    /// @dev Internal helper, no reentrancy guard. Public/externals must be `nonReentrant`
    ///      and delegate to this to avoid nested `nonReentrant` calls.
    function _removeLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) internal ensure(deadline) returns (uint amountA, uint amountB) {
        address pair = pairFor(tokenA, tokenB, stable);
        require(IPair(pair).transferFrom(msg.sender, pair, liquidity), "ITFM"); // send liquidity to pair
        (uint amount0, uint amount1) = IPair(pair).burn(to);
        (address token0,) = sortTokens(tokenA, tokenB);
        (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
        require(amountA >= amountAMin && amountB >= amountBMin, 'IAA');
    }

    function removeLiquidity(
        address tokenA,
        address tokenB,
        bool stable,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) public ensure(deadline) nonReentrant returns (uint amountA, uint amountB) {
        (amountA, amountB) = _removeLiquidity(
            tokenA,
            tokenB,
            stable,
            liquidity,
            amountAMin,
            amountBMin,
            to,
            deadline
        );
    }

    function removeLiquidityETH(
        address token,
        bool stable,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) public ensure(deadline) nonReentrant returns (uint amountToken, uint amountETH) {
        (amountToken, amountETH) = _removeLiquidity(
            token,
            address(wETH),
            stable,
            liquidity,
            amountTokenMin,
            amountETHMin,
            address(this),
            deadline
        );
        _safeTransfer(token, to, amountToken);
        wETH.withdraw(amountETH);
        _safeTransferETH(to, amountETH);
    }

    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        bool stable,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external nonReentrant returns (uint amountA, uint amountB) {
        address pair = pairFor(tokenA, tokenB, stable);
        
        try IPair(pair).permit(
            msg.sender, 
            address(this), 
            approveMax ? type(uint).max : liquidity, 
            deadline, 
            v, 
            r, 
            s
        ) {
            // Permit succeeded
        } catch {
            // Permit failed, check if we have sufficient allowance
            require(
                IPair(pair).allowance(msg.sender, address(this)) >= liquidity,
                "IA"
            );
        }

        (amountA, amountB) = _removeLiquidity(
            tokenA,
            tokenB,
            stable,
            liquidity,
            amountAMin,
            amountBMin,
            to,
            deadline
        );
    }

    function removeLiquidityETHWithPermit(
        address token,
        bool stable,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external nonReentrant returns (uint amountToken, uint amountETH) {
        address pair = pairFor(token, address(wETH), stable);
        
        try IPair(pair).permit(
            msg.sender, 
            address(this), 
            approveMax ? type(uint).max : liquidity, 
            deadline, 
            v, 
            r, 
            s
        ) {
            // Permit succeeded
        } catch {
            // Permit failed, check if we have sufficient allowance
            require(
                IPair(pair).allowance(msg.sender, address(this)) >= liquidity,
                "IA"
            );
        }

        (amountToken, amountETH) = _removeLiquidity(
            token,
            address(wETH),
            stable,
            liquidity,
            amountTokenMin,
            amountETHMin,
            to,
            deadline
        );
    }

    // **** SWAP ****
    // requires the initial amount to have already been sent to the first pair
    function _swap(uint[] memory amounts, IRouter.route[] memory routes, uint deadline) internal virtual {
        uint256 routesLen = routes.length;
        for (uint i = 0; i < routesLen; i++) {
            require(routes[i].receiver != address(0), "ZA");
            if(routes[i].concentrated){
                if (IERC20(routes[i].from).allowance(address(this), swapRouter) < amounts[i]) {
                    IERC20(routes[i].from).forceApprove(swapRouter, amounts[i]);
                }
                ISwapRouter.ExactInputSingleParams memory inputParams;
                inputParams = ISwapRouter.ExactInputSingleParams ({
                    tokenIn: routes[i].from,
                    tokenOut: routes[i].to,
                    deployer: IAlgebraPoolAPIStorage(algebraPoolAPIStorage).pairToDeployer(routes[i].pair),
                    recipient: routes[i].receiver,
                    deadline: deadline,
                    amountIn: amounts[i],
                    amountOutMinimum: 0,
                    limitSqrtPrice: 0
                });

                amounts[i+1] = ISwapRouter(swapRouter).exactInputSingle(inputParams);
            }
            else{
                (address token0,) = sortTokens(routes[i].from, routes[i].to);
                uint amountOut = amounts[i + 1];
                (uint amount0Out, uint amount1Out) = routes[i].from == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
                IPair(pairFor(routes[i].from, routes[i].to, routes[i].stable)).swap(
                    amount0Out, amount1Out, routes[i].receiver, new bytes(0)
                );
            }

            emit Swap(msg.sender, amounts[i], amounts[i+1], routes[i].from, routes[i].receiver, routes[i].stable); 
        }
    }

    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        IRouter.route[] calldata routes,
        address to,
        uint deadline
    ) external ensure(deadline) nonReentrant returns (uint[] memory amounts) {
        require(routes[routes.length - 1].receiver == to, 'IR'); // Invalid recipient
        (amounts,,) = IRouterHelper(routerHelper).getAmountsOut(amountIn, routes);
        require(amounts[amounts.length - 1] >= amountOutMin, 'IOA');
        if(!routes[0].concentrated)
        {
            _safeTransferFrom(
                routes[0].from, msg.sender, routes[0].pair, amounts[0]
            );
        }
        else{
             _safeTransferFrom(
                routes[0].from, msg.sender, address(this), amounts[0]
            );
            if (IERC20(routes[0].from).allowance(address(this), swapRouter) < amounts[0]) {
                IERC20(routes[0].from).forceApprove(swapRouter, amounts[0]);
            }
        }
        _swap(amounts, routes, deadline);
    }

    function swapExactETHForTokens(uint amountOutMin, IRouter.route[] calldata routes, address to, uint deadline) external payable ensure(deadline) nonReentrant returns (uint[] memory amounts) {
        require(routes[0].from == address(wETH), 'INP');
        require(routes[routes.length - 1].receiver == to, 'IR'); // Invalid recipient
        (amounts,,) = IRouterHelper(routerHelper).getAmountsOut(msg.value, routes);
        require(amounts[amounts.length - 1] >= amountOutMin, 'IOA');
        wETH.deposit{value: amounts[0]}();

        if (!routes[0].concentrated) {
            assert(wETH.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable),amounts[0]));
        } else {
            if (IERC20(address(wETH)).allowance(address(this), swapRouter) < amounts[0]) {
                IERC20(address(wETH)).forceApprove(swapRouter, amounts[0]);
            }
        }
        _swap(amounts, routes, deadline);
    }

    function swapExactTokensForETH(uint amountIn, uint amountOutMin, IRouter.route[] calldata routes, address to, uint deadline)
    external
    ensure(deadline)
    nonReentrant
    returns (uint[] memory amounts)
    {
        require(routes[routes.length - 1].to == address(wETH), 'INP');
        (amounts,,) = IRouterHelper(routerHelper).getAmountsOut(amountIn, routes);
        require(amounts[amounts.length - 1] >= amountOutMin, 'IOA');

        if(!routes[0].concentrated)
        {
            _safeTransferFrom(
                routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]
            );
        }
        else{
             _safeTransferFrom(
                routes[0].from, msg.sender, address(this), amounts[0]
            );
            if (IERC20(routes[0].from).allowance(address(this), swapRouter) < amounts[0]) {
                IERC20(routes[0].from).forceApprove(swapRouter, amounts[0]);
            }
        }
        _swap(amounts, routes, deadline);
        wETH.withdraw(amounts[amounts.length - 1]);
        _safeTransferETH(to, amounts[amounts.length - 1]);
    }

    function UNSAFE_swapExactTokensForTokens(
        uint[] memory amounts,
        IRouter.route[] calldata routes,
        address to,
        uint deadline
    ) external ensure(deadline) nonReentrant returns (uint[] memory) {
        require(routes[routes.length - 1].receiver == to, 'IR'); // Invalid recipient
        _safeTransferFrom(routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]);
        _swap(amounts, routes, deadline);
        return amounts;
    }

    function _safeTransferETH(address to, uint value) internal {
        (bool success,) = to.call{value:value}(new bytes(0));
        require(success, 'ETF');
    }

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

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

    // Experimental Extension [ETH.guru/solidly/BaseV1Router02]

    // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens)****
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        bool stable,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) public ensure(deadline) nonReentrant returns (uint amountToken, uint amountETH) {
        (amountToken, 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);
    }
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        bool stable,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external nonReentrant returns (uint amountToken, uint amountETH) {
        address pair = pairFor(token, address(wETH), stable);
        uint value = approveMax ? type(uint).max : liquidity;
        try IPair(pair).permit(
            msg.sender, 
            address(this), 
            value, 
            deadline, 
            v, 
            r, 
            s
        ) {
            // Permit succeeded
        } catch {
            // Permit failed, check if we have sufficient allowance
            require(
                IPair(pair).allowance(msg.sender, address(this)) >= liquidity,
                "IA"
            );
        }
        (amountToken, amountETH) = removeLiquidityETHSupportingFeeOnTransferTokens(
            token, stable, liquidity, amountTokenMin, amountETHMin, to, deadline
        );
    }
    
    // **** SWAP (supporting fee-on-transfer tokens) ****
    // requires the initial amount to have already been sent to the first pair
    function _swapSupportingFeeOnTransferTokens(IRouter.route[] calldata routes, address _to) internal virtual {
        for (uint i; i < routes.length; i++) {
        	(address input, address output) = (routes[i].from, routes[i].to);
            (address token0,) = sortTokens(input, output);
            IPair pair = IPair(pairFor(routes[i].from, routes[i].to, routes[i].stable));
            uint amountInput;
            uint amountOutput;
            { // scope to avoid stack too deep errors
            (uint reserve0, uint reserve1,) = pair.getReserves();
            (uint reserveInput,) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
            amountInput = IERC20(input).balanceOf(address(pair)) - reserveInput;
            (amountOutput,) = IRouterHelper(routerHelper).getAmountOutForFeeOnTransfer(amountInput, input, output);
            }
            (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
            address to = i < routes.length - 1 ? pairFor(routes[i+1].from, routes[i+1].to, routes[i+1].stable) : _to;
            pair.swap(amount0Out, amount1Out, to, new bytes(0));

            bool _stable = routes[i].stable;
            emit Swap(msg.sender,amountInput,amountOutput,input,_to,_stable);  
        }
    }

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        IRouter.route[] calldata routes,
        address to,
        uint deadline
    ) external ensure(deadline) nonReentrant {
        _safeTransferFrom(
        	routes[0].from,
        	msg.sender,
        	pairFor(routes[0].from, routes[0].to, routes[0].stable),
        	amountIn
        );
        uint routesLen = routes.length;
        uint balanceBefore = IERC20(routes[routesLen - 1].to).balanceOf(to);
        _swapSupportingFeeOnTransferTokens(routes, to);
        require(
            IERC20(routes[routesLen - 1].to).balanceOf(to) - balanceBefore >= amountOutMin,
            'IOA'
        );
    }

    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        IRouter.route[] calldata routes,
        address to,
        uint deadline
    )
        external
        payable
        ensure(deadline)
        nonReentrant
    {
        require(routes[0].from == address(wETH), 'INP');
        uint amountIn = msg.value;
        wETH.deposit{value: amountIn}();
        uint routesLen = routes.length;
        assert(wETH.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable), amountIn));
        uint balanceBefore = IERC20(routes[routesLen - 1].to).balanceOf(to);
        _swapSupportingFeeOnTransferTokens(routes, to);
        require(
            IERC20(routes[routesLen - 1].to).balanceOf(to) - balanceBefore >= amountOutMin,
            'IOA'
        );
    }

    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        IRouter.route[] calldata routes,
        address to,
        uint deadline
    )
        external
        ensure(deadline)
        nonReentrant
    {
        require(routes[routes.length - 1].to == address(wETH), 'INP');
        _safeTransferFrom(
            routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amountIn
        );
        _swapSupportingFeeOnTransferTokens(routes, address(this));
        uint amountOut = IERC20(address(wETH)).balanceOf(address(this));
        require(amountOut >= amountOutMin, 'IOA');
        wETH.withdraw(amountOut);
        _safeTransferETH(to, amountOut);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Callback for IAlgebraPoolActions#swap
/// @notice Any contract that calls IAlgebraPoolActions#swap must implement this interface
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraSwapCallback {
  /// @notice Called to `msg.sender` after executing a swap via IAlgebraPool#swap.
  /// @dev In the implementation you must pay the pool tokens owed for the swap.
  /// The caller of this method _must_ be checked to be a AlgebraPool deployed by the canonical AlgebraFactory.
  /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
  /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
  /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
  /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
  /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
  /// @param data Any data passed through by the caller via the IAlgebraPoolActions#swap call
  function algebraSwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;

import './plugin/IAlgebraPluginFactory.sol';
import './vault/IAlgebraVaultFactory.sol';

/// @title The interface for the Algebra Factory
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraFactory {
  /// @notice Emitted when a process of ownership renounce is started
  /// @param timestamp The timestamp of event
  /// @param finishTimestamp The timestamp when ownership renounce will be possible to finish
  event RenounceOwnershipStart(uint256 timestamp, uint256 finishTimestamp);

  /// @notice Emitted when a process of ownership renounce cancelled
  /// @param timestamp The timestamp of event
  event RenounceOwnershipStop(uint256 timestamp);

  /// @notice Emitted when a process of ownership renounce finished
  /// @param timestamp The timestamp of ownership renouncement
  event RenounceOwnershipFinish(uint256 timestamp);

  /// @notice Emitted when a pool is created
  /// @param token0 The first token of the pool by address sort order
  /// @param token1 The second token of the pool by address sort order
  /// @param pool The address of the created pool
  event Pool(address indexed token0, address indexed token1, address pool);

  /// @notice Emitted when a pool is created
  /// @param deployer The corresponding custom deployer contract
  /// @param token0 The first token of the pool by address sort order
  /// @param token1 The second token of the pool by address sort order
  /// @param pool The address of the created pool
  event CustomPool(address indexed deployer, address indexed token0, address indexed token1, address pool);

  /// @notice Emitted when the default community fee is changed
  /// @param newDefaultCommunityFee The new default community fee value
  event DefaultCommunityFee(uint16 newDefaultCommunityFee);

  /// @notice Emitted when the default tickspacing is changed
  /// @param newDefaultTickspacing The new default tickspacing value
  event DefaultTickspacing(int24 newDefaultTickspacing);

  /// @notice Emitted when the default fee is changed
  /// @param newDefaultFee The new default fee value
  event DefaultFee(uint16 newDefaultFee);

  /// @notice Emitted when the defaultPluginFactory address is changed
  /// @param defaultPluginFactoryAddress The new defaultPluginFactory address
  event DefaultPluginFactory(address defaultPluginFactoryAddress);

  /// @notice Emitted when the vaultFactory address is changed
  /// @param newVaultFactory The new vaultFactory address
  event VaultFactory(address newVaultFactory);

  /// @notice role that can change communityFee and tickspacing in pools
  /// @return The hash corresponding to this role
  function POOLS_ADMINISTRATOR_ROLE() external view returns (bytes32);

  /// @notice role that can call `createCustomPool` function
  /// @return The hash corresponding to this role
  function CUSTOM_POOL_DEPLOYER() external view returns (bytes32);

  /// @notice Returns `true` if `account` has been granted `role` or `account` is owner.
  /// @param role The hash corresponding to the role
  /// @param account The address for which the role is checked
  /// @return bool Whether the address has this role or the owner role or not
  function hasRoleOrOwner(bytes32 role, address account) external view returns (bool);

  /// @notice Returns the current owner of the factory
  /// @dev Can be changed by the current owner via transferOwnership(address newOwner)
  /// @return The address of the factory owner
  function owner() external view returns (address);

  /// @notice Returns the current poolDeployerAddress
  /// @return The address of the poolDeployer
  function poolDeployer() external view returns (address);

  /// @notice Returns the default community fee
  /// @return Fee which will be set at the creation of the pool
  function defaultCommunityFee() external view returns (uint16);

  /// @notice Returns the default fee
  /// @return Fee which will be set at the creation of the pool
  function defaultFee() external view returns (uint16);

  /// @notice Returns the default tickspacing
  /// @return Tickspacing which will be set at the creation of the pool
  function defaultTickspacing() external view returns (int24);

  /// @notice Return the current pluginFactory address
  /// @dev This contract is used to automatically set a plugin address in new liquidity pools
  /// @return Algebra plugin factory
  function defaultPluginFactory() external view returns (IAlgebraPluginFactory);

  /// @notice Return the current vaultFactory address
  /// @dev This contract is used to automatically set a vault address in new liquidity pools
  /// @return Algebra vault factory
  function vaultFactory() external view returns (IAlgebraVaultFactory);

  /// @notice Returns the default communityFee, tickspacing, fee and communityFeeVault for pool
  /// @return communityFee which will be set at the creation of the pool
  /// @return tickSpacing which will be set at the creation of the pool
  /// @return fee which will be set at the creation of the pool
  function defaultConfigurationForPool() external view returns (uint16 communityFee, int24 tickSpacing, uint16 fee);

  /// @notice Deterministically computes the pool address given the token0 and token1
  /// @dev The method does not check if such a pool has been created
  /// @param token0 first token
  /// @param token1 second token
  /// @return pool The contract address of the Algebra pool
  function computePoolAddress(address token0, address token1) external view returns (address pool);

  /// @notice Deterministically computes the custom pool address given the customDeployer, token0 and token1
  /// @dev The method does not check if such a pool has been created
  /// @param customDeployer the address of custom plugin deployer
  /// @param token0 first token
  /// @param token1 second token
  /// @return customPool The contract address of the Algebra pool
  function computeCustomPoolAddress(address customDeployer, address token0, address token1) external view returns (address customPool);

  /// @notice Returns the pool address for a given pair of tokens, or address 0 if it does not exist
  /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
  /// @param tokenA The contract address of either token0 or token1
  /// @param tokenB The contract address of the other token
  /// @return pool The pool address
  function poolByPair(address tokenA, address tokenB) external view returns (address pool);

  /// @notice Returns the custom pool address for a customDeployer and a given pair of tokens, or address 0 if it does not exist
  /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
  /// @param customDeployer The address of custom plugin deployer
  /// @param tokenA The contract address of either token0 or token1
  /// @param tokenB The contract address of the other token
  /// @return customPool The pool address
  function customPoolByPair(address customDeployer, address tokenA, address tokenB) external view returns (address customPool);

  /// @notice returns keccak256 of AlgebraPool init bytecode.
  /// @dev the hash value changes with any change in the pool bytecode
  /// @return Keccak256 hash of AlgebraPool contract init bytecode
  function POOL_INIT_CODE_HASH() external view returns (bytes32);

  /// @return timestamp The timestamp of the beginning of the renounceOwnership process
  function renounceOwnershipStartTimestamp() external view returns (uint256 timestamp);

  /// @notice Creates a pool for the given two tokens
  /// @param tokenA One of the two tokens in the desired pool
  /// @param tokenB The other of the two tokens in the desired pool
  /// @param data Data for plugin creation
  /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0.
  /// The call will revert if the pool already exists or the token arguments are invalid.
  /// @return pool The address of the newly created pool
  function createPool(address tokenA, address tokenB, bytes calldata data) external returns (address pool);

  /// @notice Creates a custom pool for the given two tokens using `deployer` contract
  /// @param deployer The address of plugin deployer, also used for custom pool address calculation
  /// @param creator The initiator of custom pool creation
  /// @param tokenA One of the two tokens in the desired pool
  /// @param tokenB The other of the two tokens in the desired pool
  /// @param data The additional data bytes
  /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0.
  /// The call will revert if the pool already exists or the token arguments are invalid.
  /// @return customPool The address of the newly created custom pool
  function createCustomPool(
    address deployer,
    address creator,
    address tokenA,
    address tokenB,
    bytes calldata data
  ) external returns (address customPool);

  /// @dev updates default community fee for new pools
  /// @param newDefaultCommunityFee The new community fee, _must_ be <= MAX_COMMUNITY_FEE
  function setDefaultCommunityFee(uint16 newDefaultCommunityFee) external;

  /// @dev updates default fee for new pools
  /// @param newDefaultFee The new  fee, _must_ be <= MAX_DEFAULT_FEE
  function setDefaultFee(uint16 newDefaultFee) external;

  /// @dev updates default tickspacing for new pools
  /// @param newDefaultTickspacing The new tickspacing, _must_ be <= MAX_TICK_SPACING and >= MIN_TICK_SPACING
  function setDefaultTickspacing(int24 newDefaultTickspacing) external;

  /// @dev updates pluginFactory address
  /// @param newDefaultPluginFactory address of new plugin factory
  function setDefaultPluginFactory(address newDefaultPluginFactory) external;

  /// @dev updates vaultFactory address
  /// @param newVaultFactory address of new vault factory
  function setVaultFactory(address newVaultFactory) external;

  /// @notice Starts process of renounceOwnership. After that, a certain period
  /// of time must pass before the ownership renounce can be completed.
  function startRenounceOwnership() external;

  /// @notice Stops process of renounceOwnership and removes timer.
  function stopRenounceOwnership() external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title An interface for a contract that is capable of deploying Algebra plugins
/// @dev Such a factory can be used for automatic plugin creation for new pools.
/// Also a factory be used as an entry point for custom (additional) pools creation
interface IAlgebraPluginFactory {
  /// @notice Deploys new plugin contract for pool
  /// @param pool The address of the new pool
  /// @param creator The address that initiated the pool creation
  /// @param deployer The address of new plugin deployer contract (0 if not used)
  /// @param token0 First token of the pool
  /// @param token1 Second token of the pool
  /// @return New plugin address
  function beforeCreatePoolHook(
    address pool,
    address creator,
    address deployer,
    address token0,
    address token1,
    bytes calldata data
  ) external returns (address);

  /// @notice Called after the pool is created
  /// @param plugin The plugin address
  /// @param pool The address of the new pool
  /// @param deployer The address of new plugin deployer contract (0 if not used)
  function afterCreatePoolHook(address plugin, address pool, address deployer) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the Algebra Vault Factory
/// @notice This contract can be used for automatic vaults creation
/// @dev Version: Algebra Integral
interface IAlgebraVaultFactory {
  /// @notice returns address of the community fee vault for the pool
  /// @param pool the address of Algebra Integral pool
  /// @return communityFeeVault the address of community fee vault
  function getVaultForPool(address pool) external view returns (address communityFeeVault);

  /// @notice creates the community fee vault for the pool if needed
  /// @param pool the address of Algebra Integral pool
  /// @return communityFeeVault the address of community fee vault
  function createVaultForPool(
    address pool,
    address creator,
    address deployer,
    address token0,
    address token1
  ) external returns (address communityFeeVault);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

import '@cryptoalgebra/integral-core/contracts/interfaces/callback/IAlgebraSwapCallback.sol';

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Algebra
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-periphery
interface ISwapRouter is IAlgebraSwapCallback {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        address deployer;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 limitSqrtPrice;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);

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

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);

    struct ExactOutputSingleParams {
        address tokenIn;
        address tokenOut;
        address deployer;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
        uint160 limitSqrtPrice;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another token
    /// @dev If native token is used as input, this function should be accompanied by a `refundNativeToken` in multicall to avoid potential loss of native tokens
    /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);

    struct ExactOutputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
    /// @dev If native token is used as input, this function should be accompanied by a `refundNativeToken` in multicall to avoid potential loss of native tokens
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @dev Unlike standard swaps, handles transferring from user before the actual swap.
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingleSupportingFeeOnTransferTokens(
        ExactInputSingleParams calldata params
    ) external payable returns (uint256 amountOut);
}

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

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    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");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    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");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation 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).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

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

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

// SPDX-License-Identifier: GPL-3.0-or-later
// BlackHole Foundation 2025

pragma solidity 0.8.13;

import { IAlgebraFactory } from '@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol';

interface IAlgebraCLFactory is IAlgebraFactory{
    function allPairsLength() external view returns (uint);
    function allPairs(uint256 index) external view returns (address);
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IAlgebraPoolAPIStorage {
    function pairToDeployer(address) external view returns (address);
    function setDeployerForPair(address _pair) external;
    function customDeployers(uint256 i) external view returns(address);
    function setDeployerForPair(address _pair, address _deployer) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface IPair {
    function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1);
    function claimFees() external returns (uint, uint);
    function tokens() external view returns (address, address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function transferFrom(address src, address dst, uint amount) external returns (bool);
    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function burn(address to) external returns (uint amount0, uint amount1);
    function mint(address to) external returns (uint liquidity);
    function getReserves() external view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast);
    function getAmountOut(uint, address) external view returns (uint);

    function name() external view returns(string memory);
    function symbol() external view returns(string memory);
    function totalSupply() external view returns (uint);
    function decimals() external view returns (uint8);

    function claimable0(address _user) external view returns (uint);
    function claimable1(address _user) external view returns (uint);

    function isStable() external view returns(bool);
    function allowance(address owner, address spender) external view returns (uint);
}

// SPDX-License-Identifier: MIT OR GPL-3.0-or-later
pragma solidity 0.8.13;

interface IPairFactory {
    function allPairsLength() external view returns (uint);
    function isPair(address pair) external view returns (bool);
    function allPairs(uint index) external view returns (address);
    function pairCodeHash() external view returns (bytes32);
    function pairGenerator() external view returns (address);
    function getPair(address tokenA, address token, bool stable) external view returns (address);
    function createPair(address tokenA, address tokenB, bool stable) external returns (address pair);
    function getFee(address _pairAddress, bool _stable) external view returns(uint256);
    function dibs() external view returns (address);
    function getReferralFee(address _pairAddress) external view returns (uint256);
    function isPaused() external view returns (bool);
}

// SPDX-License-Identifier: MIT OR GPL-3.0-or-later
pragma solidity 0.8.13;

interface IRouter {
    struct route {
        address pair;
        address from;
        address to;
        bool stable;
        bool concentrated;
        address receiver;
    }
    function pairFor(address tokenA, address tokenB, bool stable) external view returns (address pair);
    function swapExactTokensForTokens(uint amountIn,uint amountOutMin,route[] calldata routes,address to,uint deadline) external returns (uint[] memory amounts);
    function addLiquidity(address tokenA,address tokenB,bool stable,uint amountADesired,uint amountBDesired,uint amountAMin,uint amountBMin,address to,uint deadline) external returns (uint amountA, uint amountB, uint liquidity);
    function getReserves(address tokenA, address tokenB, bool stable) external view returns (uint reserveA, uint reserveB);
    function getPoolAmountOut(uint amountIn, address tokenIn, address pair) external view returns (uint amount);
}

// SPDX-License-Identifier: MIT OR GPL-3.0-or-later
pragma solidity 0.8.13;

import "./IRouter.sol";

interface IRouterHelper {
    function getAmountsOut(uint amountIn, IRouter.route[] memory routes) external returns (uint[] memory amounts, uint[] memory priceBeforeSwap, uint[] memory priceAfterSwap);
    function getAmountOut(uint amountIn, address tokenIn, address tokenOut) external view returns (uint amount, bool stable);
    function getAmountOutForFeeOnTransfer(uint amountIn, address tokenIn, address tokenOut) external view returns (uint amount, bool stable);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface IWETH {
    function deposit() external payable;
    function transfer(address to, uint256 value) external returns (bool);
    function withdraw(uint256) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

library Math {
    function max(uint a, uint b) internal pure returns (uint) {
        return a >= b ? a : b;
    }
    function min(uint a, uint b) internal pure returns (uint) {
        return a < b ? a : b;
    }
    function sqrt(uint y) internal pure returns (uint z) {
        if (y > 3) {
            z = y;
            uint x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }
    function cbrt(uint256 n) internal pure returns (uint256) { unchecked {
        uint256 x = 0;
        for (uint256 y = 1 << 255; y > 0; y >>= 3) {
            x <<= 1;
            uint256 z = 3 * x * (x + 1) + 1;
            if (n / y >= z) {
                n -= y * z;
                x += 1;
            }
        }
        return x;
    }}
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_wETH","type":"address"},{"internalType":"address","name":"_swapRouter","type":"address"},{"internalType":"address","name":"_algebraPoolAPIStorage","type":"address"},{"internalType":"address","name":"_routerHelper","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount0Out","type":"uint256"},{"indexed":false,"internalType":"address","name":"_tokenIn","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"bool","name":"stable","type":"bool"}],"name":"Swap","type":"event"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"components":[{"internalType":"address","name":"pair","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"bool","name":"concentrated","type":"bool"},{"internalType":"address","name":"receiver","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":"algebraPoolAPIStorage","outputs":[{"internalType":"contract IAlgebraPoolAPIStorage","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"pair","type":"address"}],"name":"getPoolAmountOut","outputs":[{"internalType":"uint256","name":"amount","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"}],"name":"getReserves","outputs":[{"internalType":"uint256","name":"reserveA","type":"uint256"},{"internalType":"uint256","name":"reserveB","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"}],"name":"pairFor","outputs":[{"internalType":"address","name":"pair","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":"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":"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":"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":"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"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermit","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"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermitSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","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":"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"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityWithPermit","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"routerHelper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":"pair","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"bool","name":"concentrated","type":"bool"},{"internalType":"address","name":"receiver","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":"pair","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"bool","name":"concentrated","type":"bool"},{"internalType":"address","name":"receiver","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":"pair","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"bool","name":"concentrated","type":"bool"},{"internalType":"address","name":"receiver","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":"pair","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"bool","name":"concentrated","type":"bool"},{"internalType":"address","name":"receiver","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":"pair","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"bool","name":"concentrated","type":"bool"},{"internalType":"address","name":"receiver","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":"pair","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"bool","name":"concentrated","type":"bool"},{"internalType":"address","name":"receiver","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":"swapRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wETH","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

6101206040523480156200001257600080fd5b5060405162005c1638038062005c16833981016040819052620000359162000081565b60016000556001600160a01b0394851660805292841660a05290831660c052821660e0521661010052620000f1565b80516001600160a01b03811681146200007c57600080fd5b919050565b600080600080600060a086880312156200009a57600080fd5b620000a58662000064565b9450620000b56020870162000064565b9350620000c56040870162000064565b9250620000d56060870162000064565b9150620000e56080870162000064565b90509295509295909350565b60805160a05160c05160e051610100516159a562000271600039600081816104e6015281816106c401528181611297015281816129f60152614323015260008181610356015261351c01526000818161045e015281816108d70152818161095f015281816115c10152818161164901528181612ba901528181612c310152818161337e0152818161340601526136ac0152600081816101950152818161057a01528181610bc001528181610cf1015281816111a3015281816113660152818161142301528181611591015281816116850152818161199a01528181611a8201528181611b2d01528181611f2a01528181611f6001528181611f950152818161202a015281816121bc0152818161223c015281816122b801528181612532015281816125820152818161261a0152818161296501528181612cb70152612dbd01526000818161049201528181610a4d01528181610da301528181610e2901528181611bb701528181613ab101528181613b520152613fec01526159a56000f3fe6080604052600436106101855760003560e01c806398a0fb3c116100d1578063d7b0e0a51161008a578063e9e7941d11610064578063e9e7941d14610528578063f0aff68d14610548578063f242862114610568578063fe411f141461059c57600080fd5b8063d7b0e0a5146104b4578063dcba74f2146104d4578063e2d9d4dc1461050857600080fd5b806398a0fb3c146103e6578063a32b1fcd14610406578063b7e0d4c014610426578063bc641f2714610439578063c31c9c071461044c578063c45a01551461048057600080fd5b80635a47ddc31161013e5780637f64ba03116101185780637f64ba031461034457806383509d0a1461037857806383b9a7d31461039857806397607530146103c657600080fd5b80635a47ddc3146102d65780635e60dab5146103115780636ba165431461033157600080fd5b80630dede6c4146101c9578063204b5c0a146102035780634386e63c14610230578063448725b4146102505780634c1ee03e14610270578063544caa56146102a857600080fd5b366101c457336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146101c2576101c2614bd8565b005b600080fd5b3480156101d557600080fd5b506101e96101e4366004614c2a565b6105bc565b604080519283526020830191909152015b60405180910390f35b34801561020f57600080fd5b5061022361021e366004614cf4565b61061e565b6040516101fa9190614d67565b34801561023c57600080fd5b506101e961024b366004614dab565b610a46565b34801561025c57600080fd5b506101e961026b366004614e0d565b610bad565b34801561027c57600080fd5b5061029061028b366004614eb7565b610d3c565b6040516001600160a01b0390911681526020016101fa565b3480156102b457600080fd5b506102c86102c3366004614f02565b610f0c565b6040516101fa929190614f3b565b3480156102e257600080fd5b506102f66102f1366004614f55565b610f84565b604080519384526020840192909252908201526060016101fa565b34801561031d57600080fd5b506101e961032c366004614eb7565b6110be565b61022361033f366004614fe4565b611176565b34801561035057600080fd5b506102907f000000000000000000000000000000000000000000000000000000000000000081565b34801561038457600080fd5b506102236103933660046150b6565b611722565b3480156103a457600080fd5b506103b86103b3366004615182565b6118be565b6040519081526020016101fa565b3480156103d257600080fd5b506101c26103e1366004614cf4565b611967565b3480156103f257600080fd5b506102f66104013660046151b9565b611baf565b34801561041257600080fd5b506101e9610421366004615214565b611da6565b6102f66104343660046152d0565b611ef6565b6101c2610447366004614fe4565b612191565b34801561045857600080fd5b506102907f000000000000000000000000000000000000000000000000000000000000000081565b34801561048c57600080fd5b506102907f000000000000000000000000000000000000000000000000000000000000000081565b3480156104c057600080fd5b506101e96104cf3660046152d0565b612500565b3480156104e057600080fd5b506102907f000000000000000000000000000000000000000000000000000000000000000081565b34801561051457600080fd5b506101e9610523366004614e0d565b612607565b34801561053457600080fd5b506101c2610543366004614cf4565b61276b565b34801561055457600080fd5b50610223610563366004614cf4565b612930565b34801561057457600080fd5b506102907f000000000000000000000000000000000000000000000000000000000000000081565b3480156105a857600080fd5b506101e96105b73660046152d0565b612d8b565b60008082428110156105e95760405162461bcd60e51b81526004016105e090615342565b60405180910390fd5b6105f1612e61565b6106018b8b8b8b8b8b8b8b612eba565b90935091506106106001600055565b509850989650505050505050565b606081428110156106415760405162461bcd60e51b81526004016105e090615342565b610649612e61565b6001600160a01b0384168686610660600182615375565b81811061066f5761066f61538c565b905060c0020160a001602081019061068791906153a2565b6001600160a01b0316146106ad5760405162461bcd60e51b81526004016105e0906153bf565b60405163161681db60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632c2d03b6906106fd908b908a908a906004016153db565b6000604051808303816000875af115801561071c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107449190810190615500565b9050508092505086826001845161075b9190615375565b8151811061076b5761076b61538c565b602002602001015110156107915760405162461bcd60e51b81526004016105e090615588565b858560008181106107a4576107a461538c565b905060c0020160800160208101906107bc91906155a5565b61083d57610838868660008181106107d6576107d661538c565b905060c0020160200160208101906107ee91906153a2565b33888860008181106108025761080261538c565b61081892602060c09092020190810191506153a2565b8560008151811061082b5761082b61538c565b60200260200101516130a0565b6109d4565b610880868660008181106108535761085361538c565b905060c00201602001602081019061086b91906153a2565b33308560008151811061082b5761082b61538c565b816000815181106108935761089361538c565b6020026020010151868660008181106108ae576108ae61538c565b905060c0020160200160208101906108c691906153a2565b6001600160a01b031663dd62ed3e307f00000000000000000000000000000000000000000000000000000000000000006040518363ffffffff1660e01b8152600401610913929190614f3b565b602060405180830381865afa158015610930573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061095491906155c2565b10156109d4576109d47f0000000000000000000000000000000000000000000000000000000000000000836000815181106109915761099161538c565b6020026020010151888860008181106109ac576109ac61538c565b905060c0020160200160208101906109c491906153a2565b6001600160a01b031691906131e7565b610a31828787808060200260200160405190810160405280939291908181526020016000905b82821015610a2657610a1760c083028601368190038101906155db565b815260200190600101906109fa565b5050505050856132a1565b610a3b6001600055565b509695505050505050565b60008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636801cc308888886040518463ffffffff1660e01b8152600401610a9b93929190615686565b602060405180830381865afa158015610ab8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610adc91906156aa565b90506001600160a01b038116610af9576000809250925050610ba4565b600080610b078989896110be565b915091506000836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6f91906155c2565b905080610b7c84896156c7565b610b8691906156e6565b955080610b9383896156c7565b610b9d91906156e6565b9450505050505b94509492505050565b600080610bb8612e61565b6000610be58e7f00000000000000000000000000000000000000000000000000000000000000008f610d3c565b9050806001600160a01b031663d505accf33308a610c03578f610c07565b6000195b8c8b8b8b6040518863ffffffff1660e01b8152600401610c2d9796959493929190615708565b600060405180830381600087803b158015610c4757600080fd5b505af1925050508015610c58575060015b610ceb57604051636eb1769f60e11b81528c906001600160a01b0383169063dd62ed3e90610c8c9033903090600401614f3b565b602060405180830381865afa158015610ca9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccd91906155c2565b1015610ceb5760405162461bcd60e51b81526004016105e090615749565b610d1b8e7f00000000000000000000000000000000000000000000000000000000000000008f8f8f8f8f8f612eba565b9093509150610d2c90506001600055565b9b509b9950505050505050505050565b6000806000610d4b8686610f0c565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015286151560f81b6048820152919350915060009060490160405160208183030381529060405280519060200120905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639aab92486040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2391906155c2565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f65bd0b76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea991906156aa565b6040516001600160f81b031960208201526bffffffffffffffffffffffff19606083901b166021820152603581018590526055810184905290915060750160408051601f1981840301815291905280516020909101209998505050505050505050565b600080826001600160a01b0316846001600160a01b031610610f2f578284610f32565b83835b90925090506001600160a01b03821615801590610f615750806001600160a01b0316826001600160a01b031614155b610f7d5760405162461bcd60e51b81526004016105e090615749565b9250929050565b60008060008342811015610faa5760405162461bcd60e51b81526004016105e090615342565b610fb2612e61565b610fc18d8d8d8d8d8d8d613a4f565b90945092506000610fd38e8e8e610d3c565b9050610fe18e3383886130a0565b610fed8d3383876130a0565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af1158015611035573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105991906155c2565b9250600083116110a35760405162461bcd60e51b815260206004820152601560248201527416995c9bc81b1a5c5d5a591a5d1e481b5a5b9d1959605a1b60448201526064016105e0565b506110ae6001600055565b5099509950999650505050505050565b60008060006110cd8686610f0c565b5090506000806110de888888610d3c565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561111b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113f9190615765565b5091509150826001600160a01b0316886001600160a01b031614611164578082611167565b81815b90999098509650505050505050565b606081428110156111995760405162461bcd60e51b81526004016105e090615342565b6111a1612e61565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316868660008181106111de576111de61538c565b905060c0020160200160208101906111f691906153a2565b6001600160a01b03161461121c5760405162461bcd60e51b81526004016105e090615793565b6001600160a01b0384168686611233600182615375565b8181106112425761124261538c565b905060c0020160a001602081019061125a91906153a2565b6001600160a01b0316146112805760405162461bcd60e51b81526004016105e0906153bf565b60405163161681db60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632c2d03b6906112d09034908a908a906004016153db565b6000604051808303816000875af11580156112ef573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113179190810190615500565b9050508092505086826001845161132e9190615375565b8151811061133e5761133e61538c565b602002602001015110156113645760405162461bcd60e51b81526004016105e090615588565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0836000815181106113a6576113a661538c565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156113d957600080fd5b505af11580156113ed573d6000803e3d6000fd5b5050505050858560008181106114055761140561538c565b905060c00201608001602081019061141d91906155a5565b611574577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb6114d4888860008181106114665761146661538c565b905060c00201602001602081019061147e91906153a2565b898960008181106114915761149161538c565b905060c0020160400160208101906114a991906153a2565b8a8a60008181106114bc576114bc61538c565b905060c00201606001602081019061028b91906155a5565b846000815181106114e7576114e761538c565b60200260200101516040518363ffffffff1660e01b81526004016115209291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af115801561153f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061156391906157b0565b61156f5761156f614bd8565b6116bc565b816000815181106115875761158761538c565b60200260200101517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663dd62ed3e307f00000000000000000000000000000000000000000000000000000000000000006040518363ffffffff1660e01b81526004016115fd929190614f3b565b602060405180830381865afa15801561161a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061163e91906155c2565b10156116bc576116bc7f00000000000000000000000000000000000000000000000000000000000000008360008151811061167b5761167b61538c565b60200260200101517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166131e79092919063ffffffff16565b61170e828787808060200260200160405190810160405280939291908181526020016000905b82821015610a26576116ff60c083028601368190038101906155db565b815260200190600101906116e2565b6117186001600055565b5095945050505050565b606081428110156117455760405162461bcd60e51b81526004016105e090615342565b61174d612e61565b6001600160a01b0384168686611764600182615375565b8181106117735761177361538c565b905060c0020160a001602081019061178b91906153a2565b6001600160a01b0316146117b15760405162461bcd60e51b81526004016105e0906153bf565b61185f868660008181106117c7576117c761538c565b905060c0020160200160208101906117df91906153a2565b3361184c898960008181106117f6576117f661538c565b905060c00201602001602081019061180e91906153a2565b8a8a60008181106118215761182161538c565b905060c00201604001602081019061183991906153a2565b8b8b60008181106114bc576114bc61538c565b8a60008151811061082b5761082b61538c565b6118b1878787808060200260200160405190810160405280939291908181526020016000905b82821015610a26576118a260c083028601368190038101906155db565b81526020019060010190611885565b8691506117186001600055565b6040516378a051ad60e11b8152600481018490526001600160a01b038381166024830152600091829184169063f140a35a90604401602060405180830381865afa92505050801561192c575060408051601f3d908101601f19168201909252611929918101906155c2565b60015b6119385750600061193b565b90505b600061194986868685613cf6565b9050801561195957509050611960565b6000925050505b9392505050565b80428110156119885760405162461bcd60e51b81526004016105e090615342565b611990612e61565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001685856119c7600182615375565b8181106119d6576119d661538c565b905060c0020160400160208101906119ee91906153a2565b6001600160a01b031614611a145760405162461bcd60e51b81526004016105e090615793565b611a5f85856000818110611a2a57611a2a61538c565b905060c002016020016020810190611a4291906153a2565b33611a59888860008181106114665761146661538c565b8a6130a0565b611a6a858530614104565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611ad1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af591906155c2565b905086811015611b175760405162461bcd60e51b81526004016105e090615588565b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611b7957600080fd5b505af1158015611b8d573d6000803e3d6000fd5b50505050611b9b848261457c565b50611ba66001600055565b50505050505050565b6000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636801cc308a8a8a6040518463ffffffff1660e01b8152600401611c0593929190615686565b602060405180830381865afa158015611c22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4691906156aa565b9050600080806001600160a01b03841615611cd057836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cbd91906155c2565b9050611cca8c8c8c6110be565b90935091505b80600003611d04578896508795506103e8611cf3611cee888a6156c7565b614624565b611cfd9190615375565b9450611d97565b6000611d118a8585614694565b9050888111611d5957899750955085611d5284611d2e848b6156c7565b611d3891906156e6565b84611d43858b6156c7565b611d4d91906156e6565b6146f8565b9550611d95565b6000611d668a8587614694565b9850899750889050611d9185611d7c85846156c7565b611d8691906156e6565b85611d43868c6156c7565b9650505b505b50505050955095509592505050565b600080611db1612e61565b6000611dbe8f8f8f610d3c565b9050806001600160a01b031663d505accf33308a611ddc578f611de0565b6000195b8c8b8b8b6040518863ffffffff1660e01b8152600401611e069796959493929190615708565b600060405180830381600087803b158015611e2057600080fd5b505af1925050508015611e31575060015b611ec457604051636eb1769f60e11b81528c906001600160a01b0383169063dd62ed3e90611e659033903090600401614f3b565b602060405180830381865afa158015611e82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea691906155c2565b1015611ec45760405162461bcd60e51b81526004016105e090615749565b611ed48f8f8f8f8f8f8f8f612eba565b9093509150611ee590506001600055565b9c509c9a5050505050505050505050565b60008060008342811015611f1c5760405162461bcd60e51b81526004016105e090615342565b611f24612e61565b611f538b7f00000000000000000000000000000000000000000000000000000000000000008c8c348d8d613a4f565b90945092506000611f858c7f00000000000000000000000000000000000000000000000000000000000000008d610d3c565b9050611f938c3383886130a0565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b158015611fee57600080fd5b505af1158015612002573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b038581166004830152602482018990527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb925060440190506020604051808303816000875af1158015612077573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061209b91906157b0565b6120a7576120a7614bd8565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af11580156120ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061211391906155c2565b92506000831161215d5760405162461bcd60e51b815260206004820152601560248201527416995c9bc81b1a5c5d5a591a5d1e481b5a5b9d1959605a1b60448201526064016105e0565b8334111561217857612178336121738634615375565b61457c565b506121836001600055565b509750975097945050505050565b80428110156121b25760405162461bcd60e51b81526004016105e090615342565b6121ba612e61565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316858560008181106121f7576121f761538c565b905060c00201602001602081019061220f91906153a2565b6001600160a01b0316146122355760405162461bcd60e51b81526004016105e090615793565b60003490507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561229557600080fd5b505af11580156122a9573d6000803e3d6000fd5b50889350506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016915063a9059cbb90506122f589846000816117f6576117f661538c565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018590526044016020604051808303816000875af1158015612342573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236691906157b0565b61237257612372614bd8565b60008787612381600185615375565b8181106123905761239061538c565b905060c0020160400160208101906123a891906153a2565b6040516370a0823160e01b81526001600160a01b03888116600483015291909116906370a0823190602401602060405180830381865afa1580156123f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061241491906155c2565b9050612421888888614104565b88818989612430600187615375565b81811061243f5761243f61538c565b905060c00201604001602081019061245791906153a2565b6040516370a0823160e01b81526001600160a01b038a8116600483015291909116906370a0823190602401602060405180830381865afa15801561249f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124c391906155c2565b6124cd9190615375565b10156124eb5760405162461bcd60e51b81526004016105e090615588565b5050506124f86001600055565b505050505050565b60008082428110156125245760405162461bcd60e51b81526004016105e090615342565b61252c612e61565b61255c8a7f00000000000000000000000000000000000000000000000000000000000000008b8b8b8b308b612eba565b909350915061256c8a868561470e565b604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156125ce57600080fd5b505af11580156125e2573d6000803e3d6000fd5b505050506125f0858361457c565b6125fa6001600055565b5097509795505050505050565b600080612612612e61565b600061263f8e7f00000000000000000000000000000000000000000000000000000000000000008f610d3c565b905060008761264e578c612652565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf9061268d903390309086908f908e908e908e90600401615708565b600060405180830381600087803b1580156126a757600080fd5b505af19250505080156126b8575060015b61274b57604051636eb1769f60e11b81528d906001600160a01b0384169063dd62ed3e906126ec9033903090600401614f3b565b602060405180830381865afa158015612709573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061272d91906155c2565b101561274b5760405162461bcd60e51b81526004016105e090615749565b61275a8f8f8f8f8f8f8f612d8b565b9094509250610d2c91506130999050565b804281101561278c5760405162461bcd60e51b81526004016105e090615342565b612794612e61565b6127aa85856000818110611a2a57611a2a61538c565b83600086826127ba600182615375565b8181106127c9576127c961538c565b905060c0020160400160208101906127e191906153a2565b6040516370a0823160e01b81526001600160a01b03878116600483015291909116906370a0823190602401602060405180830381865afa158015612829573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061284d91906155c2565b905061285a878787614104565b87818888612869600187615375565b8181106128785761287861538c565b905060c00201604001602081019061289091906153a2565b6040516370a0823160e01b81526001600160a01b03898116600483015291909116906370a0823190602401602060405180830381865afa1580156128d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128fc91906155c2565b6129069190615375565b10156129245760405162461bcd60e51b81526004016105e090615588565b5050611ba66001600055565b606081428110156129535760405162461bcd60e51b81526004016105e090615342565b61295b612e61565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168686612992600182615375565b8181106129a1576129a161538c565b905060c0020160400160208101906129b991906153a2565b6001600160a01b0316146129df5760405162461bcd60e51b81526004016105e090615793565b60405163161681db60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632c2d03b690612a2f908b908a908a906004016153db565b6000604051808303816000875af1158015612a4e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612a769190810190615500565b90505080925050868260018451612a8d9190615375565b81518110612a9d57612a9d61538c565b60200260200101511015612ac35760405162461bcd60e51b81526004016105e090615588565b85856000818110612ad657612ad661538c565b905060c002016080016020810190612aee91906155a5565b612b3c57612b3786866000818110612b0857612b0861538c565b905060c002016020016020810190612b2091906153a2565b33610818898960008181106117f6576117f661538c565b612c63565b612b52868660008181106108535761085361538c565b81600081518110612b6557612b6561538c565b602002602001015186866000818110612b8057612b8061538c565b905060c002016020016020810190612b9891906153a2565b6001600160a01b031663dd62ed3e307f00000000000000000000000000000000000000000000000000000000000000006040518363ffffffff1660e01b8152600401612be5929190614f3b565b602060405180830381865afa158015612c02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c2691906155c2565b1015612c6357612c637f0000000000000000000000000000000000000000000000000000000000000000836000815181106109915761099161538c565b612cb5828787808060200260200160405190810160405280939291908181526020016000905b82821015610a2657612ca660c083028601368190038101906155db565b81526020019060010190612c89565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632e1a7d4d8360018551612cf39190615375565b81518110612d0357612d0361538c565b60200260200101516040518263ffffffff1660e01b8152600401612d2991815260200190565b600060405180830381600087803b158015612d4357600080fd5b505af1158015612d57573d6000803e3d6000fd5b50505050610a31848360018551612d6e9190615375565b81518110612d7e57612d7e61538c565b602002602001015161457c565b6000808242811015612daf5760405162461bcd60e51b81526004016105e090615342565b612db7612e61565b612de78a7f00000000000000000000000000000000000000000000000000000000000000008b8b8b8b308b612eba565b6040516370a0823160e01b8152306004820152919450925061256c908b9087906001600160a01b038316906370a0823190602401602060405180830381865afa158015612e38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5c91906155c2565b61470e565b600260005403612eb35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105e0565b6002600055565b6000808242811015612ede5760405162461bcd60e51b81526004016105e090615342565b6000612eeb8c8c8c610d3c565b6040516323b872dd60e01b81523360048201526001600160a01b03821660248201819052604482018c90529192506323b872dd906064016020604051808303816000875af1158015612f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f6591906157b0565b612f9a5760405162461bcd60e51b81526004016105e0906020808252600490820152634954464d60e01b604082015260600190565b60405163226bf2d160e21b81526001600160a01b03878116600483015260009182918416906389afcb449060240160408051808303816000875af1158015612fe6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061300a91906157cd565b91509150600061301a8f8f610f0c565b509050806001600160a01b03168f6001600160a01b03161461303d578183613040565b82825b90975095508a87108015906130555750898610155b6130875760405162461bcd60e51b815260206004820152600360248201526249414160e81b60448201526064016105e0565b50505050509850989650505050505050565b6001600055565b6000846001600160a01b03163b116130e45760405162461bcd60e51b815260206004820152600760248201526621a7a222a622a760c91b60448201526064016105e0565b6040516001600160a01b038481166024830152838116604483015260648201839052600091829187169060840160408051601f198184030181529181526020820180516001600160e01b03166323b872dd60e01b17905251613146919061581d565b6000604051808303816000865af19150503d8060008114613183576040519150601f19603f3d011682016040523d82523d6000602084013e613188565b606091505b50915091508180156131b25750805115806131b25750808060200190518101906131b291906157b0565b6124f85760405162461bcd60e51b81526004016105e09060208082526004908201526324a9aa2360e11b604082015260600190565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613238848261484a565b61329b57604080516001600160a01b038516602482015260006044808301919091528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526132919085906148f1565b61329b84826148f1565b50505050565b815160005b81811015613a485760006001600160a01b03168482815181106132cb576132cb61538c565b602002602001015160a001516001600160a01b0316036133125760405162461bcd60e51b81526020600482015260026024820152615a4160f01b60448201526064016105e0565b8381815181106133245761332461538c565b6020026020010151608001511561374a578481815181106133475761334761538c565b60200260200101518482815181106133615761336161538c565b6020026020010151602001516001600160a01b031663dd62ed3e307f00000000000000000000000000000000000000000000000000000000000000006040518363ffffffff1660e01b81526004016133ba929190614f3b565b602060405180830381865afa1580156133d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133fb91906155c2565b1015613475576134757f00000000000000000000000000000000000000000000000000000000000000008683815181106134375761343761538c565b60200260200101518684815181106134515761345161538c565b6020026020010151602001516001600160a01b03166131e79092919063ffffffff16565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101919091526040518061010001604052808684815181106134d4576134d461538c565b6020026020010151602001516001600160a01b031681526020018684815181106135005761350061538c565b6020026020010151604001516001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638992848788868151811061355b5761355b61538c565b6020908102919091010151516040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156135ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135cf91906156aa565b6001600160a01b031681526020018684815181106135ef576135ef61538c565b602002602001015160a001516001600160a01b031681526020018581526020018784815181106136215761362161538c565b602090810291909101810151825260008282018190526040928301528151630b3ce3c960e11b815283516001600160a01b039081166004830152918401518216602482015291830151811660448301526060830151811660648301526080830151608483015260a083015160a483015260c083015160c483015260e0830151811660e48301529192507f000000000000000000000000000000000000000000000000000000000000000090911690631679c79290610104016020604051808303816000875af11580156136f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061371c91906155c2565b86613728846001615839565b815181106137385761373861538c565b6020026020010181815250505061392f565b60006137908583815181106137615761376161538c565b60200260200101516020015186848151811061377f5761377f61538c565b602002602001015160400151610f0c565b5090506000866137a1846001615839565b815181106137b1576137b161538c565b60200260200101519050600080836001600160a01b03168886815181106137da576137da61538c565b6020026020010151602001516001600160a01b0316146137fc57826000613800565b6000835b915091506138668886815181106138195761381961538c565b6020026020010151602001518987815181106138375761383761538c565b6020026020010151604001518a88815181106138555761385561538c565b602002602001015160600151610d3c565b6001600160a01b031663022c0d9f83838b89815181106138885761388861538c565b602002602001015160a00151600067ffffffffffffffff8111156138ae576138ae61504b565b6040519080825280601f01601f1916602001820160405280156138d8576020820181803683370190505b506040518563ffffffff1660e01b81526004016138f8949392919061587d565b600060405180830381600087803b15801561391257600080fd5b505af1158015613926573d6000803e3d6000fd5b50505050505050505b8381815181106139415761394161538c565b602002602001015160a001516001600160a01b0316336001600160a01b03167fe7857fba7d66e2e29213fda7a0394efbaa80d435dfc7b4c1f147d78c2f9caa628784815181106139935761399361538c565b6020026020010151888560016139a99190615839565b815181106139b9576139b961538c565b60200260200101518886815181106139d3576139d361538c565b6020026020010151602001518987815181106139f1576139f161538c565b602002602001015160600151604051613a2e949392919093845260208401929092526001600160a01b031660408301521515606082015260800190565b60405180910390a380613a40816158aa565b9150506132a6565b5050505050565b600080838610158015613a625750828510155b613a975760405162461bcd60e51b81526004016105e090602080825260049082015263444c4d4160e01b604082015260600190565b6040516306801cc360e41b81526000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636801cc3090613aea908d908d908d90600401615686565b602060405180830381865afa158015613b07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b2b91906156aa565b90506001600160a01b038116613bd1576040516320b7f73960e21b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906382dfdce490613b8b908d908d908d90600401615686565b6020604051808303816000875af1158015613baa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bce91906156aa565b90505b600080613bdf8c8c8c6110be565b915091506000836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c4791906155c2565b905080600003613c5c57899550889450613ca4565b6000613c698b8585614694565b9050898111613c7d578a9650945084613ca2565b6000613c8a8b8587614694565b90508b811115613c9c57613c9c614bd8565b96508995505b505b878610158015613cb45750868510155b613ce65760405162461bcd60e51b815260206004820152600360248201526249414160e81b60448201526064016105e0565b5050505097509795505050505050565b6000613d77604051806101a001604052806000815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b836001600160a01b031663392f37e96040518163ffffffff1660e01b815260040160e060405180830381865afa158015613db5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dd991906158c3565b6001600160a01b0390811660c089015290811660a08801819052911515608088015260608701929092526040808701939093526020860193909352928452516370a0823160e01b815291861660048301526000916370a0823190602401602060405180830381865afa158015613e53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e7791906155c2565b60c08301516040516370a0823160e01b81526001600160a01b038881166004830152929350600092909116906370a0823190602401602060405180830381865afa158015613ec9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613eed91906155c2565b90508260a001516001600160a01b0316876001600160a01b031614613f13578082613f16565b81815b61010085015260e084015260a08301516001600160a01b03888116911614613f475782606001518360400151613f52565b826040015183606001515b61014085015261012084015260a08301516001600160a01b03888116911614613f815760208301518351613f89565b825160208401515b61018085015261016084015261012083015160e0840151600091613fac91615375565b613fb6908a615839565b608085015160405163cc56b2c560e01b81526001600160a01b038a811660048301529115156024820152919250600091612710917f0000000000000000000000000000000000000000000000000000000000000000169063cc56b2c590604401602060405180830381865afa158015614033573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061405791906155c2565b61406190846156c7565b61406b91906156e6565b9050808a8660e0015161407e9190615839565b6140889190615375565b60e0860152610100850180518891906140a2908390615375565b915081815250506140ce85610120015186610140015187610160015188610180015189608001516149c6565b6140f28660e001518761010001518861016001518961018001518a608001516149c6565b1015955050505050505b949350505050565b60005b8281101561329b576000808585848181106141245761412461538c565b905060c00201602001602081019061413c91906153a2565b86868581811061414e5761414e61538c565b905060c00201604001602081019061416691906153a2565b9150915060006141768383610f0c565b50905060006141e48888878181106141905761419061538c565b905060c0020160200160208101906141a891906153a2565b8989888181106141ba576141ba61538c565b905060c0020160400160208101906141d291906153a2565b8a8a898181106114bc576114bc61538c565b9050600080600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561422a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061424e9190615765565b50915091506000866001600160a01b0316896001600160a01b031614614275578183614278565b82825b506040516370a0823160e01b81526001600160a01b03888116600483015291925082918b16906370a0823190602401602060405180830381865afa1580156142c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142e891906155c2565b6142f29190615375565b604051631aa6a63f60e01b8152600481018290526001600160a01b038b811660248301528a811660448301529196507f000000000000000000000000000000000000000000000000000000000000000090911690631aa6a63f906064016040805180830381865afa15801561436b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061438f9190615937565b5080945050505050600080856001600160a01b0316886001600160a01b0316146143bb578260006143bf565b6000835b909250905060006143d160018d615375565b8a106143dd578a614464565b6144648d8d6143ed8d6001615839565b8181106143fc576143fc61538c565b905060c00201602001602081019061441491906153a2565b8e8e6144218e6001615839565b8181106144305761443061538c565b905060c00201604001602081019061444891906153a2565b8f8f6144558f6001615839565b8181106114bc576114bc61538c565b6040805160008152602081019182905263022c0d9f60e01b9091529091506001600160a01b0387169063022c0d9f906144a6908690869086906024810161587d565b600060405180830381600087803b1580156144c057600080fd5b505af11580156144d4573d6000803e3d6000fd5b5050505060008d8d8c8181106144ec576144ec61538c565b905060c00201606001602081019061450491906155a5565b60408051888152602081018890526001600160a01b038d81168284015283151560608301529151929350908e169133917fe7857fba7d66e2e29213fda7a0394efbaa80d435dfc7b4c1f147d78c2f9caa62919081900360800190a3505050505050505050508080614574906158aa565b915050614107565b604080516000808252602082019092526001600160a01b0384169083906040516145a6919061581d565b60006040518083038185875af1925050503d80600081146145e3576040519150601f19603f3d011682016040523d82523d6000602084013e6145e8565b606091505b505090508061461f5760405162461bcd60e51b815260206004820152600360248201526222aa2360e91b60448201526064016105e0565b505050565b60006003821115614685575080600061463e6002836156e6565b614649906001615839565b90505b8181101561467f5790508060028161466481866156e6565b61466e9190615839565b61467891906156e6565b905061464c565b50919050565b811561468f575060015b919050565b600080841180156146a55750600083115b80156146b15750600082115b6146e35760405162461bcd60e51b815260206004820152600360248201526212539360ea1b60448201526064016105e0565b826146ee83866156c7565b6140fc91906156e6565b60008183106147075781611960565b5090919050565b6000836001600160a01b03163b116147525760405162461bcd60e51b815260206004820152600760248201526621a7a222a622a760c91b60448201526064016105e0565b6040516001600160a01b03838116602483015260448201839052600091829186169060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b179052516147ac919061581d565b6000604051808303816000865af19150503d80600081146147e9576040519150601f19603f3d011682016040523d82523d6000602084013e6147ee565b606091505b509150915081801561481857508051158061481857508080602001905181019061481891906157b0565b613a485760405162461bcd60e51b81526020600482015260036024820152621254d560ea1b60448201526064016105e0565b6000806000846001600160a01b031684604051614867919061581d565b6000604051808303816000865af19150503d80600081146148a4576040519150601f19603f3d011682016040523d82523d6000602084013e6148a9565b606091505b50915091508180156148d35750805115806148d35750808060200190518101906148d391906157b0565b80156148e857506001600160a01b0385163b15155b95945050505050565b6000614946826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614ab59092919063ffffffff16565b905080516000148061496757508080602001905181019061496791906157b0565b61461f5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016105e0565b60008115614aa1576000846149e388670de0b6b3a76400006156c7565b6149ed91906156e6565b9050600084614a0488670de0b6b3a76400006156c7565b614a0e91906156e6565b90506000670de0b6b3a7640000614a2583856156c7565b614a2f91906156e6565b90506000670de0b6b3a7640000614a4684806156c7565b614a5091906156e6565b670de0b6b3a7640000614a6386806156c7565b614a6d91906156e6565b614a779190615839565b9050670de0b6b3a7640000614a8c82846156c7565b614a9691906156e6565b9450505050506148e8565b614aab85876156c7565b9695505050505050565b60606140fc848460008585600080866001600160a01b03168587604051614adc919061581d565b60006040518083038185875af1925050503d8060008114614b19576040519150601f19603f3d011682016040523d82523d6000602084013e614b1e565b606091505b5091509150614b2f87838387614b3a565b979650505050505050565b60608315614ba9578251600003614ba2576001600160a01b0385163b614ba25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016105e0565b50816140fc565b6140fc8383815115614bbe5781518083602001fd5b8060405162461bcd60e51b81526004016105e0919061595c565b634e487b7160e01b600052600160045260246000fd5b6001600160a01b0381168114614c0357600080fd5b50565b803561468f81614bee565b8015158114614c0357600080fd5b803561468f81614c11565b600080600080600080600080610100898b031215614c4757600080fd5b8835614c5281614bee565b97506020890135614c6281614bee565b96506040890135614c7281614c11565b9550606089013594506080890135935060a0890135925060c0890135614c9781614bee565b8092505060e089013590509295985092959890939650565b60008083601f840112614cc157600080fd5b50813567ffffffffffffffff811115614cd957600080fd5b60208301915083602060c083028501011115610f7d57600080fd5b60008060008060008060a08789031215614d0d57600080fd5b8635955060208701359450604087013567ffffffffffffffff811115614d3257600080fd5b614d3e89828a01614caf565b9095509350506060870135614d5281614bee565b80925050608087013590509295509295509295565b6020808252825182820181905260009190848201906040850190845b81811015614d9f57835183529284019291840191600101614d83565b50909695505050505050565b60008060008060808587031215614dc157600080fd5b8435614dcc81614bee565b93506020850135614ddc81614bee565b92506040850135614dec81614c11565b9396929550929360600135925050565b803560ff8116811461468f57600080fd5b60008060008060008060008060008060006101608c8e031215614e2f57600080fd5b8b35614e3a81614bee565b9a5060208c0135614e4a81614c11565b995060408c0135985060608c0135975060808c0135965060a08c0135614e6f81614bee565b955060c08c0135945060e08c0135614e8681614c11565b9350614e956101008d01614dfc565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600060608486031215614ecc57600080fd5b8335614ed781614bee565b92506020840135614ee781614bee565b91506040840135614ef781614c11565b809150509250925092565b60008060408385031215614f1557600080fd5b8235614f2081614bee565b91506020830135614f3081614bee565b809150509250929050565b6001600160a01b0392831681529116602082015260400190565b60008060008060008060008060006101208a8c031215614f7457600080fd5b8935614f7f81614bee565b985060208a0135614f8f81614bee565b975060408a0135614f9f81614c11565b965060608a0135955060808a0135945060a08a0135935060c08a0135925060e08a0135614fcb81614bee565b809250506101008a013590509295985092959850929598565b600080600080600060808688031215614ffc57600080fd5b85359450602086013567ffffffffffffffff81111561501a57600080fd5b61502688828901614caf565b909550935050604086013561503a81614bee565b949793965091946060013592915050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561508a5761508a61504b565b604052919050565b600067ffffffffffffffff8211156150ac576150ac61504b565b5060051b60200190565b6000806000806000608086880312156150ce57600080fd5b853567ffffffffffffffff808211156150e657600080fd5b818801915088601f8301126150fa57600080fd5b8135602061510f61510a83615092565b615061565b82815260059290921b8401810191818101908c84111561512e57600080fd5b948201945b8386101561514c57853582529482019490820190615133565b9950508901359250508082111561516257600080fd5b5061516f88828901614caf565b909550935061503a905060408701614c06565b60008060006060848603121561519757600080fd5b8335925060208401356151a981614bee565b91506040840135614ef781614bee565b600080600080600060a086880312156151d157600080fd5b85356151dc81614bee565b945060208601356151ec81614bee565b935060408601356151fc81614c11565b94979396509394606081013594506080013592915050565b6000806000806000806000806000806000806101808d8f03121561523757600080fd5b8c3561524281614bee565b9b5060208d013561525281614bee565b9a5060408d013561526281614c11565b995060608d0135985060808d0135975060a08d0135965060c08d013561528781614bee565b955060e08d013594506101008d013561529f81614c11565b93506152ae6101208e01614dfc565b92506101408d013591506101608d013590509295989b509295989b509295989b565b600080600080600080600060e0888a0312156152eb57600080fd5b87356152f681614bee565b9650602088013561530681614c11565b955060408801359450606088013593506080880135925060a088013561532b81614bee565b8092505060c0880135905092959891949750929550565b60208082526003908201526204558560ec1b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b6000828210156153875761538761535f565b500390565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156153b457600080fd5b813561196081614bee565b60208082526002908201526124a960f11b604082015260600190565b838152604060208083018290528282018490526000919060609081850187855b8881101561549657813561540e81614bee565b6001600160a01b039081168452828501359061542982614bee565b90811684860152828701359061543e82614bee565b16838701528185013561545081614c11565b1515838601526080615463838201614c1f565b15159084015260a0615476838201614c06565b6001600160a01b03169084015260c09283019291909101906001016153fb565b50909998505050505050505050565b600082601f8301126154b657600080fd5b815160206154c661510a83615092565b82815260059290921b840181019181810190868411156154e557600080fd5b8286015b84811015610a3b57805183529183019183016154e9565b60008060006060848603121561551557600080fd5b835167ffffffffffffffff8082111561552d57600080fd5b615539878388016154a5565b9450602086015191508082111561554f57600080fd5b61555b878388016154a5565b9350604086015191508082111561557157600080fd5b5061557e868287016154a5565b9150509250925092565b602080825260039082015262494f4160e81b604082015260600190565b6000602082840312156155b757600080fd5b813561196081614c11565b6000602082840312156155d457600080fd5b5051919050565b600060c082840312156155ed57600080fd5b60405160c0810181811067ffffffffffffffff821117156156105761561061504b565b604052823561561e81614bee565b8152602083013561562e81614bee565b6020820152604083013561564181614bee565b6040820152606083013561565481614c11565b6060820152608083013561566781614c11565b608082015260a083013561567a81614bee565b60a08201529392505050565b6001600160a01b039384168152919092166020820152901515604082015260600190565b6000602082840312156156bc57600080fd5b815161196081614bee565b60008160001904831182151516156156e1576156e161535f565b500290565b60008261570357634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b602080825260029082015261494160f01b604082015260600190565b60008060006060848603121561577a57600080fd5b8351925060208401519150604084015190509250925092565b6020808252600390820152620494e560ec1b604082015260600190565b6000602082840312156157c257600080fd5b815161196081614c11565b600080604083850312156157e057600080fd5b505080516020909101519092909150565b60005b8381101561580c5781810151838201526020016157f4565b8381111561329b5750506000910152565b6000825161582f8184602087016157f1565b9190910192915050565b6000821982111561584c5761584c61535f565b500190565b600081518084526158698160208601602086016157f1565b601f01601f19169290920160200192915050565b84815283602082015260018060a01b0383166040820152608060608201526000614aab6080830184615851565b6000600182016158bc576158bc61535f565b5060010190565b600080600080600080600060e0888a0312156158de57600080fd5b87519650602088015195506040880151945060608801519350608088015161590581614c11565b60a089015190935061591681614bee565b60c089015190925061592781614bee565b8091505092959891949750929550565b6000806040838503121561594a57600080fd5b825191506020830151614f3081614c11565b602081526000611960602083018461585156fea26469706673582212203868d4230ffe741b4a7033e2723ee023591770045c558090f62aedf165dfb57264736f6c634300080d00330000000000000000000000005aef44edfc5a7edd30826c724ea12d7be15bdc30000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac0000000000000000000000003d219d5089331f6bf99cb20b9b199ab3b429337a000000000000000000000000d8377aea61c4c4d43bf0588956f4e861720803c6

Deployed Bytecode

0x6080604052600436106101855760003560e01c806398a0fb3c116100d1578063d7b0e0a51161008a578063e9e7941d11610064578063e9e7941d14610528578063f0aff68d14610548578063f242862114610568578063fe411f141461059c57600080fd5b8063d7b0e0a5146104b4578063dcba74f2146104d4578063e2d9d4dc1461050857600080fd5b806398a0fb3c146103e6578063a32b1fcd14610406578063b7e0d4c014610426578063bc641f2714610439578063c31c9c071461044c578063c45a01551461048057600080fd5b80635a47ddc31161013e5780637f64ba03116101185780637f64ba031461034457806383509d0a1461037857806383b9a7d31461039857806397607530146103c657600080fd5b80635a47ddc3146102d65780635e60dab5146103115780636ba165431461033157600080fd5b80630dede6c4146101c9578063204b5c0a146102035780634386e63c14610230578063448725b4146102505780634c1ee03e14610270578063544caa56146102a857600080fd5b366101c457336001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216146101c2576101c2614bd8565b005b600080fd5b3480156101d557600080fd5b506101e96101e4366004614c2a565b6105bc565b604080519283526020830191909152015b60405180910390f35b34801561020f57600080fd5b5061022361021e366004614cf4565b61061e565b6040516101fa9190614d67565b34801561023c57600080fd5b506101e961024b366004614dab565b610a46565b34801561025c57600080fd5b506101e961026b366004614e0d565b610bad565b34801561027c57600080fd5b5061029061028b366004614eb7565b610d3c565b6040516001600160a01b0390911681526020016101fa565b3480156102b457600080fd5b506102c86102c3366004614f02565b610f0c565b6040516101fa929190614f3b565b3480156102e257600080fd5b506102f66102f1366004614f55565b610f84565b604080519384526020840192909252908201526060016101fa565b34801561031d57600080fd5b506101e961032c366004614eb7565b6110be565b61022361033f366004614fe4565b611176565b34801561035057600080fd5b506102907f0000000000000000000000003d219d5089331f6bf99cb20b9b199ab3b429337a81565b34801561038457600080fd5b506102236103933660046150b6565b611722565b3480156103a457600080fd5b506103b86103b3366004615182565b6118be565b6040519081526020016101fa565b3480156103d257600080fd5b506101c26103e1366004614cf4565b611967565b3480156103f257600080fd5b506102f66104013660046151b9565b611baf565b34801561041257600080fd5b506101e9610421366004615214565b611da6565b6102f66104343660046152d0565b611ef6565b6101c2610447366004614fe4565b612191565b34801561045857600080fd5b506102907f00000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac81565b34801561048c57600080fd5b506102907f0000000000000000000000005aef44edfc5a7edd30826c724ea12d7be15bdc3081565b3480156104c057600080fd5b506101e96104cf3660046152d0565b612500565b3480156104e057600080fd5b506102907f000000000000000000000000d8377aea61c4c4d43bf0588956f4e861720803c681565b34801561051457600080fd5b506101e9610523366004614e0d565b612607565b34801561053457600080fd5b506101c2610543366004614cf4565b61276b565b34801561055457600080fd5b50610223610563366004614cf4565b612930565b34801561057457600080fd5b506102907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b3480156105a857600080fd5b506101e96105b73660046152d0565b612d8b565b60008082428110156105e95760405162461bcd60e51b81526004016105e090615342565b60405180910390fd5b6105f1612e61565b6106018b8b8b8b8b8b8b8b612eba565b90935091506106106001600055565b509850989650505050505050565b606081428110156106415760405162461bcd60e51b81526004016105e090615342565b610649612e61565b6001600160a01b0384168686610660600182615375565b81811061066f5761066f61538c565b905060c0020160a001602081019061068791906153a2565b6001600160a01b0316146106ad5760405162461bcd60e51b81526004016105e0906153bf565b60405163161681db60e11b81526001600160a01b037f000000000000000000000000d8377aea61c4c4d43bf0588956f4e861720803c61690632c2d03b6906106fd908b908a908a906004016153db565b6000604051808303816000875af115801561071c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107449190810190615500565b9050508092505086826001845161075b9190615375565b8151811061076b5761076b61538c565b602002602001015110156107915760405162461bcd60e51b81526004016105e090615588565b858560008181106107a4576107a461538c565b905060c0020160800160208101906107bc91906155a5565b61083d57610838868660008181106107d6576107d661538c565b905060c0020160200160208101906107ee91906153a2565b33888860008181106108025761080261538c565b61081892602060c09092020190810191506153a2565b8560008151811061082b5761082b61538c565b60200260200101516130a0565b6109d4565b610880868660008181106108535761085361538c565b905060c00201602001602081019061086b91906153a2565b33308560008151811061082b5761082b61538c565b816000815181106108935761089361538c565b6020026020010151868660008181106108ae576108ae61538c565b905060c0020160200160208101906108c691906153a2565b6001600160a01b031663dd62ed3e307f00000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac6040518363ffffffff1660e01b8152600401610913929190614f3b565b602060405180830381865afa158015610930573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061095491906155c2565b10156109d4576109d47f00000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac836000815181106109915761099161538c565b6020026020010151888860008181106109ac576109ac61538c565b905060c0020160200160208101906109c491906153a2565b6001600160a01b031691906131e7565b610a31828787808060200260200160405190810160405280939291908181526020016000905b82821015610a2657610a1760c083028601368190038101906155db565b815260200190600101906109fa565b5050505050856132a1565b610a3b6001600055565b509695505050505050565b60008060007f0000000000000000000000005aef44edfc5a7edd30826c724ea12d7be15bdc306001600160a01b0316636801cc308888886040518463ffffffff1660e01b8152600401610a9b93929190615686565b602060405180830381865afa158015610ab8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610adc91906156aa565b90506001600160a01b038116610af9576000809250925050610ba4565b600080610b078989896110be565b915091506000836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6f91906155c2565b905080610b7c84896156c7565b610b8691906156e6565b955080610b9383896156c7565b610b9d91906156e6565b9450505050505b94509492505050565b600080610bb8612e61565b6000610be58e7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28f610d3c565b9050806001600160a01b031663d505accf33308a610c03578f610c07565b6000195b8c8b8b8b6040518863ffffffff1660e01b8152600401610c2d9796959493929190615708565b600060405180830381600087803b158015610c4757600080fd5b505af1925050508015610c58575060015b610ceb57604051636eb1769f60e11b81528c906001600160a01b0383169063dd62ed3e90610c8c9033903090600401614f3b565b602060405180830381865afa158015610ca9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccd91906155c2565b1015610ceb5760405162461bcd60e51b81526004016105e090615749565b610d1b8e7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28f8f8f8f8f8f612eba565b9093509150610d2c90506001600055565b9b509b9950505050505050505050565b6000806000610d4b8686610f0c565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015286151560f81b6048820152919350915060009060490160405160208183030381529060405280519060200120905060007f0000000000000000000000005aef44edfc5a7edd30826c724ea12d7be15bdc306001600160a01b0316639aab92486040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2391906155c2565b905060007f0000000000000000000000005aef44edfc5a7edd30826c724ea12d7be15bdc306001600160a01b031663f65bd0b76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea991906156aa565b6040516001600160f81b031960208201526bffffffffffffffffffffffff19606083901b166021820152603581018590526055810184905290915060750160408051601f1981840301815291905280516020909101209998505050505050505050565b600080826001600160a01b0316846001600160a01b031610610f2f578284610f32565b83835b90925090506001600160a01b03821615801590610f615750806001600160a01b0316826001600160a01b031614155b610f7d5760405162461bcd60e51b81526004016105e090615749565b9250929050565b60008060008342811015610faa5760405162461bcd60e51b81526004016105e090615342565b610fb2612e61565b610fc18d8d8d8d8d8d8d613a4f565b90945092506000610fd38e8e8e610d3c565b9050610fe18e3383886130a0565b610fed8d3383876130a0565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af1158015611035573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105991906155c2565b9250600083116110a35760405162461bcd60e51b815260206004820152601560248201527416995c9bc81b1a5c5d5a591a5d1e481b5a5b9d1959605a1b60448201526064016105e0565b506110ae6001600055565b5099509950999650505050505050565b60008060006110cd8686610f0c565b5090506000806110de888888610d3c565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561111b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113f9190615765565b5091509150826001600160a01b0316886001600160a01b031614611164578082611167565b81815b90999098509650505050505050565b606081428110156111995760405162461bcd60e51b81526004016105e090615342565b6111a1612e61565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316868660008181106111de576111de61538c565b905060c0020160200160208101906111f691906153a2565b6001600160a01b03161461121c5760405162461bcd60e51b81526004016105e090615793565b6001600160a01b0384168686611233600182615375565b8181106112425761124261538c565b905060c0020160a001602081019061125a91906153a2565b6001600160a01b0316146112805760405162461bcd60e51b81526004016105e0906153bf565b60405163161681db60e11b81526001600160a01b037f000000000000000000000000d8377aea61c4c4d43bf0588956f4e861720803c61690632c2d03b6906112d09034908a908a906004016153db565b6000604051808303816000875af11580156112ef573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113179190810190615500565b9050508092505086826001845161132e9190615375565b8151811061133e5761133e61538c565b602002602001015110156113645760405162461bcd60e51b81526004016105e090615588565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836000815181106113a6576113a661538c565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156113d957600080fd5b505af11580156113ed573d6000803e3d6000fd5b5050505050858560008181106114055761140561538c565b905060c00201608001602081019061141d91906155a5565b611574577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663a9059cbb6114d4888860008181106114665761146661538c565b905060c00201602001602081019061147e91906153a2565b898960008181106114915761149161538c565b905060c0020160400160208101906114a991906153a2565b8a8a60008181106114bc576114bc61538c565b905060c00201606001602081019061028b91906155a5565b846000815181106114e7576114e761538c565b60200260200101516040518363ffffffff1660e01b81526004016115209291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af115801561153f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061156391906157b0565b61156f5761156f614bd8565b6116bc565b816000815181106115875761158761538c565b60200260200101517f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663dd62ed3e307f00000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac6040518363ffffffff1660e01b81526004016115fd929190614f3b565b602060405180830381865afa15801561161a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061163e91906155c2565b10156116bc576116bc7f00000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac8360008151811061167b5761167b61538c565b60200260200101517f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b03166131e79092919063ffffffff16565b61170e828787808060200260200160405190810160405280939291908181526020016000905b82821015610a26576116ff60c083028601368190038101906155db565b815260200190600101906116e2565b6117186001600055565b5095945050505050565b606081428110156117455760405162461bcd60e51b81526004016105e090615342565b61174d612e61565b6001600160a01b0384168686611764600182615375565b8181106117735761177361538c565b905060c0020160a001602081019061178b91906153a2565b6001600160a01b0316146117b15760405162461bcd60e51b81526004016105e0906153bf565b61185f868660008181106117c7576117c761538c565b905060c0020160200160208101906117df91906153a2565b3361184c898960008181106117f6576117f661538c565b905060c00201602001602081019061180e91906153a2565b8a8a60008181106118215761182161538c565b905060c00201604001602081019061183991906153a2565b8b8b60008181106114bc576114bc61538c565b8a60008151811061082b5761082b61538c565b6118b1878787808060200260200160405190810160405280939291908181526020016000905b82821015610a26576118a260c083028601368190038101906155db565b81526020019060010190611885565b8691506117186001600055565b6040516378a051ad60e11b8152600481018490526001600160a01b038381166024830152600091829184169063f140a35a90604401602060405180830381865afa92505050801561192c575060408051601f3d908101601f19168201909252611929918101906155c2565b60015b6119385750600061193b565b90505b600061194986868685613cf6565b9050801561195957509050611960565b6000925050505b9392505050565b80428110156119885760405162461bcd60e51b81526004016105e090615342565b611990612e61565b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21685856119c7600182615375565b8181106119d6576119d661538c565b905060c0020160400160208101906119ee91906153a2565b6001600160a01b031614611a145760405162461bcd60e51b81526004016105e090615793565b611a5f85856000818110611a2a57611a2a61538c565b905060c002016020016020810190611a4291906153a2565b33611a59888860008181106114665761146661538c565b8a6130a0565b611a6a858530614104565b6040516370a0823160e01b81523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401602060405180830381865afa158015611ad1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af591906155c2565b905086811015611b175760405162461bcd60e51b81526004016105e090615588565b604051632e1a7d4d60e01b8152600481018290527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611b7957600080fd5b505af1158015611b8d573d6000803e3d6000fd5b50505050611b9b848261457c565b50611ba66001600055565b50505050505050565b6000806000807f0000000000000000000000005aef44edfc5a7edd30826c724ea12d7be15bdc306001600160a01b0316636801cc308a8a8a6040518463ffffffff1660e01b8152600401611c0593929190615686565b602060405180830381865afa158015611c22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4691906156aa565b9050600080806001600160a01b03841615611cd057836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cbd91906155c2565b9050611cca8c8c8c6110be565b90935091505b80600003611d04578896508795506103e8611cf3611cee888a6156c7565b614624565b611cfd9190615375565b9450611d97565b6000611d118a8585614694565b9050888111611d5957899750955085611d5284611d2e848b6156c7565b611d3891906156e6565b84611d43858b6156c7565b611d4d91906156e6565b6146f8565b9550611d95565b6000611d668a8587614694565b9850899750889050611d9185611d7c85846156c7565b611d8691906156e6565b85611d43868c6156c7565b9650505b505b50505050955095509592505050565b600080611db1612e61565b6000611dbe8f8f8f610d3c565b9050806001600160a01b031663d505accf33308a611ddc578f611de0565b6000195b8c8b8b8b6040518863ffffffff1660e01b8152600401611e069796959493929190615708565b600060405180830381600087803b158015611e2057600080fd5b505af1925050508015611e31575060015b611ec457604051636eb1769f60e11b81528c906001600160a01b0383169063dd62ed3e90611e659033903090600401614f3b565b602060405180830381865afa158015611e82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea691906155c2565b1015611ec45760405162461bcd60e51b81526004016105e090615749565b611ed48f8f8f8f8f8f8f8f612eba565b9093509150611ee590506001600055565b9c509c9a5050505050505050505050565b60008060008342811015611f1c5760405162461bcd60e51b81526004016105e090615342565b611f24612e61565b611f538b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28c8c348d8d613a4f565b90945092506000611f858c7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28d610d3c565b9050611f938c3383886130a0565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b158015611fee57600080fd5b505af1158015612002573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b038581166004830152602482018990527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216935063a9059cbb925060440190506020604051808303816000875af1158015612077573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061209b91906157b0565b6120a7576120a7614bd8565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af11580156120ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061211391906155c2565b92506000831161215d5760405162461bcd60e51b815260206004820152601560248201527416995c9bc81b1a5c5d5a591a5d1e481b5a5b9d1959605a1b60448201526064016105e0565b8334111561217857612178336121738634615375565b61457c565b506121836001600055565b509750975097945050505050565b80428110156121b25760405162461bcd60e51b81526004016105e090615342565b6121ba612e61565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316858560008181106121f7576121f761538c565b905060c00201602001602081019061220f91906153a2565b6001600160a01b0316146122355760405162461bcd60e51b81526004016105e090615793565b60003490507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561229557600080fd5b505af11580156122a9573d6000803e3d6000fd5b50889350506001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216915063a9059cbb90506122f589846000816117f6576117f661538c565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018590526044016020604051808303816000875af1158015612342573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236691906157b0565b61237257612372614bd8565b60008787612381600185615375565b8181106123905761239061538c565b905060c0020160400160208101906123a891906153a2565b6040516370a0823160e01b81526001600160a01b03888116600483015291909116906370a0823190602401602060405180830381865afa1580156123f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061241491906155c2565b9050612421888888614104565b88818989612430600187615375565b81811061243f5761243f61538c565b905060c00201604001602081019061245791906153a2565b6040516370a0823160e01b81526001600160a01b038a8116600483015291909116906370a0823190602401602060405180830381865afa15801561249f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124c391906155c2565b6124cd9190615375565b10156124eb5760405162461bcd60e51b81526004016105e090615588565b5050506124f86001600055565b505050505050565b60008082428110156125245760405162461bcd60e51b81526004016105e090615342565b61252c612e61565b61255c8a7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28b8b8b8b308b612eba565b909350915061256c8a868561470e565b604051632e1a7d4d60e01b8152600481018390527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156125ce57600080fd5b505af11580156125e2573d6000803e3d6000fd5b505050506125f0858361457c565b6125fa6001600055565b5097509795505050505050565b600080612612612e61565b600061263f8e7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28f610d3c565b905060008761264e578c612652565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf9061268d903390309086908f908e908e908e90600401615708565b600060405180830381600087803b1580156126a757600080fd5b505af19250505080156126b8575060015b61274b57604051636eb1769f60e11b81528d906001600160a01b0384169063dd62ed3e906126ec9033903090600401614f3b565b602060405180830381865afa158015612709573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061272d91906155c2565b101561274b5760405162461bcd60e51b81526004016105e090615749565b61275a8f8f8f8f8f8f8f612d8b565b9094509250610d2c91506130999050565b804281101561278c5760405162461bcd60e51b81526004016105e090615342565b612794612e61565b6127aa85856000818110611a2a57611a2a61538c565b83600086826127ba600182615375565b8181106127c9576127c961538c565b905060c0020160400160208101906127e191906153a2565b6040516370a0823160e01b81526001600160a01b03878116600483015291909116906370a0823190602401602060405180830381865afa158015612829573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061284d91906155c2565b905061285a878787614104565b87818888612869600187615375565b8181106128785761287861538c565b905060c00201604001602081019061289091906153a2565b6040516370a0823160e01b81526001600160a01b03898116600483015291909116906370a0823190602401602060405180830381865afa1580156128d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128fc91906155c2565b6129069190615375565b10156129245760405162461bcd60e51b81526004016105e090615588565b5050611ba66001600055565b606081428110156129535760405162461bcd60e51b81526004016105e090615342565b61295b612e61565b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2168686612992600182615375565b8181106129a1576129a161538c565b905060c0020160400160208101906129b991906153a2565b6001600160a01b0316146129df5760405162461bcd60e51b81526004016105e090615793565b60405163161681db60e11b81526001600160a01b037f000000000000000000000000d8377aea61c4c4d43bf0588956f4e861720803c61690632c2d03b690612a2f908b908a908a906004016153db565b6000604051808303816000875af1158015612a4e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612a769190810190615500565b90505080925050868260018451612a8d9190615375565b81518110612a9d57612a9d61538c565b60200260200101511015612ac35760405162461bcd60e51b81526004016105e090615588565b85856000818110612ad657612ad661538c565b905060c002016080016020810190612aee91906155a5565b612b3c57612b3786866000818110612b0857612b0861538c565b905060c002016020016020810190612b2091906153a2565b33610818898960008181106117f6576117f661538c565b612c63565b612b52868660008181106108535761085361538c565b81600081518110612b6557612b6561538c565b602002602001015186866000818110612b8057612b8061538c565b905060c002016020016020810190612b9891906153a2565b6001600160a01b031663dd62ed3e307f00000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac6040518363ffffffff1660e01b8152600401612be5929190614f3b565b602060405180830381865afa158015612c02573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c2691906155c2565b1015612c6357612c637f00000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac836000815181106109915761099161538c565b612cb5828787808060200260200160405190810160405280939291908181526020016000905b82821015610a2657612ca660c083028601368190038101906155db565b81526020019060010190612c89565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316632e1a7d4d8360018551612cf39190615375565b81518110612d0357612d0361538c565b60200260200101516040518263ffffffff1660e01b8152600401612d2991815260200190565b600060405180830381600087803b158015612d4357600080fd5b505af1158015612d57573d6000803e3d6000fd5b50505050610a31848360018551612d6e9190615375565b81518110612d7e57612d7e61538c565b602002602001015161457c565b6000808242811015612daf5760405162461bcd60e51b81526004016105e090615342565b612db7612e61565b612de78a7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28b8b8b8b308b612eba565b6040516370a0823160e01b8152306004820152919450925061256c908b9087906001600160a01b038316906370a0823190602401602060405180830381865afa158015612e38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5c91906155c2565b61470e565b600260005403612eb35760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105e0565b6002600055565b6000808242811015612ede5760405162461bcd60e51b81526004016105e090615342565b6000612eeb8c8c8c610d3c565b6040516323b872dd60e01b81523360048201526001600160a01b03821660248201819052604482018c90529192506323b872dd906064016020604051808303816000875af1158015612f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f6591906157b0565b612f9a5760405162461bcd60e51b81526004016105e0906020808252600490820152634954464d60e01b604082015260600190565b60405163226bf2d160e21b81526001600160a01b03878116600483015260009182918416906389afcb449060240160408051808303816000875af1158015612fe6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061300a91906157cd565b91509150600061301a8f8f610f0c565b509050806001600160a01b03168f6001600160a01b03161461303d578183613040565b82825b90975095508a87108015906130555750898610155b6130875760405162461bcd60e51b815260206004820152600360248201526249414160e81b60448201526064016105e0565b50505050509850989650505050505050565b6001600055565b6000846001600160a01b03163b116130e45760405162461bcd60e51b815260206004820152600760248201526621a7a222a622a760c91b60448201526064016105e0565b6040516001600160a01b038481166024830152838116604483015260648201839052600091829187169060840160408051601f198184030181529181526020820180516001600160e01b03166323b872dd60e01b17905251613146919061581d565b6000604051808303816000865af19150503d8060008114613183576040519150601f19603f3d011682016040523d82523d6000602084013e613188565b606091505b50915091508180156131b25750805115806131b25750808060200190518101906131b291906157b0565b6124f85760405162461bcd60e51b81526004016105e09060208082526004908201526324a9aa2360e11b604082015260600190565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613238848261484a565b61329b57604080516001600160a01b038516602482015260006044808301919091528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526132919085906148f1565b61329b84826148f1565b50505050565b815160005b81811015613a485760006001600160a01b03168482815181106132cb576132cb61538c565b602002602001015160a001516001600160a01b0316036133125760405162461bcd60e51b81526020600482015260026024820152615a4160f01b60448201526064016105e0565b8381815181106133245761332461538c565b6020026020010151608001511561374a578481815181106133475761334761538c565b60200260200101518482815181106133615761336161538c565b6020026020010151602001516001600160a01b031663dd62ed3e307f00000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac6040518363ffffffff1660e01b81526004016133ba929190614f3b565b602060405180830381865afa1580156133d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133fb91906155c2565b1015613475576134757f00000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac8683815181106134375761343761538c565b60200260200101518684815181106134515761345161538c565b6020026020010151602001516001600160a01b03166131e79092919063ffffffff16565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101919091526040518061010001604052808684815181106134d4576134d461538c565b6020026020010151602001516001600160a01b031681526020018684815181106135005761350061538c565b6020026020010151604001516001600160a01b031681526020017f0000000000000000000000003d219d5089331f6bf99cb20b9b199ab3b429337a6001600160a01b0316638992848788868151811061355b5761355b61538c565b6020908102919091010151516040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156135ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135cf91906156aa565b6001600160a01b031681526020018684815181106135ef576135ef61538c565b602002602001015160a001516001600160a01b031681526020018581526020018784815181106136215761362161538c565b602090810291909101810151825260008282018190526040928301528151630b3ce3c960e11b815283516001600160a01b039081166004830152918401518216602482015291830151811660448301526060830151811660648301526080830151608483015260a083015160a483015260c083015160c483015260e0830151811660e48301529192507f00000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac90911690631679c79290610104016020604051808303816000875af11580156136f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061371c91906155c2565b86613728846001615839565b815181106137385761373861538c565b6020026020010181815250505061392f565b60006137908583815181106137615761376161538c565b60200260200101516020015186848151811061377f5761377f61538c565b602002602001015160400151610f0c565b5090506000866137a1846001615839565b815181106137b1576137b161538c565b60200260200101519050600080836001600160a01b03168886815181106137da576137da61538c565b6020026020010151602001516001600160a01b0316146137fc57826000613800565b6000835b915091506138668886815181106138195761381961538c565b6020026020010151602001518987815181106138375761383761538c565b6020026020010151604001518a88815181106138555761385561538c565b602002602001015160600151610d3c565b6001600160a01b031663022c0d9f83838b89815181106138885761388861538c565b602002602001015160a00151600067ffffffffffffffff8111156138ae576138ae61504b565b6040519080825280601f01601f1916602001820160405280156138d8576020820181803683370190505b506040518563ffffffff1660e01b81526004016138f8949392919061587d565b600060405180830381600087803b15801561391257600080fd5b505af1158015613926573d6000803e3d6000fd5b50505050505050505b8381815181106139415761394161538c565b602002602001015160a001516001600160a01b0316336001600160a01b03167fe7857fba7d66e2e29213fda7a0394efbaa80d435dfc7b4c1f147d78c2f9caa628784815181106139935761399361538c565b6020026020010151888560016139a99190615839565b815181106139b9576139b961538c565b60200260200101518886815181106139d3576139d361538c565b6020026020010151602001518987815181106139f1576139f161538c565b602002602001015160600151604051613a2e949392919093845260208401929092526001600160a01b031660408301521515606082015260800190565b60405180910390a380613a40816158aa565b9150506132a6565b5050505050565b600080838610158015613a625750828510155b613a975760405162461bcd60e51b81526004016105e090602080825260049082015263444c4d4160e01b604082015260600190565b6040516306801cc360e41b81526000906001600160a01b037f0000000000000000000000005aef44edfc5a7edd30826c724ea12d7be15bdc301690636801cc3090613aea908d908d908d90600401615686565b602060405180830381865afa158015613b07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b2b91906156aa565b90506001600160a01b038116613bd1576040516320b7f73960e21b81526001600160a01b037f0000000000000000000000005aef44edfc5a7edd30826c724ea12d7be15bdc3016906382dfdce490613b8b908d908d908d90600401615686565b6020604051808303816000875af1158015613baa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bce91906156aa565b90505b600080613bdf8c8c8c6110be565b915091506000836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c4791906155c2565b905080600003613c5c57899550889450613ca4565b6000613c698b8585614694565b9050898111613c7d578a9650945084613ca2565b6000613c8a8b8587614694565b90508b811115613c9c57613c9c614bd8565b96508995505b505b878610158015613cb45750868510155b613ce65760405162461bcd60e51b815260206004820152600360248201526249414160e81b60448201526064016105e0565b5050505097509795505050505050565b6000613d77604051806101a001604052806000815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b836001600160a01b031663392f37e96040518163ffffffff1660e01b815260040160e060405180830381865afa158015613db5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dd991906158c3565b6001600160a01b0390811660c089015290811660a08801819052911515608088015260608701929092526040808701939093526020860193909352928452516370a0823160e01b815291861660048301526000916370a0823190602401602060405180830381865afa158015613e53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e7791906155c2565b60c08301516040516370a0823160e01b81526001600160a01b038881166004830152929350600092909116906370a0823190602401602060405180830381865afa158015613ec9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613eed91906155c2565b90508260a001516001600160a01b0316876001600160a01b031614613f13578082613f16565b81815b61010085015260e084015260a08301516001600160a01b03888116911614613f475782606001518360400151613f52565b826040015183606001515b61014085015261012084015260a08301516001600160a01b03888116911614613f815760208301518351613f89565b825160208401515b61018085015261016084015261012083015160e0840151600091613fac91615375565b613fb6908a615839565b608085015160405163cc56b2c560e01b81526001600160a01b038a811660048301529115156024820152919250600091612710917f0000000000000000000000005aef44edfc5a7edd30826c724ea12d7be15bdc30169063cc56b2c590604401602060405180830381865afa158015614033573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061405791906155c2565b61406190846156c7565b61406b91906156e6565b9050808a8660e0015161407e9190615839565b6140889190615375565b60e0860152610100850180518891906140a2908390615375565b915081815250506140ce85610120015186610140015187610160015188610180015189608001516149c6565b6140f28660e001518761010001518861016001518961018001518a608001516149c6565b1015955050505050505b949350505050565b60005b8281101561329b576000808585848181106141245761412461538c565b905060c00201602001602081019061413c91906153a2565b86868581811061414e5761414e61538c565b905060c00201604001602081019061416691906153a2565b9150915060006141768383610f0c565b50905060006141e48888878181106141905761419061538c565b905060c0020160200160208101906141a891906153a2565b8989888181106141ba576141ba61538c565b905060c0020160400160208101906141d291906153a2565b8a8a898181106114bc576114bc61538c565b9050600080600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561422a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061424e9190615765565b50915091506000866001600160a01b0316896001600160a01b031614614275578183614278565b82825b506040516370a0823160e01b81526001600160a01b03888116600483015291925082918b16906370a0823190602401602060405180830381865afa1580156142c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142e891906155c2565b6142f29190615375565b604051631aa6a63f60e01b8152600481018290526001600160a01b038b811660248301528a811660448301529196507f000000000000000000000000d8377aea61c4c4d43bf0588956f4e861720803c690911690631aa6a63f906064016040805180830381865afa15801561436b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061438f9190615937565b5080945050505050600080856001600160a01b0316886001600160a01b0316146143bb578260006143bf565b6000835b909250905060006143d160018d615375565b8a106143dd578a614464565b6144648d8d6143ed8d6001615839565b8181106143fc576143fc61538c565b905060c00201602001602081019061441491906153a2565b8e8e6144218e6001615839565b8181106144305761443061538c565b905060c00201604001602081019061444891906153a2565b8f8f6144558f6001615839565b8181106114bc576114bc61538c565b6040805160008152602081019182905263022c0d9f60e01b9091529091506001600160a01b0387169063022c0d9f906144a6908690869086906024810161587d565b600060405180830381600087803b1580156144c057600080fd5b505af11580156144d4573d6000803e3d6000fd5b5050505060008d8d8c8181106144ec576144ec61538c565b905060c00201606001602081019061450491906155a5565b60408051888152602081018890526001600160a01b038d81168284015283151560608301529151929350908e169133917fe7857fba7d66e2e29213fda7a0394efbaa80d435dfc7b4c1f147d78c2f9caa62919081900360800190a3505050505050505050508080614574906158aa565b915050614107565b604080516000808252602082019092526001600160a01b0384169083906040516145a6919061581d565b60006040518083038185875af1925050503d80600081146145e3576040519150601f19603f3d011682016040523d82523d6000602084013e6145e8565b606091505b505090508061461f5760405162461bcd60e51b815260206004820152600360248201526222aa2360e91b60448201526064016105e0565b505050565b60006003821115614685575080600061463e6002836156e6565b614649906001615839565b90505b8181101561467f5790508060028161466481866156e6565b61466e9190615839565b61467891906156e6565b905061464c565b50919050565b811561468f575060015b919050565b600080841180156146a55750600083115b80156146b15750600082115b6146e35760405162461bcd60e51b815260206004820152600360248201526212539360ea1b60448201526064016105e0565b826146ee83866156c7565b6140fc91906156e6565b60008183106147075781611960565b5090919050565b6000836001600160a01b03163b116147525760405162461bcd60e51b815260206004820152600760248201526621a7a222a622a760c91b60448201526064016105e0565b6040516001600160a01b03838116602483015260448201839052600091829186169060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b179052516147ac919061581d565b6000604051808303816000865af19150503d80600081146147e9576040519150601f19603f3d011682016040523d82523d6000602084013e6147ee565b606091505b509150915081801561481857508051158061481857508080602001905181019061481891906157b0565b613a485760405162461bcd60e51b81526020600482015260036024820152621254d560ea1b60448201526064016105e0565b6000806000846001600160a01b031684604051614867919061581d565b6000604051808303816000865af19150503d80600081146148a4576040519150601f19603f3d011682016040523d82523d6000602084013e6148a9565b606091505b50915091508180156148d35750805115806148d35750808060200190518101906148d391906157b0565b80156148e857506001600160a01b0385163b15155b95945050505050565b6000614946826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614ab59092919063ffffffff16565b905080516000148061496757508080602001905181019061496791906157b0565b61461f5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016105e0565b60008115614aa1576000846149e388670de0b6b3a76400006156c7565b6149ed91906156e6565b9050600084614a0488670de0b6b3a76400006156c7565b614a0e91906156e6565b90506000670de0b6b3a7640000614a2583856156c7565b614a2f91906156e6565b90506000670de0b6b3a7640000614a4684806156c7565b614a5091906156e6565b670de0b6b3a7640000614a6386806156c7565b614a6d91906156e6565b614a779190615839565b9050670de0b6b3a7640000614a8c82846156c7565b614a9691906156e6565b9450505050506148e8565b614aab85876156c7565b9695505050505050565b60606140fc848460008585600080866001600160a01b03168587604051614adc919061581d565b60006040518083038185875af1925050503d8060008114614b19576040519150601f19603f3d011682016040523d82523d6000602084013e614b1e565b606091505b5091509150614b2f87838387614b3a565b979650505050505050565b60608315614ba9578251600003614ba2576001600160a01b0385163b614ba25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016105e0565b50816140fc565b6140fc8383815115614bbe5781518083602001fd5b8060405162461bcd60e51b81526004016105e0919061595c565b634e487b7160e01b600052600160045260246000fd5b6001600160a01b0381168114614c0357600080fd5b50565b803561468f81614bee565b8015158114614c0357600080fd5b803561468f81614c11565b600080600080600080600080610100898b031215614c4757600080fd5b8835614c5281614bee565b97506020890135614c6281614bee565b96506040890135614c7281614c11565b9550606089013594506080890135935060a0890135925060c0890135614c9781614bee565b8092505060e089013590509295985092959890939650565b60008083601f840112614cc157600080fd5b50813567ffffffffffffffff811115614cd957600080fd5b60208301915083602060c083028501011115610f7d57600080fd5b60008060008060008060a08789031215614d0d57600080fd5b8635955060208701359450604087013567ffffffffffffffff811115614d3257600080fd5b614d3e89828a01614caf565b9095509350506060870135614d5281614bee565b80925050608087013590509295509295509295565b6020808252825182820181905260009190848201906040850190845b81811015614d9f57835183529284019291840191600101614d83565b50909695505050505050565b60008060008060808587031215614dc157600080fd5b8435614dcc81614bee565b93506020850135614ddc81614bee565b92506040850135614dec81614c11565b9396929550929360600135925050565b803560ff8116811461468f57600080fd5b60008060008060008060008060008060006101608c8e031215614e2f57600080fd5b8b35614e3a81614bee565b9a5060208c0135614e4a81614c11565b995060408c0135985060608c0135975060808c0135965060a08c0135614e6f81614bee565b955060c08c0135945060e08c0135614e8681614c11565b9350614e956101008d01614dfc565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600060608486031215614ecc57600080fd5b8335614ed781614bee565b92506020840135614ee781614bee565b91506040840135614ef781614c11565b809150509250925092565b60008060408385031215614f1557600080fd5b8235614f2081614bee565b91506020830135614f3081614bee565b809150509250929050565b6001600160a01b0392831681529116602082015260400190565b60008060008060008060008060006101208a8c031215614f7457600080fd5b8935614f7f81614bee565b985060208a0135614f8f81614bee565b975060408a0135614f9f81614c11565b965060608a0135955060808a0135945060a08a0135935060c08a0135925060e08a0135614fcb81614bee565b809250506101008a013590509295985092959850929598565b600080600080600060808688031215614ffc57600080fd5b85359450602086013567ffffffffffffffff81111561501a57600080fd5b61502688828901614caf565b909550935050604086013561503a81614bee565b949793965091946060013592915050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561508a5761508a61504b565b604052919050565b600067ffffffffffffffff8211156150ac576150ac61504b565b5060051b60200190565b6000806000806000608086880312156150ce57600080fd5b853567ffffffffffffffff808211156150e657600080fd5b818801915088601f8301126150fa57600080fd5b8135602061510f61510a83615092565b615061565b82815260059290921b8401810191818101908c84111561512e57600080fd5b948201945b8386101561514c57853582529482019490820190615133565b9950508901359250508082111561516257600080fd5b5061516f88828901614caf565b909550935061503a905060408701614c06565b60008060006060848603121561519757600080fd5b8335925060208401356151a981614bee565b91506040840135614ef781614bee565b600080600080600060a086880312156151d157600080fd5b85356151dc81614bee565b945060208601356151ec81614bee565b935060408601356151fc81614c11565b94979396509394606081013594506080013592915050565b6000806000806000806000806000806000806101808d8f03121561523757600080fd5b8c3561524281614bee565b9b5060208d013561525281614bee565b9a5060408d013561526281614c11565b995060608d0135985060808d0135975060a08d0135965060c08d013561528781614bee565b955060e08d013594506101008d013561529f81614c11565b93506152ae6101208e01614dfc565b92506101408d013591506101608d013590509295989b509295989b509295989b565b600080600080600080600060e0888a0312156152eb57600080fd5b87356152f681614bee565b9650602088013561530681614c11565b955060408801359450606088013593506080880135925060a088013561532b81614bee565b8092505060c0880135905092959891949750929550565b60208082526003908201526204558560ec1b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b6000828210156153875761538761535f565b500390565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156153b457600080fd5b813561196081614bee565b60208082526002908201526124a960f11b604082015260600190565b838152604060208083018290528282018490526000919060609081850187855b8881101561549657813561540e81614bee565b6001600160a01b039081168452828501359061542982614bee565b90811684860152828701359061543e82614bee565b16838701528185013561545081614c11565b1515838601526080615463838201614c1f565b15159084015260a0615476838201614c06565b6001600160a01b03169084015260c09283019291909101906001016153fb565b50909998505050505050505050565b600082601f8301126154b657600080fd5b815160206154c661510a83615092565b82815260059290921b840181019181810190868411156154e557600080fd5b8286015b84811015610a3b57805183529183019183016154e9565b60008060006060848603121561551557600080fd5b835167ffffffffffffffff8082111561552d57600080fd5b615539878388016154a5565b9450602086015191508082111561554f57600080fd5b61555b878388016154a5565b9350604086015191508082111561557157600080fd5b5061557e868287016154a5565b9150509250925092565b602080825260039082015262494f4160e81b604082015260600190565b6000602082840312156155b757600080fd5b813561196081614c11565b6000602082840312156155d457600080fd5b5051919050565b600060c082840312156155ed57600080fd5b60405160c0810181811067ffffffffffffffff821117156156105761561061504b565b604052823561561e81614bee565b8152602083013561562e81614bee565b6020820152604083013561564181614bee565b6040820152606083013561565481614c11565b6060820152608083013561566781614c11565b608082015260a083013561567a81614bee565b60a08201529392505050565b6001600160a01b039384168152919092166020820152901515604082015260600190565b6000602082840312156156bc57600080fd5b815161196081614bee565b60008160001904831182151516156156e1576156e161535f565b500290565b60008261570357634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b602080825260029082015261494160f01b604082015260600190565b60008060006060848603121561577a57600080fd5b8351925060208401519150604084015190509250925092565b6020808252600390820152620494e560ec1b604082015260600190565b6000602082840312156157c257600080fd5b815161196081614c11565b600080604083850312156157e057600080fd5b505080516020909101519092909150565b60005b8381101561580c5781810151838201526020016157f4565b8381111561329b5750506000910152565b6000825161582f8184602087016157f1565b9190910192915050565b6000821982111561584c5761584c61535f565b500190565b600081518084526158698160208601602086016157f1565b601f01601f19169290920160200192915050565b84815283602082015260018060a01b0383166040820152608060608201526000614aab6080830184615851565b6000600182016158bc576158bc61535f565b5060010190565b600080600080600080600060e0888a0312156158de57600080fd5b87519650602088015195506040880151945060608801519350608088015161590581614c11565b60a089015190935061591681614bee565b60c089015190925061592781614bee565b8091505092959891949750929550565b6000806040838503121561594a57600080fd5b825191506020830151614f3081614c11565b602081526000611960602083018461585156fea26469706673582212203868d4230ffe741b4a7033e2723ee023591770045c558090f62aedf165dfb57264736f6c634300080d0033

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

0000000000000000000000005aef44edfc5a7edd30826c724ea12d7be15bdc30000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac0000000000000000000000003d219d5089331f6bf99cb20b9b199ab3b429337a000000000000000000000000d8377aea61c4c4d43bf0588956f4e861720803c6

-----Decoded View---------------
Arg [0] : _factory (address): 0x5aEf44EDFc5A7eDd30826c724eA12D7Be15bDc30
Arg [1] : _wETH (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [2] : _swapRouter (address): 0x72D63A5B080e1B89cC93F9B9F50cbfA5e291c8ac
Arg [3] : _algebraPoolAPIStorage (address): 0x3d219d5089331f6BF99Cb20b9B199aB3b429337A
Arg [4] : _routerHelper (address): 0xd8377AEa61C4C4d43bF0588956f4E861720803C6

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000005aef44edfc5a7edd30826c724ea12d7be15bdc30
Arg [1] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [2] : 00000000000000000000000072d63a5b080e1b89cc93f9b9f50cbfa5e291c8ac
Arg [3] : 0000000000000000000000003d219d5089331f6bf99cb20b9b199ab3b429337a
Arg [4] : 000000000000000000000000d8377aea61c4c4d43bf0588956f4e861720803c6


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.