ETH Price: $2,638.29 (-0.09%)

Contract

0xa6d8C77FFf3CDcc43804275aD9997b603a7817c5
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Add Liquidity ET...196955772024-04-20 8:56:47162 days ago1713603407IN
0xa6d8C77F...03a7817c5
1.64007972 ETH0.002126888.8864389
Add Liquidity ET...196955122024-04-20 8:43:47162 days ago1713602627IN
0xa6d8C77F...03a7817c5
0.993411 ETH0.0019818.27417138
Swap Exact Token...196951092024-04-20 7:22:23163 days ago1713597743IN
0xa6d8C77F...03a7817c5
0 ETH0.001445297.97411381
Add Liquidity196949582024-04-20 6:52:11163 days ago1713595931IN
0xa6d8C77F...03a7817c5
0 ETH0.001895748.54120511
Swap Exact Token...196949282024-04-20 6:45:59163 days ago1713595559IN
0xa6d8C77F...03a7817c5
0 ETH0.001249137.46055824
Swap Exact Token...196948882024-04-20 6:37:59163 days ago1713595079IN
0xa6d8C77F...03a7817c5
0 ETH0.001444067.8224597
Remove Liquidity195895632024-04-05 12:33:23177 days ago1712320403IN
0xa6d8C77F...03a7817c5
0 ETH0.0020676817.91427502
Remove Liquidity...195821392024-04-04 11:35:23178 days ago1712230523IN
0xa6d8C77F...03a7817c5
0 ETH0.0032784618.14876311
Remove Liquidity194974692024-03-23 13:02:35190 days ago1711198955IN
0xa6d8C77F...03a7817c5
0 ETH0.0023957218
Remove Liquidity194806522024-03-21 4:25:23193 days ago1710995123IN
0xa6d8C77F...03a7817c5
0 ETH0.0037145928.77058904
Swap Exact Token...194776692024-03-20 18:23:11193 days ago1710958991IN
0xa6d8C77F...03a7817c5
0 ETH0.0070305938.0849656
Remove Liquidity194399442024-03-15 11:07:23198 days ago1710500843IN
0xa6d8C77F...03a7817c5
0 ETH0.0043525237.70050259
Add Liquidity Wi...194244152024-03-13 6:38:47201 days ago1710311927IN
0xa6d8C77F...03a7817c5
0 ETH0.0072067942.12946486
Add Liquidity Wi...194025872024-03-10 5:24:23204 days ago1710048263IN
0xa6d8C77F...03a7817c5
0 ETH0.0082828949.15751715
Add Liquidity193761502024-03-06 12:32:11207 days ago1709728331IN
0xa6d8C77F...03a7817c5
0 ETH0.0188228684.78120792
Remove Liquidity...193497642024-03-02 20:06:59211 days ago1709410019IN
0xa6d8C77F...03a7817c5
0 ETH0.0112855462.46395256
Add Liquidity ET...193337612024-02-29 14:27:35213 days ago1709216855IN
0xa6d8C77F...03a7817c5
0.66279172 ETH0.0181509474.39918976
Swap Exact Token...193158672024-02-27 2:20:35216 days ago1709000435IN
0xa6d8C77F...03a7817c5
0 ETH0.0078485546.69426649
Swap Exact Token...192663752024-02-20 3:55:47223 days ago1708401347IN
0xa6d8C77F...03a7817c5
0 ETH0.0046373925.44985694
Remove Liquidity192663682024-02-20 3:54:23223 days ago1708401263IN
0xa6d8C77F...03a7817c5
0 ETH0.0028752724.91116881
Remove Liquidity...192040232024-02-11 9:46:35231 days ago1707644795IN
0xa6d8C77F...03a7817c5
0 ETH0.004974626.90166074
Add Liquidity ET...191983182024-02-10 14:33:35232 days ago1707575615IN
0xa6d8C77F...03a7817c5
0.017 ETH0.0047988927.33852791
Remove Liquidity...191495282024-02-03 18:12:23239 days ago1706983943IN
0xa6d8C77F...03a7817c5
0 ETH0.0040529921.94745428
Add Liquidity191207302024-01-30 17:11:23243 days ago1706634683IN
0xa6d8C77F...03a7817c5
0 ETH0.0080787636.39445736
Swap Exact Token...191207222024-01-30 17:09:47243 days ago1706634587IN
0xa6d8C77F...03a7817c5
0 ETH0.0080928739.49051464
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
196955772024-04-20 8:56:47162 days ago1713603407
0xa6d8C77F...03a7817c5
1.64007972 ETH
196955122024-04-20 8:43:47162 days ago1713602627
0xa6d8C77F...03a7817c5
0.993411 ETH
195821392024-04-04 11:35:23178 days ago1712230523
0xa6d8C77F...03a7817c5
0.52362051 ETH
195821392024-04-04 11:35:23178 days ago1712230523
0xa6d8C77F...03a7817c5
0.52362051 ETH
193497642024-03-02 20:06:59211 days ago1709410019
0xa6d8C77F...03a7817c5
1.00183833 ETH
193497642024-03-02 20:06:59211 days ago1709410019
0xa6d8C77F...03a7817c5
1.00183833 ETH
193337612024-02-29 14:27:35213 days ago1709216855
0xa6d8C77F...03a7817c5
0.66279172 ETH
192040232024-02-11 9:46:35231 days ago1707644795
0xa6d8C77F...03a7817c5
0.00849985 ETH
192040232024-02-11 9:46:35231 days ago1707644795
0xa6d8C77F...03a7817c5
0.00849985 ETH
191983182024-02-10 14:33:35232 days ago1707575615
0xa6d8C77F...03a7817c5
0.017 ETH
191495282024-02-03 18:12:23239 days ago1706983943
0xa6d8C77F...03a7817c5
0.09974175 ETH
191495282024-02-03 18:12:23239 days ago1706983943
0xa6d8C77F...03a7817c5
0.09974175 ETH
191100952024-01-29 5:27:47245 days ago1706506067
0xa6d8C77F...03a7817c5
0.49816151 ETH
190581022024-01-21 22:20:23252 days ago1705875623
0xa6d8C77F...03a7817c5
1.99384078 ETH
190581022024-01-21 22:20:23252 days ago1705875623
0xa6d8C77F...03a7817c5
1.99384078 ETH
190578692024-01-21 21:31:35252 days ago1705872695
0xa6d8C77F...03a7817c5
1.99384078 ETH
190194922024-01-16 12:36:59257 days ago1705408619
0xa6d8C77F...03a7817c5
0.09974175 ETH
190005662024-01-13 21:10:59260 days ago1705180259
0xa6d8C77F...03a7817c5
0.200877 ETH
190005662024-01-13 21:10:59260 days ago1705180259
0xa6d8C77F...03a7817c5
0.200877 ETH
188797002023-12-27 21:35:59277 days ago1703712959
0xa6d8C77F...03a7817c5
0.99936628 ETH
188514762023-12-23 22:26:35281 days ago1703370395
0xa6d8C77F...03a7817c5
2.00009329 ETH
188514762023-12-23 22:26:35281 days ago1703370395
0xa6d8C77F...03a7817c5
2.00009329 ETH
188511042023-12-23 21:11:23281 days ago1703365883
0xa6d8C77F...03a7817c5
2.00009329 ETH
188395972023-12-22 6:27:11283 days ago1703226431
0xa6d8C77F...03a7817c5
0.003 ETH
187673212023-12-12 3:06:59293 days ago1702350419
0xa6d8C77F...03a7817c5
0.016 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ButtonswapRouter

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 25 : ButtonswapRouter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IButtonswapRouter} from "./interfaces/IButtonswapRouter/IButtonswapRouter.sol";
import {IButtonswapFactory} from
    "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapFactory/IButtonswapFactory.sol";
import {IButtonswapPair} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapPair/IButtonswapPair.sol";
import {TransferHelper} from "./libraries/TransferHelper.sol";
import {ButtonswapLibrary} from "./libraries/ButtonswapLibrary.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import {IWETH} from "./interfaces/IWETH.sol";
import {ETHButtonswapRouter} from "./ETHButtonswapRouter.sol";

contract ButtonswapRouter is ETHButtonswapRouter, IButtonswapRouter {
    constructor(address _factory, address _WETH) ETHButtonswapRouter(_factory, _WETH) {}

    /**
     * @inheritdoc IButtonswapRouter
     */
    function getPair(address tokenA, address tokenB) external view returns (address pair) {
        return IButtonswapFactory(factory).getPair(tokenA, tokenB);
    }

    /**
     * @inheritdoc IButtonswapRouter
     */
    function isCreationRestricted() external view returns (bool _isCreationRestricted) {
        _isCreationRestricted = IButtonswapFactory(factory).isCreationRestricted();
    }

    // **** LIBRARY FUNCTIONS ****

    /**
     * @inheritdoc IButtonswapRouter
     */
    function quote(uint256 amountA, uint256 poolA, uint256 poolB)
        external
        pure
        virtual
        override
        returns (uint256 amountB)
    {
        return ButtonswapLibrary.quote(amountA, poolA, poolB);
    }

    /**
     * @inheritdoc IButtonswapRouter
     */
    function getAmountOut(uint256 amountIn, uint256 poolIn, uint256 poolOut)
        external
        pure
        virtual
        override
        returns (uint256 amountOut)
    {
        return ButtonswapLibrary.getAmountOut(amountIn, poolIn, poolOut);
    }

    /**
     * @inheritdoc IButtonswapRouter
     */
    function getAmountIn(uint256 amountOut, uint256 poolIn, uint256 poolOut)
        external
        pure
        virtual
        override
        returns (uint256 amountIn)
    {
        return ButtonswapLibrary.getAmountIn(amountOut, poolIn, poolOut);
    }

    /**
     * @inheritdoc IButtonswapRouter
     */
    function getAmountsOut(uint256 amountIn, address[] memory path)
        external
        view
        virtual
        override
        returns (uint256[] memory amounts)
    {
        return ButtonswapLibrary.getAmountsOut(factory, amountIn, path);
    }

    /**
     * @inheritdoc IButtonswapRouter
     */
    function getAmountsIn(uint256 amountOut, address[] memory path)
        external
        view
        virtual
        override
        returns (uint256[] memory amounts)
    {
        return ButtonswapLibrary.getAmountsIn(factory, amountOut, path);
    }

    /**
     * @inheritdoc IButtonswapRouter
     */
    function getMintSwappedAmounts(address tokenA, address tokenB, uint256 mintAmountA)
        external
        view
        virtual
        override
        returns (uint256 tokenAToSwap, uint256 swappedReservoirAmountB)
    {
        return ButtonswapLibrary.getMintSwappedAmounts(factory, tokenA, tokenB, mintAmountA);
    }

    /**
     * @inheritdoc IButtonswapRouter
     */
    function getBurnSwappedAmounts(address tokenA, address tokenB, uint256 liquidity)
        external
        view
        virtual
        override
        returns (uint256 tokenOutA, uint256 swappedReservoirAmountA)
    {
        return ButtonswapLibrary.getBurnSwappedAmounts(factory, tokenA, tokenB, liquidity);
    }
}

File 2 of 25 : IButtonswapRouter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IETHButtonswapRouter} from "./IETHButtonswapRouter.sol";

interface IButtonswapRouter is IETHButtonswapRouter {
    /**
     * @notice Returns the Pair contract for given tokens. Returns the zero address if no pair exists
     * @param tokenA First token address
     * @param tokenB Second token address
     * @return pair The pair address
     */
    function getPair(address tokenA, address tokenB) external view returns (address pair);

    /**
     * @notice Returns the factory state of `isCreationRestricted`
     * @return _isCreationRestricted The `isCreationRestricted` state of the factory.
     */
    function isCreationRestricted() external view returns (bool _isCreationRestricted);

    /**
     * @notice Given some amount of an asset and pair pools, returns an equivalent amount of the other asset
     * @param amountA The amount of token A
     * @param poolA The balance of token A in the pool
     * @param poolB The balance of token B in the pool
     * @return amountB The amount of token B
     */
    function quote(uint256 amountA, uint256 poolA, uint256 poolB) external pure returns (uint256 amountB);

    /**
     * @notice Given an input amount of an asset and pair pools, returns the maximum output amount of the other asset
     * Factors in the fee on the input amount.
     * @param amountIn The input amount of the asset
     * @param poolIn The balance of the input asset in the pool
     * @param poolOut The balance of the output asset in the pool
     * @return amountOut The output amount of the other asset
     */
    function getAmountOut(uint256 amountIn, uint256 poolIn, uint256 poolOut)
        external
        pure
        returns (uint256 amountOut);

    /**
     * @notice Given an output amount of an asset and pair pools, returns a required input amount of the other asset
     * @param amountOut The output amount of the asset
     * @param poolIn The balance of the input asset in the pool
     * @param poolOut The balance of the output asset in the pool
     * @return amountIn The required input amount of the other asset
     */
    function getAmountIn(uint256 amountOut, uint256 poolIn, uint256 poolOut) external pure returns (uint256 amountIn);

