Contract 0x27218d498ad7953afd702a1923e4a09ae96a6a9e

 

Contract Overview

Balance:
0 MATIC

MATIC Value:
$0.00

Token:
 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x8f2e5c3076af736964726e06b3eb1bb85e08fd637e87078880c4a67aa92467eaSet Pending Owne...235718032022-01-10 22:21:0413 days 8 hrs ago0xa66745f0092f7460f107e4c66c224553bf4cd727 IN  0x27218d498ad7953afd702a1923e4a09ae96a6a9e0 MATIC0.000830730
0xeeb350707570c564a62b28aaa822883e340560f0ff8fe780c1fd89e792216e28Set Pending Owne...235716212022-01-10 22:14:4813 days 8 hrs ago0xa66745f0092f7460f107e4c66c224553bf4cd727 IN  0x27218d498ad7953afd702a1923e4a09ae96a6a9e0 MATIC0.0009691535
0x7a31233067946963d1f11a27489d716425e107015d00075ec0128da0fd71aedeSet Pending Owne...235716212022-01-10 22:14:4813 days 8 hrs ago0xa66745f0092f7460f107e4c66c224553bf4cd727 IN  0x27218d498ad7953afd702a1923e4a09ae96a6a9e0 MATIC0.001427730
0x69778153045c2c8a042569d0374b054dbe9db7349697240a7ffbf99d01e4d4fdSet Pending Owne...235714882022-01-10 22:08:0613 days 8 hrs ago0xa66745f0092f7460f107e4c66c224553bf4cd727 IN  0x27218d498ad7953afd702a1923e4a09ae96a6a9e0 MATIC0.0006429630
[ Download CSV Export 
Latest 1 internal transaction
Parent Txn Hash Block From To Value
0x3351744f6ebdb9d0c68de4925b79f674dc8d245a0ad70390826ae72f21a90d0a235714722022-01-10 22:07:3413 days 8 hrs ago 0xdbfd940f57e63049039404c1b35b9e47e90f2b3e  Contract Creation0 MATIC
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ZapHandlerV1

Compiler Version
v0.8.6+commit.11564f7e

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 12 : ZapHandlerV1.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.6;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import "./dependencies/Ownable.sol";
import "./interfaces/IZap.sol";
import "./interfaces/IZapHandler.sol";

import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";

import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";

/**
 * @notice The ZapHandlerV1 is the first implementation of the Violin Zap protocol.
 * @notice It allows the owner to define routes that span over multiple uniswap factories.
 * @notice Furthermore, individual hops in the route can set the zero factory to indicate that this hop should be subroutes by an existing route in the handler.
 * @notice All though routes need to be added manually, swaps from token a to token b will create the route [a, main, b] if the main token is set.
 * @notice The ZapHandlerV1 supports token->token, pair->token and token->pair swaps.
 * @notice For pair->token swaps, the pair will first be burned and then two swaps to token are made.
 * @notice For token->pair swaps, two swaps to each subtoken are made and then the pair is minted.
 */
contract ZapHandlerV1 is Ownable, IZapHandler, ReentrancyGuard {
    using EnumerableSet for EnumerableSet.AddressSet;
    using SafeERC20 for IERC20;
    
    /// @dev A list of ways the swap can occur, each way has distinct logic in the algorithm.
    enum SwapType {
        UNDEFINED,
        TOKEN_TO_TOKEN,
        TOKEN_TO_PAIR,
        PAIR_TO_TOKEN,
        PAIR_TO_PAIR
    }

    /// @dev represents the swap type of the route from token0 to token1, for example TOKEN_TO_PAIR.
    mapping(IERC20 => mapping(IERC20 => SwapType)) public tokenSwapType;

    /// @dev Represents the token composition of an LP pair. Used for minting and burning LPs.
    struct PairInfo {
        IERC20 token0;
        IERC20 token1;
    }

    /// @dev The token0 and token1 of the pair if the pair has been registered.
    mapping(IERC20 => PairInfo) public pairInfo;

    /// @dev Represents a UniswapV2Factory compatible factory.
    struct Factory {
        /// @dev The address of the factory.
        address factory;
        /// @dev The fee nominator of the AMM, usually set to 997 for a 0.3% fee.
        uint32 amountsOutNominator;
        /// @dev The fee denominator of the AMM, usually set to 1000.
        uint32 amountsOutDenominator;
    }
    /// @dev An enumerable list of all registered factories.
    EnumerableSet.AddressSet factorySet;
    /// @dev Factory address to the specification of the factory including it's fee structure.
    mapping(address => Factory) public factories;


    /// @dev Represents a single step in a swap route.
    /// @dev Within the algorithm, two special steps are employed: The getFromZapperStep and getFromThisContractStep.
    struct RouteStep {
        /// @dev The token to swap from.
        /// @dev address(0) to indicate that the to token can be pulled from the Zap (msg.sender) contract.
        /// @dev address(1) to indicate that the `to` token can be pulled from this contract.
        IERC20 from;
        /// @dev The token to swap to.
        IERC20 to;
        /// @dev The UniswapV2 compatible pair address to swap over. 
        IUniswapV2Pair pair;
        /// @dev The fee nominator of the AMM, usually set to 997 for a 0.3% fee.
        uint32 amountsOutNominator;
        /// @dev The fee denominator of the AMM, usually set to 1000.
        uint32 amountsOutDenominator;
    }

    /// @dev For any registered from and to pair, provide a route used by the algorithm to execute the swap.
    mapping(IERC20 => mapping(IERC20 => RouteStep[])) public routes;

    /// @dev The main token most AMMs and pairs use, eg. WETH.
    IERC20 public mainToken;


    event FactorySet(
        address indexed factory,
        bool indexed alreadyExists,
        uint32 amountsOutNominator,
        uint32 amountsOutDenominator
    );
    event FactoryRemoved(address indexed factory);
    event RouteAdded(
        IERC20 indexed from,
        IERC20 indexed to,
        bool indexed alreadyExists
    );
    event MainTokenSet(IERC20 indexed mainToken);

    constructor(address _owner) {
        _transferOwnership(_owner);
    }
    
    //** ROUTING **/

    /**
    * @notice Swap `amount` of `fromToken` to `toToken` and send them to the recipient.
    * @notice The `fromToken` and `toToken` arguments can be AMM pairs.
    * @notice Requires `msg.sender` to be a Zap instance.
    * @dev Switches over the different routing types to let the specific handler functions take care of them.
    * @param fromToken The token to take from `msg.sender` and exchange for `toToken`.
    * @param toToken The token that will be bought and sent to the recipient.
    * @param recipient The destination address to receive the `toToken`.
    * @param amount The amount that the zapper should take from the `msg.sender` and swap.
    */
    function convertERC20(
        IERC20 fromToken,
        IERC20 toToken,
        address recipient,
        uint256 amount
    ) external override {
        // Fetch the type of swap or discover it if it's not cached yet.
        SwapType edgeType = tokenSwapType[fromToken][toToken];
        if (edgeType == SwapType.UNDEFINED)
            edgeType = generateSwapType(fromToken, toToken);

        // Execute the swap according to its type.
        if (edgeType == SwapType.TOKEN_TO_TOKEN) {
            handleTokenToToken(fromToken, toToken, recipient, amount);
        } else if (edgeType == SwapType.TOKEN_TO_PAIR) {
            handleTokenToPair(fromToken, toToken, recipient, amount);
        } else if (edgeType == SwapType.PAIR_TO_TOKEN) {
            handlePairToToken(fromToken, toToken, recipient, amount);
        } else if (edgeType == SwapType.PAIR_TO_PAIR) {
            handlePairToPair(fromToken, toToken, recipient, amount);
        }
    }

    /// @notice Swap a token to another token, both tokens are not LPs. This is the most simple swap type.
    /// @param fromToken The token to take from `msg.sender` and exchange for `toToken`.
    /// @param toToken The token that will be bought and sent to the recipient.
    /// @param recipient The destination address to receive the `toToken`.
    /// @param amount The amount that the zapper should take from the `msg.sender` and swap.
    function handleTokenToToken(
        IERC20 fromToken,
        IERC20 toToken,
        address recipient,
        uint256 amount
    ) private {
        // Swap along the route from the `fromToken` to the `toToken`
        // The initial step is set to the dummy to take the tokens out of the Zap contract.
        RouteStep memory lastStep = handleRoute(
            getFromZapperStep(),
            fromToken,
            toToken,
            amount
        );
        // Take the tokens out of the last step and forward them to the recipient. The last amount parameter is only used for the dummies so is set to zero.
        handleSwap(lastStep, recipient, 0);
    }

    /// @notice Swap an LP pair to a token. This is done by burning the LP pair into token0 and token1, temporarily stored inside this contract.
    /// @notice Then, token0 and token1 are both swapped to the `toToken` as if they were TOKEN_TO_TOKEN.
    /// @param fromToken The token to take from `msg.sender` and exchange for `toToken`.
    /// @param toToken The token that will be bought and sent to the `recipient` address.
    /// @param recipient The destination address to receive the `toToken`.
    /// @param amount The amount that the zapper should take from the `msg.sender` and swap.
    function handlePairToToken(
        IERC20 fromToken,
        IERC20 toToken,
        address recipient,
        uint256 amount
    ) private {
        // Get the token0 and token1 pair info of the from token, this has already been generated.
        PairInfo memory fromInfo = pairInfo[fromToken];

        // Pull all LP tokens into the LP contract and burn them to receive the underlying tokens inside of this address.
        IZap(msg.sender).pullAmountTo(address(fromToken), amount);
        IUniswapV2Pair(address(fromToken)).burn(address(this));
        
        /// Executes a swap from `token0` to the `toToken` and sends them to the recipient`.
        uint256 token0bal = fromInfo.token0.balanceOf(address(this));
        if (fromInfo.token0 == toToken) {
            toToken.safeTransfer(recipient, token0bal);
        } else {
            RouteStep memory lastStepToken0 = handleRoute(
                getFromThisContractStep(fromInfo.token0),
                fromInfo.token0,
                toToken,
                token0bal
            );
            handleSwap(lastStepToken0, recipient, 0);
        }

        /// Executes a swap from `token1` to the `toToken` and sends them to the recipient`.
        uint256 token1bal = fromInfo.token1.balanceOf(address(this));
        if (fromInfo.token1 == toToken) {
            toToken.safeTransfer(recipient, token1bal);
        } else {
            RouteStep memory lastStepToken1 = handleRoute(
                getFromThisContractStep(fromInfo.token1),
                fromInfo.token1,
                toToken,
                token1bal
            );
            handleSwap(lastStepToken1, recipient, 0);
        }
    }

    /// @notice Swap a token to an LP pair. This is done by swapping half of the token `amount` to token0 and the other half to token1.
    /// @notice Finally, token0 and token1, stored inside the contract, are forwarded to the LP pair and the LP pair tokens are minted and sent to the recipient.
    /// @param fromToken The token to take from `msg.sender` and exchange for `toToken`.
    /// @param toToken The token that will be bought and sent to the `recipient` address.
    /// @param recipient The destination address to receive the `toToken`.
    /// @param amount The amount that the zapper should take from the `msg.sender` and swap.
    function handleTokenToPair(
        IERC20 fromToken,
        IERC20 toToken,
        address recipient,
        uint256 amount
    ) private {
        // Get the token0 and token1 pair info of the from token, this has already been generated.
        PairInfo memory toInfo = pairInfo[toToken];

        uint256 amount0 = amount / 2;
        uint256 amount1 = amount - amount0;

        /// Swap amount0 (half of amount) to token0 and store it in this contract.
        if (fromToken == toInfo.token0) {
            IZap(msg.sender).pullAmountTo(address(this), amount0);
        } else {
            RouteStep memory lastStepToken0 = handleRoute(
                getFromZapperStep(),
                fromToken,
                toInfo.token0,
                amount0
            );

            handleSwap(lastStepToken0, address(this), amount0);
        }

        /// Swap amount1 (half of amount) to token1 and store it in this contract.
        if (fromToken == toInfo.token1) {
            IZap(msg.sender).pullAmountTo(address(this), amount1);
        } else {
            RouteStep memory lastStepToken1 = handleRoute(
                getFromZapperStep(),
                fromToken,
                toInfo.token1,
                amount1
            );
            handleSwap(lastStepToken1, address(this), amount1);
        }
        // Calculate the correct amounts to add to the AMM pair, similar to the uniswap router, there's some inefficiency for transfer-tax tokens.
        uint256 balance0 = toInfo.token0.balanceOf(address(this));
        uint256 balance1 = toInfo.token1.balanceOf(address(this));
        (uint256 res0, uint256 res1, ) = IUniswapV2Pair(address(toToken))
            .getReserves();
        uint256 amount0ToPair = balance0;
        uint256 amount1ToPair = (balance0 * res1) / res0;

        if (amount1ToPair > balance1) {
            amount0ToPair = (balance1 * res0) / res1;
            amount1ToPair = balance1;
        }
        
        // Transfer the ideal amount of tokens to the LP pair.
        toInfo.token0.safeTransfer(address(toToken), amount0ToPair);
        toInfo.token1.safeTransfer(address(toToken), amount1ToPair);
        
        // The lp pair is minted.
        IUniswapV2Pair(address(toToken)).mint(recipient);

        // The dust is transfered to the owner as this is otherwise lost and `to` will not handle this for our contracts.
        if (amount0ToPair < balance0) {
            toInfo.token0.safeTransfer(owner(), balance0 - amount0ToPair);
        }
        if (amount1ToPair < balance1) {
            toInfo.token1.safeTransfer(owner(), balance1 - amount1ToPair);
        }

    }

    /// @dev This iteration of the zap handler does not support Pair->Pair swaps yet. If needed, this could be done by two pair->token swaps.
    function handlePairToPair(
        IERC20 /**fromToken*/,
        IERC20 /**toToken*/,
        address /**recipient*/,
        uint256 /**amount*/
    ) private pure {
        // TODO: PAIR TO PAIR IMPLEMENTATION
        revert("unimplemented feature: pair to pair routing");
    }

    //** ROUTE HANDLERS **/

    /**
     * @dev Swaps tokens along the saved route between `from` and `to`. The last swap step is not yet handled (swapped) to allow the caller to chose a destination for it.
     * @dev This pull mechanism (using lastStep) is done to allow the wildcard (zero factory) notation to function efficiently. Eg. routes like [token0, 0, wmatic, 0, token1].
     * @dev handleRoute goes through all steps along the route and recurses through subroutes to do the same. It pulls funds from the previous step pairs into the current step pairs.
     * @dev If a route does not exist yet, it attempts to tunnel a route over the main token. This will revert if there is no such route possible.
     * @param from The token to swap from.
     * @param to The token to swap to.
     * @param firstAmount The amount of tokens to pull from the Zap, will only be used if the previousStep is set to the from-zap dummy.
     * @return lastStep The last swap step that still needs to be handled. This needs to be handled to actually send the to tokens to a location.
     */
    function handleRoute(
        RouteStep memory previousStep,
        IERC20 from,
        IERC20 to,
        uint256 firstAmount
    ) private returns (RouteStep memory lastStep) {
        RouteStep[] memory route = routes[from][to];
        if (route.length == 0) {
            generateAutomaticRoute(from, to);
            route = routes[from][to];
        }

        for (uint256 i = 0; i < route.length; i++) {
            RouteStep memory step = route[i];
            // Zero pair indicates nested routing.
            if (address(step.pair) == address(0)) {
                previousStep = handleRoute(
                    previousStep,
                    step.from,
                    step.to,
                    firstAmount
                );
            } else {
                handleSwap(previousStep, address(step.pair), firstAmount);
                previousStep = step;
            }
        }
        return (previousStep);
    }

    /**
     * @dev handleSwap executes a swap on the provided `step` pair. It requires that there are already `from` tokens deposited into this pair.
     * @dev As many `to` tokens will then be sent to the recipient as is allowed by the pair curve.
     * @dev It thus executes a swap on a pair given the pre-existing deposit, and forwards the result to recipient.
     * @dev Dummy steps can be provided to pull tokens from either the Zap contract or from this contract.
     * @param step The step to swap in and transfer from.
     * @param recipient The recipient to sent the swap result to.
     * @param amountIn The amount of tokens to pull from the Zap. Should only be set if ussing a from zap dummy (otherwise it's ignored).
     */
    function handleSwap(
        RouteStep memory step,
        address recipient,
        uint256 amountIn
    ) private {
        // We first handle the dummy step cases.
        // from=address(0): Transfer `amountIn` from the zap contract to the recipient. This is used as the first step in the route.
        // from=addres(1): Transfer all tokens in this contract to the recipient. This is used for the LP related functionality.
        if (address(step.from) == address(0)) {
            IZap(msg.sender).pullAmountTo(recipient, amountIn);
        } else if (address(step.from) == address(1)) {
            step.to.safeTransfer(recipient, step.to.balanceOf(address(this)));
        } else {
            // If the step is not a dummy, this means an actual AMM swap has to occur.
            // For gas optimization, we write out the two cases fully (where from is token0 and where from is token1).
            if (address(step.from) < address(step.to)) {
                (uint256 reserveIn, uint256 reserveOut, ) = step
                    .pair
                    .getReserves(); // get both reserves, should be zero, zero.
                amountIn = step.from.balanceOf(address(step.pair)) - reserveIn;
                uint256 amountOut = getAmountOut(
                    amountIn,
                    reserveIn,
                    reserveOut,
                    step.amountsOutNominator,
                    step.amountsOutDenominator
                );
                step.pair.swap(0, amountOut, recipient, "");
            } else {
                (uint256 reserveOut, uint256 reserveIn, ) = step
                    .pair
                    .getReserves();
                amountIn = step.from.balanceOf(address(step.pair)) - reserveIn;
                uint256 amountOut = getAmountOut(
                    amountIn,
                    reserveIn,
                    reserveOut,
                    step.amountsOutNominator,
                    step.amountsOutDenominator
                );
                step.pair.swap(amountOut, 0, recipient, "");
            }
        }
    }

    //** CONFIGURATION **/
    /// @dev Attempts to tunnel a route over the main token and save it.
    /// @dev This route is of the form [from, 0, main, 0, to]
    function generateAutomaticRoute(IERC20 from, IERC20 to) private {
        IERC20 main = mainToken;
        require(from != main && to != main, "!no route found");
        address[] memory route = new address[](5);
        route[0] = address(from);
        route[1] = address(0);
        route[2] = address(main);
        route[3] = address(0);
        route[4] = address(to);
        _setRoute(from, to, route);
    }

    /// @dev Adds a factory to the list of registered factories that can be used within RouteSpec.
    function setFactory(
        address factory,
        uint32 amountsOutNominator,
        uint32 amountsOutDenominator
    ) external onlyOwner {
        require(amountsOutDenominator >= amountsOutNominator, "!nom > denom");
        require(amountsOutNominator != 0, "!zero");
        assert(amountsOutNominator != 0);
        require(factory != address(0), "!zero factory"); // reserved for subroutes
        bool alreadyExists = factorySet.contains(factory); // for event

        factories[factory] = Factory({
            factory: factory,
            amountsOutNominator: amountsOutNominator,
            amountsOutDenominator: amountsOutDenominator
        });
        factorySet.add(factory);

        emit FactorySet(
            factory,
            alreadyExists,
            amountsOutNominator,
            amountsOutDenominator
        );
    }
    /// @notice Removes a factory from the list of registered factories.
    function removeFactory(address factory) external onlyOwner {
        require(factorySet.contains(factory), "!exists");
        factorySet.remove(factory);
        delete factories[factory];

        emit FactoryRemoved(factory);
    }

    /**
     * @notice Generates and saves a route (and inverse of this route) based on the RouteSpec encoded `inputRoute`.
     * @param from the token to swap from.
     * @param to the token to swap to.
     * @param inputRoute A route in RouteSpec notation indicating the swap steps and the uniswap like factories these swaps should be made.
     */
    function setRoute(
        IERC20 from,
        IERC20 to,
        address[] memory inputRoute
    ) external onlyOwner {
        _setRoute(from, to, inputRoute);
    }

    /**
     * @dev Generates a route based on the RouteSpec encoded `inputRoute` and saves it under routes.
     * @dev A route is denoted as a list of RouteSteps.
     * @dev Also generates and saves the inverse of the route.
     */
    function _setRoute(
        IERC20 from,
        IERC20 to,
        address[] memory inputRoute
    ) private {
        bool alreadyExists = routes[from][to].length > 0;

        generateRoute(from, to, inputRoute);
        emit RouteAdded(from, to, alreadyExists);

        generateInvertedRoute(from, to);
        emit RouteAdded(to, from, alreadyExists);
    }

    /// @dev Updates the main token, this is used for automatic route tunneling.
    function setMainToken(IERC20 _mainToken) external onlyOwner {
        mainToken = _mainToken;
        emit MainTokenSet(_mainToken);
    }

    //** ROUTE GENERATION **/

    /**
     * @dev Generates a new route from token0 and token1 using RouteSpec notation.
     */
    function generateRoute(
        IERC20 token0,
        IERC20 token1,
        address[] memory route
    ) private {
        require(route.length >= 3, "!route too short");
        require(route.length % 2 == 1, "!route has even length");
        require(route[0] == address(token0), "!token0 not route beginning");
        require(
            route[route.length - 1] == address(token1),
            "!token1 not route ending"
        );
        delete routes[token0][token1];

        IERC20 from = IERC20(route[0]);
        from.balanceOf(address(this)); // validate from

        for (uint256 i = 1; i < route.length; i += 2) {
            address factory = route[i];
            IERC20 to = IERC20(route[i + 1]);
            if (factory == address(0)) {
                require(
                    routes[from][to].length > 0,
                    "!swap subroute not created yet"
                );
                routes[token0][token1].push(
                    RouteStep({
                        from: from,
                        pair: IUniswapV2Pair(address(0)),
                        to: to,
                        amountsOutNominator: 0,
                        amountsOutDenominator: 0
                    })
                );
            } else {
                require(
                    factorySet.contains(factory),
                    "!factory does not exist"
                );
                address pairAddress = IUniswapV2Factory(factory).getPair(
                    address(from),
                    address(to)
                );
                require(pairAddress != address(0), "pair does not exist");
                routes[token0][token1].push(
                    RouteStep({
                        from: from,
                        pair: IUniswapV2Pair(pairAddress),
                        to: to,
                        amountsOutNominator: factories[factory]
                            .amountsOutNominator,
                        amountsOutDenominator: factories[factory]
                            .amountsOutDenominator
                    })
                );
            }

            from = to;
        }
    }

    /// @dev Inverts the stored [`from`, `to`] route and stores this as a new route.
    function generateInvertedRoute(IERC20 from, IERC20 to) private {
        delete routes[to][from];
        uint256 length = routes[from][to].length;
        uint256 index;
        RouteStep memory step;
        for (uint256 i = 0; i < length; i++) {
            index = length - 1 - i;
            step = routes[from][to][index];
            routes[to][from].push(
                RouteStep({
                    from: step.to,
                    pair: step.pair,
                    to: step.from,
                    amountsOutNominator: step.amountsOutNominator,
                    amountsOutDenominator: step.amountsOutDenominator
                })
            );
        }
    }

    //** TOKEN INFO GENERATION **/

    /// @dev Figures out the SwapType (eg. TOKEN_TO_TOKEN) for the token pair and stores it.
    /// @dev Calls common UniswapV2Pair functions to guess that the tokens are a pair or not.
    function generateSwapType(IERC20 from, IERC20 to)
        private
        returns (SwapType)
    {
        bool fromPair = getPair(from);
        bool toPair = getPair(to);
        SwapType swapType;
        if (fromPair) {
            swapType = toPair
                ? SwapType.PAIR_TO_PAIR
                : SwapType.PAIR_TO_TOKEN;
        } else {
            swapType = toPair
                ? SwapType.TOKEN_TO_PAIR
                : SwapType.TOKEN_TO_TOKEN;
        }
        tokenSwapType[from][to] = swapType;
        return swapType;
    }

    /// @dev Returns whether `token` is a pair or not. If it is a pair, stores the pairInfo.
    function getPair(IERC20 token) private returns (bool) {
        IUniswapV2Pair pair = IUniswapV2Pair(address(token));
        // WETH has fallback function
        if (token == mainToken)
            return false;
        try pair.getReserves() {
            // get token0
            try pair.token0() returns (address token0) {
                // get token1
                try pair.token1() returns (address token1) {
                    pairInfo[token].token0 = IERC20(token0);
                    pairInfo[token].token1 = IERC20(token1);
                    return true;
                } catch {}
            } catch {}
        } catch {}
        return false;
    }

    //** UTILITIES **/
    
    /// @dev Uses the Uniswap formula to calculate how many tokens can be taken out given `amountIn`.
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut,
        uint256 feeNom,
        uint256 feeDenom
    ) private pure returns (uint256 amountOut) {
        uint256 amountInWithFee = amountIn * feeNom;
        uint256 numerator = amountInWithFee * reserveOut;
        uint256 denominator = reserveIn * feeDenom + amountInWithFee;
        unchecked {
            return denominator == 0 ? 0 : numerator / denominator;
        }
    }

    /// @dev Returns a dummy step that indactes the token should be pulled from the zapper.
    function getFromZapperStep()
        private
        pure
        returns (RouteStep memory)
    {
        RouteStep memory fromZapper;
        return fromZapper;
    }

    /// @dev Returns a dummy step that indactes the `token` should be pulled from this contract.
    function getFromThisContractStep(IERC20 token)
        private
        pure
        returns (RouteStep memory)
    {
        RouteStep memory fromZapper;
        fromZapper.from = IERC20(address(1));
        fromZapper.to = token;
        return fromZapper;
    }

    /// @notice Gets a registered factory at a specific index, use factoryLength() for the upper bound.
    function getFactory(uint256 index) external view returns (address) {
        return factorySet.at(index);
    }

    /// @notice Returns the total number of registered factories.
    function factoryLength() external view returns (uint256) {
        return factorySet.length();
    }

    /// @notice Returns the number of steps on the route from token0 to token1.
    function routeLength(IERC20 token0, IERC20 token1)
        external
        view
        returns (uint256)
    {
        return routes[token0][token1].length;
    }
}