    /**
     * @notice Given an ordered array of tokens and an input amount of the first asset, performs chained getAmountOut calculations to calculate the output amount of the final asset
     * @param amountIn The input amount of the first asset
     * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token
     * @return amounts The output amounts of each asset in the path
     */
    function getAmountsOut(uint256 amountIn, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    /**
     * @notice Given an ordered array of tokens and an output amount of the final asset, performs chained getAmountIn calculations to calculate the input amount of the first asset
     * @param amountOut The output amount of the final asset
     * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token
     * @return amounts The input amounts of each asset in the path
     */
    function getAmountsIn(uint256 amountOut, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    /**
     * @notice Returns how much of the much of mintAmountA will be swapped for tokenB and for how much during a mintWithReservoir operation.
     * @param tokenA First token address
     * @param tokenB Second token address
     * @param mintAmountA The amount of tokenA to be minted
     * @return tokenAToSwap The amount of tokenA to be exchanged for tokenB from the reservoir
     * @return swappedReservoirAmountB The amount of tokenB returned from the reservoir
     */
    function getMintSwappedAmounts(address tokenA, address tokenB, uint256 mintAmountA)
        external
        view
        returns (uint256 tokenAToSwap, uint256 swappedReservoirAmountB);

    /**
     * @notice Returns how much of tokenA will be withdrawn from the pair and how much of it came from the reservoir during a burnFromReservoir operation.
     * @param tokenA First token address
     * @param tokenB Second token address
     * @param liquidity The amount of liquidity to be burned
     * @return tokenOutA The amount of tokenA to be withdrawn from the pair
     * @return swappedReservoirAmountA The amount of tokenA returned from the reservoir
     */
    function getBurnSwappedAmounts(address tokenA, address tokenB, uint256 liquidity)
        external
        view
        returns (uint256 tokenOutA, uint256 swappedReservoirAmountA);
}

File 3 of 25 : IButtonswapFactory.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

import {IButtonswapFactoryErrors} from "./IButtonswapFactoryErrors.sol";
import {IButtonswapFactoryEvents} from "./IButtonswapFactoryEvents.sol";

interface IButtonswapFactory is IButtonswapFactoryErrors, IButtonswapFactoryEvents {
    /**
     * @notice Returns the current address for `feeTo`.
     * The owner of this address receives the protocol fee as it is collected over time.
     * @return _feeTo The `feeTo` address
     */
    function feeTo() external view returns (address _feeTo);

    /**
     * @notice Returns the current address for `feeToSetter`.
     * The owner of this address has the power to update both `feeToSetter` and `feeTo`.
     * @return _feeToSetter The `feeToSetter` address
     */
    function feeToSetter() external view returns (address _feeToSetter);

    /**
     * @notice The name of the ERC20 liquidity token.
     * @return _tokenName The `tokenName`
     */
    function tokenName() external view returns (string memory _tokenName);

    /**
     * @notice The symbol of the ERC20 liquidity token.
     * @return _tokenSymbol The `tokenSymbol`
     */
    function tokenSymbol() external view returns (string memory _tokenSymbol);

    /**
     * @notice Returns the current state of restricted creation.
     * If true, then no new pairs, only feeToSetter can create new pairs
     * @return _isCreationRestricted The `isCreationRestricted` state
     */
    function isCreationRestricted() external view returns (bool _isCreationRestricted);

    /**
     * @notice Returns the current address for `isCreationRestrictedSetter`.
     * The owner of this address has the power to update both `isCreationRestrictedSetter` and `isCreationRestricted`.
     * @return _isCreationRestrictedSetter The `isCreationRestrictedSetter` address
     */
    function isCreationRestrictedSetter() external view returns (address _isCreationRestrictedSetter);

    /**
     * @notice Get the (unique) Pair address created for the given combination of `tokenA` and `tokenB`.
     * If the Pair does not exist then zero address is returned.
     * @param tokenA The first unsorted token
     * @param tokenB The second unsorted token
     * @return pair The address of the Pair instance
     */
    function getPair(address tokenA, address tokenB) external view returns (address pair);

    /**
     * @notice Get the Pair address at the given `index`, ordered chronologically.
     * @param index The index to query
     * @return pair The address of the Pair created at the given `index`
     */
    function allPairs(uint256 index) external view returns (address pair);

    /**
     * @notice Get the current total number of Pairs created
     * @return count The total number of Pairs created
     */
    function allPairsLength() external view returns (uint256 count);

    /**
     * @notice Creates a new {ButtonswapPair} instance for the given unsorted tokens `tokenA` and `tokenB`.
     * @dev The tokens are sorted later, but can be provided to this method in either order.
     * @param tokenA The first unsorted token address
     * @param tokenB The second unsorted token address
     * @return pair The address of the new {ButtonswapPair} instance
     */
    function createPair(address tokenA, address tokenB) external returns (address pair);

    /**
     * @notice Updates the address that receives the protocol fee.
     * This can only be called by the `feeToSetter` address.
     * @param _feeTo The new address
     */
    function setFeeTo(address _feeTo) external;

    /**
     * @notice Updates the address that has the power to set the `feeToSetter` and `feeTo` addresses.
     * This can only be called by the `feeToSetter` address.
     * @param _feeToSetter The new address
     */
    function setFeeToSetter(address _feeToSetter) external;

    /**
     * @notice Updates the state of restricted creation.
     * This can only be called by the `feeToSetter` address.
     * @param _isCreationRestricted The new state
     */
    function setIsCreationRestricted(bool _isCreationRestricted) external;

    /**
     * @notice Updates the address that has the power to set the `isCreationRestrictedSetter` and `isCreationRestricted`.
     * This can only be called by the `isCreationRestrictedSetter` address.
     * @param _isCreationRestrictedSetter The new address
     */
    function setIsCreationRestrictedSetter(address _isCreationRestrictedSetter) external;

    /**
     * @notice Returns the current address for `isPausedSetter`.
     * The owner of this address has the power to update both `isPausedSetter` and call `setIsPaused`.
     * @return _isPausedSetter The `isPausedSetter` address
     */
    function isPausedSetter() external view returns (address _isPausedSetter);

    /**
     * @notice Updates the address that has the power to set the `isPausedSetter` and call `setIsPaused`.
     * This can only be called by the `isPausedSetter` address.
     * @param _isPausedSetter The new address
     */
    function setIsPausedSetter(address _isPausedSetter) external;

    /**
     * @notice Updates the pause state of given Pairs.
     * This can only be called by the `feeToSetter` address.
     * @param pairs A list of addresses for the pairs that should be updated
     * @param isPausedNew The new pause state
     */
    function setIsPaused(address[] calldata pairs, bool isPausedNew) external;

    /**
     * @notice Returns the current address for `paramSetter`.
     * The owner of this address has the power to update `paramSetter`, default parameters, and current parameters on existing pairs
     * @return _paramSetter The `paramSetter` address
     */
    function paramSetter() external view returns (address _paramSetter);

    /**
     * @notice Updates the address that has the power to set the `paramSetter` and update the default params.
     * This can only be called by the `paramSetter` address.
     * @param _paramSetter The new address
     */
    function setParamSetter(address _paramSetter) external;

    /**
     * @notice Returns the default value of `movingAverageWindow` used for new pairs.
     * @return _defaultMovingAverageWindow The `defaultMovingAverageWindow` value
     */
    function defaultMovingAverageWindow() external view returns (uint32 _defaultMovingAverageWindow);

    /**
     * @notice Returns the default value of `maxVolatilityBps` used for new pairs.
     * @return _defaultMaxVolatilityBps The `defaultMaxVolatilityBps` value
     */
    function defaultMaxVolatilityBps() external view returns (uint16 _defaultMaxVolatilityBps);

    /**
     * @notice Returns the default value of `minTimelockDuration` used for new pairs.
     * @return _defaultMinTimelockDuration The `defaultMinTimelockDuration` value
     */
    function defaultMinTimelockDuration() external view returns (uint32 _defaultMinTimelockDuration);

    /**
     * @notice Returns the default value of `maxTimelockDuration` used for new pairs.
     * @return _defaultMaxTimelockDuration The `defaultMaxTimelockDuration` value
     */
    function defaultMaxTimelockDuration() external view returns (uint32 _defaultMaxTimelockDuration);

    /**
     * @notice Returns the default value of `maxSwappableReservoirLimitBps` used for new pairs.
     * @return _defaultMaxSwappableReservoirLimitBps The `defaultMaxSwappableReservoirLimitBps` value
     */
    function defaultMaxSwappableReservoirLimitBps()
        external
        view
        returns (uint16 _defaultMaxSwappableReservoirLimitBps);

    /**
     * @notice Returns the default value of `swappableReservoirGrowthWindow` used for new pairs.
     * @return _defaultSwappableReservoirGrowthWindow The `defaultSwappableReservoirGrowthWindow` value
     */
    function defaultSwappableReservoirGrowthWindow()
        external
        view
        returns (uint32 _defaultSwappableReservoirGrowthWindow);

    /**
     * @notice Updates the default parameters used for new pairs.
     * This can only be called by the `paramSetter` address.
     * @param newDefaultMovingAverageWindow The new defaultMovingAverageWindow
     * @param newDefaultMaxVolatilityBps The new defaultMaxVolatilityBps
     * @param newDefaultMinTimelockDuration The new defaultMinTimelockDuration
     * @param newDefaultMaxTimelockDuration The new defaultMaxTimelockDuration
     * @param newDefaultMaxSwappableReservoirLimitBps The new defaultMaxSwappableReservoirLimitBps
     * @param newDefaultSwappableReservoirGrowthWindow The new defaultSwappableReservoirGrowthWindow
     */
    function setDefaultParameters(
        uint32 newDefaultMovingAverageWindow,
        uint16 newDefaultMaxVolatilityBps,
        uint32 newDefaultMinTimelockDuration,
        uint32 newDefaultMaxTimelockDuration,
        uint16 newDefaultMaxSwappableReservoirLimitBps,
        uint32 newDefaultSwappableReservoirGrowthWindow
    ) external;

    /**
     * @notice Updates the `movingAverageWindow` value of given Pairs.
     * This can only be called by the `paramSetter` address.
     * @param pairs A list of addresses for the pairs that should be updated
     * @param newMovingAverageWindow The new `movingAverageWindow` value
     */
    function setMovingAverageWindow(address[] calldata pairs, uint32 newMovingAverageWindow) external;

    /**
     * @notice Updates the `maxVolatilityBps` value of given Pairs.
     * This can only be called by the `paramSetter` address.
     * @param pairs A list of addresses for the pairs that should be updated
     * @param newMaxVolatilityBps The new `maxVolatilityBps` value
     */
    function setMaxVolatilityBps(address[] calldata pairs, uint16 newMaxVolatilityBps) external;

    /**
     * @notice Updates the `minTimelockDuration` value of given Pairs.
     * This can only be called by the `paramSetter` address.
     * @param pairs A list of addresses for the pairs that should be updated
     * @param newMinTimelockDuration The new `minTimelockDuration` value
     */
    function setMinTimelockDuration(address[] calldata pairs, uint32 newMinTimelockDuration) external;

    /**
     * @notice Updates the `maxTimelockDuration` value of given Pairs.
     * This can only be called by the `paramSetter` address.
     * @param pairs A list of addresses for the pairs that should be updated
     * @param newMaxTimelockDuration The new `maxTimelockDuration` value
     */
    function setMaxTimelockDuration(address[] calldata pairs, uint32 newMaxTimelockDuration) external;

    /**
     * @notice Updates the `maxSwappableReservoirLimitBps` value of given Pairs.
     * This can only be called by the `paramSetter` address.
     * @param pairs A list of addresses for the pairs that should be updated
     * @param newMaxSwappableReservoirLimitBps The new `maxSwappableReservoirLimitBps` value
     */
    function setMaxSwappableReservoirLimitBps(address[] calldata pairs, uint16 newMaxSwappableReservoirLimitBps)
        external;

    /**
     * @notice Updates the `swappableReservoirGrowthWindow` value of given Pairs.
     * This can only be called by the `paramSetter` address.
     * @param pairs A list of addresses for the pairs that should be updated
     * @param newSwappableReservoirGrowthWindow The new `swappableReservoirGrowthWindow` value
     */
    function setSwappableReservoirGrowthWindow(address[] calldata pairs, uint32 newSwappableReservoirGrowthWindow)
        external;

    /**
     * @notice Returns the last token pair created and the parameters used.
     * @return token0 The first token address
     * @return token1 The second token address
     * @return movingAverageWindow The moving average window
     * @return maxVolatilityBps The max volatility bps
     * @return minTimelockDuration The minimum time lock duration
     * @return maxTimelockDuration The maximum time lock duration
     * @return maxSwappableReservoirLimitBps The max swappable reservoir limit bps
     * @return swappableReservoirGrowthWindow The swappable reservoir growth window
     */
    function lastCreatedTokensAndParameters()
        external
        returns (
            address token0,
            address token1,
            uint32 movingAverageWindow,
            uint16 maxVolatilityBps,
            uint32 minTimelockDuration,
            uint32 maxTimelockDuration,
            uint16 maxSwappableReservoirLimitBps,
            uint32 swappableReservoirGrowthWindow
        );
}

File 4 of 25 : IButtonswapPair.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

import {IButtonswapPairErrors} from "./IButtonswapPairErrors.sol";
import {IButtonswapPairEvents} from "./IButtonswapPairEvents.sol";
import {IButtonswapERC20} from "../IButtonswapERC20/IButtonswapERC20.sol";

interface IButtonswapPair is IButtonswapPairErrors, IButtonswapPairEvents, IButtonswapERC20 {
    /**
     * @notice The smallest value that {IButtonswapERC20-totalSupply} can be.
     * @dev After the first mint the total liquidity (represented by the liquidity token total supply) can never drop below this value.
     *
     * This is to protect against an attack where the attacker mints a very small amount of liquidity, and then donates pool tokens to skew the ratio.
     * This results in future minters receiving no liquidity tokens when they deposit.
     * By enforcing a minimum liquidity value this attack becomes prohibitively expensive to execute.
     * @return MINIMUM_LIQUIDITY The MINIMUM_LIQUIDITY value
     */
    function MINIMUM_LIQUIDITY() external pure returns (uint256 MINIMUM_LIQUIDITY);

    /**
     * @notice The duration for which the moving average is calculated for.
     * @return _movingAverageWindow The value of movingAverageWindow
     */
    function movingAverageWindow() external view returns (uint32 _movingAverageWindow);

    /**
     * @notice Updates the movingAverageWindow parameter of the pair.
     * This can only be called by the Factory address.
     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#movingaveragewindow) for more detail.
     * @param newMovingAverageWindow The new value for movingAverageWindow
     */
    function setMovingAverageWindow(uint32 newMovingAverageWindow) external;

    /**
     * @notice Numerator (over 10_000) of the threshold when price volatility triggers maximum single-sided timelock duration.
     * @return _maxVolatilityBps The value of maxVolatilityBps
     */
    function maxVolatilityBps() external view returns (uint16 _maxVolatilityBps);

    /**
     * @notice Updates the maxVolatilityBps parameter of the pair.
     * This can only be called by the Factory address.
     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#maxvolatilitybps) for more detail.
     * @param newMaxVolatilityBps The new value for maxVolatilityBps
     */
    function setMaxVolatilityBps(uint16 newMaxVolatilityBps) external;

    /**
     * @notice How long the minimum singled-sided timelock lasts for.
     * @return _minTimelockDuration The value of minTimelockDuration
     */
    function minTimelockDuration() external view returns (uint32 _minTimelockDuration);

    /**
     * @notice Updates the minTimelockDuration parameter of the pair.
     * This can only be called by the Factory address.
     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#mintimelockduration) for more detail.
     * @param newMinTimelockDuration The new value for minTimelockDuration
     */
    function setMinTimelockDuration(uint32 newMinTimelockDuration) external;

    /**
     * @notice How long the maximum singled-sided timelock lasts for.
     * @return _maxTimelockDuration The value of maxTimelockDuration
     */
    function maxTimelockDuration() external view returns (uint32 _maxTimelockDuration);

    /**
     * @notice Updates the maxTimelockDuration parameter of the pair.
     * This can only be called by the Factory address.
     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#maxtimelockduration) for more detail.
     * @param newMaxTimelockDuration The new value for maxTimelockDuration
     */
    function setMaxTimelockDuration(uint32 newMaxTimelockDuration) external;

    /**
     * @notice Numerator (over 10_000) of the fraction of the pool balance that acts as the maximum limit on how much of the reservoir
     * can be swapped in a given timeframe.
     * @return _maxSwappableReservoirLimitBps The value of maxSwappableReservoirLimitBps
     */
    function maxSwappableReservoirLimitBps() external view returns (uint16 _maxSwappableReservoirLimitBps);

    /**
     * @notice Updates the maxSwappableReservoirLimitBps parameter of the pair.
     * This can only be called by the Factory address.
     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#maxswappablereservoirlimitbps) for more detail.
     * @param newMaxSwappableReservoirLimitBps The new value for maxSwappableReservoirLimitBps
     */
    function setMaxSwappableReservoirLimitBps(uint16 newMaxSwappableReservoirLimitBps) external;

    /**
     * @notice How much time it takes for the swappable reservoir value to grow from nothing to its maximum value.
     * @return _swappableReservoirGrowthWindow The value of swappableReservoirGrowthWindow
     */
    function swappableReservoirGrowthWindow() external view returns (uint32 _swappableReservoirGrowthWindow);

    /**
     * @notice Updates the swappableReservoirGrowthWindow parameter of the pair.
     * This can only be called by the Factory address.
     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#swappablereservoirgrowthwindow) for more detail.
     * @param newSwappableReservoirGrowthWindow The new value for swappableReservoirGrowthWindow
     */
    function setSwappableReservoirGrowthWindow(uint32 newSwappableReservoirGrowthWindow) external;

    /**
     * @notice The address of the {ButtonswapFactory} instance used to create this Pair.
     * @dev Set to `msg.sender` in the Pair constructor.
     * @return factory The factory address
     */
    function factory() external view returns (address factory);

    /**
     * @notice The address of the first sorted token.
     * @return token0 The token address
     */
    function token0() external view returns (address token0);

    /**
     * @notice The address of the second sorted token.
     * @return token1 The token address
     */
    function token1() external view returns (address token1);

    /**
     * @notice The time-weighted average price of the Pair.
     * The price is of `token0` in terms of `token1`.
     * @dev The price is represented as a [UQ112x112](https://en.wikipedia.org/wiki/Q_(number_format)) to maintain precision.
     * Consequently this value must be divided by `2^112` to get the actual price.
     *
     * Because of the time weighting, `price0CumulativeLast` must also be divided by the total Pair lifetime to get the average price over that time period.
     * @return price0CumulativeLast The current cumulative `token0` price
     */
    function price0CumulativeLast() external view returns (uint256 price0CumulativeLast);

    /**
     * @notice The time-weighted average price of the Pair.
     * The price is of `token1` in terms of `token0`.
     * @dev The price is represented as a [UQ112x112](https://en.wikipedia.org/wiki/Q_(number_format)) to maintain precision.
     * Consequently this value must be divided by `2^112` to get the actual price.
     *
     * Because of the time weighting, `price1CumulativeLast` must also be divided by the total Pair lifetime to get the average price over that time period.
     * @return price1CumulativeLast The current cumulative `token1` price
     */
    function price1CumulativeLast() external view returns (uint256 price1CumulativeLast);

    /**
     * @notice The timestamp for when the single-sided timelock concludes.
     * The timelock is initiated based on price volatility of swaps over the last `movingAverageWindow`, and can be
     *   extended by new swaps if they are sufficiently volatile.
     * The timelock protects against attempts to manipulate the price that is used to valuate the reservoir tokens during
     *   single-sided operations.
     * It also guards against general legitimate volatility, as it is preferable to defer single-sided operations until
     *   it is clearer what the market considers the price to be.
     * @return singleSidedTimelockDeadline The current deadline timestamp
     */
    function singleSidedTimelockDeadline() external view returns (uint120 singleSidedTimelockDeadline);

    /**
     * @notice The timestamp by which the amount of reservoir tokens that can be exchanged during a single-sided operation
     *   reaches its maximum value.
     * This maximum value is not necessarily the entirety of the reservoir, instead being calculated as a fraction of the
     *   corresponding token's active liquidity.
     * @return swappableReservoirLimitReachesMaxDeadline The current deadline timestamp
     */
    function swappableReservoirLimitReachesMaxDeadline()
        external
        view
        returns (uint120 swappableReservoirLimitReachesMaxDeadline);

    /**
     * @notice Returns the current limit on the number of reservoir tokens that can be exchanged during a single-sided mint/burn operation.
     * @return swappableReservoirLimit The amount of reservoir token that can be exchanged
     */
    function getSwappableReservoirLimit() external view returns (uint256 swappableReservoirLimit);

    /**
     * @notice Whether the Pair is currently paused
     * @return _isPaused The paused state
     */
    function getIsPaused() external view returns (bool _isPaused);

    /**
     * @notice Updates the pause state.
     * This can only be called by the Factory address.
     * @param isPausedNew The new value for isPaused
     */
    function setIsPaused(bool isPausedNew) external;

    /**
     * @notice Get the current liquidity values.
     * @return _pool0 The active `token0` liquidity
     * @return _pool1 The active `token1` liquidity
     * @return _reservoir0 The inactive `token0` liquidity
     * @return _reservoir1 The inactive `token1` liquidity
     * @return _blockTimestampLast The timestamp of when the price was last updated
     */
    function getLiquidityBalances()
        external
        view
        returns (uint112 _pool0, uint112 _pool1, uint112 _reservoir0, uint112 _reservoir1, uint32 _blockTimestampLast);

    /**
     * @notice The current `movingAveragePrice0` value, based on the current block timestamp.
     * @dev This is the `token0` price, time weighted to prevent manipulation.
     * Refer to [reservoir-valuation.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/reservoir-valuation.md#price-stability) for more detail.
     *
     * The price is represented as a [UQ112x112](https://en.wikipedia.org/wiki/Q_(number_format)) to maintain precision.
     * It is used to valuate the reservoir tokens that are exchanged during single-sided operations.
     * @return _movingAveragePrice0 The current `movingAveragePrice0` value
     */
    function movingAveragePrice0() external view returns (uint256 _movingAveragePrice0);

    /**
     * @notice Mints new liquidity tokens to `to` based on `amountIn0` of `token0` and `amountIn1  of`token1` deposited.
     * Expects both tokens to be deposited in a ratio that matches the current Pair price.
     * @dev The token deposits are deduced to be the delta between token balance before and after the transfers in order to account for unusual tokens.
     * Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#dual-sided-mint) for more detail.
     * @param amountIn0 The amount of `token0` that should be transferred in from the user
     * @param amountIn1 The amount of `token1` that should be transferred in from the user
     * @param to The account that receives the newly minted liquidity tokens
     * @return liquidityOut THe amount of liquidity tokens minted
     */
    function mint(uint256 amountIn0, uint256 amountIn1, address to) external returns (uint256 liquidityOut);

    /**
     * @notice Mints new liquidity tokens to `to` based on how much `token0` or `token1` has been deposited.
     * The token transferred is the one that the Pair does not have a non-zero inactive liquidity balance for.
     * Expects only one token to be deposited, so that it can be paired with the other token's inactive liquidity.
     * @dev The token deposits are deduced to be the delta between token balance before and after the transfers in order to account for unusual tokens.
     * Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#single-sided-mint) for more detail.
     * @param amountIn The amount of tokens that should be transferred in from the user
     * @param to The account that receives the newly minted liquidity tokens
     * @return liquidityOut THe amount of liquidity tokens minted
     */
    function mintWithReservoir(uint256 amountIn, address to) external returns (uint256 liquidityOut);

    /**
     * @notice Burns `liquidityIn` liquidity tokens to redeem to `to` the corresponding `amountOut0` of `token0` and `amountOut1` of `token1`.
     * @dev Refer to [burn-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/burn-math.md#dual-sided-burn) for more detail.
     * @param liquidityIn The amount of liquidity tokens to burn
     * @param to The account that receives the redeemed tokens
     * @return amountOut0 The amount of `token0` that the liquidity tokens are redeemed for
     * @return amountOut1 The amount of `token1` that the liquidity tokens are redeemed for
     */
    function burn(uint256 liquidityIn, address to) external returns (uint256 amountOut0, uint256 amountOut1);

    /**
     * @notice Burns `liquidityIn` liquidity tokens to redeem to `to` the corresponding `amountOut0` of `token0` and `amountOut1` of `token1`.
     * Only returns tokens from the non-zero inactive liquidity balance, meaning one of `amountOut0` and `amountOut1` will be zero.
     * @dev Refer to [burn-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/burn-math.md#single-sided-burn) for more detail.
     * @param liquidityIn The amount of liquidity tokens to burn
     * @param to The account that receives the redeemed tokens
     * @return amountOut0 The amount of `token0` that the liquidity tokens are redeemed for
     * @return amountOut1 The amount of `token1` that the liquidity tokens are redeemed for
     */
    function burnFromReservoir(uint256 liquidityIn, address to)
        external
        returns (uint256 amountOut0, uint256 amountOut1);

    /**
     * @notice Swaps one token for the other, taking `amountIn0` of `token0` and `amountIn1` of `token1` from the sender and sending `amountOut0` of `token0` and `amountOut1` of `token1` to `to`.
     * The price of the swap is determined by maintaining the "K Invariant".
     * A 0.3% fee is collected to distribute between liquidity providers and the protocol.
     * @dev The token deposits are deduced to be the delta between the current Pair contract token balances and the last stored balances.
     * Optional calldata can be passed to `data`, which will be used to confirm the output token transfer with `to` if `to` is a contract that implements the {IButtonswapCallee} interface.
     * Refer to [swap-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/swap-math.md) for more detail.
     * @param amountIn0 The amount of `token0` that the sender sends
     * @param amountIn1 The amount of `token1` that the sender sends
     * @param amountOut0 The amount of `token0` that the recipient receives
     * @param amountOut1 The amount of `token1` that the recipient receives
     * @param to The account that receives the swap output
     */
    function swap(uint256 amountIn0, uint256 amountIn1, uint256 amountOut0, uint256 amountOut1, address to) external;
}

File 5 of 25 : TransferHelper.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// ToDo: Replace with solmate/SafeTransferLib
pragma solidity >=0.6.0;

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(address token, address to, uint256 value) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: APPROVE_FAILED");
    }

    function safeTransfer(address token, address to, uint256 value) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FAILED");
    }

    function safeTransferFrom(address token, address from, address to, uint256 value) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FROM_FAILED");
    }

    function safeTransferETH(address to, uint256 value) internal {
        (bool success,) = to.call{value: value}(new bytes(0));
        require(success, "TransferHelper: ETH_TRANSFER_FAILED");
    }
}

File 6 of 25 : ButtonswapLibrary.sol
pragma solidity ^0.8.13;

import {IButtonswapPair} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapPair/IButtonswapPair.sol";
import {Math} from "buttonswap-periphery_buttonswap-core/libraries/Math.sol";
import {IERC20} from "../interfaces/IERC20.sol";

library ButtonswapLibrary {
    /// @notice Identical addresses provided
    error IdenticalAddresses();
    /// @notice Zero address provided
    error ZeroAddress();
    /// @notice Insufficient amount provided
    error InsufficientAmount();
    /// @notice Insufficient liquidity provided
    error InsufficientLiquidity();
    /// @notice Insufficient input amount provided
    error InsufficientInputAmount();
    /// @notice Insufficient output amount provided
    error InsufficientOutputAmount();
    /// @notice Invalid path provided
    error InvalidPath();

    /**
     * @dev Returns sorted token addresses, used to handle return values from pairs sorted in this order
     * @param tokenA First token address
     * @param tokenB Second token address
     * @return token0 First sorted token address
     * @return token1 Second sorted token address
     */
    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        if (tokenA == tokenB) {
            revert IdenticalAddresses();
        }
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        // If the tokens are different and sorted, only token0 can be the zero address
        if (token0 == address(0)) {
            revert ZeroAddress();
        }
    }

    /**
     * @dev Predicts the address that the Pair contract for given tokens would have been deployed to
     * @dev Specifically, this calculates the CREATE2 address for a Pair contract.
     * @dev It's done this way to avoid making any external calls, and thus saving on gas versus other approaches.
     * @param factory The address of the ButtonswapFactory used to create the pair
     * @param tokenA First token address
     * @param tokenB Second token address
     * @return pair The pair address
     */
    function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        // Init Hash Code is generated by the following command:
        //        bytes32 initHashCode = keccak256(abi.encodePacked(type(ButtonswapPair).creationCode));
        pair = address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            hex"ff",
                            factory,
                            keccak256(abi.encodePacked(token0, token1)),
                            hex"912fa011211d18178fef8f22392edc90ca8f101645ab8347e1359b5ce2f890df" // init code hash
                        )
                    )
                )
            )
        );
    }

    /**
     * @dev Fetches and sorts the pools for a pair. Pools are the current token balances in the pair contract serving as liquidity.
     * @param factory The address of the ButtonswapFactory
     * @param tokenA First token address
     * @param tokenB Second token address
     * @return poolA Pool corresponding to tokenA
     * @return poolB Pool corresponding to tokenB
     */
    function getPools(address factory, address tokenA, address tokenB)
        internal
        view
        returns (uint256 poolA, uint256 poolB)
    {
        (address token0,) = sortTokens(tokenA, tokenB);
        (uint256 pool0, uint256 pool1,,,) = IButtonswapPair(pairFor(factory, tokenA, tokenB)).getLiquidityBalances();
        (poolA, poolB) = tokenA == token0 ? (pool0, pool1) : (pool1, pool0);
    }

    /**
     * @dev Fetches and sorts the reservoirs for a pair. Reservoirs are the current token balances in the pair contract not actively serving as liquidity.
     * @param factory The address of the ButtonswapFactory
     * @param tokenA First token address
     * @param tokenB Second token address
     * @return reservoirA Reservoir corresponding to tokenA
     * @return reservoirB Reservoir corresponding to tokenB
     */
    function getReservoirs(address factory, address tokenA, address tokenB)
        internal
        view
        returns (uint256 reservoirA, uint256 reservoirB)
    {
        (address token0,) = sortTokens(tokenA, tokenB);
        (,, uint256 reservoir0, uint256 reservoir1,) =
            IButtonswapPair(pairFor(factory, tokenA, tokenB)).getLiquidityBalances();
        (reservoirA, reservoirB) = tokenA == token0 ? (reservoir0, reservoir1) : (reservoir1, reservoir0);
    }

    /**
     * @dev Fetches and sorts the pools and reservoirs for a pair.
     *   - Pools are the current token balances in the pair contract serving as liquidity.
     *   - Reservoirs are the current token balances in the pair contract not actively serving as liquidity.
     * @param factory The address of the ButtonswapFactory
     * @param tokenA First token address
     * @param tokenB Second token address
     * @return poolA Pool corresponding to tokenA
     * @return poolB Pool corresponding to tokenB
     * @return reservoirA Reservoir corresponding to tokenA
     * @return reservoirB Reservoir corresponding to tokenB
     */
    function getLiquidityBalances(address factory, address tokenA, address tokenB)
        internal
        view
        returns (uint256 poolA, uint256 poolB, uint256 reservoirA, uint256 reservoirB)
    {
        (address token0,) = sortTokens(tokenA, tokenB);
        (uint256 pool0, uint256 pool1, uint256 reservoir0, uint256 reservoir1,) =
            IButtonswapPair(pairFor(factory, tokenA, tokenB)).getLiquidityBalances();
        (poolA, poolB, reservoirA, reservoirB) =
            tokenA == token0 ? (pool0, pool1, reservoir0, reservoir1) : (pool1, pool0, reservoir1, reservoir0);
    }

    /**
     * @dev Given some amount of an asset and pair pools, returns an equivalent amount of the other asset
     * @param amountA The amount of token A
     * @param poolA The balance of token A in the pool
     * @param poolB The balance of token B in the pool
     * @return amountB The amount of token B
     */
    function quote(uint256 amountA, uint256 poolA, uint256 poolB) internal pure returns (uint256 amountB) {
        if (amountA == 0) {
            revert InsufficientAmount();
        }
        if (poolA == 0 || poolB == 0) {
            revert InsufficientLiquidity();
        }
        amountB = (amountA * poolB) / poolA;
    }

    /**
     * @dev Given a factory, two tokens, and a mintAmount of the first, returns how much of the much of the mintAmount will be swapped for the other token and for how much during a mintWithReservoir operation.
     * @dev The logic is a condensed version of PairMath.getSingleSidedMintLiquidityOutAmountA and PairMath.getSingleSidedMintLiquidityOutAmountB
     * @param factory The address of the ButtonswapFactory that created the pairs
     * @param tokenA First token address
     * @param tokenB Second token address
     * @param mintAmountA The amount of tokenA to be minted
     * @return tokenAToSwap The amount of tokenA to be exchanged for tokenB from the reservoir
     * @return swappedReservoirAmountB The amount of tokenB returned from the reservoir
     */
    function getMintSwappedAmounts(address factory, address tokenA, address tokenB, uint256 mintAmountA)
        internal
        view
        returns (uint256 tokenAToSwap, uint256 swappedReservoirAmountB)
    {
        IButtonswapPair pair = IButtonswapPair(pairFor(factory, tokenA, tokenB));
        uint256 totalA = IERC20(tokenA).balanceOf(address(pair));
        uint256 totalB = IERC20(tokenB).balanceOf(address(pair));
        uint256 movingAveragePrice0 = pair.movingAveragePrice0();

        // tokenA == token0
        if (tokenA < tokenB) {
            tokenAToSwap =
                (mintAmountA * totalB) / (Math.mulDiv(movingAveragePrice0, (totalA + mintAmountA), 2 ** 112) + totalB);
            swappedReservoirAmountB = (tokenAToSwap * movingAveragePrice0) / 2 ** 112;
        } else {
            tokenAToSwap =
                (mintAmountA * totalB) / (((2 ** 112 * (totalA + mintAmountA)) / movingAveragePrice0) + totalB);
            // Inverse price so again we can use it without overflow risk
            swappedReservoirAmountB = (tokenAToSwap * (2 ** 112)) / movingAveragePrice0;
        }
    }

    /**
     * @dev Given a factory, two tokens, and a liquidity amount, returns how much of the first token will be withdrawn from the pair and how much of it came from the reservoir during a burnFromReservoir operation.
     * @dev The logic is a condensed version of PairMath.getSingleSidedBurnOutputAmountA and PairMath.getSingleSidedBurnOutputAmountB
     * @param factory The address of the ButtonswapFactory that created the pairs
     * @param tokenA First token address
     * @param tokenB Second token address
     * @param liquidity The amount of liquidity to be burned
     * @return tokenOutA The amount of tokenA to be withdrawn from the pair
     * @return swappedReservoirAmountA The amount of tokenA returned from the reservoir
     */
    function getBurnSwappedAmounts(address factory, address tokenA, address tokenB, uint256 liquidity)
        internal
        view
        returns (uint256 tokenOutA, uint256 swappedReservoirAmountA)
    {
        IButtonswapPair pair = IButtonswapPair(pairFor(factory, tokenA, tokenB));
        uint256 totalLiquidity = pair.totalSupply();
        uint256 totalA = IERC20(tokenA).balanceOf(address(pair));
        uint256 totalB = IERC20(tokenB).balanceOf(address(pair));
        uint256 movingAveragePrice0 = pair.movingAveragePrice0();
        uint256 tokenBToSwap = (totalB * liquidity) / totalLiquidity;
        tokenOutA = (totalA * liquidity) / totalLiquidity;

        // tokenA == token0
        if (tokenA < tokenB) {
            swappedReservoirAmountA = (tokenBToSwap * (2 ** 112)) / movingAveragePrice0;
        } else {
            swappedReservoirAmountA = (tokenBToSwap * movingAveragePrice0) / 2 ** 112;
        }
        tokenOutA += swappedReservoirAmountA;
    }

    /**
     * @dev Given an input amount of an asset and pair pools, returns the maximum output amount of the other asset
     * Factors in the fee on the input amount.
     * @param amountIn The input amount of the asset
     * @param poolIn The balance of the input asset in the pool
     * @param poolOut The balance of the output asset in the pool
     * @return amountOut The output amount of the other asset
     */
    function getAmountOut(uint256 amountIn, uint256 poolIn, uint256 poolOut)
        internal
        pure
        returns (uint256 amountOut)
    {
        if (amountIn == 0) {
            revert InsufficientInputAmount();
        }
        if (poolIn == 0 || poolOut == 0) {
            revert InsufficientLiquidity();
        }
        uint256 amountInWithFee = amountIn * 997;
        uint256 numerator = amountInWithFee * poolOut;
        uint256 denominator = (poolIn * 1000) + amountInWithFee;
        amountOut = numerator / denominator;
    }

    /**
     * @dev Given an output amount of an asset and pair pools, returns a required input amount of the other asset
     * @param amountOut The output amount of the asset
     * @param poolIn The balance of the input asset in the pool
     * @param poolOut The balance of the output asset in the pool
     * @return amountIn The required input amount of the other asset
     */
    function getAmountIn(uint256 amountOut, uint256 poolIn, uint256 poolOut) internal pure returns (uint256 amountIn) {
        if (amountOut == 0) {
            revert InsufficientOutputAmount();
        }
        if (poolIn == 0 || poolOut == 0) {
            revert InsufficientLiquidity();
        }
        uint256 numerator = poolIn * amountOut * 1000;
        uint256 denominator = (poolOut - amountOut) * 997;
        amountIn = (numerator / denominator) + 1;
    }

    /**
     * @dev Given an ordered array of tokens and an input amount of the first asset, performs chained getAmountOut calculations to calculate the output amount of the final asset
     * @param factory The address of the ButtonswapFactory that created the pairs
     * @param amountIn The input amount of the first asset
     * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token
     * @return amounts The output amounts of each asset in the path
     */
    function getAmountsOut(address factory, uint256 amountIn, address[] memory path)
        internal
        view
        returns (uint256[] memory amounts)
    {
        if (path.length < 2) {
            revert InvalidPath();
        }
        amounts = new uint256[](path.length);
        amounts[0] = amountIn;
        for (uint256 i; i < path.length - 1; i++) {
            (uint256 poolIn, uint256 poolOut,,) = getLiquidityBalances(factory, path[i], path[i + 1]);
            amounts[i + 1] = getAmountOut(amounts[i], poolIn, poolOut);
        }
    }

    /**
     * @dev Given an ordered array of tokens and an output amount of the final asset, performs chained getAmountIn calculations to calculate the input amount of the first asset
     * @param factory The address of the ButtonswapFactory that created the pairs
     * @param amountOut The output amount of the final asset
     * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token
     * @return amounts The input amounts of each asset in the path
     */
    function getAmountsIn(address factory, uint256 amountOut, address[] memory path)
        internal
        view
        returns (uint256[] memory amounts)
    {
        if (path.length < 2) {
            revert InvalidPath();
        }
        amounts = new uint256[](path.length);
        amounts[amounts.length - 1] = amountOut;
        for (uint256 i = path.length - 1; i > 0; i--) {
            (uint256 poolIn, uint256 poolOut,,) = getLiquidityBalances(factory, path[i - 1], path[i]);
            amounts[i - 1] = getAmountIn(amounts[i], poolIn, poolOut);
        }
    }
}

File 7 of 25 : IERC20.sol
pragma solidity >=0.5.0;

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

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

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

    function decimals() external view returns (uint8);

    function totalSupply() external view returns (uint256);

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

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

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

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

    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

File 8 of 25 : IWETH.sol
pragma solidity >=0.5.0;

interface IWETH {
    function deposit() external payable;

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

    function withdraw(uint256) external;

    function approve(address guy, uint256 wad) external returns (bool);
}

File 9 of 25 : ETHButtonswapRouter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IButtonswapFactory} from
    "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapFactory/IButtonswapFactory.sol";
import {IButtonswapPair} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapPair/IButtonswapPair.sol";
import {TransferHelper} from "./libraries/TransferHelper.sol";
import {IETHButtonswapRouter} from "./interfaces/IButtonswapRouter/IETHButtonswapRouter.sol";
import {ButtonswapLibrary} from "./libraries/ButtonswapLibrary.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import {IWETH} from "./interfaces/IWETH.sol";
import {BasicButtonswapRouter} from "./BasicButtonswapRouter.sol";

contract ETHButtonswapRouter is BasicButtonswapRouter, IETHButtonswapRouter {
    /**
     * @inheritdoc IETHButtonswapRouter
     */
    address public immutable override WETH;

    constructor(address _factory, address _WETH) BasicButtonswapRouter(_factory) {
        WETH = _WETH;
    }

    /**
     * @dev Only accepts ETH via fallback from the WETH contract
     */
    receive() external payable {
        if (msg.sender != WETH) {
            revert NonWETHSender();
        }
    }

    /**
     * @inheritdoc IETHButtonswapRouter
     */
    function addLiquidityETH(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        uint16 movingAveragePrice0ThresholdBps,
        address to,
        uint256 deadline
    )
        external
        payable
        virtual
        override
        ensure(deadline)
        returns (uint256 amountToken, uint256 amountETH, uint256 liquidity)
    {
        (amountToken, amountETH) = _addLiquidity(
            token, WETH, amountTokenDesired, msg.value, amountTokenMin, amountETHMin, movingAveragePrice0ThresholdBps
        );
        address pair = ButtonswapLibrary.pairFor(factory, token, WETH);
        TransferHelper.safeTransferFrom(token, msg.sender, address(this), amountToken);
        TransferHelper.safeApprove(token, pair, amountToken);
        IWETH(WETH).deposit{value: amountETH}();
        TransferHelper.safeApprove(WETH, pair, amountETH);

        (address token0,) = ButtonswapLibrary.sortTokens(token, WETH);
        liquidity = (token == token0)
            ? IButtonswapPair(pair).mint(amountToken, amountETH, to)
            : IButtonswapPair(pair).mint(amountETH, amountToken, to);

        // refund dust eth, if any
        if (msg.value > amountETH) {
            TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
        }
    }

    /**
     * @inheritdoc IETHButtonswapRouter
     */
    function addLiquidityETHWithReservoir(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    )
        external
        payable
        virtual
        override
        ensure(deadline)
        returns (uint256 amountToken, uint256 amountETH, uint256 liquidity)
    {
        (amountToken, amountETH) =
            _addLiquidityWithReservoir(token, WETH, amountTokenDesired, msg.value, amountTokenMin, amountETHMin);
        address pair = ButtonswapLibrary.pairFor(factory, token, WETH);
        if (amountToken > 0) {
            TransferHelper.safeTransferFrom(token, msg.sender, address(this), amountToken);
            TransferHelper.safeApprove(token, pair, amountToken);
            liquidity = IButtonswapPair(pair).mintWithReservoir(amountToken, to);
        } else if (amountETH > 0) {
            IWETH(WETH).deposit{value: amountETH}();
            TransferHelper.safeApprove(WETH, pair, amountETH);
            liquidity = IButtonswapPair(pair).mintWithReservoir(amountETH, to);
        }
        // refund dust eth, if any
        if (msg.value > amountETH) {
            TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
        }
    }

    /**
     * @inheritdoc IETHButtonswapRouter
     */
    function removeLiquidityETH(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) public virtual override ensure(deadline) returns (uint256 amountToken, uint256 amountETH) {
        (amountToken, amountETH) =
            removeLiquidity(token, WETH, liquidity, amountTokenMin, amountETHMin, address(this), deadline);
        TransferHelper.safeTransfer(token, to, amountToken);
        IWETH(WETH).withdraw(amountETH);
        TransferHelper.safeTransferETH(to, amountETH);
    }

    /**
     * @inheritdoc IETHButtonswapRouter
     */
    function removeLiquidityETHFromReservoir(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) public virtual override ensure(deadline) returns (uint256 amountToken, uint256 amountETH) {
        (amountToken, amountETH) =
            removeLiquidityFromReservoir(token, WETH, liquidity, amountTokenMin, amountETHMin, address(this), deadline);
        if (amountToken > 0) {
            TransferHelper.safeTransfer(token, to, amountToken);
        } else if (amountETH > 0) {
            IWETH(WETH).withdraw(amountETH);
            TransferHelper.safeTransferETH(to, amountETH);
        }
    }

    function removeLiquidityETHWithPermit(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external virtual override returns (uint256 amountToken, uint256 amountETH) {
        address pair = ButtonswapLibrary.pairFor(factory, token, WETH);
        uint256 value = approveMax ? type(uint256).max : liquidity;
        IButtonswapPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
        (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);
    }

    /**
     * @inheritdoc IETHButtonswapRouter
     */
    function swapExactETHForTokens(uint256 amountOutMin, address[] calldata path, address to, uint256 deadline)
        external
        payable
        virtual
        override
        ensure(deadline)
        returns (uint256[] memory amounts)
    {
        if (path[0] != WETH) {
            revert InvalidPath();
        }
        amounts = ButtonswapLibrary.getAmountsOut(factory, msg.value, path);
        if (amounts[amounts.length - 1] < amountOutMin) {
            revert InsufficientOutputAmount();
        }

        IWETH(WETH).deposit{value: amounts[0]}();
        if (!IWETH(WETH).transfer(address(this), amounts[0])) {
            revert FailedWETHTransfer();
        }
        _swap(amounts, path, to);
    }

    /**
     * @inheritdoc IETHButtonswapRouter
     */
    function swapTokensForExactETH(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) returns (uint256[] memory amounts) {
        if (path[path.length - 1] != WETH) {
            revert InvalidPath();
        }
        amounts = ButtonswapLibrary.getAmountsIn(factory, amountOut, path);
        if (amounts[0] > amountInMax) {
            revert ExcessiveInputAmount();
        }

        TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amounts[0]);
        _swap(amounts, path, address(this));

        // Convert final token to ETH and send to `to`
        IWETH(WETH).withdraw(amounts[amounts.length - 1]);
        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
    }

    /**
     * @inheritdoc IETHButtonswapRouter
     */
    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) returns (uint256[] memory amounts) {
        if (path[path.length - 1] != WETH) {
            revert InvalidPath();
        }
        amounts = ButtonswapLibrary.getAmountsOut(factory, amountIn, path);
        if (amounts[amounts.length - 1] < amountOutMin) {
            revert InsufficientOutputAmount();
        }

        TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amounts[0]);
        _swap(amounts, path, address(this));
        IWETH(WETH).withdraw(amounts[amounts.length - 1]);
        TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
    }

    /**
     * @inheritdoc IETHButtonswapRouter
     */
    function swapETHForExactTokens(uint256 amountOut, address[] calldata path, address to, uint256 deadline)
        external
        payable
        virtual
        override
        ensure(deadline)
        returns (uint256[] memory amounts)
    {
        if (path[0] != WETH) {
            revert InvalidPath();
        }
        amounts = ButtonswapLibrary.getAmountsIn(factory, amountOut, path);
        if (amounts[0] > msg.value) {
            revert ExcessiveInputAmount();
        }

        IWETH(WETH).deposit{value: amounts[0]}();
        if (!IWETH(WETH).transfer(address(this), amounts[0])) {
            revert FailedWETHTransfer();
        }

        _swap(amounts, path, to);
        // refund dust eth, if any
        if (msg.value > amounts[0]) {
            TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
        }
    }
}

File 10 of 25 : IETHButtonswapRouter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IBasicButtonswapRouter} from "./IBasicButtonswapRouter.sol";
import {IETHButtonswapRouterErrors} from "./IETHButtonswapRouterErrors.sol";

interface IETHButtonswapRouter is IBasicButtonswapRouter, IETHButtonswapRouterErrors {
    /**
     * @notice Returns the address of the WETH token
     * @return WETH The address of the WETH token
     */
    function WETH() external view returns (address WETH);