File 2 of 12 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

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 make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

File 3 of 12 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

File 4 of 12 : EnumerableSet.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastvalue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastvalue;
                // Update the index for the moved value
                set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly {
            result := store
        }

        return result;
    }
}

File 5 of 12 : Ownable.sol
// SPDX-License-Identifier: MIT

// Derived from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/1b27c13096d6e4389d62e7b0766a1db53fbb3f1b/contracts/access/Ownable.sol
// Adds pending owner

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

    event PendingOwnershipTransferred(address indexed previousPendingOwner, address indexed newPendingOwner);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

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

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

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

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

    /**
     * @dev Transfers ownership of the contract to the pendingOwner.
     * Can only be called by the pendingOwner.
     */
    function transferOwnership() public virtual {
        require(_msgSender() == pendingOwner, "Ownable: caller is not the pendingOwner");
        require(pendingOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(pendingOwner);
    }

    /**
     * @dev Sets the pendingOwner, ownership is only transferred when they call transferOwnership.
     * Can only be called by the current owner.
     */
    function setPendingOwner(address newPendingOwner) external onlyOwner {
        address oldPendingOwner = pendingOwner;
        pendingOwner = newPendingOwner;

        emit PendingOwnershipTransferred(oldPendingOwner, pendingOwner);
    }

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

File 6 of 12 : IZap.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.6;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IZapHandler.sol";

/// @notice The IZap interface allows contracts to swap a token for another token without having to directly interact with verbose AMMs directly.
/// @notice It furthermore allows to zap to and from an LP pair within a single transaction.
interface IZap {
    /**
    * @notice Swap `amount` of `fromToken` to `toToken` and send them to the `recipient`.
    * @notice The `fromToken` and `toToken` arguments can be AMM pairs.
    * @notice Reverts if the `recipient` received less tokens than `minReceived`.
    * @notice Requires approval.
    * @param fromToken The token to take from `msg.sender` and exchange for `toToken`.
    * @param toToken The token that will be bought and sent to the `recipient`.
    * @param recipient The destination address to receive the `toToken`.
    * @param amount The amount that the zapper should take from the `msg.sender` and swap.
    * @param minReceived The minimum amount of `toToken` the `recipient` should receive. Otherwise the transaction reverts.
    */
    function swapERC20(IERC20 fromToken, IERC20 toToken, address recipient, uint256 amount, uint256 minReceived) external returns (uint256 received);


    /**
    * @notice Swap `amount` of `fromToken` to `toToken` and send them to the `msg.sender`.
    * @notice The `fromToken` and `toToken` arguments can be AMM pairs.
    * @notice Requires approval.
    * @param fromToken The token to take from `msg.sender` and exchange for `toToken`.
    * @param toToken The token that will be bought and sent to the `msg.sender`.
    * @param amount The amount that the zapper should take from the `msg.sender` and swap.
    */
    function swapERC20Fast(IERC20 fromToken, IERC20 toToken, uint256 amount) external;

    /**
    * @notice When the implementation calls pullTo while in a swap, the remaining tokens of the `swap` amount will be sent from the swap `msg.sender` to the `to` address chosen by the implementation.
    * @notice This amount cannot exceed the amount set in the original swap transaction.
    * @notice Traditionally these funds would just be transferred to the implementation which then forwards them to the pairs.
    * @notice However, by using pull hooks, one avoids a transfer which is important for transfer-tax tokens.
    * @dev Can only be called by the implementation.
    * @param to The address to send all remaining tokens of the swap to. This is presumably the first AMM pair in the route.
    */
    function pullTo(address to) external;

    /**
    * @notice When the implementation calls pullAmountTo while in a swap, `amount` tokens of the `swap` amount will be sent from the swap`msg.sender` to the `to` address chosen by the implementation.
    * @notice This amount cannot exceed the amount set in the original swap transaction.
    * @notice Traditionally these funds would just be transferred to the implementation which then forwards them to the pairs.
    * @notice However, by using pull hooks, one avoids a transfer which is important for transfer-tax tokens.
    * @dev Can only be called by the implementation.
    * @param to The address to send `amount` tokens of the swap to. This is presumably the first AMM pair in the route.
    * @param amount The amount of tokens to send to the `to` address, cannot exceed the remaining amount indicated by the swap `amount` parameter.
    */
    function pullAmountTo(address to, uint256 amount) external;

    /**
     * @notice Sets the underlying implementation that fulfills the swap orders.
     * @dev Can only be called by the contract owner.
     * @param implementation The new implementation.
     */
    function setImplementation(IZapHandler implementation) external;
}

File 7 of 12 : IZapHandler.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.6;

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

interface IZapHandler {

    /**
    * @notice Swap `amount` of `fromToken` to `toToken` and send them to the recipient.
    * @notice The `fromToken` and `toToken` arguments can be AMM pairs.
    * @notice Requires `msg.sender` to be a Zap instance.
    * @param fromToken The token to take from `msg.sender` and exchange for `toToken`
    * @param toToken The token that will be bought and sent to the recipient.
    * @param recipient The destination address to receive the `toToken`.
    * @param amount The amount that the zapper should take from the `msg.sender` and swap.
    */
    function convertERC20(IERC20 fromToken, IERC20 toToken, address recipient, uint256 amount) external;
}

File 8 of 12 : IUniswapV2Pair.sol
pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

File 9 of 12 : IUniswapV2Factory.sol
pragma solidity >=0.5.0;

interface IUniswapV2Factory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);

    function getPair(address tokenA, address tokenB) external view returns (address pair);
    function allPairs(uint) external view returns (address pair);
    function allPairsLength() external view returns (uint);

    function createPair(address tokenA, address tokenB) external returns (address pair);

    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
}