    /**
     * @notice Similar to `addLiquidity` but one of the tokens is ETH wrapped into WETH.
     * Adds liquidity to a pair, creating it if it doesn't exist yet, and transfers the liquidity tokens to the recipient.
     * @dev If the pair is empty, amountTokenMin and amountETHMin are ignored.
     * If the pair is nonempty, it deposits as much of token and WETH as possible while maintaining 3 conditions:
     * 1. The ratio of token to WETH in the pair remains approximately the same
     * 2. The amount of token in the pair is at least amountTokenMin but less than or equal to amountTokenDesired
     * 3. The amount of WETH in the pair is at least amountETHMin but less than or equal to ETH sent
     * @param token The address of the non-WETH token in the pair.
     * @param amountTokenDesired The maximum amount of the non-ETH token to add to the pair.
     * @param amountTokenMin The minimum amount of the non-ETH token to add to the pair.
     * @param amountETHMin The minimum amount of ETH/WETH to add to the pair.
     * @param movingAveragePrice0ThresholdBps The percentage threshold that movingAveragePrice0 can deviate from the current price.
     * @param to The address to send the liquidity tokens to.
     * @param deadline The time after which this transaction can no longer be executed.
     * @return amountToken The amount of token actually added to the pair.
     * @return amountETH The amount of ETH/WETH actually added to the pair.
     * @return liquidity The amount of liquidity tokens minted.
     */
    function addLiquidityETH(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        uint16 movingAveragePrice0ThresholdBps,
        address to,
        uint256 deadline
    ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);

    /**
     * @notice Similar to `addLiquidityWithReservoir` but one of the tokens is ETH wrapped into WETH.
     *     Adds liquidity to a pair, opposite to the existing reservoir, and transfers the liquidity tokens to the recipient
     * @dev Since there at most one reservoir at a given time, some conditions are checked:
     * 1. If there is no reservoir, it rejects
     * 2. If the non-WETH token has the reservoir, amountTokenDesired parameter ignored.
     * 3. The token/WETH with the reservoir has its amount deducted from the reservoir (checked against corresponding amountMin parameter)
     * @param token The address of the non-WETH token in the pair.
     * @param amountTokenDesired The maximum amount of the non-WETH token to add to the pair.
     * @param amountTokenMin The minimum amount of the non-WETH token to add to the pair.
     * @param amountETHMin The minimum amount of WETH to add to the pair.
     * @param to The address to send the liquidity tokens to.
     * @param deadline The time after which this transaction can no longer be executed.
     * @return amountToken The amount of the non-ETH token actually added to the pair.
     * @return amountETH The amount of WETH actually added to the pair.
     * @return liquidity The amount of liquidity tokens minted.
     */
    function addLiquidityETHWithReservoir(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);

    /**
     * @notice Similar to `removeLiquidity()` but one of the tokens is ETH wrapped into WETH.
     * Removes liquidity from a pair, and transfers the tokens to the recipient.
     * @param token The address of the non-WETH token in the pair.
     * @param liquidity The amount of liquidity tokens to burn.
     * @param amountTokenMin The minimum amount of the non-WETH token to withdraw from the pair.
     * @param amountETHMin The minimum amount of ETH/WETH to withdraw from the pair.
     * @param to The address to send the tokens to.
     * @param deadline The time after which this transaction can no longer be executed.
     * @return amountToken The amount of the non-WETH token actually withdrawn from the pair.
     * @return amountETH The amount of ETH/WETH actually withdrawn from the pair.
     */
    function removeLiquidityETH(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountToken, uint256 amountETH);

    /**
     * @notice Similar to `removeLiquidityFromReservoir()` but one of the tokens is ETH wrapped into WETH.
     * Removes liquidity from the reservoir of a pair and transfers the tokens to the recipient.
     * @param token The address of the non-WETH token in the pair.
     * @param liquidity The amount of liquidity tokens to burn.
     * @param amountTokenMin The minimum amount of the non-WETH token to withdraw from the pair.
     * @param amountETHMin The minimum amount of ETH/WETH to withdraw from the pair.
     * @param to The address to send the tokens to.
     * @param deadline The time after which this transaction can no longer be executed.
     * @return amountToken The amount of the non-WETH token actually withdrawn from the pair.
     * @return amountETH The amount of ETH/WETH actually withdrawn from the pair.
     */
    function removeLiquidityETHFromReservoir(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountToken, uint256 amountETH);

    /**
     * @notice Similar to `removeLiquidityWETH()` but utilizes the Permit signatures to reduce gas consumption.
     * Removes liquidity from a pair where one of the tokens is ETH wrapped into WETH, and transfers the tokens to the recipient.
     * @param token The address of the non-WETH token in the pair.
     * @param liquidity The amount of liquidity tokens to burn.
     * @param amountTokenMin The minimum amount of the non-WETH token to withdraw from the pair.
     * @param amountETHMin The minimum amount of ETH/WETH to withdraw from the pair.
     * @param to The address to send the tokens to.
     * @param deadline The time after which this transaction can no longer be executed.
     * @param approveMax Whether the signature is for the max uint256 or liquidity value
     * @param v Part of the signature
     * @param r Part of the signature
     * @param s Part of the signature
     * @return amountToken The amount of the non-WETH token actually withdrawn from the pair.
     * @return amountETH The amount of ETH/WETH actually withdrawn from the pair.
     */
    function removeLiquidityETHWithPermit(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountToken, uint256 amountETH);

    /**
     * @notice Similar to `swapExactTokensForTokens()` the first token is ETH wrapped into WETH.
     * Given an ordered array of tokens, performs consecutive swaps from a specific amount of the first token to the last token in the array.
     * @param amountOutMin The minimum amount of the last token to receive from the swap.
     * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token
     * @param to The address to send the output token to.
     * @param deadline The time after which this transaction can no longer be executed.
     */
    function swapExactETHForTokens(uint256 amountOutMin, address[] calldata path, address to, uint256 deadline)
        external
        payable
        returns (uint256[] memory amounts);

    /**
     * @notice Similar to `swapTokensForExactTokens()` the last token is ETH wrapped into WETH.
     * Given an ordered array of tokens, performs consecutive swaps from the first token to a specific amount of the last token in the array.
     * @param amountOut The amount of ETH to receive from the swap.
     * @param amountInMax The maximum amount of the first token to swap.
     * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token
     * @param to The address to send the output token to.
     * @param deadline The time after which this transaction can no longer be executed.
     */
    function swapTokensForExactETH(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    /**
     * @notice Similar to `swapExactTokensForTokens()` but the last token is ETH wrapped into WETH.
     * Given an ordered array of tokens, performs consecutive swaps from a specific amount of the first token to the last token in the array.
     * @param amountIn The amount of the first token to swap.
     * @param amountOutMin The minimum amount of the last token to receive from the swap.
     * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token
     * @param to The address to send the output token to.
     * @param deadline The time after which this transaction can no longer be executed.
     */
    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    /**
     * @notice Similar to `swapTokensForExactTokens()` but the first token is ETH wrapped into WETH.
     * Given an ordered array of tokens, performs consecutive swaps from the first token to a specific amount of the last token in the array.
     * @param amountOut The amount of the last token to receive from the swap.
     * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token
     * @param to The address to send the output token to.
     * @param deadline The time after which this transaction can no longer be executed.
     */
    function swapETHForExactTokens(uint256 amountOut, address[] calldata path, address to, uint256 deadline)
        external
        payable
        returns (uint256[] memory amounts);
}

File 11 of 25 : IButtonswapFactoryErrors.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

interface IButtonswapFactoryErrors {
    /**
     * @notice The given token addresses are the same
     */
    error TokenIdenticalAddress();

    /**
     * @notice The given token address is the zero address
     */
    error TokenZeroAddress();

    /**
     * @notice The given tokens already have a {ButtonswapPair} instance
     */
    error PairExists();

    /**
     * @notice User does not have permission for the attempted operation
     */
    error Forbidden();

    /**
     * @notice There was an attempt to update a parameter to an invalid value
     */
    error InvalidParameter();
}

File 12 of 25 : IButtonswapFactoryEvents.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

interface IButtonswapFactoryEvents {
    /**
     * @notice Emitted when a new Pair is created.
     * @param token0 The first sorted token
     * @param token1 The second sorted token
     * @param pair The address of the new {ButtonswapPair} contract
     * @param count The new total number of Pairs created
     */
    event PairCreated(address indexed token0, address indexed token1, address pair, uint256 count);

    /**
     * @notice Emitted when the default parameters for a new pair have been updated.
     * @param paramSetter The address that changed the parameters
     * @param newDefaultMovingAverageWindow The new movingAverageWindow default value
     * @param newDefaultMaxVolatilityBps The new maxVolatilityBps default value
     * @param newDefaultMinTimelockDuration The new minTimelockDuration default value
     * @param newDefaultMaxTimelockDuration The new maxTimelockDuration default value
     * @param newDefaultMaxSwappableReservoirLimitBps The new maxSwappableReservoirLimitBps default value
     * @param newDefaultSwappableReservoirGrowthWindow The new swappableReservoirGrowthWindow default value
     */
    event DefaultParametersUpdated(
        address indexed paramSetter,
        uint32 newDefaultMovingAverageWindow,
        uint16 newDefaultMaxVolatilityBps,
        uint32 newDefaultMinTimelockDuration,
        uint32 newDefaultMaxTimelockDuration,
        uint16 newDefaultMaxSwappableReservoirLimitBps,
        uint32 newDefaultSwappableReservoirGrowthWindow
    );
}

File 13 of 25 : IButtonswapPairErrors.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

import {IButtonswapERC20Errors} from "../IButtonswapERC20/IButtonswapERC20Errors.sol";

interface IButtonswapPairErrors is IButtonswapERC20Errors {
    /**
     * @notice Re-entrancy guard prevented method call
     */
    error Locked();

    /**
     * @notice User does not have permission for the attempted operation
     */
    error Forbidden();

    /**
     * @notice Integer maximums exceeded
     */
    error Overflow();

    /**
     * @notice Initial deposit not yet made
     */
    error Uninitialized();

    /**
     * @notice There was not enough liquidity in the reservoir
     */
    error InsufficientReservoir();

    /**
     * @notice Not enough liquidity was created during mint
     */
    error InsufficientLiquidityMinted();

    /**
     * @notice Not enough funds added to mint new liquidity
     */
    error InsufficientLiquidityAdded();

    /**
     * @notice More liquidity must be burned to be redeemed for non-zero amounts
     */
    error InsufficientLiquidityBurned();

    /**
     * @notice Swap was attempted with zero input
     */
    error InsufficientInputAmount();

    /**
     * @notice Swap was attempted with zero output
     */
    error InsufficientOutputAmount();

    /**
     * @notice Pool doesn't have the liquidity to service the swap
     */
    error InsufficientLiquidity();

    /**
     * @notice The specified "to" address is invalid
     */
    error InvalidRecipient();

    /**
     * @notice The product of pool balances must not change during a swap (save for accounting for fees)
     */
    error KInvariant();

    /**
     * @notice The new price ratio after a swap is invalid (one or more of the price terms are zero)
     */
    error InvalidFinalPrice();

    /**
     * @notice Single sided operations are not executable at this point in time
     */
    error SingleSidedTimelock();

    /**
     * @notice The attempted operation would have swapped reservoir tokens above the current limit
     */
    error SwappableReservoirExceeded();

    /**
     * @notice All operations on the pair other than dual-sided burning are currently paused
     */
    error Paused();
}

File 14 of 25 : IButtonswapPairEvents.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

import {IButtonswapERC20Events} from "../IButtonswapERC20/IButtonswapERC20Events.sol";

interface IButtonswapPairEvents is IButtonswapERC20Events {
    /**
     * @notice Emitted when a {IButtonswapPair-mint} is performed.
     * Some `token0` and `token1` are deposited in exchange for liquidity tokens representing a claim on them.
     * @param from The account that supplied the tokens for the mint
     * @param amount0 The amount of `token0` that was deposited
     * @param amount1 The amount of `token1` that was deposited
     * @param amountOut The amount of liquidity tokens that were minted
     * @param to The account that received the tokens from the mint
     */
    event Mint(address indexed from, uint256 amount0, uint256 amount1, uint256 amountOut, address indexed to);

    /**
     * @notice Emitted when a {IButtonswapPair-burn} is performed.
     * Liquidity tokens are redeemed for underlying `token0` and `token1`.
     * @param from The account that supplied the tokens for the burn
     * @param amountIn The amount of liquidity tokens that were burned
     * @param amount0 The amount of `token0` that was received
     * @param amount1 The amount of `token1` that was received
     * @param to The account that received the tokens from the burn
     */
    event Burn(address indexed from, uint256 amountIn, uint256 amount0, uint256 amount1, address indexed to);

    /**
     * @notice Emitted when a {IButtonswapPair-swap} is performed.
     * @param from The account that supplied the tokens for the swap
     * @param amount0In The amount of `token0` that went into the swap
     * @param amount1In The amount of `token1` that went into the swap
     * @param amount0Out The amount of `token0` that came out of the swap
     * @param amount1Out The amount of `token1` that came out of the swap
     * @param to The account that received the tokens from the swap
     */
    event Swap(
        address indexed from,
        uint256 amount0In,
        uint256 amount1In,
        uint256 amount0Out,
        uint256 amount1Out,
        address indexed to
    );

    /**
     * @notice Emitted when the movingAverageWindow parameter for the pair has been updated.
     * @param newMovingAverageWindow The new movingAverageWindow value
     */
    event MovingAverageWindowUpdated(uint32 newMovingAverageWindow);

    /**
     * @notice Emitted when the maxVolatilityBps parameter for the pair has been updated.
     * @param newMaxVolatilityBps The new maxVolatilityBps value
     */
    event MaxVolatilityBpsUpdated(uint16 newMaxVolatilityBps);

    /**
     * @notice Emitted when the minTimelockDuration parameter for the pair has been updated.
     * @param newMinTimelockDuration The new minTimelockDuration value
     */
    event MinTimelockDurationUpdated(uint32 newMinTimelockDuration);

    /**
     * @notice Emitted when the maxTimelockDuration parameter for the pair has been updated.
     * @param newMaxTimelockDuration The new maxTimelockDuration value
     */
    event MaxTimelockDurationUpdated(uint32 newMaxTimelockDuration);

    /**
     * @notice Emitted when the maxSwappableReservoirLimitBps parameter for the pair has been updated.
     * @param newMaxSwappableReservoirLimitBps The new maxSwappableReservoirLimitBps value
     */
    event MaxSwappableReservoirLimitBpsUpdated(uint16 newMaxSwappableReservoirLimitBps);

    /**
     * @notice Emitted when the swappableReservoirGrowthWindow parameter for the pair has been updated.
     * @param newSwappableReservoirGrowthWindow The new swappableReservoirGrowthWindow value
     */
    event SwappableReservoirGrowthWindowUpdated(uint32 newSwappableReservoirGrowthWindow);
}

File 15 of 25 : IButtonswapERC20.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

import {IButtonswapERC20Errors} from "./IButtonswapERC20Errors.sol";
import {IButtonswapERC20Events} from "./IButtonswapERC20Events.sol";

interface IButtonswapERC20 is IButtonswapERC20Errors, IButtonswapERC20Events {
    /**
     * @notice Returns the name of the token.
     * @return _name The token name
     */
    function name() external view returns (string memory _name);

    /**
     * @notice Returns the symbol of the token, usually a shorter version of the name.
     * @return _symbol The token symbol
     */
    function symbol() external view returns (string memory _symbol);

    /**
     * @notice Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should be displayed to a user as `5.05` (`505 / 10 ** 2`).
     * @dev This information is only used for _display_ purposes: it in no way affects any of the arithmetic of the contract.
     * @return decimals The number of decimals
     */
    function decimals() external pure returns (uint8 decimals);

    /**
     * @notice Returns the amount of tokens in existence.
     * @return totalSupply The amount of tokens in existence
     */
    function totalSupply() external view returns (uint256 totalSupply);

    /**
     * @notice Returns the amount of tokens owned by `account`.
     * @param owner The account the balance is being checked for
     * @return balance The amount of tokens owned by `owner`
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @notice 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.
     * @param owner The account that owns the tokens
     * @param spender The account that can spend the tokens
     * @return allowance The amount of tokens owned by `owner` that the `spender` can transfer
     */
    function allowance(address owner, address spender) external view returns (uint256 allowance);

    /**
     * @notice Sets `value` as the allowance of `spender` over the caller's tokens.
     * @dev 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 {IButtonswapERC20Events-Approval} event.
     * @param spender The account that is granted permission to spend the tokens
     * @param value The amount of tokens that can be spent
     * @return success Whether the operation succeeded
     */
    function approve(address spender, uint256 value) external returns (bool success);

    /**
     * @notice Moves `value` tokens from the caller's account to `to`.
     * @dev Emits a {IButtonswapERC20Events-Transfer} event.
     * @param to The account that is receiving the tokens
     * @param value The amount of tokens being sent
     * @return success Whether the operation succeeded
     */
    function transfer(address to, uint256 value) external returns (bool success);

    /**
     * @notice Moves `value` tokens from `from` to `to` using the allowance mechanism.
     * `value` is then deducted from the caller's allowance.
     * @dev Emits a {IButtonswapERC20Events-Transfer} event.
     * @param from The account that is sending the tokens
     * @param to The account that is receiving the tokens
     * @param value The amount of tokens being sent
     * @return success Whether the operation succeeded
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);

    /**
     * @notice Returns the domain separator used in the encoding of the signature for {permit}, as defined by [EIP712](https://eips.ethereum.org/EIPS/eip-712).
     * @return DOMAIN_SEPARATOR The `DOMAIN_SEPARATOR` value
     */
    function DOMAIN_SEPARATOR() external view returns (bytes32 DOMAIN_SEPARATOR);

    /**
     * @notice Returns the typehash used in the encoding of the signature for {permit}, as defined by [EIP712](https://eips.ethereum.org/EIPS/eip-712).
     * @return PERMIT_TYPEHASH The `PERMIT_TYPEHASH` value
     */
    function PERMIT_TYPEHASH() external pure returns (bytes32 PERMIT_TYPEHASH);

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

    /**
     * @notice Sets `value` as the allowance of `spender` over `owner`'s tokens, given `owner`'s signed approval.
     * @dev IMPORTANT: The same issues {approve} has related to transaction ordering also apply here.
     *
     * Emits an {IButtonswapERC20Events-Approval} event.
     *
     * Requirements:
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` over the EIP712-formatted function arguments.
     * - the signature must use `owner`'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the [relevant EIP section](https://eips.ethereum.org/EIPS/eip-2612#specification).
     * @param owner The account that owns the tokens
     * @param spender The account that can spend the tokens
     * @param value The amount of `owner`'s tokens that `spender` can transfer
     * @param deadline The future time after which the permit is no longer valid
     * @param v Part of the signature
     * @param r Part of the signature
     * @param s Part of the signature
     */
    function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
        external;
}

File 16 of 25 : Math.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.13;

// a library for performing various math operations

library Math {
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x < y ? x : y;
    }

    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x > y ? x : y;
    }

    // Borrowed implementation from solmate
    // https://github.com/transmissions11/solmate/blob/2001af43aedb46fdc2335d2a7714fb2dae7cfcd1/src/utils/FixedPointMathLib.sol#L164
    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

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

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

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

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

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

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

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

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

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

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

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

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

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

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

File 17 of 25 : BasicButtonswapRouter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IButtonswapFactory} from
    "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapFactory/IButtonswapFactory.sol";
import {IButtonswapPair} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapPair/IButtonswapPair.sol";
import {TransferHelper} from "./libraries/TransferHelper.sol";
import {IBasicButtonswapRouter} from "./interfaces/IButtonswapRouter/IBasicButtonswapRouter.sol";
import {ButtonswapLibrary} from "./libraries/ButtonswapLibrary.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import {IWETH} from "./interfaces/IWETH.sol";
import {RootButtonswapRouter} from "./RootButtonswapRouter.sol";

contract BasicButtonswapRouter is RootButtonswapRouter, IBasicButtonswapRouter {
    modifier ensure(uint256 deadline) {
        if (block.timestamp > deadline) {
            revert Expired();
        }
        _;
    }

    constructor(address _factory) RootButtonswapRouter(_factory) {}

    /**
     * @inheritdoc IBasicButtonswapRouter
     */
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        uint16 movingAveragePrice0ThresholdBps,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity) {
        (amountA, amountB) = _addLiquidity(
            tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin, movingAveragePrice0ThresholdBps
        );
        address pair = ButtonswapLibrary.pairFor(factory, tokenA, tokenB);
        TransferHelper.safeTransferFrom(tokenA, msg.sender, address(this), amountA);
        TransferHelper.safeApprove(tokenA, pair, amountA);
        TransferHelper.safeTransferFrom(tokenB, msg.sender, address(this), amountB);
        TransferHelper.safeApprove(tokenB, pair, amountB);

        if (tokenA < tokenB) {
            liquidity = IButtonswapPair(pair).mint(amountA, amountB, to);
        } else {
            liquidity = IButtonswapPair(pair).mint(amountB, amountA, to);
        }
    }

    /**
     * @inheritdoc IBasicButtonswapRouter
     */
    function addLiquidityWithReservoir(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity) {
        (amountA, amountB) =
            _addLiquidityWithReservoir(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
        address pair = ButtonswapLibrary.pairFor(factory, tokenA, tokenB);

        if (amountA > 0) {
            TransferHelper.safeTransferFrom(tokenA, msg.sender, address(this), amountA);
            TransferHelper.safeApprove(tokenA, pair, amountA);
            liquidity = IButtonswapPair(pair).mintWithReservoir(amountA, to);
        } else if (amountB > 0) {
            TransferHelper.safeTransferFrom(tokenB, msg.sender, address(this), amountB);
            TransferHelper.safeApprove(tokenB, pair, amountB);
            liquidity = IButtonswapPair(pair).mintWithReservoir(amountB, to);
        }
    }

    // **** REMOVE LIQUIDITY ****
    /**
     * @inheritdoc IBasicButtonswapRouter
     */
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) public virtual override ensure(deadline) returns (uint256 amountA, uint256 amountB) {
        address pair = ButtonswapLibrary.pairFor(factory, tokenA, tokenB);
        IButtonswapPair(pair).transferFrom(msg.sender, address(this), liquidity); // send liquidity to router
        (uint256 amount0, uint256 amount1) = IButtonswapPair(pair).burn(liquidity, to);
        (address token0,) = ButtonswapLibrary.sortTokens(tokenA, tokenB);
        (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
        if (amountA < amountAMin) {
            revert InsufficientAAmount();
        }
        if (amountB < amountBMin) {
            revert InsufficientBAmount();
        }
    }

    /**
     * @inheritdoc IBasicButtonswapRouter
     */
    function removeLiquidityFromReservoir(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) public virtual override ensure(deadline) returns (uint256 amountA, uint256 amountB) {
        address pair = ButtonswapLibrary.pairFor(factory, tokenA, tokenB);
        IButtonswapPair(pair).transferFrom(msg.sender, address(this), liquidity); // send liquidity to router
        (uint256 amount0, uint256 amount1) = IButtonswapPair(pair).burnFromReservoir(liquidity, to);
        (address token0,) = ButtonswapLibrary.sortTokens(tokenA, tokenB);
        (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
        if (amountA < amountAMin) {
            revert InsufficientAAmount();
        }
        if (amountB < amountBMin) {
            revert InsufficientBAmount();
        }
    }

    /**
     * @inheritdoc IBasicButtonswapRouter
     */
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external virtual override returns (uint256 amountA, uint256 amountB) {
        address pair = ButtonswapLibrary.pairFor(factory, tokenA, tokenB);
        uint256 value = approveMax ? type(uint256).max : liquidity;
        IButtonswapPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
        (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
    }

    // **** SWAP ****
    /**
     * @inheritdoc IBasicButtonswapRouter
     */
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) returns (uint256[] memory amounts) {
        amounts = ButtonswapLibrary.getAmountsOut(factory, amountIn, path);
        if (amounts[amounts.length - 1] < amountOutMin) {
            revert InsufficientOutputAmount();
        }
        IButtonswapPair(ButtonswapLibrary.pairFor(factory, path[0], path[1]));

        TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amounts[0]);
        _swap(amounts, path, to);
    }

    /**
     * @inheritdoc IBasicButtonswapRouter
     */
    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external virtual override ensure(deadline) returns (uint256[] memory amounts) {
        amounts = ButtonswapLibrary.getAmountsIn(factory, amountOut, path);
        if (amounts[0] > amountInMax) {
            revert ExcessiveInputAmount();
        }
        //        IButtonswapPair pair = IButtonswapPair(ButtonswapLibrary.pairFor(factory, path[0], path[1]));
        TransferHelper.safeTransferFrom(path[0], msg.sender, address(this), amounts[0]);
        _swap(amounts, path, to);
    }
}

File 18 of 25 : IBasicButtonswapRouter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IRootButtonswapRouter} from "./IRootButtonswapRouter.sol";

interface IBasicButtonswapRouter is IRootButtonswapRouter {
    /**
     * @notice Adds liquidity to a pair, creating it if it doesn't exist yet, and transfers the liquidity tokens to the recipient.
     * @dev If the pair is empty, amountAMin and amountBMin are ignored.
     * If the pair is nonempty, it deposits as much of tokenA and tokenB as possible while maintaining 3 conditions:
     * 1. The ratio of tokenA to tokenB in the pair remains approximately the same
     * 2. The amount of tokenA in the pair is at least amountAMin but less than or equal to amountADesired
     * 3. The amount of tokenB in the pair is at least amountBMin but less than or equal to amountBDesired
     * @param tokenA The address of the first token in the pair.
     * @param tokenB The address of the second token in the pair.
     * @param amountADesired The maximum amount of the first token to add to the pair.
     * @param amountBDesired The maximum amount of the second token to add to the pair.
     * @param amountAMin The minimum amount of the first token to add to the pair.
     * @param amountBMin The minimum amount of the second token to add to the pair.
     * @param movingAveragePrice0ThresholdBps The percentage threshold that movingAveragePrice0 can deviate from the current price.
     * @param to The address to send the liquidity tokens to.
     * @param deadline The time after which this transaction can no longer be executed.
     * @return amountA The amount of tokenA actually added to the pair.
     * @return amountB The amount of tokenB actually added to the pair.
     * @return liquidity The amount of liquidity tokens minted.
     */
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        uint16 movingAveragePrice0ThresholdBps,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);

    /**
     * @notice Adds liquidity to a pair, opposite to the existing reservoir, and transfers the liquidity tokens to the recipient
     * @dev Since there at most one reservoir at a given time, some conditions are checked:
     * 1. If there is no reservoir, it rejects
     * 2. The token with the reservoir has its amountDesired parameter ignored
     * 3. The token with the reservoir has its amount deducted from the reservoir (checked against corresponding amountMin parameter)
     * @param tokenA The address of the first token in the pair.
     * @param tokenB The address of the second token in the pair.
     * @param amountADesired The maximum amount of the first token to add to the pair.
     * @param amountBDesired The maximum amount of the second token to add to the pair.
     * @param amountAMin The minimum amount of the first token to add to the pair.
     * @param amountBMin The minimum amount of the second token to add to the pair.
     * @param to The address to send the liquidity tokens to.
     * @param deadline The time after which this transaction can no longer be executed.
     * @return amountA The amount of tokenA actually added to the pair.
     * @return amountB The amount of tokenB actually added to the pair.
     * @return liquidity The amount of liquidity tokens minted.
     */
    function addLiquidityWithReservoir(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);

    /**
     * @notice Removes liquidity from a pair, and transfers the tokens to the recipient.
     * @param tokenA The address of the first token in the pair.
     * @param tokenB The address of the second token in the pair.
     * @param liquidity The amount of liquidity tokens to burn.
     * @param amountAMin The minimum amount of the first token to withdraw from the pair.
     * @param amountBMin The minimum amount of the second token to withdraw from the pair.
     * @param to The address to send the tokens to.
     * @param deadline The time after which this transaction can no longer be executed.
     * @return amountA The amount of tokenA actually withdrawn from the pair.
     * @return amountB The amount of tokenB actually withdrawn from the pair.
     */
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB);

    /**
     * @notice Removes liquidity from the reservoir of a pair and transfers the tokens to the recipient.
     * @param tokenA The address of the first token in the pair.
     * @param tokenB The address of the second token in the pair.
     * @param liquidity The amount of liquidity tokens to burn.
     * @param amountAMin The minimum amount of the first token to withdraw from the pair.
     * @param amountBMin The minimum amount of the second token to withdraw from the pair.
     * @param to The address to send the tokens to.
     * @param deadline The time after which this transaction can no longer be executed.
     * @return amountA The amount of tokenA actually withdrawn from the pair.
     * @return amountB The amount of tokenB actually withdrawn from the pair.
     */
    function removeLiquidityFromReservoir(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB);

    /**
     * @notice Similar to `removeLiquidity()` but utilizes the Permit signatures to reduce gas consumption.
     * Removes liquidity from a pair, and transfers the tokens to the recipient.
     * @param tokenA The address of the first token in the pair.
     * @param tokenB The address of the second token in the pair.
     * @param liquidity The amount of liquidity tokens to burn.
     * @param amountAMin The minimum amount of the first token to withdraw from the pair.
     * @param amountBMin The minimum amount of the second token to withdraw from the pair.
     * @param to The address to send the tokens to.
     * @param deadline The time after which this transaction can no longer be executed.
     * @param approveMax Whether the signature is for the max uint256 or liquidity value
     * @param v Part of the signature
     * @param r Part of the signature
     * @param s Part of the signature
     * @return amountA The amount of tokenA actually withdrawn from the pair.
     * @return amountB The amount of tokenB actually withdrawn from the pair.
     */
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountA, uint256 amountB);

    /**
     * @notice Given an ordered array of tokens, performs consecutive swaps from a specific amount of the first token to the last token in the array.
     * @param amountIn The amount of the first token to swap.
     * @param amountOutMin The minimum amount of the last token to receive from the swap.
     * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token
     * @param to The address to send the output token to.
     * @param deadline The time after which this transaction can no longer be executed.
     */
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    /**
     * @notice Given an ordered array of tokens, performs consecutive swaps from the first token to a specific amount of the last token in the array.
     * @param amountOut The amount of the last token to receive from the swap.
     * @param amountInMax The maximum amount of the first token to swap.
     * @param path An array of token addresses [tokenA, tokenB, tokenC, ...] representing the path the input token takes to get to the output token
     * @param to The address to send the output token to.
     * @param deadline The time after which this transaction can no longer be executed.
     */
    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);
}

File 19 of 25 : IETHButtonswapRouterErrors.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

interface IETHButtonswapRouterErrors {
    /// @notice Only WETH contract can send ETH to contract
    error NonWETHSender();
    /// @notice WETH transfer failed
    error FailedWETHTransfer();
}

File 20 of 25 : IButtonswapERC20Errors.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

interface IButtonswapERC20Errors {
    /**
     * @notice Permit deadline was exceeded
     */
    error PermitExpired();

    /**
     * @notice Permit signature invalid
     */
    error PermitInvalidSignature();
}

File 21 of 25 : IButtonswapERC20Events.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

interface IButtonswapERC20Events {
    /**
     * @notice Emitted when the allowance of a `spender` for an `owner` is set by a call to {IButtonswapERC20-approve}.
     * `value` is the new allowance.
     * @param owner The account that has granted approval
     * @param spender The account that has been given approval
     * @param value The amount the spender can transfer
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @notice Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
     * @param from The account that sent the tokens
     * @param to The account that received the tokens
     * @param value The amount of tokens transferred
     */
    event Transfer(address indexed from, address indexed to, uint256 value);
}

File 22 of 25 : RootButtonswapRouter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IButtonswapFactory} from
    "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapFactory/IButtonswapFactory.sol";
import {IButtonswapPair} from "buttonswap-periphery_buttonswap-core/interfaces/IButtonswapPair/IButtonswapPair.sol";
import {TransferHelper} from "./libraries/TransferHelper.sol";
import {IRootButtonswapRouter} from "./interfaces/IButtonswapRouter/IRootButtonswapRouter.sol";
import {ButtonswapLibrary} from "./libraries/ButtonswapLibrary.sol";
import {Math} from "./libraries/Math.sol";
import {IERC20} from "./interfaces/IERC20.sol";

contract RootButtonswapRouter is IRootButtonswapRouter {
    uint256 private constant BPS = 10_000;

    /**
     * @inheritdoc IRootButtonswapRouter
     */
    address public immutable override factory;

    constructor(address _factory) {
        factory = _factory;
    }

    // **** ADD LIQUIDITY ****
    // @dev Refer to [movingAveragePriceThreshold.md](https://github.com/buttonwood-protocol/buttonswap-periphery/blob/main/notes/movingAveragePriceThreshold.md) for more detail.
    function _addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        uint16 movingAveragePrice0ThresholdBps
    ) internal virtual returns (uint256 amountA, uint256 amountB) {
        // create the pair if it doesn't exist yet
        address pair = IButtonswapFactory(factory).getPair(tokenA, tokenB);
        if (pair == address(0)) {
            pair = IButtonswapFactory(factory).createPair(tokenA, tokenB);
        }

        (uint256 poolA, uint256 poolB, uint256 reservoirA, uint256 reservoirB) =
            ButtonswapLibrary.getLiquidityBalances(factory, tokenA, tokenB);

        if ((poolA + reservoirA) == 0 && (poolB + reservoirB) == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
        } else {
            uint256 amountBOptimal = ButtonswapLibrary.quote(amountADesired, poolA + reservoirA, poolB + reservoirB);
            if (amountBOptimal <= amountBDesired) {
                if (amountBOptimal < amountBMin) {
                    revert InsufficientBAmount();
                }
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } else {
                uint256 amountAOptimal = ButtonswapLibrary.quote(amountBDesired, poolB + reservoirB, poolA + reservoirA);
                assert(amountAOptimal <= amountADesired);
                if (amountAOptimal < amountAMin) {
                    revert InsufficientAAmount();
                }
                (amountA, amountB) = (amountAOptimal, amountBDesired);
            }
        }

        // Validate that the moving average price is within the threshold for pairs that exist
        if (poolA > 0 && poolB > 0) {
            uint256 movingAveragePrice0 = IButtonswapPair(pair).movingAveragePrice0();
            if (tokenA < tokenB) {
                // tokenA is token0
                uint256 cachedTerm = Math.mulDiv(movingAveragePrice0, poolA * BPS, 2 ** 112);
                if (
                    poolB * (BPS - movingAveragePrice0ThresholdBps) > cachedTerm
                        || poolB * (BPS + movingAveragePrice0ThresholdBps) < cachedTerm
                ) {
                    revert MovingAveragePriceOutOfBounds();
                }
            } else {
                // tokenB is token0
                uint256 cachedTerm = Math.mulDiv(movingAveragePrice0, poolB * BPS, 2 ** 112);
                if (
                    poolA * (BPS - movingAveragePrice0ThresholdBps) > cachedTerm
                        || poolA * (BPS + movingAveragePrice0ThresholdBps) < cachedTerm
                ) {
                    revert MovingAveragePriceOutOfBounds();
                }
            }
        }
    }

    function _addLiquidityWithReservoir(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin
    ) internal virtual returns (uint256 amountA, uint256 amountB) {
        // If the pair doesn't exist yet, there isn't any reservoir
        if (IButtonswapFactory(factory).getPair(tokenA, tokenB) == address(0)) {
            revert NoReservoir();
        }
        (uint256 poolA, uint256 poolB, uint256 reservoirA, uint256 reservoirB) =
            ButtonswapLibrary.getLiquidityBalances(factory, tokenA, tokenB);
        // the first liquidity addition should happen through _addLiquidity
        // can't initialize by matching with a reservoir
        if (poolA == 0 || poolB == 0) {
            revert NotInitialized();
        }
        if (reservoirA == 0 && reservoirB == 0) {
            revert NoReservoir();
        }

        if (reservoirA > 0) {
            // we take from reservoirA and the user-provided amountBDesired
            // But modify so that you don't do liquidityOut logic since you don't need it
            (, uint256 amountAOptimal) =
                ButtonswapLibrary.getMintSwappedAmounts(factory, tokenB, tokenA, amountBDesired);
            // User wants to drain to the res by amountAMin or more
            // Slippage-check
            if (amountAOptimal < amountAMin) {
                revert InsufficientAAmount();
            }
            (amountA, amountB) = (0, amountBDesired);
        } else {
            // we take from reservoirB and the user-provided amountADesired
            (, uint256 amountBOptimal) =
                ButtonswapLibrary.getMintSwappedAmounts(factory, tokenA, tokenB, amountADesired);
            if (amountBOptimal < amountBMin) {
                revert InsufficientBAmount();
            }
            (amountA, amountB) = (amountADesired, 0);
        }
    }

    // **** SWAP ****

    function _swap(uint256[] memory amounts, address[] memory path, address _to) internal virtual {
        for (uint256 i; i < path.length - 1; i++) {
            (address input, address output) = (path[i], path[i + 1]);
            (address token0,) = ButtonswapLibrary.sortTokens(input, output);
            uint256 amountIn = amounts[i];
            uint256 amountOut = amounts[i + 1];
            (uint256 amount0In, uint256 amount1In) = input == token0 ? (amountIn, uint256(0)) : (uint256(0), amountIn);
            (uint256 amount0Out, uint256 amount1Out) =
                input == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0));

            address to = i < path.length - 2 ? address(this) : _to;
            IButtonswapPair pair = IButtonswapPair(ButtonswapLibrary.pairFor(factory, input, output));
            TransferHelper.safeApprove(input, address(pair), amountIn);
            pair.swap(amount0In, amount1In, amount0Out, amount1Out, to);
        }
    }
}

File 23 of 25 : IRootButtonswapRouter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IButtonswapRouterErrors} from "./IButtonswapRouterErrors.sol";

interface IRootButtonswapRouter is IButtonswapRouterErrors {
    /**
     * @notice Returns the address of the Buttonswap Factory
     * @return factory The address of the Buttonswap Factory
     */
    function factory() external view returns (address factory);
}

File 24 of 25 : Math.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

// a library for performing various math operations

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

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

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

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

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

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

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

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

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

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

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

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

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

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

File 25 of 25 : IButtonswapRouterErrors.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

interface IButtonswapRouterErrors {
    /// @notice Deadline was exceeded
    error Expired();
    /// @notice Insufficient amount of token A available
    error InsufficientAAmount();
    /// @notice Insufficient amount of token B available
    error InsufficientBAmount();
    /// @notice Neither token in the pool has the required reservoir
    error NoReservoir();
    /// @notice Pools are not initialized
    error NotInitialized();
    /// @notice Insufficient amount of token A in the reservoir
    error InsufficientAReservoir();
    /// @notice Insufficient amount of token B in the reservoir
    error InsufficientBReservoir();
    /// @notice Insufficient tokens returned from operation
    error InsufficientOutputAmount();
    /// @notice Required input amount exceeds specified maximum
    error ExcessiveInputAmount();
    /// @notice Invalid path provided
    error InvalidPath();
    /// @notice movingAveragePrice0 is out of specified bounds
    error MovingAveragePriceOutOfBounds();
}