File 10 of 12 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

File 11 of 12 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @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
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 12 of 12 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"}],"name":"FactoryRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"},{"indexed":true,"internalType":"bool","name":"alreadyExists","type":"bool"},{"indexed":false,"internalType":"uint32","name":"amountsOutNominator","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"amountsOutDenominator","type":"uint32"}],"name":"FactorySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"mainToken","type":"address"}],"name":"MainTokenSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousPendingOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newPendingOwner","type":"address"}],"name":"PendingOwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"from","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"to","type":"address"},{"indexed":true,"internalType":"bool","name":"alreadyExists","type":"bool"}],"name":"RouteAdded","type":"event"},{"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"convertERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"factories","outputs":[{"internalType":"address","name":"factory","type":"address"},{"internalType":"uint32","name":"amountsOutNominator","type":"uint32"},{"internalType":"uint32","name":"amountsOutDenominator","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factoryLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mainToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"name":"pairInfo","outputs":[{"internalType":"contract IERC20","name":"token0","type":"address"},{"internalType":"contract IERC20","name":"token1","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"factory","type":"address"}],"name":"removeFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token0","type":"address"},{"internalType":"contract IERC20","name":"token1","type":"address"}],"name":"routeLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"},{"internalType":"contract IERC20","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"routes","outputs":[{"internalType":"contract IERC20","name":"from","type":"address"},{"internalType":"contract IERC20","name":"to","type":"address"},{"internalType":"contract IUniswapV2Pair","name":"pair","type":"address"},{"internalType":"uint32","name":"amountsOutNominator","type":"uint32"},{"internalType":"uint32","name":"amountsOutDenominator","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"factory","type":"address"},{"internalType":"uint32","name":"amountsOutNominator","type":"uint32"},{"internalType":"uint32","name":"amountsOutDenominator","type":"uint32"}],"name":"setFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_mainToken","type":"address"}],"name":"setMainToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPendingOwner","type":"address"}],"name":"setPendingOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"from","type":"address"},{"internalType":"contract IERC20","name":"to","type":"address"},{"internalType":"address[]","name":"inputRoute","type":"address[]"}],"name":"setRoute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"},{"internalType":"contract IERC20","name":"","type":"address"}],"name":"tokenSwapType","outputs":[{"internalType":"enum ZapHandlerV1.SwapType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b5060405162004745380380620047458339810160408190526200003491620000b0565b6200003f3362000056565b60016002556200004f8162000056565b50620000e2565b600180546001600160a01b0319908116909155600080546001600160a01b03848116938216841783556040519116929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060208284031215620000c357600080fd5b81516001600160a01b0381168114620000db57600080fd5b9392505050565b61465380620000f26000396000f3fe608060405234801561001057600080fd5b50600436106101365760003560e01c806363c6edfa116100b2578063bcbf4c8a11610081578063dc0a422911610066578063dc0a422914610332578063e30c3978146103a0578063fab52689146103c057600080fd5b8063bcbf4c8a1461030c578063c42069ec1461031f57600080fd5b806363c6edfa14610298578063715018a6146102de578063880ad0af146102e65780638da5cb5b146102ee57600080fd5b80634b37c73f116101095780635cadd6d1116100ee5780635cadd6d1146101e9578063600c557d146101fc578063616f62361461025d57600080fd5b80634b37c73f146101c357806355a204f9146101d657600080fd5b8063263409261461013b57806327ce464e146101505780633fc15f1514610163578063470f4985146101ad575b600080fd5b61014e610149366004614115565b61046e565b005b61014e61015e366004613fe4565b610504565b6009546101839073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6101b56105f4565b6040519081526020016101a4565b61014e6101d1366004613fe4565b610605565b6101836101e43660046142a3565b610777565b61014e6101f736600461401e565b61078a565b61020f61020a366004614226565b610ae0565b6040805173ffffffffffffffffffffffffffffffffffffffff96871681529486166020860152929094169183019190915263ffffffff9081166060830152909116608082015260a0016101a4565b61028b61026b36600461408b565b600360209081526000928352604080842090915290825290205460ff1681565b6040516101a49190614315565b6101b56102a636600461408b565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260086020908152604080832093909416825291909152205490565b61014e610b88565b61014e610c15565b60005473ffffffffffffffffffffffffffffffffffffffff16610183565b61014e61031a3660046140c4565b610d99565b61014e61032d366004613fe4565b610e9b565b610373610340366004613fe4565b6004602052600090815260409020805460019091015473ffffffffffffffffffffffffffffffffffffffff918216911682565b6040805173ffffffffffffffffffffffffffffffffffffffff9384168152929091166020830152016101a4565b6001546101839073ffffffffffffffffffffffffffffffffffffffff1681565b6104356103ce366004613fe4565b60076020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff81169063ffffffff740100000000000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b6040805173ffffffffffffffffffffffffffffffffffffffff909416845263ffffffff92831660208501529116908201526060016101a4565b60005473ffffffffffffffffffffffffffffffffffffffff1633146104f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6104ff838383610f93565b505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610585576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104eb565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f9071993da430171549ad7f254aef8892d82c06c4611995b307947f8a36baf1cb90600090a250565b6000610600600561109d565b905090565b60005473ffffffffffffffffffffffffffffffffffffffff163314610686576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104eb565b6106916005826110a7565b6106f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f216578697374730000000000000000000000000000000000000000000000000060448201526064016104eb565b6107026005826110d9565b5073ffffffffffffffffffffffffffffffffffffffff811660008181526007602052604080822080547fffffffff00000000000000000000000000000000000000000000000000000000169055517fafa2737b2090fa39c66b7348625f0c03726240f724defbc6216d679506f944419190a250565b60006107846005836110fb565b92915050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461080b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104eb565b8163ffffffff168163ffffffff161015610881576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f216e6f6d203e2064656e6f6d000000000000000000000000000000000000000060448201526064016104eb565b63ffffffff82166108ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f217a65726f00000000000000000000000000000000000000000000000000000060448201526064016104eb565b63ffffffff8216610901576109016144a0565b73ffffffffffffffffffffffffffffffffffffffff831661097e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f217a65726f20666163746f72790000000000000000000000000000000000000060448201526064016104eb565b600061098b6005856110a7565b6040805160608101825273ffffffffffffffffffffffffffffffffffffffff80881680835263ffffffff80891660208086019182528983168688019081526000948552600790915295909220935184549251955182167801000000000000000000000000000000000000000000000000027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff96831674010000000000000000000000000000000000000000027fffffffffffffffff00000000000000000000000000000000000000000000000090941691909416179190911793909316179055909150610a7d90600590869061110716565b506040805163ffffffff8086168252841660208201528215159173ffffffffffffffffffffffffffffffffffffffff8716917f371529b40bbd6a0fdc00ffa2f65512b7ba6152f0c2111608704538c5491c9147910160405180910390a350505050565b60086020528260005260406000206020528160005260406000208181548110610b0857600080fd5b600091825260209091206003909102018054600182015460029092015473ffffffffffffffffffffffffffffffffffffffff918216955091811693508116915063ffffffff740100000000000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041685565b60005473ffffffffffffffffffffffffffffffffffffffff163314610c09576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104eb565b610c136000611129565b565b60015473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610cd2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4f776e61626c653a2063616c6c6572206973206e6f74207468652070656e646960448201527f6e674f776e65720000000000000000000000000000000000000000000000000060648201526084016104eb565b60015473ffffffffffffffffffffffffffffffffffffffff16610d77576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016104eb565b600154610c139073ffffffffffffffffffffffffffffffffffffffff16611129565b73ffffffffffffffffffffffffffffffffffffffff808516600090815260036020908152604080832093871683529290529081205460ff1690816004811115610de457610de461452d565b1415610df757610df485856111a8565b90505b6001816004811115610e0b57610e0b61452d565b1415610e2257610e1d8585858561126e565b610e94565b6002816004811115610e3657610e3661452d565b1415610e4857610e1d858585856112d8565b6003816004811115610e5c57610e5c61452d565b1415610e6e57610e1d85858585611930565b6004816004811115610e8257610e8261452d565b1415610e9457610e9485858585611db1565b5050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610f1c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016104eb565b6001805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f1e1c2f9f5b763f63f56cae5a9a406c263521f324917ae0fe29e5cf9955e81ec890600090a35050565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152600860209081526040808320938616835292905220541515610fd3848484611e39565b8015158373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f4543854d4a9097d1b702bd5734d0a5a2834070931096c2a7a0d326bb34f71e5660405160405180910390a461103a848461272d565b8015158473ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f4543854d4a9097d1b702bd5734d0a5a2834070931096c2a7a0d326bb34f71e5660405160405180910390a450505050565b6000610784825490565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260018301602052604081205415155b9392505050565b60006110d28373ffffffffffffffffffffffffffffffffffffffff8416612c04565b60006110d28383612cf7565b60006110d28373ffffffffffffffffffffffffffffffffffffffff8416612d21565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000009081169091556000805473ffffffffffffffffffffffffffffffffffffffff848116938216841783556040519116929183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000806111b484612d70565b905060006111c184612d70565b9050600082156111e157816111d75760036111da565b60045b90506111f3565b816111ed5760016111f0565b60025b90505b73ffffffffffffffffffffffffffffffffffffffff808716600090815260036020908152604080832093891683529290522080548291907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660018360048111156112605761126061452d565b021790555095945050505050565b6040805160a080820183526000808352602080840182905283850182905260608085018390526080948501839052855193840186528284529083018290529382018190529281018390529081018290526112ca90868685612ff2565b9050610e94818460006132e4565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152600460209081526040808320815180830190925280548516825260010154909316908301526113256002846143bf565b905060006113338285614410565b9050826000015173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614156113e2576040517f5188d10d000000000000000000000000000000000000000000000000000000008152306004820152602481018390523390635188d10d90604401600060405180830381600087803b1580156113c557600080fd5b505af11580156113d9573d6000803e3d6000fd5b5050505061144f565b6040805160a080820183526000808352602080840182905283850182905260608085018390526080948501839052855193840186528284529083018290529382018190529281018390529081018290526114409085518a9086612ff2565b905061144d8130856132e4565b505b826020015173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614156114fc576040517f5188d10d000000000000000000000000000000000000000000000000000000008152306004820152602481018290523390635188d10d90604401600060405180830381600087803b1580156114df57600080fd5b505af11580156114f3573d6000803e3d6000fd5b5050505061156b565b6040805160a0808201835260008083526020808401829052838501829052606080850183905260809485018390528551938401865282845290830182905293820181905292810183905290810182905261155c9089866020015185612ff2565b90506115698130846132e4565b505b82516040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b1580156115d457600080fd5b505afa1580156115e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061160c91906142bc565b60208501516040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015291925060009173ffffffffffffffffffffffffffffffffffffffff909116906370a082319060240160206040518083038186803b15801561167d57600080fd5b505afa158015611691573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b591906142bc565b90506000808973ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561170057600080fd5b505afa158015611714573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117389190614267565b506dffffffffffffffffffffffffffff91821693501690508360008361175e84846143d3565b61176891906143bf565b90508481111561178d578261177d85876143d3565b61178791906143bf565b91508490505b88516117b09073ffffffffffffffffffffffffffffffffffffffff168d846138c1565b60208901516117d69073ffffffffffffffffffffffffffffffffffffffff168d836138c1565b6040517f6a62784200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c811660048301528d1690636a62784290602401602060405180830381600087803b15801561183f57600080fd5b505af1158015611853573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187791906142bc565b50858210156118cb576118cb6118a260005473ffffffffffffffffffffffffffffffffffffffff1690565b6118ac8489614410565b8b5173ffffffffffffffffffffffffffffffffffffffff1691906138c1565b84811015611921576119216118f560005473ffffffffffffffffffffffffffffffffffffffff1690565b6118ff8388614410565b60208c015173ffffffffffffffffffffffffffffffffffffffff1691906138c1565b50505050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260046020818152604092839020835180850185528154871681526001909101549095169085015290517f5188d10d00000000000000000000000000000000000000000000000000000000815290810191909152602481018390523390635188d10d90604401600060405180830381600087803b1580156119cc57600080fd5b505af11580156119e0573d6000803e3d6000fd5b50506040517f89afcb4400000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff881692506389afcb4491506024016040805180830381600087803b158015611a4a57600080fd5b505af1158015611a5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8291906142d5565b505080516040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b158015611aed57600080fd5b505afa158015611b01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b2591906142bc565b90508473ffffffffffffffffffffffffffffffffffffffff16826000015173ffffffffffffffffffffffffffffffffffffffff161415611b8557611b8073ffffffffffffffffffffffffffffffffffffffff861685836138c1565b611c0f565b81516040805160a080820183526000808352602080840182905283850182905260608085018390526080948501839052855193840186529483018290529382018190529181018290526001815273ffffffffffffffffffffffffffffffffffffffff9093169183019190915290611bff9084518885612ff2565b9050611c0d818660006132e4565b505b60208201516040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009173ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b158015611c7b57600080fd5b505afa158015611c8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cb391906142bc565b90508573ffffffffffffffffffffffffffffffffffffffff16836020015173ffffffffffffffffffffffffffffffffffffffff161415611d1357611d0e73ffffffffffffffffffffffffffffffffffffffff871686836138c1565b611da8565b6000611d98611d8c85602001516040805160a08082018352600080835260208084018290528385018290526060808501839052608094850183905285519384018652948301829052938201819052918101919091526001815273ffffffffffffffffffffffffffffffffffffffff929092169082015290565b85602001518985612ff2565b9050611da6818760006132e4565b505b50505050505050565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f756e696d706c656d656e74656420666561747572653a207061697220746f207060448201527f61697220726f7574696e6700000000000000000000000000000000000000000060648201526084016104eb565b600381511015611ea5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f21726f75746520746f6f2073686f72740000000000000000000000000000000060448201526064016104eb565b60028151611eb3919061448c565b600114611f1c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f21726f75746520686173206576656e206c656e6774680000000000000000000060448201526064016104eb565b8273ffffffffffffffffffffffffffffffffffffffff1681600081518110611f4657611f4661458b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614611fcb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f21746f6b656e30206e6f7420726f75746520626567696e6e696e67000000000060448201526064016104eb565b8173ffffffffffffffffffffffffffffffffffffffff168160018351611ff19190614410565b815181106120015761200161458b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614612086576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f21746f6b656e31206e6f7420726f75746520656e64696e67000000000000000060448201526064016104eb565b73ffffffffffffffffffffffffffffffffffffffff808416600090815260086020908152604080832093861683529290529081206120c391613f20565b6000816000815181106120d8576120d861458b565b60209081029190910101516040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915073ffffffffffffffffffffffffffffffffffffffff8216906370a082319060240160206040518083038186803b15801561214b57600080fd5b505afa15801561215f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218391906142bc565b5060015b8251811015610e945760008382815181106121a4576121a461458b565b602002602001015190506000848360016121be91906143a7565b815181106121ce576121ce61458b565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156124045773ffffffffffffffffffffffffffffffffffffffff8085166000908152600860209081526040808320938516835292905220546122a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f217377617020737562726f757465206e6f74206372656174656420796574000060448201526064016104eb565b73ffffffffffffffffffffffffffffffffffffffff87811660009081526008602090815260408083208a851684528252808320815160a08101835289861681528686168185019081529281018581526060820186815260808301878152845460018082018755958952969097209251600390960290920180547fffffffffffffffffffffffff00000000000000000000000000000000000000009081169689169690961781559351928401805490951692871692909217909355516002909101805492519351919094167fffffffffffffffff000000000000000000000000000000000000000000000000909216919091177401000000000000000000000000000000000000000063ffffffff93841602177fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff1678010000000000000000000000000000000000000000000000009290911691909102179055612717565b61240f6005836110a7565b612475576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f21666163746f727920646f6573206e6f7420657869737400000000000000000060448201526064016104eb565b6040517fe6a4390500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116600483015282811660248301526000919084169063e6a439059060440160206040518083038186803b1580156124e857600080fd5b505afa1580156124fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125209190614001565b905073ffffffffffffffffffffffffffffffffffffffff811661259f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f7061697220646f6573206e6f742065786973740000000000000000000000000060448201526064016104eb565b73ffffffffffffffffffffffffffffffffffffffff80891660009081526008602090815260408083208b851684528252808320815160a0810183528a86168152878616818501908152968616818401908152898716865260078086529386205463ffffffff74010000000000000000000000000000000000000000808304821660608601908152968852780100000000000000000000000000000000000000000000000092839004821660808601908152865460018181018955978b5298909920945160039098029094018054978a167fffffffffffffffffffffffff00000000000000000000000000000000000000009889161781559951948a018054958a16959097169490941790955551600290970180549351955183169094027fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff95909216027fffffffffffffffff000000000000000000000000000000000000000000000000909216959094169490941793909317161790555b925061272690506002826143a7565b9050612187565b73ffffffffffffffffffffffffffffffffffffffff8082166000908152600860209081526040808320938616835292905290812061276a91613f20565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600860209081526040808320938516835292815282822054835160a081018552838152918201839052928101829052606081018290526080810182905260005b83811015612bfc57806127db600186614410565b6127e59190614410565b73ffffffffffffffffffffffffffffffffffffffff8088166000908152600860209081526040808320938a168352929052208054919450908490811061282d5761282d61458b565b90600052602060002090600302016040518060a00160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016002820160149054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016002820160189054906101000a900463ffffffff1663ffffffff1663ffffffff16815250509150600860008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060a00160405280846020015173ffffffffffffffffffffffffffffffffffffffff168152602001846000015173ffffffffffffffffffffffffffffffffffffffff168152602001846040015173ffffffffffffffffffffffffffffffffffffffff168152602001846060015163ffffffff168152602001846080015163ffffffff16815250908060018154018082558091505060019003906000526020600020906003020160009091909190915060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060608201518160020160146101000a81548163ffffffff021916908363ffffffff16021790555060808201518160020160186101000a81548163ffffffff021916908363ffffffff16021790555050508080612bf490614453565b9150506127c7565b505050505050565b60008181526001830160205260408120548015612ced576000612c28600183614410565b8554909150600090612c3c90600190614410565b9050818114612ca1576000866000018281548110612c5c57612c5c61458b565b9060005260206000200154905080876000018481548110612c7f57612c7f61458b565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612cb257612cb261455c565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610784565b6000915050610784565b6000826000018281548110612d0e57612d0e61458b565b9060005260206000200154905092915050565b6000818152600183016020526040812054612d6857508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610784565b506000610784565b600954600090829073ffffffffffffffffffffffffffffffffffffffff80831691161415612da15750600092915050565b8073ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015612de757600080fd5b505afa925050508015612e35575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252612e3291810190614267565b60015b612e3e57612fe9565b5050508073ffffffffffffffffffffffffffffffffffffffff16630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b158015612e8757600080fd5b505afa925050508015612ed5575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252612ed291810190614001565b60015b612ede57612fe9565b8173ffffffffffffffffffffffffffffffffffffffff1663d21220a76040518163ffffffff1660e01b815260040160206040518083038186803b158015612f2457600080fd5b505afa925050508015612f72575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252612f6f91810190614001565b60015b612f7b57612fe7565b73ffffffffffffffffffffffffffffffffffffffff948516600090815260046020526040902080549286167fffffffffffffffffffffffff0000000000000000000000000000000000000000938416178155600190810180549290961691909216179093555090919050565b505b50600092915050565b6040805160a08101825260008082526020808301829052828401829052606083018290526080830182905273ffffffffffffffffffffffffffffffffffffffff878116835260088252848320908716835281528382208054855181840281018401909652808652939492939091849084015b828210156131165760008481526020908190206040805160a08101825260038602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001808301548216858701526002909201549081169284019290925263ffffffff74010000000000000000000000000000000000000000830481166060850152780100000000000000000000000000000000000000000000000090920490911660808301529083529092019101613064565b50505050905080516000141561323557613130858561394e565b73ffffffffffffffffffffffffffffffffffffffff8086166000908152600860209081526040808320938816835292815282822080548451818402810184019095528085529092909184015b8282101561322e5760008481526020908190206040805160a08101825260038602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001808301548216858701526002909201549081169284019290925263ffffffff7401000000000000000000000000000000000000000083048116606085015278010000000000000000000000000000000000000000000000009092049091166080830152908352909201910161317c565b5050505090505b60005b81518110156132d95760008282815181106132555761325561458b565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff16816040015173ffffffffffffffffffffffffffffffffffffffff1614156132b3576132ac888260000151836020015188612ff2565b97506132c6565b6132c2888260400151876132e4565b8097505b50806132d181614453565b915050613238565b509495945050505050565b825173ffffffffffffffffffffffffffffffffffffffff16613382576040517f5188d10d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602481018290523390635188d10d90604401600060405180830381600087803b15801561336e57600080fd5b505af1158015611da8573d6000803e3d6000fd5b825173ffffffffffffffffffffffffffffffffffffffff166001141561346d5760208301516040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526104ff91849173ffffffffffffffffffffffffffffffffffffffff909116906370a082319060240160206040518083038186803b15801561341357600080fd5b505afa158015613427573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061344b91906142bc565b602086015173ffffffffffffffffffffffffffffffffffffffff1691906138c1565b826020015173ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff1610156136d057600080846040015173ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156134f657600080fd5b505afa15801561350a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061352e9190614267565b50865160408089015190517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526dffffffffffffffffffffffffffff9485169650929093169350849216906370a082319060240160206040518083038186803b1580156135b857600080fd5b505afa1580156135cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135f091906142bc565b6135fa9190614410565b9250600061361f848484896060015163ffffffff168a6080015163ffffffff16613bc9565b60408088015190517f022c0d9f0000000000000000000000000000000000000000000000000000000081526000600482018190526024820184905273ffffffffffffffffffffffffffffffffffffffff898116604484015260806064840152608483019190915292935091169063022c0d9f9060a4015b600060405180830381600087803b1580156136b057600080fd5b505af11580156136c4573d6000803e3d6000fd5b50505050505050505050565b600080846040015173ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561371d57600080fd5b505afa158015613731573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137559190614267565b50865160408089015190517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526dffffffffffffffffffffffffffff9485169650929093169350839216906370a082319060240160206040518083038186803b1580156137df57600080fd5b505afa1580156137f3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061381791906142bc565b6138219190614410565b92506000613846848385896060015163ffffffff168a6080015163ffffffff16613bc9565b60408088015190517f022c0d9f0000000000000000000000000000000000000000000000000000000081526004810183905260006024820181905273ffffffffffffffffffffffffffffffffffffffff898116604484015260806064840152608483019190915292935091169063022c0d9f9060a401613696565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790526104ff908490613c2a565b60095473ffffffffffffffffffffffffffffffffffffffff90811690831681148015906139a757508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b613a0d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f216e6f20726f75746520666f756e64000000000000000000000000000000000060448201526064016104eb565b60408051600580825260c082019092526000916020820160a0803683370190505090508381600081518110613a4457613a4461458b565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600081600181518110613a9357613a9361458b565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508181600281518110613ae157613ae161458b565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600081600381518110613b3057613b3061458b565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508281600481518110613b7e57613b7e61458b565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050613bc3848483610f93565b50505050565b600080613bd684886143d3565b90506000613be486836143d3565b9050600082613bf3868a6143d3565b613bfd91906143a7565b90508015613c1a57808281613c1457613c146144fe565b04613c1d565b60005b9998505050505050505050565b6000613c8c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16613d369092919063ffffffff16565b8051909150156104ff5780806020019051810190613caa9190614069565b6104ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016104eb565b6060613d458484600085613d4d565b949350505050565b606082471015613ddf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016104eb565b843b613e47576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016104eb565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051613e7091906142f9565b60006040518083038185875af1925050503d8060008114613ead576040519150601f19603f3d011682016040523d82523d6000602084013e613eb2565b606091505b5091509150613ec2828286613ecd565b979650505050505050565b60608315613edc5750816110d2565b825115613eec5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104eb9190614356565b5080546000825560030290600052602060002090810190613f419190613f44565b50565b5b80821115613fb25780547fffffffffffffffffffffffff0000000000000000000000000000000000000000908116825560018201805490911690556002810180547fffffffff00000000000000000000000000000000000000000000000000000000169055600301613f45565b5090565b8035613fc1816145e9565b919050565b80516dffffffffffffffffffffffffffff81168114613fc157600080fd5b600060208284031215613ff657600080fd5b81356110d2816145e9565b60006020828403121561401357600080fd5b81516110d2816145e9565b60008060006060848603121561403357600080fd5b833561403e816145e9565b9250602084013561404e8161460b565b9150604084013561405e8161460b565b809150509250925092565b60006020828403121561407b57600080fd5b815180151581146110d257600080fd5b6000806040838503121561409e57600080fd5b82356140a9816145e9565b915060208301356140b9816145e9565b809150509250929050565b600080600080608085870312156140da57600080fd5b84356140e5816145e9565b935060208501356140f5816145e9565b92506040850135614105816145e9565b9396929550929360600135925050565b60008060006060848603121561412a57600080fd5b8335614135816145e9565b9250602084810135614146816145e9565b9250604085013567ffffffffffffffff8082111561416357600080fd5b818701915087601f83011261417757600080fd5b813581811115614189576141896145ba565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f830116810181811085821117156141cc576141cc6145ba565b604052828152858101935084860182860187018c10156141eb57600080fd5b600095505b838610156142155761420181613fb6565b8552600195909501949386019386016141f0565b508096505050505050509250925092565b60008060006060848603121561423b57600080fd5b8335614246816145e9565b92506020840135614256816145e9565b929592945050506040919091013590565b60008060006060848603121561427c57600080fd5b61428584613fc6565b925061429360208501613fc6565b9150604084015161405e8161460b565b6000602082840312156142b557600080fd5b5035919050565b6000602082840312156142ce57600080fd5b5051919050565b600080604083850312156142e857600080fd5b505080516020909101519092909150565b6000825161430b818460208701614427565b9190910192915050565b6020810160058310614350577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b6020815260008251806020840152614375816040850160208701614427565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b600082198211156143ba576143ba6144cf565b500190565b6000826143ce576143ce6144fe565b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561440b5761440b6144cf565b500290565b600082821015614422576144226144cf565b500390565b60005b8381101561444257818101518382015260200161442a565b83811115613bc35750506000910152565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415614485576144856144cf565b5060010190565b60008261449b5761449b6144fe565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff81168114613f4157600080fd5b63ffffffff81168114613f4157600080fdfea264697066735822122007b3107d089c991f4c960ecae261d21c6a82dc8b32fce8ea136345f55caeb9dc64736f6c63430008060033000000000000000000000000a66745f0092f7460f107e4c66c224553bf4cd727

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

000000000000000000000000a66745f0092f7460f107e4c66c224553bf4cd727

-----Decoded View---------------
Arg [0] : _owner (address): 0xa66745f0092f7460f107e4c66c224553bf4cd727

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000a66745f0092f7460f107e4c66c224553bf4cd727


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.