Settings
{
  "remappings": [
    "@openzeppelin-contracts/=null/",
    "buttonswap-core/=null/",
    "buttonswap-core_@openzeppelin-contracts/=lib/buttonswap-core/lib/openzeppelin-contracts/contracts/",
    "buttonswap-core_forge-std/=lib/buttonswap-core/lib/forge-std/src/",
    "buttonswap-core_mock-contracts/=lib/buttonswap-core/lib/mock-contracts/src/",
    "buttonswap-periphery_@openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
    "buttonswap-periphery_buttonswap-core/=lib/buttonswap-core/src/",
    "buttonswap-periphery_forge-std/=lib/forge-std/src/",
    "buttonswap-periphery_mock-contracts/=lib/mock-contracts/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/buttonswap-core/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=null/",
    "mock-contracts/=null/",
    "mock-contracts_@openzeppelin-contracts/=lib/mock-contracts/lib/openzeppelin-contracts/contracts/",
    "mock-contracts_forge-std/=lib/mock-contracts/lib/forge-std/src/",
    "openzeppelin-contracts/=null/",
    "openzeppelin/=null/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_WETH","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ExcessiveInputAmount","type":"error"},{"inputs":[],"name":"Expired","type":"error"},{"inputs":[],"name":"FailedWETHTransfer","type":"error"},{"inputs":[],"name":"IdenticalAddresses","type":"error"},{"inputs":[],"name":"InsufficientAAmount","type":"error"},{"inputs":[],"name":"InsufficientAReservoir","type":"error"},{"inputs":[],"name":"InsufficientAmount","type":"error"},{"inputs":[],"name":"InsufficientBAmount","type":"error"},{"inputs":[],"name":"InsufficientBReservoir","type":"error"},{"inputs":[],"name":"InsufficientInputAmount","type":"error"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[],"name":"InsufficientOutputAmount","type":"error"},{"inputs":[],"name":"InsufficientOutputAmount","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"MovingAveragePriceOutOfBounds","type":"error"},{"inputs":[],"name":"NoReservoir","type":"error"},{"inputs":[],"name":"NonWETHSender","type":"error"},{"inputs":[],"name":"NotInitialized","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"uint16","name":"movingAveragePrice0ThresholdBps","type":"uint16"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"uint16","name":"movingAveragePrice0ThresholdBps","type":"uint16"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityETHWithReservoir","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityWithReservoir","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"poolIn","type":"uint256"},{"internalType":"uint256","name":"poolOut","type":"uint256"}],"name":"getAmountIn","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"poolIn","type":"uint256"},{"internalType":"uint256","name":"poolOut","type":"uint256"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsIn","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsOut","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"getBurnSwappedAmounts","outputs":[{"internalType":"uint256","name":"tokenOutA","type":"uint256"},{"internalType":"uint256","name":"swappedReservoirAmountA","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"mintAmountA","type":"uint256"}],"name":"getMintSwappedAmounts","outputs":[{"internalType":"uint256","name":"tokenAToSwap","type":"uint256"},{"internalType":"uint256","name":"swappedReservoirAmountB","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"pair","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isCreationRestricted","outputs":[{"internalType":"bool","name":"_isCreationRestricted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"poolA","type":"uint256"},{"internalType":"uint256","name":"poolB","type":"uint256"}],"name":"quote","outputs":[{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETHFromReservoir","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermit","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityFromReservoir","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityWithPermit","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapETHForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60c06040523480156200001157600080fd5b50604051620046f9380380620046f9833981016040819052620000349162000069565b6001600160a01b039182166080521660a052620000a1565b80516001600160a01b03811681146200006457600080fd5b919050565b600080604083850312156200007d57600080fd5b62000088836200004c565b915062000098602084016200004c565b90509250929050565b60805160a0516144bf6200023a600039600081816101b001528181610435015281816105e101528181610630015281816108c301528181610a7001528181610e6001528181610eb701528181610ef601528181610f6d01528181610f9b0152818161112f0152818161126f015281816112c50152818161138a0152818161140101528181611570015281816116900152818161171e01528181611c5201528181611cb201528181611d42015281816120690152818161217d015261220b015260008181610501015281816106ee0152818161094001528181610b5601528181610b8f01528181610c6601528181610cc201528181610d6901528181610e95015281816111ac015281816112a3015281816114c5015281816115ec015281816118770152818161192b0152818161198b01528181611aa801528181611cf001528181611d2001528181611e3301528181611ee1015281816120e501528181612b1f01528181612fa70152818161304b015281816130c6015281816134a00152818161353f015281816135cf015261362e01526144bf6000f3fe6080604052600436106101a05760003560e01c806385f8c259116100ec578063c2de19331161008a578063ded9382a11610064578063ded9382a14610543578063e6a4390514610563578063e78e168414610583578063fb3bdb41146105a357600080fd5b8063c2de1933146104cf578063c45a0155146104ef578063d06ca61f1461052357600080fd5b8063ad5c4648116100c6578063ad5c464814610423578063ad615dec1461046f578063baa2abde1461048f578063c2772c24146104af57600080fd5b806385f8c259146103c35780638803dbee146103e35780639adf412b1461040357600080fd5b806329c89c48116101595780634a25d94a116101335780634a25d94a146103585780635a0f2c2d146103785780637561d3751461038b5780637ff36ab5146103b057600080fd5b806329c89c48146102ea57806338ed17391461030a57806344794dbf1461032a57600080fd5b806302751cec146101f5578063054d50d41461022f5780630917e98b1461025d57806318cbafe51461027d5780631f00ca74146102aa5780632195995c146102ca57600080fd5b366101f057336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146101ee576040516305bbb6bf60e31b815260040160405180910390fd5b005b600080fd5b34801561020157600080fd5b50610215610210366004613bb8565b6105b6565b604080519283526020830191909152015b60405180910390f35b34801561023b57600080fd5b5061024f61024a366004613c16565b6106ab565b604051908152602001610226565b34801561026957600080fd5b50610215610278366004613c42565b6106c2565b34801561028957600080fd5b5061029d610298366004613cf9565b610895565b6040516102269190613d6c565b3480156102b657600080fd5b5061029d6102c5366004613dc6565b610b4f565b3480156102d657600080fd5b506102156102e5366004613eb6565b610b85565b3480156102f657600080fd5b50610215610305366004613f60565b610c5e565b34801561031657600080fd5b5061029d610325366004613cf9565b610c99565b61033d610338366004613fb3565b610e33565b60408051938452602084019290925290820152606001610226565b34801561036457600080fd5b5061029d610373366004613cf9565b611101565b61033d610386366004613bb8565b611242565b34801561039757600080fd5b506103a06114c1565b6040519015158152602001610226565b61029d6103be36600461400c565b61154a565b3480156103cf57600080fd5b5061024f6103de366004613c16565b611841565b3480156103ef57600080fd5b5061029d6103fe366004613cf9565b61184e565b34801561040f57600080fd5b5061021561041e366004613f60565b611923565b34801561042f57600080fd5b506104577f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610226565b34801561047b57600080fd5b5061024f61048a366004613c16565b611952565b34801561049b57600080fd5b506102156104aa366004613c42565b61195f565b3480156104bb57600080fd5b5061033d6104ca366004614073565b611a66565b3480156104db57600080fd5b506102156104ea366004613bb8565b611c27565b3480156104fb57600080fd5b506104577f000000000000000000000000000000000000000000000000000000000000000081565b34801561052f57600080fd5b5061029d61053e366004613dc6565b611ce9565b34801561054f57600080fd5b5061021561055e366004614100565b611d16565b34801561056f57600080fd5b5061045761057e366004614196565b611e09565b34801561058f57600080fd5b5061033d61059e3660046141cf565b611ea0565b61029d6105b136600461400c565b612043565b60008082804211156105db57604051630407b05b60e31b815260040160405180910390fd5b61060a897f00000000000000000000000000000000000000000000000000000000000000008a8a8a308a61195f565b909350915061061a898685612370565b604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d906024015b600060405180830381600087803b15801561067d57600080fd5b505af1158015610691573d6000803e3d6000fd5b5050505061069f8583612490565b50965096945050505050565b60006106b884848461255e565b90505b9392505050565b60008082804211156106e757604051630407b05b60e31b815260040160405180910390fd5b60006107147f00000000000000000000000000000000000000000000000000000000000000008c8c6125f8565b6040516323b872dd60e01b8152336004820152306024820152604481018b90529091506001600160a01b038216906323b872dd906064016020604051808303816000875af115801561076a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078e919061424b565b5060405163bd3a5c2d60e01b8152600481018a90526001600160a01b038781166024830152600091829184169063bd3a5c2d906044015b60408051808303816000875af11580156107e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108079190614268565b9150915060006108178e8e6126d1565b509050806001600160a01b03168e6001600160a01b03161461083a57818361083d565b82825b90975095508a87101561086357604051638dc525d160e01b815260040160405180910390fd5b898610156108845760405163ef71d09160e01b815260040160405180910390fd5b505050505097509795505050505050565b606081804211156108b957604051630407b05b60e31b815260040160405180910390fd5b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001686866108f06001826142a2565b8181106108ff576108ff6142b5565b905060200201602081019061091491906142cb565b6001600160a01b03161461093b576040516320db826760e01b815260040160405180910390fd5b6109997f00000000000000000000000000000000000000000000000000000000000000008988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b91508682600184516109ab91906142a2565b815181106109bb576109bb6142b5565b602002602001015110156109e2576040516342301c2360e01b815260040160405180910390fd5b610a2f868660008181106109f8576109f86142b5565b9050602002016020810190610a0d91906142cb565b333085600081518110610a2257610a226142b5565b60200260200101516128ba565b610a6e828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152503092506129ea915050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632e1a7d4d8360018551610aac91906142a2565b81518110610abc57610abc6142b5565b60200260200101516040518263ffffffff1660e01b8152600401610ae291815260200190565b600060405180830381600087803b158015610afc57600080fd5b505af1158015610b10573d6000803e3d6000fd5b50505050610b44848360018551610b2791906142a2565b81518110610b3757610b376142b5565b6020026020010151612490565b509695505050505050565b6060610b7c7f00000000000000000000000000000000000000000000000000000000000000008484612bed565b90505b92915050565b6000806000610bb57f00000000000000000000000000000000000000000000000000000000000000008f8f6125f8565b9050600087610bc4578c610bc8565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610c03903390309086908f908e908e908e906004016142e8565b600060405180830381600087803b158015610c1d57600080fd5b505af1158015610c31573d6000803e3d6000fd5b50505050610c448f8f8f8f8f8f8f61195f565b809450819550505050509b509b9950505050505050505050565b600080610c8d7f0000000000000000000000000000000000000000000000000000000000000000868686612d41565b91509150935093915050565b60608180421115610cbd57604051630407b05b60e31b815260040160405180910390fd5b610d1b7f00000000000000000000000000000000000000000000000000000000000000008988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b9150868260018451610d2d91906142a2565b81518110610d3d57610d3d6142b5565b60200260200101511015610d64576040516342301c2360e01b815260040160405180910390fd5b610ddd7f000000000000000000000000000000000000000000000000000000000000000087876000818110610d9b57610d9b6142b5565b9050602002016020810190610db091906142cb565b88886001818110610dc357610dc36142b5565b9050602002016020810190610dd891906142cb565b6125f8565b50610df4868660008181106109f8576109f86142b5565b610b44828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b60008060008380421115610e5a57604051630407b05b60e31b815260040160405180910390fd5b610e898b7f00000000000000000000000000000000000000000000000000000000000000008c348d8d8d612f79565b90945092506000610edb7f00000000000000000000000000000000000000000000000000000000000000008d7f00000000000000000000000000000000000000000000000000000000000000006125f8565b9050610ee98c3330886128ba565b610ef48c828761335e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b158015610f4f57600080fd5b505af1158015610f63573d6000803e3d6000fd5b5050505050610f937f0000000000000000000000000000000000000000000000000000000000000000828661335e565b6000610fbf8d7f00000000000000000000000000000000000000000000000000000000000000006126d1565b509050806001600160a01b03168d6001600160a01b03161461105a5760405163e7d3fe6b60e01b815260048101869052602481018790526001600160a01b03898116604483015283169063e7d3fe6b906064016020604051808303816000875af1158015611031573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110559190614329565b6110d4565b60405163e7d3fe6b60e01b815260048101879052602481018690526001600160a01b03898116604483015283169063e7d3fe6b906064016020604051808303816000875af11580156110b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d49190614329565b9350843411156110f1576110f1336110ec87346142a2565b612490565b5050509750975097945050505050565b6060818042111561112557604051630407b05b60e31b815260040160405180910390fd5b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016868661115c6001826142a2565b81811061116b5761116b6142b5565b905060200201602081019061118091906142cb565b6001600160a01b0316146111a7576040516320db826760e01b815260040160405180910390fd5b6112057f000000000000000000000000000000000000000000000000000000000000000089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b9150868260008151811061121b5761121b6142b5565b602002602001015111156109e25760405163e1b0da4f60e01b815260040160405180910390fd5b6000806000838042111561126957604051630407b05b60e31b815260040160405180910390fd5b6112978a7f00000000000000000000000000000000000000000000000000000000000000008b348c8c613472565b909450925060006112e97f00000000000000000000000000000000000000000000000000000000000000008c7f00000000000000000000000000000000000000000000000000000000000000006125f8565b90508415611382576112fd8b3330886128ba565b6113088b828761335e565b604051630a03949160e31b8152600481018690526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611357573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137b9190614329565b925061149d565b831561149d577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b1580156113e357600080fd5b505af11580156113f7573d6000803e3d6000fd5b50505050506114277f0000000000000000000000000000000000000000000000000000000000000000828661335e565b604051630a03949160e31b8152600481018590526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611476573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149a9190614329565b92505b833411156114b3576114b3336110ec86346142a2565b505096509650969350505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637561d3756040518163ffffffff1660e01b8152600401602060405180830381865afa158015611521573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611545919061424b565b905090565b6060818042111561156e57604051630407b05b60e31b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316868660008181106115ab576115ab6142b5565b90506020020160208101906115c091906142cb565b6001600160a01b0316146115e7576040516320db826760e01b815260040160405180910390fd5b6116457f00000000000000000000000000000000000000000000000000000000000000003488888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b915086826001845161165791906142a2565b81518110611667576116676142b5565b6020026020010151101561168e576040516342301c2360e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0836000815181106116d0576116d06142b5565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561170357600080fd5b505af1158015611717573d6000803e3d6000fd5b50505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb308460008151811061175f5761175f6142b5565b60200260200101516040518363ffffffff1660e01b81526004016117989291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af11580156117b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117db919061424b565b6117f857604051631f0236bb60e01b815260040160405180910390fd5b611837828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b5095945050505050565b60006106b8848484613691565b6060818042111561187257604051630407b05b60e31b815260040160405180910390fd5b6118d07f000000000000000000000000000000000000000000000000000000000000000089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b915086826000815181106118e6576118e66142b5565b6020026020010151111561190d5760405163e1b0da4f60e01b815260040160405180910390fd5b610df4868660008181106109f8576109f86142b5565b600080610c8d7f0000000000000000000000000000000000000000000000000000000000000000868686613730565b60006106b884848461398f565b600080828042111561198457604051630407b05b60e31b815260040160405180910390fd5b60006119b17f00000000000000000000000000000000000000000000000000000000000000008c8c6125f8565b6040516323b872dd60e01b8152336004820152306024820152604481018b90529091506001600160a01b038216906323b872dd906064016020604051808303816000875af1158015611a07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a2b919061424b565b50604051633f34d4cf60e21b8152600481018a90526001600160a01b038781166024830152600091829184169063fcd3533c906044016107c5565b60008060008380421115611a8d57604051630407b05b60e31b815260040160405180910390fd5b611a9c8d8d8d8d8d8d8d612f79565b90945092506000611ace7f00000000000000000000000000000000000000000000000000000000000000008f8f6125f8565b9050611adc8e3330886128ba565b611ae78e828761335e565b611af38d3330876128ba565b611afe8d828661335e565b8c6001600160a01b03168e6001600160a01b03161015611b995760405163e7d3fe6b60e01b815260048101869052602481018590526001600160a01b03888116604483015282169063e7d3fe6b906064016020604051808303816000875af1158015611b6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b929190614329565b9250611c16565b60405163e7d3fe6b60e01b815260048101859052602481018690526001600160a01b03888116604483015282169063e7d3fe6b906064016020604051808303816000875af1158015611bef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c139190614329565b92505b505099509950999650505050505050565b6000808280421115611c4c57604051630407b05b60e31b815260040160405180910390fd5b611c7b897f00000000000000000000000000000000000000000000000000000000000000008a8a8a308a6106c2565b90935091508215611c9657611c91898685612370565b61069f565b811561069f57604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401610663565b6060610b7c7f0000000000000000000000000000000000000000000000000000000000000000848461275c565b6000806000611d667f00000000000000000000000000000000000000000000000000000000000000008e7f00000000000000000000000000000000000000000000000000000000000000006125f8565b9050600087611d75578c611d79565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90611db4903390309086908f908e908e908e906004016142e8565b600060405180830381600087803b158015611dce57600080fd5b505af1158015611de2573d6000803e3d6000fd5b50505050611df48e8e8e8e8e8e6105b6565b909f909e509c50505050505050505050505050565b60405163e6a4390560e01b81526001600160a01b03838116600483015282811660248301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063e6a4390590604401602060405180830381865afa158015611e7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7c9190614342565b60008060008380421115611ec757604051630407b05b60e31b815260040160405180910390fd5b611ed58c8c8c8c8c8c613472565b90945092506000611f077f00000000000000000000000000000000000000000000000000000000000000008e8e6125f8565b90508415611fa057611f1b8d3330886128ba565b611f268d828761335e565b604051630a03949160e31b8152600481018690526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611f75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f999190614329565b9250612033565b831561203357611fb28c3330876128ba565b611fbd8c828661335e565b604051630a03949160e31b8152600481018590526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af115801561200c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120309190614329565b92505b5050985098509895505050505050565b6060818042111561206757604051630407b05b60e31b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316868660008181106120a4576120a46142b5565b90506020020160208101906120b991906142cb565b6001600160a01b0316146120e0576040516320db826760e01b815260040160405180910390fd5b61213e7f000000000000000000000000000000000000000000000000000000000000000088888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b91503482600081518110612154576121546142b5565b6020026020010151111561217b5760405163e1b0da4f60e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0836000815181106121bd576121bd6142b5565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156121f057600080fd5b505af1158015612204573d6000803e3d6000fd5b50505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb308460008151811061224c5761224c6142b5565b60200260200101516040518363ffffffff1660e01b81526004016122859291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af11580156122a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c8919061424b565b6122e557604051631f0236bb60e01b815260040160405180910390fd5b612324828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b81600081518110612337576123376142b5565b602002602001015134111561183757611837338360008151811061235d5761235d6142b5565b6020026020010151346110ec91906142a2565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916123cc919061435f565b6000604051808303816000865af19150503d8060008114612409576040519150601f19603f3d011682016040523d82523d6000602084013e61240e565b606091505b5091509150818015612438575080511580612438575080806020019051810190612438919061424b565b6124895760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c45440060448201526064015b60405180910390fd5b5050505050565b604080516000808252602082019092526001600160a01b0384169083906040516124ba919061435f565b60006040518083038185875af1925050503d80600081146124f7576040519150601f19603f3d011682016040523d82523d6000602084013e6124fc565b606091505b50509050806125595760405162461bcd60e51b815260206004820152602360248201527f5472616e7366657248656c7065723a204554485f5452414e534645525f46414960448201526213115160ea1b6064820152608401612480565b505050565b6000836000036125815760405163098fb56160e01b815260040160405180910390fd5b82158061258c575081155b156125aa5760405163bb55fd2760e01b815260040160405180910390fd5b60006125b8856103e561438e565b905060006125c6848361438e565b90506000826125d7876103e861438e565b6125e191906143a5565b90506125ed81836143ce565b979650505050505050565b600080600061260785856126d1565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015291935091508690604801604051602081830303815290604052805190602001206040516020016126af9291906001600160f81b0319815260609290921b6bffffffffffffffffffffffff1916600183015260158201527f912fa011211d18178fef8f22392edc90ca8f101645ab8347e1359b5ce2f890df603582015260550190565b60408051601f1981840301815291905280516020909101209695505050505050565b600080826001600160a01b0316846001600160a01b03160361270657604051630bd969eb60e41b815260040160405180910390fd5b826001600160a01b0316846001600160a01b031610612726578284612729565b83835b90925090506001600160a01b0382166127555760405163d92e233d60e01b815260040160405180910390fd5b9250929050565b6060600282511015612781576040516320db826760e01b815260040160405180910390fd5b815167ffffffffffffffff81111561279b5761279b613db0565b6040519080825280602002602001820160405280156127c4578160200160208202803683370190505b50905082816000815181106127db576127db6142b5565b60200260200101818152505060005b600183516127f891906142a2565b8110156128b25760008061284b87868581518110612818576128186142b5565b60200260200101518786600161282e91906143a5565b8151811061283e5761283e6142b5565b60200260200101516139f0565b505091509150612875848481518110612866576128666142b5565b6020026020010151838361255e565b846128818560016143a5565b81518110612891576128916142b5565b602002602001018181525050505080806128aa906143f0565b9150506127ea565b509392505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283929088169161291e919061435f565b6000604051808303816000865af19150503d806000811461295b576040519150601f19603f3d011682016040523d82523d6000602084013e612960565b606091505b509150915081801561298a57508051158061298a57508080602001905181019061298a919061424b565b6129e25760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401612480565b505050505050565b60005b600183516129fb91906142a2565b811015612be757600080848381518110612a1757612a176142b5565b602002602001015185846001612a2d91906143a5565b81518110612a3d57612a3d6142b5565b6020026020010151915091506000612a5583836126d1565b5090506000878581518110612a6c57612a6c6142b5565b60200260200101519050600088866001612a8691906143a5565b81518110612a9657612a966142b5565b60200260200101519050600080846001600160a01b0316876001600160a01b031614612ac457600084612ac8565b8360005b91509150600080866001600160a01b0316896001600160a01b031614612af057846000612af4565b6000855b91509150600060028d51612b0891906142a2565b8b10612b14578b612b16565b305b90506000612b457f00000000000000000000000000000000000000000000000000000000000000008c8c6125f8565b9050612b528b828a61335e565b60405163562e19df60e01b8152600481018790526024810186905260448101859052606481018490526001600160a01b03838116608483015282169063562e19df9060a401600060405180830381600087803b158015612bb157600080fd5b505af1158015612bc5573d6000803e3d6000fd5b5050505050505050505050505050508080612bdf906143f0565b9150506129ed565b50505050565b6060600282511015612c12576040516320db826760e01b815260040160405180910390fd5b815167ffffffffffffffff811115612c2c57612c2c613db0565b604051908082528060200260200182016040528015612c55578160200160208202803683370190505b509050828160018351612c6891906142a2565b81518110612c7857612c786142b5565b602002602001018181525050600060018351612c9491906142a2565b90505b80156128b257600080612cda8786612cb06001876142a2565b81518110612cc057612cc06142b5565b602002602001015187868151811061283e5761283e6142b5565b505091509150612d04848481518110612cf557612cf56142b5565b60200260200101518383613691565b84612d106001866142a2565b81518110612d2057612d206142b5565b60200260200101818152505050508080612d3990614409565b915050612c97565b6000806000612d518787876125f8565b6040516370a0823160e01b81526001600160a01b0380831660048301529192506000918816906370a0823190602401602060405180830381865afa158015612d9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc19190614329565b6040516370a0823160e01b81526001600160a01b0384811660048301529192506000918816906370a0823190602401602060405180830381865afa158015612e0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e319190614329565b90506000836001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e979190614329565b9050876001600160a01b0316896001600160a01b03161015612f0c5781612ecc82612ec28a876143a5565b600160701b613ae1565b612ed691906143a5565b612ee0838961438e565b612eea91906143ce565b9550600160701b612efb828861438e565b612f0591906143ce565b9450612f6c565b8181612f1889866143a5565b612f2690600160701b61438e565b612f3091906143ce565b612f3a91906143a5565b612f44838961438e565b612f4e91906143ce565b955080612f5f87600160701b61438e565b612f6991906143ce565b94505b5050505094509492505050565b60405163e6a4390560e01b81526001600160a01b0388811660048301528781166024830152600091829182917f00000000000000000000000000000000000000000000000000000000000000009091169063e6a4390590604401602060405180830381865afa158015612ff0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130149190614342565b90506001600160a01b0381166130bb576040516364e329cb60e11b81526001600160a01b038b811660048301528a811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063c9c65396906044016020604051808303816000875af1158015613094573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130b89190614342565b90505b6000806000806130ec7f00000000000000000000000000000000000000000000000000000000000000008f8f6139f0565b9350935093509350818461310091906143a5565b158015613114575061311281846143a5565b155b15613124578b96508a95506131cd565b60006131438d61313485886143a5565b61313e85886143a5565b61398f565b90508b8111613178578981101561316d5760405163ef71d09160e01b815260040160405180910390fd5b8c97509550856131cb565b60006131928d61318885886143a5565b61313e878a6143a5565b90508d8111156131a4576131a4614420565b8b8110156131c557604051638dc525d160e01b815260040160405180910390fd5b97508b96505b505b6000841180156131dd5750600083115b15610884576000856001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613222573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132469190614329565b90508d6001600160a01b03168f6001600160a01b031610156132d957600061327482612ec26127108961438e565b90508061328761ffff8c166127106142a2565b613291908761438e565b11806132b55750806132a961ffff8c166127106143a5565b6132b3908761438e565b105b156132d357604051630d683ed760e21b815260040160405180910390fd5b5061334c565b60006132eb82612ec26127108861438e565b9050806132fe61ffff8c166127106142a2565b613308908861438e565b118061332c57508061332061ffff8c166127106143a5565b61332a908861438e565b105b1561334a57604051630d683ed760e21b815260040160405180910390fd5b505b50505050505097509795505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b17905291516000928392908716916133ba919061435f565b6000604051808303816000865af19150503d80600081146133f7576040519150601f19603f3d011682016040523d82523d6000602084013e6133fc565b606091505b5091509150818015613426575080511580613426575080806020019051810190613426919061424b565b6124895760405162461bcd60e51b815260206004820152601e60248201527f5472616e7366657248656c7065723a20415050524f56455f4641494c454400006044820152606401612480565b60405163e6a4390560e01b81526001600160a01b0387811660048301528681166024830152600091829182917f00000000000000000000000000000000000000000000000000000000000000009091169063e6a4390590604401602060405180830381865afa1580156134e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061350d9190614342565b6001600160a01b031603613534576040516391bc00d960e01b815260040160405180910390fd5b6000806000806135657f00000000000000000000000000000000000000000000000000000000000000008d8d6139f0565b9350935093509350836000148061357a575082155b15613598576040516321c4e35760e21b815260040160405180910390fd5b811580156135a4575080155b156135c2576040516391bc00d960e01b815260040160405180910390fd5b81156136275760006135f67f00000000000000000000000000000000000000000000000000000000000000008d8f8d612d41565b9150508881101561361a57604051638dc525d160e01b815260040160405180910390fd5b5060009550889450613682565b60006136557f00000000000000000000000000000000000000000000000000000000000000008e8e8e612d41565b915050878110156136795760405163ef71d09160e01b815260040160405180910390fd5b50899550600094505b50505050965096945050505050565b6000836000036136b4576040516342301c2360e01b815260040160405180910390fd5b8215806136bf575081155b156136dd5760405163bb55fd2760e01b815260040160405180910390fd5b60006136e9858561438e565b6136f5906103e861438e565b9050600061370386856142a2565b61370f906103e561438e565b905061371b81836143ce565b6137269060016143a5565b9695505050505050565b60008060006137408787876125f8565b90506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613782573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137a69190614329565b6040516370a0823160e01b81526001600160a01b0384811660048301529192506000918916906370a0823190602401602060405180830381865afa1580156137f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138169190614329565b6040516370a0823160e01b81526001600160a01b0385811660048301529192506000918916906370a0823190602401602060405180830381865afa158015613862573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138869190614329565b90506000846001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138ec9190614329565b90506000846138fb8a8561438e565b61390591906143ce565b9050846139128a8661438e565b61391c91906143ce565b9750896001600160a01b03168b6001600160a01b03161015613958578161394782600160701b61438e565b61395191906143ce565b9650613974565b600160701b613967838361438e565b61397191906143ce565b96505b61397e87896143a5565b975050505050505094509492505050565b6000836000036139b257604051632ca2f52b60e11b815260040160405180910390fd5b8215806139bd575081155b156139db5760405163bb55fd2760e01b815260040160405180910390fd5b826139e6838661438e565b6106b891906143ce565b6000806000806000613a0287876126d1565b509050600080600080613a168c8c8c6125f8565b6001600160a01b031663976bf4166040518163ffffffff1660e01b815260040160a060405180830381865afa158015613a53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a77919061444d565b506001600160701b031693506001600160701b031693506001600160701b031693506001600160701b03169350846001600160a01b03168b6001600160a01b031614613ac65782848284613acb565b838383835b929f919e509c50909a5098505050505050505050565b6000808060001985870985870292508281108382030391505080600003613b1b57838281613b1157613b116143b8565b04925050506106bb565b808411613b2757600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6001600160a01b0381168114613ba557600080fd5b50565b8035613bb381613b90565b919050565b60008060008060008060c08789031215613bd157600080fd5b8635613bdc81613b90565b95506020870135945060408701359350606087013592506080870135613c0181613b90565b8092505060a087013590509295509295509295565b600080600060608486031215613c2b57600080fd5b505081359360208301359350604090920135919050565b600080600080600080600060e0888a031215613c5d57600080fd5b8735613c6881613b90565b96506020880135613c7881613b90565b955060408801359450606088013593506080880135925060a0880135613c9d81613b90565b8092505060c0880135905092959891949750929550565b60008083601f840112613cc657600080fd5b50813567ffffffffffffffff811115613cde57600080fd5b6020830191508360208260051b850101111561275557600080fd5b60008060008060008060a08789031215613d1257600080fd5b8635955060208701359450604087013567ffffffffffffffff811115613d3757600080fd5b613d4389828a01613cb4565b9095509350506060870135613d5781613b90565b80925050608087013590509295509295509295565b6020808252825182820181905260009190848201906040850190845b81811015613da457835183529284019291840191600101613d88565b50909695505050505050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215613dd957600080fd5b8235915060208084013567ffffffffffffffff80821115613df957600080fd5b818601915086601f830112613e0d57600080fd5b813581811115613e1f57613e1f613db0565b8060051b604051601f19603f83011681018181108582111715613e4457613e44613db0565b604052918252848201925083810185019189831115613e6257600080fd5b938501935b82851015613e8757613e7885613ba8565b84529385019392850192613e67565b8096505050505050509250929050565b8015158114613ba557600080fd5b803560ff81168114613bb357600080fd5b60008060008060008060008060008060006101608c8e031215613ed857600080fd5b8b35613ee381613b90565b9a5060208c0135613ef381613b90565b995060408c0135985060608c0135975060808c0135965060a08c0135613f1881613b90565b955060c08c0135945060e08c0135613f2f81613e97565b9350613f3e6101008d01613ea5565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600060608486031215613f7557600080fd5b8335613f8081613b90565b92506020840135613f9081613b90565b929592945050506040919091013590565b803561ffff81168114613bb357600080fd5b600080600080600080600060e0888a031215613fce57600080fd5b8735613fd981613b90565b9650602088013595506040880135945060608801359350613ffc60808901613fa1565b925060a0880135613c9d81613b90565b60008060008060006080868803121561402457600080fd5b85359450602086013567ffffffffffffffff81111561404257600080fd5b61404e88828901613cb4565b909550935050604086013561406281613b90565b949793965091946060013592915050565b60008060008060008060008060006101208a8c03121561409257600080fd5b893561409d81613b90565b985060208a01356140ad81613b90565b975060408a0135965060608a0135955060808a0135945060a08a013593506140d760c08b01613fa1565b925060e08a01356140e781613b90565b809250506101008a013590509295985092959850929598565b6000806000806000806000806000806101408b8d03121561412057600080fd5b8a3561412b81613b90565b995060208b0135985060408b0135975060608b0135965060808b013561415081613b90565b955060a08b0135945060c08b013561416781613e97565b935061417560e08c01613ea5565b92506101008b013591506101208b013590509295989b9194979a5092959850565b600080604083850312156141a957600080fd5b82356141b481613b90565b915060208301356141c481613b90565b809150509250929050565b600080600080600080600080610100898b0312156141ec57600080fd5b88356141f781613b90565b9750602089013561420781613b90565b965060408901359550606089013594506080890135935060a0890135925060c089013561423381613b90565b8092505060e089013590509295985092959890939650565b60006020828403121561425d57600080fd5b81516106bb81613e97565b6000806040838503121561427b57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b81810381811115610b7f57610b7f61428c565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156142dd57600080fd5b81356106bb81613b90565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b60006020828403121561433b57600080fd5b5051919050565b60006020828403121561435457600080fd5b81516106bb81613b90565b6000825160005b818110156143805760208186018101518583015201614366565b506000920191825250919050565b8082028115828204841417610b7f57610b7f61428c565b80820180821115610b7f57610b7f61428c565b634e487b7160e01b600052601260045260246000fd5b6000826143eb57634e487b7160e01b600052601260045260246000fd5b500490565b6000600182016144025761440261428c565b5060010190565b6000816144185761441861428c565b506000190190565b634e487b7160e01b600052600160045260246000fd5b80516001600160701b0381168114613bb357600080fd5b600080600080600060a0868803121561446557600080fd5b61446e86614436565b945061447c60208701614436565b935061448a60408701614436565b925061449860608701614436565b9150608086015163ffffffff811681146144b157600080fd5b80915050929550929590935056000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

Deployed Bytecode

0x6080604052600436106101a05760003560e01c806385f8c259116100ec578063c2de19331161008a578063ded9382a11610064578063ded9382a14610543578063e6a4390514610563578063e78e168414610583578063fb3bdb41146105a357600080fd5b8063c2de1933146104cf578063c45a0155146104ef578063d06ca61f1461052357600080fd5b8063ad5c4648116100c6578063ad5c464814610423578063ad615dec1461046f578063baa2abde1461048f578063c2772c24146104af57600080fd5b806385f8c259146103c35780638803dbee146103e35780639adf412b1461040357600080fd5b806329c89c48116101595780634a25d94a116101335780634a25d94a146103585780635a0f2c2d146103785780637561d3751461038b5780637ff36ab5146103b057600080fd5b806329c89c48146102ea57806338ed17391461030a57806344794dbf1461032a57600080fd5b806302751cec146101f5578063054d50d41461022f5780630917e98b1461025d57806318cbafe51461027d5780631f00ca74146102aa5780632195995c146102ca57600080fd5b366101f057336001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216146101ee576040516305bbb6bf60e31b815260040160405180910390fd5b005b600080fd5b34801561020157600080fd5b50610215610210366004613bb8565b6105b6565b604080519283526020830191909152015b60405180910390f35b34801561023b57600080fd5b5061024f61024a366004613c16565b6106ab565b604051908152602001610226565b34801561026957600080fd5b50610215610278366004613c42565b6106c2565b34801561028957600080fd5b5061029d610298366004613cf9565b610895565b6040516102269190613d6c565b3480156102b657600080fd5b5061029d6102c5366004613dc6565b610b4f565b3480156102d657600080fd5b506102156102e5366004613eb6565b610b85565b3480156102f657600080fd5b50610215610305366004613f60565b610c5e565b34801561031657600080fd5b5061029d610325366004613cf9565b610c99565b61033d610338366004613fb3565b610e33565b60408051938452602084019290925290820152606001610226565b34801561036457600080fd5b5061029d610373366004613cf9565b611101565b61033d610386366004613bb8565b611242565b34801561039757600080fd5b506103a06114c1565b6040519015158152602001610226565b61029d6103be36600461400c565b61154a565b3480156103cf57600080fd5b5061024f6103de366004613c16565b611841565b3480156103ef57600080fd5b5061029d6103fe366004613cf9565b61184e565b34801561040f57600080fd5b5061021561041e366004613f60565b611923565b34801561042f57600080fd5b506104577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040516001600160a01b039091168152602001610226565b34801561047b57600080fd5b5061024f61048a366004613c16565b611952565b34801561049b57600080fd5b506102156104aa366004613c42565b61195f565b3480156104bb57600080fd5b5061033d6104ca366004614073565b611a66565b3480156104db57600080fd5b506102156104ea366004613bb8565b611c27565b3480156104fb57600080fd5b506104577f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5081565b34801561052f57600080fd5b5061029d61053e366004613dc6565b611ce9565b34801561054f57600080fd5b5061021561055e366004614100565b611d16565b34801561056f57600080fd5b5061045761057e366004614196565b611e09565b34801561058f57600080fd5b5061033d61059e3660046141cf565b611ea0565b61029d6105b136600461400c565b612043565b60008082804211156105db57604051630407b05b60e31b815260040160405180910390fd5b61060a897f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28a8a8a308a61195f565b909350915061061a898685612370565b604051632e1a7d4d60e01b8152600481018390527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d906024015b600060405180830381600087803b15801561067d57600080fd5b505af1158015610691573d6000803e3d6000fd5b5050505061069f8583612490565b50965096945050505050565b60006106b884848461255e565b90505b9392505050565b60008082804211156106e757604051630407b05b60e31b815260040160405180910390fd5b60006107147f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508c8c6125f8565b6040516323b872dd60e01b8152336004820152306024820152604481018b90529091506001600160a01b038216906323b872dd906064016020604051808303816000875af115801561076a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078e919061424b565b5060405163bd3a5c2d60e01b8152600481018a90526001600160a01b038781166024830152600091829184169063bd3a5c2d906044015b60408051808303816000875af11580156107e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108079190614268565b9150915060006108178e8e6126d1565b509050806001600160a01b03168e6001600160a01b03161461083a57818361083d565b82825b90975095508a87101561086357604051638dc525d160e01b815260040160405180910390fd5b898610156108845760405163ef71d09160e01b815260040160405180910390fd5b505050505097509795505050505050565b606081804211156108b957604051630407b05b60e31b815260040160405180910390fd5b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21686866108f06001826142a2565b8181106108ff576108ff6142b5565b905060200201602081019061091491906142cb565b6001600160a01b03161461093b576040516320db826760e01b815260040160405180910390fd5b6109997f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b91508682600184516109ab91906142a2565b815181106109bb576109bb6142b5565b602002602001015110156109e2576040516342301c2360e01b815260040160405180910390fd5b610a2f868660008181106109f8576109f86142b5565b9050602002016020810190610a0d91906142cb565b333085600081518110610a2257610a226142b5565b60200260200101516128ba565b610a6e828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152503092506129ea915050565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316632e1a7d4d8360018551610aac91906142a2565b81518110610abc57610abc6142b5565b60200260200101516040518263ffffffff1660e01b8152600401610ae291815260200190565b600060405180830381600087803b158015610afc57600080fd5b505af1158015610b10573d6000803e3d6000fd5b50505050610b44848360018551610b2791906142a2565b81518110610b3757610b376142b5565b6020026020010151612490565b509695505050505050565b6060610b7c7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508484612bed565b90505b92915050565b6000806000610bb57f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508f8f6125f8565b9050600087610bc4578c610bc8565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610c03903390309086908f908e908e908e906004016142e8565b600060405180830381600087803b158015610c1d57600080fd5b505af1158015610c31573d6000803e3d6000fd5b50505050610c448f8f8f8f8f8f8f61195f565b809450819550505050509b509b9950505050505050505050565b600080610c8d7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50868686612d41565b91509150935093915050565b60608180421115610cbd57604051630407b05b60e31b815260040160405180910390fd5b610d1b7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508988888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b9150868260018451610d2d91906142a2565b81518110610d3d57610d3d6142b5565b60200260200101511015610d64576040516342301c2360e01b815260040160405180910390fd5b610ddd7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5087876000818110610d9b57610d9b6142b5565b9050602002016020810190610db091906142cb565b88886001818110610dc357610dc36142b5565b9050602002016020810190610dd891906142cb565b6125f8565b50610df4868660008181106109f8576109f86142b5565b610b44828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b60008060008380421115610e5a57604051630407b05b60e31b815260040160405180910390fd5b610e898b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28c348d8d8d612f79565b90945092506000610edb7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508d7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26125f8565b9050610ee98c3330886128ba565b610ef48c828761335e565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b158015610f4f57600080fd5b505af1158015610f63573d6000803e3d6000fd5b5050505050610f937f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2828661335e565b6000610fbf8d7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26126d1565b509050806001600160a01b03168d6001600160a01b03161461105a5760405163e7d3fe6b60e01b815260048101869052602481018790526001600160a01b03898116604483015283169063e7d3fe6b906064016020604051808303816000875af1158015611031573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110559190614329565b6110d4565b60405163e7d3fe6b60e01b815260048101879052602481018690526001600160a01b03898116604483015283169063e7d3fe6b906064016020604051808303816000875af11580156110b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d49190614329565b9350843411156110f1576110f1336110ec87346142a2565b612490565b5050509750975097945050505050565b6060818042111561112557604051630407b05b60e31b815260040160405180910390fd5b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216868661115c6001826142a2565b81811061116b5761116b6142b5565b905060200201602081019061118091906142cb565b6001600160a01b0316146111a7576040516320db826760e01b815260040160405180910390fd5b6112057f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b9150868260008151811061121b5761121b6142b5565b602002602001015111156109e25760405163e1b0da4f60e01b815260040160405180910390fd5b6000806000838042111561126957604051630407b05b60e31b815260040160405180910390fd5b6112978a7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28b348c8c613472565b909450925060006112e97f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508c7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26125f8565b90508415611382576112fd8b3330886128ba565b6113088b828761335e565b604051630a03949160e31b8152600481018690526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611357573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137b9190614329565b925061149d565b831561149d577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0856040518263ffffffff1660e01b81526004016000604051808303818588803b1580156113e357600080fd5b505af11580156113f7573d6000803e3d6000fd5b50505050506114277f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2828661335e565b604051630a03949160e31b8152600481018590526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611476573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149a9190614329565b92505b833411156114b3576114b3336110ec86346142a2565b505096509650969350505050565b60007f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b0316637561d3756040518163ffffffff1660e01b8152600401602060405180830381865afa158015611521573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611545919061424b565b905090565b6060818042111561156e57604051630407b05b60e31b815260040160405180910390fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316868660008181106115ab576115ab6142b5565b90506020020160208101906115c091906142cb565b6001600160a01b0316146115e7576040516320db826760e01b815260040160405180910390fd5b6116457f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce503488888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061275c92505050565b915086826001845161165791906142a2565b81518110611667576116676142b5565b6020026020010151101561168e576040516342301c2360e01b815260040160405180910390fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836000815181106116d0576116d06142b5565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561170357600080fd5b505af1158015611717573d6000803e3d6000fd5b50505050507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663a9059cbb308460008151811061175f5761175f6142b5565b60200260200101516040518363ffffffff1660e01b81526004016117989291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af11580156117b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117db919061424b565b6117f857604051631f0236bb60e01b815260040160405180910390fd5b611837828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b5095945050505050565b60006106b8848484613691565b6060818042111561187257604051630407b05b60e31b815260040160405180910390fd5b6118d07f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5089888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b915086826000815181106118e6576118e66142b5565b6020026020010151111561190d5760405163e1b0da4f60e01b815260040160405180910390fd5b610df4868660008181106109f8576109f86142b5565b600080610c8d7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50868686613730565b60006106b884848461398f565b600080828042111561198457604051630407b05b60e31b815260040160405180910390fd5b60006119b17f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508c8c6125f8565b6040516323b872dd60e01b8152336004820152306024820152604481018b90529091506001600160a01b038216906323b872dd906064016020604051808303816000875af1158015611a07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a2b919061424b565b50604051633f34d4cf60e21b8152600481018a90526001600160a01b038781166024830152600091829184169063fcd3533c906044016107c5565b60008060008380421115611a8d57604051630407b05b60e31b815260040160405180910390fd5b611a9c8d8d8d8d8d8d8d612f79565b90945092506000611ace7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508f8f6125f8565b9050611adc8e3330886128ba565b611ae78e828761335e565b611af38d3330876128ba565b611afe8d828661335e565b8c6001600160a01b03168e6001600160a01b03161015611b995760405163e7d3fe6b60e01b815260048101869052602481018590526001600160a01b03888116604483015282169063e7d3fe6b906064016020604051808303816000875af1158015611b6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b929190614329565b9250611c16565b60405163e7d3fe6b60e01b815260048101859052602481018690526001600160a01b03888116604483015282169063e7d3fe6b906064016020604051808303816000875af1158015611bef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c139190614329565b92505b505099509950999650505050505050565b6000808280421115611c4c57604051630407b05b60e31b815260040160405180910390fd5b611c7b897f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28a8a8a308a6106c2565b90935091508215611c9657611c91898685612370565b61069f565b811561069f57604051632e1a7d4d60e01b8152600481018390527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d90602401610663565b6060610b7c7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50848461275c565b6000806000611d667f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508e7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26125f8565b9050600087611d75578c611d79565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90611db4903390309086908f908e908e908e906004016142e8565b600060405180830381600087803b158015611dce57600080fd5b505af1158015611de2573d6000803e3d6000fd5b50505050611df48e8e8e8e8e8e6105b6565b909f909e509c50505050505050505050505050565b60405163e6a4390560e01b81526001600160a01b03838116600483015282811660248301526000917f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce509091169063e6a4390590604401602060405180830381865afa158015611e7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7c9190614342565b60008060008380421115611ec757604051630407b05b60e31b815260040160405180910390fd5b611ed58c8c8c8c8c8c613472565b90945092506000611f077f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508e8e6125f8565b90508415611fa057611f1b8d3330886128ba565b611f268d828761335e565b604051630a03949160e31b8152600481018690526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af1158015611f75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f999190614329565b9250612033565b831561203357611fb28c3330876128ba565b611fbd8c828661335e565b604051630a03949160e31b8152600481018590526001600160a01b03888116602483015282169063501ca488906044016020604051808303816000875af115801561200c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120309190614329565b92505b5050985098509895505050505050565b6060818042111561206757604051630407b05b60e31b815260040160405180910390fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316868660008181106120a4576120a46142b5565b90506020020160208101906120b991906142cb565b6001600160a01b0316146120e0576040516320db826760e01b815260040160405180910390fd5b61213e7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5088888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612bed92505050565b91503482600081518110612154576121546142b5565b6020026020010151111561217b5760405163e1b0da4f60e01b815260040160405180910390fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836000815181106121bd576121bd6142b5565b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156121f057600080fd5b505af1158015612204573d6000803e3d6000fd5b50505050507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663a9059cbb308460008151811061224c5761224c6142b5565b60200260200101516040518363ffffffff1660e01b81526004016122859291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af11580156122a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c8919061424b565b6122e557604051631f0236bb60e01b815260040160405180910390fd5b612324828787808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508992506129ea915050565b81600081518110612337576123376142b5565b602002602001015134111561183757611837338360008151811061235d5761235d6142b5565b6020026020010151346110ec91906142a2565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916123cc919061435f565b6000604051808303816000865af19150503d8060008114612409576040519150601f19603f3d011682016040523d82523d6000602084013e61240e565b606091505b5091509150818015612438575080511580612438575080806020019051810190612438919061424b565b6124895760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c45440060448201526064015b60405180910390fd5b5050505050565b604080516000808252602082019092526001600160a01b0384169083906040516124ba919061435f565b60006040518083038185875af1925050503d80600081146124f7576040519150601f19603f3d011682016040523d82523d6000602084013e6124fc565b606091505b50509050806125595760405162461bcd60e51b815260206004820152602360248201527f5472616e7366657248656c7065723a204554485f5452414e534645525f46414960448201526213115160ea1b6064820152608401612480565b505050565b6000836000036125815760405163098fb56160e01b815260040160405180910390fd5b82158061258c575081155b156125aa5760405163bb55fd2760e01b815260040160405180910390fd5b60006125b8856103e561438e565b905060006125c6848361438e565b90506000826125d7876103e861438e565b6125e191906143a5565b90506125ed81836143ce565b979650505050505050565b600080600061260785856126d1565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015291935091508690604801604051602081830303815290604052805190602001206040516020016126af9291906001600160f81b0319815260609290921b6bffffffffffffffffffffffff1916600183015260158201527f912fa011211d18178fef8f22392edc90ca8f101645ab8347e1359b5ce2f890df603582015260550190565b60408051601f1981840301815291905280516020909101209695505050505050565b600080826001600160a01b0316846001600160a01b03160361270657604051630bd969eb60e41b815260040160405180910390fd5b826001600160a01b0316846001600160a01b031610612726578284612729565b83835b90925090506001600160a01b0382166127555760405163d92e233d60e01b815260040160405180910390fd5b9250929050565b6060600282511015612781576040516320db826760e01b815260040160405180910390fd5b815167ffffffffffffffff81111561279b5761279b613db0565b6040519080825280602002602001820160405280156127c4578160200160208202803683370190505b50905082816000815181106127db576127db6142b5565b60200260200101818152505060005b600183516127f891906142a2565b8110156128b25760008061284b87868581518110612818576128186142b5565b60200260200101518786600161282e91906143a5565b8151811061283e5761283e6142b5565b60200260200101516139f0565b505091509150612875848481518110612866576128666142b5565b6020026020010151838361255e565b846128818560016143a5565b81518110612891576128916142b5565b602002602001018181525050505080806128aa906143f0565b9150506127ea565b509392505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283929088169161291e919061435f565b6000604051808303816000865af19150503d806000811461295b576040519150601f19603f3d011682016040523d82523d6000602084013e612960565b606091505b509150915081801561298a57508051158061298a57508080602001905181019061298a919061424b565b6129e25760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401612480565b505050505050565b60005b600183516129fb91906142a2565b811015612be757600080848381518110612a1757612a176142b5565b602002602001015185846001612a2d91906143a5565b81518110612a3d57612a3d6142b5565b6020026020010151915091506000612a5583836126d1565b5090506000878581518110612a6c57612a6c6142b5565b60200260200101519050600088866001612a8691906143a5565b81518110612a9657612a966142b5565b60200260200101519050600080846001600160a01b0316876001600160a01b031614612ac457600084612ac8565b8360005b91509150600080866001600160a01b0316896001600160a01b031614612af057846000612af4565b6000855b91509150600060028d51612b0891906142a2565b8b10612b14578b612b16565b305b90506000612b457f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508c8c6125f8565b9050612b528b828a61335e565b60405163562e19df60e01b8152600481018790526024810186905260448101859052606481018490526001600160a01b03838116608483015282169063562e19df9060a401600060405180830381600087803b158015612bb157600080fd5b505af1158015612bc5573d6000803e3d6000fd5b5050505050505050505050505050508080612bdf906143f0565b9150506129ed565b50505050565b6060600282511015612c12576040516320db826760e01b815260040160405180910390fd5b815167ffffffffffffffff811115612c2c57612c2c613db0565b604051908082528060200260200182016040528015612c55578160200160208202803683370190505b509050828160018351612c6891906142a2565b81518110612c7857612c786142b5565b602002602001018181525050600060018351612c9491906142a2565b90505b80156128b257600080612cda8786612cb06001876142a2565b81518110612cc057612cc06142b5565b602002602001015187868151811061283e5761283e6142b5565b505091509150612d04848481518110612cf557612cf56142b5565b60200260200101518383613691565b84612d106001866142a2565b81518110612d2057612d206142b5565b60200260200101818152505050508080612d3990614409565b915050612c97565b6000806000612d518787876125f8565b6040516370a0823160e01b81526001600160a01b0380831660048301529192506000918816906370a0823190602401602060405180830381865afa158015612d9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc19190614329565b6040516370a0823160e01b81526001600160a01b0384811660048301529192506000918816906370a0823190602401602060405180830381865afa158015612e0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e319190614329565b90506000836001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e979190614329565b9050876001600160a01b0316896001600160a01b03161015612f0c5781612ecc82612ec28a876143a5565b600160701b613ae1565b612ed691906143a5565b612ee0838961438e565b612eea91906143ce565b9550600160701b612efb828861438e565b612f0591906143ce565b9450612f6c565b8181612f1889866143a5565b612f2690600160701b61438e565b612f3091906143ce565b612f3a91906143a5565b612f44838961438e565b612f4e91906143ce565b955080612f5f87600160701b61438e565b612f6991906143ce565b94505b5050505094509492505050565b60405163e6a4390560e01b81526001600160a01b0388811660048301528781166024830152600091829182917f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce509091169063e6a4390590604401602060405180830381865afa158015612ff0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130149190614342565b90506001600160a01b0381166130bb576040516364e329cb60e11b81526001600160a01b038b811660048301528a811660248301527f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50169063c9c65396906044016020604051808303816000875af1158015613094573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130b89190614342565b90505b6000806000806130ec7f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508f8f6139f0565b9350935093509350818461310091906143a5565b158015613114575061311281846143a5565b155b15613124578b96508a95506131cd565b60006131438d61313485886143a5565b61313e85886143a5565b61398f565b90508b8111613178578981101561316d5760405163ef71d09160e01b815260040160405180910390fd5b8c97509550856131cb565b60006131928d61318885886143a5565b61313e878a6143a5565b90508d8111156131a4576131a4614420565b8b8110156131c557604051638dc525d160e01b815260040160405180910390fd5b97508b96505b505b6000841180156131dd5750600083115b15610884576000856001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613222573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132469190614329565b90508d6001600160a01b03168f6001600160a01b031610156132d957600061327482612ec26127108961438e565b90508061328761ffff8c166127106142a2565b613291908761438e565b11806132b55750806132a961ffff8c166127106143a5565b6132b3908761438e565b105b156132d357604051630d683ed760e21b815260040160405180910390fd5b5061334c565b60006132eb82612ec26127108861438e565b9050806132fe61ffff8c166127106142a2565b613308908861438e565b118061332c57508061332061ffff8c166127106143a5565b61332a908861438e565b105b1561334a57604051630d683ed760e21b815260040160405180910390fd5b505b50505050505097509795505050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b17905291516000928392908716916133ba919061435f565b6000604051808303816000865af19150503d80600081146133f7576040519150601f19603f3d011682016040523d82523d6000602084013e6133fc565b606091505b5091509150818015613426575080511580613426575080806020019051810190613426919061424b565b6124895760405162461bcd60e51b815260206004820152601e60248201527f5472616e7366657248656c7065723a20415050524f56455f4641494c454400006044820152606401612480565b60405163e6a4390560e01b81526001600160a01b0387811660048301528681166024830152600091829182917f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce509091169063e6a4390590604401602060405180830381865afa1580156134e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061350d9190614342565b6001600160a01b031603613534576040516391bc00d960e01b815260040160405180910390fd5b6000806000806135657f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508d8d6139f0565b9350935093509350836000148061357a575082155b15613598576040516321c4e35760e21b815260040160405180910390fd5b811580156135a4575080155b156135c2576040516391bc00d960e01b815260040160405180910390fd5b81156136275760006135f67f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508d8f8d612d41565b9150508881101561361a57604051638dc525d160e01b815260040160405180910390fd5b5060009550889450613682565b60006136557f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce508e8e8e612d41565b915050878110156136795760405163ef71d09160e01b815260040160405180910390fd5b50899550600094505b50505050965096945050505050565b6000836000036136b4576040516342301c2360e01b815260040160405180910390fd5b8215806136bf575081155b156136dd5760405163bb55fd2760e01b815260040160405180910390fd5b60006136e9858561438e565b6136f5906103e861438e565b9050600061370386856142a2565b61370f906103e561438e565b905061371b81836143ce565b6137269060016143a5565b9695505050505050565b60008060006137408787876125f8565b90506000816001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613782573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137a69190614329565b6040516370a0823160e01b81526001600160a01b0384811660048301529192506000918916906370a0823190602401602060405180830381865afa1580156137f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138169190614329565b6040516370a0823160e01b81526001600160a01b0385811660048301529192506000918916906370a0823190602401602060405180830381865afa158015613862573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138869190614329565b90506000846001600160a01b0316632025070a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138ec9190614329565b90506000846138fb8a8561438e565b61390591906143ce565b9050846139128a8661438e565b61391c91906143ce565b9750896001600160a01b03168b6001600160a01b03161015613958578161394782600160701b61438e565b61395191906143ce565b9650613974565b600160701b613967838361438e565b61397191906143ce565b96505b61397e87896143a5565b975050505050505094509492505050565b6000836000036139b257604051632ca2f52b60e11b815260040160405180910390fd5b8215806139bd575081155b156139db5760405163bb55fd2760e01b815260040160405180910390fd5b826139e6838661438e565b6106b891906143ce565b6000806000806000613a0287876126d1565b509050600080600080613a168c8c8c6125f8565b6001600160a01b031663976bf4166040518163ffffffff1660e01b815260040160a060405180830381865afa158015613a53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a77919061444d565b506001600160701b031693506001600160701b031693506001600160701b031693506001600160701b03169350846001600160a01b03168b6001600160a01b031614613ac65782848284613acb565b838383835b929f919e509c50909a5098505050505050505050565b6000808060001985870985870292508281108382030391505080600003613b1b57838281613b1157613b116143b8565b04925050506106bb565b808411613b2757600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6001600160a01b0381168114613ba557600080fd5b50565b8035613bb381613b90565b919050565b60008060008060008060c08789031215613bd157600080fd5b8635613bdc81613b90565b95506020870135945060408701359350606087013592506080870135613c0181613b90565b8092505060a087013590509295509295509295565b600080600060608486031215613c2b57600080fd5b505081359360208301359350604090920135919050565b600080600080600080600060e0888a031215613c5d57600080fd5b8735613c6881613b90565b96506020880135613c7881613b90565b955060408801359450606088013593506080880135925060a0880135613c9d81613b90565b8092505060c0880135905092959891949750929550565b60008083601f840112613cc657600080fd5b50813567ffffffffffffffff811115613cde57600080fd5b6020830191508360208260051b850101111561275557600080fd5b60008060008060008060a08789031215613d1257600080fd5b8635955060208701359450604087013567ffffffffffffffff811115613d3757600080fd5b613d4389828a01613cb4565b9095509350506060870135613d5781613b90565b80925050608087013590509295509295509295565b6020808252825182820181905260009190848201906040850190845b81811015613da457835183529284019291840191600101613d88565b50909695505050505050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215613dd957600080fd5b8235915060208084013567ffffffffffffffff80821115613df957600080fd5b818601915086601f830112613e0d57600080fd5b813581811115613e1f57613e1f613db0565b8060051b604051601f19603f83011681018181108582111715613e4457613e44613db0565b604052918252848201925083810185019189831115613e6257600080fd5b938501935b82851015613e8757613e7885613ba8565b84529385019392850192613e67565b8096505050505050509250929050565b8015158114613ba557600080fd5b803560ff81168114613bb357600080fd5b60008060008060008060008060008060006101608c8e031215613ed857600080fd5b8b35613ee381613b90565b9a5060208c0135613ef381613b90565b995060408c0135985060608c0135975060808c0135965060a08c0135613f1881613b90565b955060c08c0135945060e08c0135613f2f81613e97565b9350613f3e6101008d01613ea5565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600060608486031215613f7557600080fd5b8335613f8081613b90565b92506020840135613f9081613b90565b929592945050506040919091013590565b803561ffff81168114613bb357600080fd5b600080600080600080600060e0888a031215613fce57600080fd5b8735613fd981613b90565b9650602088013595506040880135945060608801359350613ffc60808901613fa1565b925060a0880135613c9d81613b90565b60008060008060006080868803121561402457600080fd5b85359450602086013567ffffffffffffffff81111561404257600080fd5b61404e88828901613cb4565b909550935050604086013561406281613b90565b949793965091946060013592915050565b60008060008060008060008060006101208a8c03121561409257600080fd5b893561409d81613b90565b985060208a01356140ad81613b90565b975060408a0135965060608a0135955060808a0135945060a08a013593506140d760c08b01613fa1565b925060e08a01356140e781613b90565b809250506101008a013590509295985092959850929598565b6000806000806000806000806000806101408b8d03121561412057600080fd5b8a3561412b81613b90565b995060208b0135985060408b0135975060608b0135965060808b013561415081613b90565b955060a08b0135945060c08b013561416781613e97565b935061417560e08c01613ea5565b92506101008b013591506101208b013590509295989b9194979a5092959850565b600080604083850312156141a957600080fd5b82356141b481613b90565b915060208301356141c481613b90565b809150509250929050565b600080600080600080600080610100898b0312156141ec57600080fd5b88356141f781613b90565b9750602089013561420781613b90565b965060408901359550606089013594506080890135935060a0890135925060c089013561423381613b90565b8092505060e089013590509295985092959890939650565b60006020828403121561425d57600080fd5b81516106bb81613e97565b6000806040838503121561427b57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b81810381811115610b7f57610b7f61428c565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156142dd57600080fd5b81356106bb81613b90565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b60006020828403121561433b57600080fd5b5051919050565b60006020828403121561435457600080fd5b81516106bb81613b90565b6000825160005b818110156143805760208186018101518583015201614366565b506000920191825250919050565b8082028115828204841417610b7f57610b7f61428c565b80820180821115610b7f57610b7f61428c565b634e487b7160e01b600052601260045260246000fd5b6000826143eb57634e487b7160e01b600052601260045260246000fd5b500490565b6000600182016144025761440261428c565b5060010190565b6000816144185761441861428c565b506000190190565b634e487b7160e01b600052600160045260246000fd5b80516001600160701b0381168114613bb357600080fd5b600080600080600060a0868803121561446557600080fd5b61446e86614436565b945061447c60208701614436565b935061448a60408701614436565b925061449860608701614436565b9150608086015163ffffffff811681146144b157600080fd5b80915050929550929590935056

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

000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

-----Decoded View---------------
Arg [0] : _factory (address): 0xB8DE4aB6C65e274630F5279f74eB69b66327CE50
Arg [1] : _WETH (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50
Arg [1] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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