ETH Price: $3,389.91 (-1.53%)
Gas: 2 Gwei

Contract

0xb0ac070ae1f9BC564c1F4EA23bD2Ed0aF1B6BA5A
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Approve200951792024-06-15 5:41:4714 days ago1718430107IN
0xb0ac070a...aF1B6BA5A
0 ETH0.000099653.79650553
Approve200951762024-06-15 5:41:1114 days ago1718430071IN
0xb0ac070a...aF1B6BA5A
0 ETH0.000094093.88016283
Approve199638382024-05-27 21:17:4732 days ago1716844667IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0009026619.53408175
Approve199344622024-05-23 18:46:4736 days ago1716490007IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0005798412.5481318
Approve199343352024-05-23 18:21:1136 days ago1716488471IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0006882914.89502178
Approve199156492024-05-21 3:38:5939 days ago1716262739IN
0xb0ac070a...aF1B6BA5A
0 ETH0.000465510.07368907
Approve198992162024-05-18 20:29:1141 days ago1716064151IN
0xb0ac070a...aF1B6BA5A
0 ETH0.000138833.00454294
Approve198054102024-05-05 17:34:2354 days ago1714930463IN
0xb0ac070a...aF1B6BA5A
0 ETH0.000415928.9380258
Approve197750882024-05-01 11:49:5958 days ago1714564199IN
0xb0ac070a...aF1B6BA5A
0 ETH0.000328217.05323513
Approve196989592024-04-20 20:17:5969 days ago1713644279IN
0xb0ac070a...aF1B6BA5A
0 ETH0.000337517.30208468
Approve196950082024-04-20 7:02:1170 days ago1713596531IN
0xb0ac070a...aF1B6BA5A
0 ETH0.000439919.4536379
Approve195895612024-04-05 12:32:5984 days ago1712320379IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0008466918.19525296
Approve194974652024-03-23 13:01:4797 days ago1711198907IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0007781516.83952309
Approve194806492024-03-21 4:24:47100 days ago1710995087IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0011589824.90629578
Approve194399352024-03-15 11:05:35105 days ago1710500735IN
0xb0ac070a...aF1B6BA5A
0 ETH0.001806838.82763869
Transfer194244292024-03-13 6:41:47108 days ago1710312107IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0020489443.78841293
Transfer194037872024-03-10 9:25:47111 days ago1710062747IN
0xb0ac070a...aF1B6BA5A
0 ETH0.00553932118.38182939
Approve193796432024-03-07 0:13:59114 days ago1709770439IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0034321573.75576237
Approve193763792024-03-06 13:18:23114 days ago1709731103IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0032477369.79270713
Transfer193763442024-03-06 13:11:23114 days ago1709730683IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0034974.58547822
Approve192663642024-02-20 3:53:35130 days ago1708401215IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0011774325.30264603
Approve191207402024-01-30 17:13:23150 days ago1706634803IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0016934436.39150605
Approve191197512024-01-30 13:53:47150 days ago1706622827IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0011423624.54906835
Approve190364262024-01-18 21:27:47162 days ago1705613267IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0016643335.76589737
Approve190158102024-01-16 0:17:23165 days ago1705364243IN
0xb0ac070a...aF1B6BA5A
0 ETH0.0011421724.71068403
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To Value
178779302023-08-09 14:25:35324 days ago1691591135  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ButtonswapPair

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 18 : ButtonswapPair.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

import {IButtonswapPair} from "./interfaces/IButtonswapPair/IButtonswapPair.sol";
import {IButtonswapERC20} from "./interfaces/IButtonswapERC20/IButtonswapERC20.sol";
import {ButtonswapERC20} from "./ButtonswapERC20.sol";
import {Math} from "./libraries/Math.sol";
import {PairMath} from "./libraries/PairMath.sol";
import {UQ112x112} from "./libraries/UQ112x112.sol";
import {IERC20} from "buttonswap-core_@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "buttonswap-core_@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {IButtonswapFactory} from "./interfaces/IButtonswapFactory/IButtonswapFactory.sol";

contract ButtonswapPair is IButtonswapPair, ButtonswapERC20 {
    using UQ112x112 for uint224;

    /**
     * @dev A set of liquidity values.
     * @param pool0 The active `token0` liquidity
     * @param pool1 The active `token1` liquidity
     * @param reservoir0 The inactive `token0` liquidity
     * @param reservoir1 The inactive `token1` liquidity
     */
    struct LiquidityBalances {
        uint256 pool0;
        uint256 pool1;
        uint256 reservoir0;
        uint256 reservoir1;
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    uint256 public constant MINIMUM_LIQUIDITY = 10 ** 3;

    /**
     * @dev Denominator for basis points.
     */
    uint256 private constant BPS = 10_000;

    /**
     * @inheritdoc IButtonswapPair
     */
    uint32 public movingAverageWindow;

    /**
     * @inheritdoc IButtonswapPair
     */
    uint16 public maxVolatilityBps;

    /**
     * @inheritdoc IButtonswapPair
     */
    uint32 public minTimelockDuration;

    /**
     * @inheritdoc IButtonswapPair
     */
    uint32 public maxTimelockDuration;

    /**
     * @inheritdoc IButtonswapPair
     */
    uint16 public maxSwappableReservoirLimitBps;

    /**
     * @inheritdoc IButtonswapPair
     */
    uint32 public swappableReservoirGrowthWindow;

    /**
     * @inheritdoc IButtonswapPair
     */
    address public immutable factory;

    /**
     * @inheritdoc IButtonswapPair
     */
    address public immutable token0;

    /**
     * @inheritdoc IButtonswapPair
     */
    address public immutable token1;

    /**
     * @dev The active `token0` liquidity amount following the last swap.
     * This value is used to determine active liquidity balances after potential rebases until the next future swap.
     */
    uint112 internal pool0Last;

    /**
     * @dev The active `token1` liquidity amount following the last swap.
     * This value is used to determine active liquidity balances after potential rebases until the next future swap.
     */
    uint112 internal pool1Last;

    /**
     * @dev The timestamp of the block that the last swap occurred in.
     */
    uint32 internal blockTimestampLast;

    /**
     * @inheritdoc IButtonswapPair
     */
    uint256 public price0CumulativeLast;

    /**
     * @inheritdoc IButtonswapPair
     */
    uint256 public price1CumulativeLast;

    /**
     * @dev The value of `movingAveragePrice0` at the time of the last swap.
     */
    uint256 internal movingAveragePrice0Last;

    /**
     * @inheritdoc IButtonswapPair
     */
    uint120 public singleSidedTimelockDeadline;

    /**
     * @inheritdoc IButtonswapPair
     */
    uint120 public swappableReservoirLimitReachesMaxDeadline;

    /**
     * @dev Whether or not the pair is isPaused (paused = 1, unPaused = 0).
     * When paused, all operations other than dual-sided burning LP tokens are disabled.
     */
    uint8 internal isPaused;

    /**
     * @dev Value to track the state of the re-entrancy guard.
     */
    uint8 private unlocked = 1;

    /**
     * @dev Guards against re-entrancy.
     */
    modifier lock() {
        if (unlocked == 0) {
            revert Locked();
        }
        unlocked = 0;
        _;
        unlocked = 1;
    }

    /**
     * @dev Prevents certain operations from being executed if the price volatility induced timelock has yet to conclude.
     */
    modifier singleSidedTimelock() {
        if (block.timestamp < singleSidedTimelockDeadline) {
            revert SingleSidedTimelock();
        }
        _;
    }

    /**
     * @dev Prevents operations from being executed if the Pair is currently paused.
     */
    modifier checkPaused() {
        if (isPaused == 1) {
            revert Paused();
        }
        _;
    }

    /**
     * @dev Called whenever an LP wants to burn their LP tokens to make sure they get their fair share of fees.
     * If `feeTo` is defined, `balanceOf(address(this))` gets transferred to `feeTo`.
     * If `feeTo` is not defined, `balanceOf(address(this))` gets burned and the LP tokens all grow in value.
     */
    modifier sendOrRefundFee() {
        if (balanceOf[address(this)] > 0) {
            address feeTo = IButtonswapFactory(factory).feeTo();
            if (feeTo != address(0)) {
                _transfer(address(this), feeTo, balanceOf[address(this)]);
            } else {
                _burn(address(this), balanceOf[address(this)]);
            }
        }
        _;
    }

    /**
     * @dev Prevents operations from being executed if the caller is not the factory.
     */
    modifier onlyFactory() {
        if (msg.sender != factory) {
            revert Forbidden();
        }
        _;
    }

    constructor() {
        factory = msg.sender;
        (
            token0,
            token1,
            movingAverageWindow,
            maxVolatilityBps,
            minTimelockDuration,
            maxTimelockDuration,
            maxSwappableReservoirLimitBps,
            swappableReservoirGrowthWindow
        ) = IButtonswapFactory(factory).lastCreatedTokensAndParameters();
    }

    /**
     * @inheritdoc IButtonswapERC20
     */
    function name() external view override(ButtonswapERC20, IButtonswapERC20) returns (string memory _name) {
        _name = IButtonswapFactory(factory).tokenName();
    }

    /**
     * @inheritdoc IButtonswapERC20
     */
    function symbol() external view override(ButtonswapERC20, IButtonswapERC20) returns (string memory _symbol) {
        _symbol = IButtonswapFactory(factory).tokenSymbol();
    }

    /**
     * @dev Always mints liquidity equivalent to 1/6th of the growth in sqrt(k) and allocates to address(this)
     * If there isn't a `feeTo` address defined, these LP tokens will get burned this 1/6th gets reallocated to LPs
     * @param pool0 The `token0` active liquidity balance at the start of the ongoing swap
     * @param pool1 The `token1` active liquidity balance at the start of the ongoing swap
     * @param pool0New The `token0` active liquidity balance at the end of the ongoing swap
     * @param pool1New The `token1` active liquidity balance at the end of the ongoing swap
     */
    function _mintFee(uint256 pool0, uint256 pool1, uint256 pool0New, uint256 pool1New) internal {
        uint256 liquidityOut = PairMath.getProtocolFeeLiquidityMinted(totalSupply, pool0 * pool1, pool0New * pool1New);
        if (liquidityOut > 0) {
            _mint(address(this), liquidityOut);
        }
    }

    /**
     * @dev Updates `price0CumulativeLast` and `price1CumulativeLast` based on the current timestamp.
     * @param pool0 The `token0` active liquidity balance at the start of the ongoing swap
     * @param pool1 The `token1` active liquidity balance at the start of the ongoing swap
     */
    function _updatePriceCumulative(uint256 pool0, uint256 pool1) internal {
        uint112 _pool0 = uint112(pool0);
        uint112 _pool1 = uint112(pool1);
        uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32);
        uint32 timeElapsed;
        unchecked {
            // underflow is desired
            timeElapsed = blockTimestamp - blockTimestampLast;
        }
        if (timeElapsed > 0 && pool0 != 0 && pool1 != 0) {
            // * never overflows, and + overflow is desired
            unchecked {
                price0CumulativeLast += ((pool1 << 112) * timeElapsed) / _pool0;
                price1CumulativeLast += ((pool0 << 112) * timeElapsed) / _pool1;
            }
            blockTimestampLast = blockTimestamp;
        }
    }

    /**
     * @dev Refer to [closest-bound-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/closest-bound-math.md) for more detail.
     * @param poolALower The lower bound for the active liquidity balance of the non-fixed token
     * @param poolB The active liquidity balance of the fixed token
     * @param _poolALast The active liquidity balance at the end of the last swap for the non-fixed token
     * @param _poolBLast The active liquidity balance at the end of the last swap for the fixed token
     * @return closestBound The bound for the active liquidity balance of the non-fixed token that produces a price ratio closest to last swap price
     */
    function _closestBound(uint256 poolALower, uint256 poolB, uint256 _poolALast, uint256 _poolBLast)
        internal
        pure
        returns (uint256 closestBound)
    {
        if ((2 * poolALower * _poolBLast) + _poolBLast < 2 * _poolALast * poolB) {
            closestBound = poolALower + 1;
        } else {
            closestBound = poolALower;
        }
    }

    /**
     * @dev Refer to [liquidity-balances-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/liquidity-balances-math.md) for more detail.
     * @param total0 The total amount of `token0` held by the Pair
     * @param total1 The total amount of `token1` held by the Pair
     * @return lb The current active and inactive liquidity balances
     */
    function _getLiquidityBalances(uint256 total0, uint256 total1)
        internal
        view
        returns (LiquidityBalances memory lb)
    {
        uint256 _pool0Last = uint256(pool0Last);
        uint256 _pool1Last = uint256(pool1Last);
        if (_pool0Last == 0 || _pool1Last == 0) {
            // Before Pair is initialized by first dual mint just return zeroes
        } else if (total0 == 0 || total1 == 0) {
            // Save the extra calculations and just return zeroes
        } else {
            if (total0 * _pool1Last < total1 * _pool0Last) {
                lb.pool0 = total0;
                // pool0Last/pool1Last == pool0/pool1 => pool1 == (pool0*pool1Last)/pool0Last
                // pool1Last/pool0Last == pool1/pool0 => pool1 == (pool0*pool1Last)/pool0Last
                lb.pool1 = (lb.pool0 * _pool1Last) / _pool0Last;
                lb.pool1 = _closestBound(lb.pool1, lb.pool0, _pool1Last, _pool0Last);
                // reservoir0 is zero, so no need to set it
                lb.reservoir1 = total1 - lb.pool1;
            } else {
                lb.pool1 = total1;
                // pool0Last/pool1Last == pool0/pool1 => pool0 == (pool1*pool0Last)/pool1Last
                // pool1Last/pool0Last == pool1/pool0 => pool0 == (pool1*pool0Last)/pool1Last
                lb.pool0 = (lb.pool1 * _pool0Last) / _pool1Last;
                lb.pool0 = _closestBound(lb.pool0, lb.pool1, _pool0Last, _pool1Last);
                // reservoir1 is zero, so no need to set it
                lb.reservoir0 = total0 - lb.pool0;
            }
            if (lb.pool0 > type(uint112).max || lb.pool1 > type(uint112).max) {
                revert Overflow();
            }
        }
    }

    /**
     * @dev Calculates current price volatility and initiates a timelock scaled to the volatility size.
     * This timelock prohibits single-sided operations from being executed until enough time has passed for the timelock
     *   to conclude.
     * This protects against attempts to manipulate the price that the reservoir is valued at during single-sided operations.
     * @param _movingAveragePrice0 The current `movingAveragePrice0` value
     * @param pool0New The `token0` active liquidity balance at the end of the ongoing swap
     * @param pool1New The `token1` active liquidity balance at the end of the ongoing swap
     */
    function _updateSingleSidedTimelock(uint256 _movingAveragePrice0, uint112 pool0New, uint112 pool1New) internal {
        uint256 newPrice0 = uint256(UQ112x112.encode(pool1New).uqdiv(pool0New));
        uint256 priceDifference;
        if (newPrice0 > _movingAveragePrice0) {
            priceDifference = newPrice0 - _movingAveragePrice0;
        } else {
            priceDifference = _movingAveragePrice0 - newPrice0;
        }
        // priceDifference / ((_movingAveragePrice0 * maxVolatilityBps)/BPS)
        uint32 _minTimelockDuration = minTimelockDuration;
        uint32 _maxTimelockDuration = maxTimelockDuration;
        uint256 timelock = Math.min(
            _minTimelockDuration
                + (
                    (priceDifference * BPS * (_maxTimelockDuration - _minTimelockDuration))
                        / (_movingAveragePrice0 * maxVolatilityBps)
                ),
            _maxTimelockDuration
        );
        uint120 timelockDeadline = uint120(block.timestamp + timelock);
        if (timelockDeadline > singleSidedTimelockDeadline) {
            singleSidedTimelockDeadline = timelockDeadline;
        }
    }

    /**
     * @dev Calculates the current limit on the number of reservoir tokens that can be exchanged during a single-sided
     *   operation.
     * This is based on corresponding active liquidity size and time since and size of the last single-sided operation.
     * @param poolA The active liquidity balance for the non-zero reservoir token
     * @return swappableReservoir The amount of non-zero reservoir token that can be exchanged as part of a single-sided operation
     */
    function _getSwappableReservoirLimit(uint256 poolA) internal view returns (uint256 swappableReservoir) {
        // Calculate the maximum the limit can be as a fraction of the corresponding active liquidity
        uint256 maxSwappableReservoirLimit = (poolA * maxSwappableReservoirLimitBps) / BPS;
        uint256 _swappableReservoirLimitReachesMaxDeadline = swappableReservoirLimitReachesMaxDeadline;
        if (_swappableReservoirLimitReachesMaxDeadline > block.timestamp) {
            // If the current deadline is still active then calculate the progress towards reaching it
            uint32 _swappableReservoirGrowthWindow = swappableReservoirGrowthWindow;
            uint256 progress =
                _swappableReservoirGrowthWindow - (_swappableReservoirLimitReachesMaxDeadline - block.timestamp);
            // The greater the progress, the closer to the max limit we get
            swappableReservoir = (maxSwappableReservoirLimit * progress) / _swappableReservoirGrowthWindow;
        } else {
            // If the current deadline has expired then the full limit is available
            swappableReservoir = maxSwappableReservoirLimit;
        }
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function getSwappableReservoirLimit() external view returns (uint256 swappableReservoirLimit) {
        uint256 total0 = IERC20(token0).balanceOf(address(this));
        uint256 total1 = IERC20(token1).balanceOf(address(this));
        LiquidityBalances memory lb = _getLiquidityBalances(total0, total1);

        if (lb.reservoir0 > 0) {
            swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool0);
        } else {
            swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool1);
        }
    }

    /**
     * @dev Updates the value of `swappableReservoirLimitReachesMaxDeadline` which is the time at which the maximum
     *   amount of inactive liquidity tokens can be exchanged during a single-sided operation.
     * @dev Assumes `swappedAmountA` is less than or equal to `maxSwappableReservoirLimit`
     * @param poolA The active liquidity balance for the non-zero reservoir token
     * @param swappedAmountA The amount of non-zero reservoir tokens that were exchanged during the ongoing single-sided
     *   operation
     */
    function _updateSwappableReservoirDeadline(uint256 poolA, uint256 swappedAmountA) internal {
        // Calculate the maximum the limit can be as a fraction of the corresponding active liquidity
        uint256 maxSwappableReservoirLimit = (poolA * maxSwappableReservoirLimitBps) / BPS;
        // Calculate how much time delay the swap instigates
        uint256 delay;
        // Check non-zero to avoid div by zero error
        if (maxSwappableReservoirLimit > 0) {
            // Since `swappedAmountA/maxSwappableReservoirLimit <= 1`, `delay <= swappableReservoirGrowthWindow`
            delay = (swappableReservoirGrowthWindow * swappedAmountA) / maxSwappableReservoirLimit;
        } else {
            // If it is zero then it's in an extreme condition and a delay is most appropriate way to handle it
            delay = swappableReservoirGrowthWindow;
        }
        // Apply the delay
        uint256 _swappableReservoirLimitReachesMaxDeadline = swappableReservoirLimitReachesMaxDeadline;
        if (_swappableReservoirLimitReachesMaxDeadline > block.timestamp) {
            // If the current deadline hasn't expired yet then add the delay to it
            swappableReservoirLimitReachesMaxDeadline = uint120(_swappableReservoirLimitReachesMaxDeadline + delay);
        } else {
            // If the current deadline has expired already then add the delay to the current time, so that the full
            //   delay is still applied
            swappableReservoirLimitReachesMaxDeadline = uint120(block.timestamp + delay);
        }
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function getIsPaused() external view returns (bool _isPaused) {
        _isPaused = isPaused == 1;
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function setIsPaused(bool isPausedNew) external onlyFactory {
        if (isPausedNew) {
            isPaused = 1;
        } else {
            singleSidedTimelockDeadline = uint120(block.timestamp + maxTimelockDuration);
            isPaused = 0;
        }
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function getLiquidityBalances()
        external
        view
        returns (uint112 _pool0, uint112 _pool1, uint112 _reservoir0, uint112 _reservoir1, uint32 _blockTimestampLast)
    {
        uint256 total0 = IERC20(token0).balanceOf(address(this));
        uint256 total1 = IERC20(token1).balanceOf(address(this));
        LiquidityBalances memory lb = _getLiquidityBalances(total0, total1);
        _pool0 = uint112(lb.pool0);
        _pool1 = uint112(lb.pool1);
        _reservoir0 = uint112(lb.reservoir0);
        _reservoir1 = uint112(lb.reservoir1);
        _blockTimestampLast = blockTimestampLast;
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function movingAveragePrice0() public view returns (uint256 _movingAveragePrice0) {
        uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32);
        uint32 timeElapsed;
        unchecked {
            // overflow is desired
            timeElapsed = blockTimestamp - blockTimestampLast;
        }
        uint256 currentPrice0 = uint256(UQ112x112.encode(pool1Last).uqdiv(pool0Last));
        if (timeElapsed == 0) {
            _movingAveragePrice0 = movingAveragePrice0Last;
        } else if (timeElapsed >= movingAverageWindow) {
            _movingAveragePrice0 = currentPrice0;
        } else {
            uint32 _movingAverageWindow = movingAverageWindow;
            _movingAveragePrice0 = (
                (movingAveragePrice0Last * (_movingAverageWindow - timeElapsed)) + (currentPrice0 * timeElapsed)
            ) / _movingAverageWindow;
        }
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function mint(uint256 amountIn0, uint256 amountIn1, address to)
        external
        lock
        checkPaused
        sendOrRefundFee
        returns (uint256 liquidityOut)
    {
        uint256 _totalSupply = totalSupply;
        uint256 total0 = IERC20(token0).balanceOf(address(this));
        uint256 total1 = IERC20(token1).balanceOf(address(this));
        SafeERC20.safeTransferFrom(IERC20(token0), msg.sender, address(this), amountIn0);
        SafeERC20.safeTransferFrom(IERC20(token1), msg.sender, address(this), amountIn1);
        // Use the balance delta as input amounts to ensure feeOnTransfer or similar tokens don't disrupt Pair math
        amountIn0 = IERC20(token0).balanceOf(address(this)) - total0;
        amountIn1 = IERC20(token1).balanceOf(address(this)) - total1;

        if (_totalSupply == 0) {
            liquidityOut = Math.sqrt(amountIn0 * amountIn1) - MINIMUM_LIQUIDITY;
            // permanently lock the first MINIMUM_LIQUIDITY tokens
            _mint(address(0), MINIMUM_LIQUIDITY);
            // Initialize Pair last swap price
            pool0Last = uint112(amountIn0);
            pool1Last = uint112(amountIn1);
            // Initialize timestamp so first price update is accurate
            blockTimestampLast = uint32(block.timestamp % 2 ** 32);
            // Initialize moving average to price from initial amounts
            movingAveragePrice0Last = uint256(UQ112x112.encode(uint112(amountIn1)).uqdiv(uint112(amountIn0)));
        } else {
            // Don't need to check that amountIn{0,1} are in the right ratio because the least generous ratio is used
            //   to determine the liquidityOut value, meaning any tokens that exceed that ratio are donated.
            // If total0 or total1 are zero (eg. due to negative rebases) then the function call reverts with div by zero
            liquidityOut =
                PairMath.getDualSidedMintLiquidityOutAmount(_totalSupply, amountIn0, amountIn1, total0, total1);
        }

        if (liquidityOut == 0) {
            revert InsufficientLiquidityMinted();
        }
        _mint(to, liquidityOut);
        emit Mint(msg.sender, amountIn0, amountIn1, liquidityOut, to);
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function mintWithReservoir(uint256 amountIn, address to)
        external
        lock
        checkPaused
        singleSidedTimelock
        sendOrRefundFee
        returns (uint256 liquidityOut)
    {
        if (amountIn == 0) {
            revert InsufficientLiquidityAdded();
        }
        uint256 _totalSupply = totalSupply;
        if (_totalSupply == 0) {
            revert Uninitialized();
        }
        uint256 total0 = IERC20(token0).balanceOf(address(this));
        uint256 total1 = IERC20(token1).balanceOf(address(this));
        // Determine current pool liquidity
        LiquidityBalances memory lb = _getLiquidityBalances(total0, total1);
        if (lb.pool0 == 0 || lb.pool1 == 0) {
            revert InsufficientLiquidity();
        }
        if (lb.reservoir0 == 0) {
            // If reservoir0 is empty then we're adding token0 to pair with token1 reservoir liquidity
            SafeERC20.safeTransferFrom(IERC20(token0), msg.sender, address(this), amountIn);
            // Use the balance delta as input amounts to ensure feeOnTransfer or similar tokens don't disrupt Pair math
            amountIn = IERC20(token0).balanceOf(address(this)) - total0;

            // Ensure there's enough reservoir1 liquidity to do this without growing reservoir0
            LiquidityBalances memory lbNew = _getLiquidityBalances(total0 + amountIn, total1);
            if (lbNew.reservoir0 > 0) {
                revert InsufficientReservoir();
            }

            uint256 swappedReservoirAmount1;
            (liquidityOut, swappedReservoirAmount1) = PairMath.getSingleSidedMintLiquidityOutAmountA(
                _totalSupply, amountIn, total0, total1, movingAveragePrice0()
            );

            uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool1);
            if (swappedReservoirAmount1 > swappableReservoirLimit) {
                revert SwappableReservoirExceeded();
            }
            _updateSwappableReservoirDeadline(lb.pool1, swappedReservoirAmount1);
        } else {
            // If reservoir1 is empty then we're adding token1 to pair with token0 reservoir liquidity
            SafeERC20.safeTransferFrom(IERC20(token1), msg.sender, address(this), amountIn);
            // Use the balance delta as input amounts to ensure feeOnTransfer or similar tokens don't disrupt Pair math
            amountIn = IERC20(token1).balanceOf(address(this)) - total1;

            // Ensure there's enough reservoir0 liquidity to do this without growing reservoir1
            LiquidityBalances memory lbNew = _getLiquidityBalances(total0, total1 + amountIn);
            if (lbNew.reservoir1 > 0) {
                revert InsufficientReservoir();
            }

            uint256 swappedReservoirAmount0;
            (liquidityOut, swappedReservoirAmount0) = PairMath.getSingleSidedMintLiquidityOutAmountB(
                _totalSupply, amountIn, total0, total1, movingAveragePrice0()
            );

            uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool0);
            if (swappedReservoirAmount0 > swappableReservoirLimit) {
                revert SwappableReservoirExceeded();
            }
            _updateSwappableReservoirDeadline(lb.pool0, swappedReservoirAmount0);
        }

        if (liquidityOut == 0) {
            revert InsufficientLiquidityMinted();
        }
        _mint(to, liquidityOut);
        if (lb.reservoir0 == 0) {
            emit Mint(msg.sender, amountIn, 0, liquidityOut, to);
        } else {
            emit Mint(msg.sender, 0, amountIn, liquidityOut, to);
        }
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function burn(uint256 liquidityIn, address to)
        external
        lock
        sendOrRefundFee
        returns (uint256 amountOut0, uint256 amountOut1)
    {
        if (liquidityIn == 0) {
            revert InsufficientLiquidityBurned();
        }
        uint256 _totalSupply = totalSupply;
        uint256 total0 = IERC20(token0).balanceOf(address(this));
        uint256 total1 = IERC20(token1).balanceOf(address(this));

        (amountOut0, amountOut1) = PairMath.getDualSidedBurnOutputAmounts(_totalSupply, liquidityIn, total0, total1);

        _burn(msg.sender, liquidityIn);
        SafeERC20.safeTransfer(IERC20(token0), to, amountOut0);
        SafeERC20.safeTransfer(IERC20(token1), to, amountOut1);
        emit Burn(msg.sender, liquidityIn, amountOut0, amountOut1, to);
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function burnFromReservoir(uint256 liquidityIn, address to)
        external
        lock
        checkPaused
        singleSidedTimelock
        sendOrRefundFee
        returns (uint256 amountOut0, uint256 amountOut1)
    {
        uint256 _totalSupply = totalSupply;
        uint256 total0 = IERC20(token0).balanceOf(address(this));
        uint256 total1 = IERC20(token1).balanceOf(address(this));
        // Determine current pool liquidity
        LiquidityBalances memory lb = _getLiquidityBalances(total0, total1);
        if (lb.pool0 == 0 || lb.pool1 == 0) {
            revert InsufficientLiquidity();
        }
        if (lb.reservoir0 == 0) {
            // If reservoir0 is empty then we're swapping amountOut0 for token1 from reservoir1
            uint256 swappedReservoirAmount1;
            (amountOut1, swappedReservoirAmount1) = PairMath.getSingleSidedBurnOutputAmountB(
                _totalSupply, liquidityIn, total0, total1, movingAveragePrice0()
            );
            // Check there's enough reservoir liquidity to withdraw from
            // If `amountOut1` exceeds reservoir1 then it will result in reservoir0 growing from excess token0
            if (amountOut1 > lb.reservoir1) {
                revert InsufficientReservoir();
            }

            uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool1);
            if (swappedReservoirAmount1 > swappableReservoirLimit) {
                revert SwappableReservoirExceeded();
            }
            _updateSwappableReservoirDeadline(lb.pool1, swappedReservoirAmount1);
        } else {
            // If reservoir0 isn't empty then we're swapping amountOut1 for token0 from reservoir0
            uint256 swappedReservoirAmount0;
            (amountOut0, swappedReservoirAmount0) = PairMath.getSingleSidedBurnOutputAmountA(
                _totalSupply, liquidityIn, total0, total1, movingAveragePrice0()
            );
            // Check there's enough reservoir liquidity to withdraw from
            // If `amountOut0` exceeds reservoir0 then it will result in reservoir1 growing from excess token1
            if (amountOut0 > lb.reservoir0) {
                revert InsufficientReservoir();
            }

            uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool0);
            if (swappedReservoirAmount0 > swappableReservoirLimit) {
                revert SwappableReservoirExceeded();
            }
            _updateSwappableReservoirDeadline(lb.pool0, swappedReservoirAmount0);
        }
        _burn(msg.sender, liquidityIn);
        if (amountOut0 > 0) {
            SafeERC20.safeTransfer(IERC20(token0), to, amountOut0);
        } else if (amountOut1 > 0) {
            SafeERC20.safeTransfer(IERC20(token1), to, amountOut1);
        } else {
            revert InsufficientLiquidityBurned();
        }
        emit Burn(msg.sender, liquidityIn, amountOut0, amountOut1, to);
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function swap(uint256 amountIn0, uint256 amountIn1, uint256 amountOut0, uint256 amountOut1, address to)
        external
        lock
        checkPaused
    {
        {
            if (amountOut0 == 0 && amountOut1 == 0) {
                revert InsufficientOutputAmount();
            }
            if (to == token0 || to == token1) {
                revert InvalidRecipient();
            }
            uint256 total0 = IERC20(token0).balanceOf(address(this));
            uint256 total1 = IERC20(token1).balanceOf(address(this));
            // Determine current pool liquidity
            LiquidityBalances memory lb = _getLiquidityBalances(total0, total1);
            if (amountOut0 >= lb.pool0 || amountOut1 >= lb.pool1) {
                revert InsufficientLiquidity();
            }
            // Transfer in the specified input
            if (amountIn0 > 0) {
                SafeERC20.safeTransferFrom(IERC20(token0), msg.sender, address(this), amountIn0);
            }
            if (amountIn1 > 0) {
                SafeERC20.safeTransferFrom(IERC20(token1), msg.sender, address(this), amountIn1);
            }
            // Optimistically transfer output
            if (amountOut0 > 0) {
                SafeERC20.safeTransfer(IERC20(token0), to, amountOut0);
            }
            if (amountOut1 > 0) {
                SafeERC20.safeTransfer(IERC20(token1), to, amountOut1);
            }

            // Refresh balances
            total0 = IERC20(token0).balanceOf(address(this));
            total1 = IERC20(token1).balanceOf(address(this));
            // The reservoir balances must remain unchanged during a swap, so all balance changes impact the pool balances
            uint256 pool0New = total0 - lb.reservoir0;
            uint256 pool1New = total1 - lb.reservoir1;
            if (pool0New == 0 || pool1New == 0) {
                revert InvalidFinalPrice();
            }
            // Update to the actual amount of tokens the user sent in based on the delta between old and new pool balances
            if (pool0New > lb.pool0) {
                amountIn0 = pool0New - lb.pool0;
                amountOut0 = 0;
            } else {
                amountIn0 = 0;
                amountOut0 = lb.pool0 - pool0New;
            }
            if (pool1New > lb.pool1) {
                amountIn1 = pool1New - lb.pool1;
                amountOut1 = 0;
            } else {
                amountIn1 = 0;
                amountOut1 = lb.pool1 - pool1New;
            }
            // If after accounting for input and output cancelling one another out, fee on transfer, etc there is no
            //   input tokens in real terms then revert.
            if (amountIn0 == 0 && amountIn1 == 0) {
                revert InsufficientInputAmount();
            }
            uint256 pool0NewAdjusted = (pool0New * 1000) - (amountIn0 * 3);
            uint256 pool1NewAdjusted = (pool1New * 1000) - (amountIn1 * 3);
            // After account for 0.3% fees, the new K must not be less than the old K
            if (pool0NewAdjusted * pool1NewAdjusted < (lb.pool0 * lb.pool1 * 1000 ** 2)) {
                revert KInvariant();
            }
            // Update moving average before `_updatePriceCumulative` updates `blockTimestampLast` and the new `poolXLast` values are set
            uint256 _movingAveragePrice0 = movingAveragePrice0();
            movingAveragePrice0Last = _movingAveragePrice0;
            _mintFee(lb.pool0, lb.pool1, pool0New, pool1New);
            _updatePriceCumulative(lb.pool0, lb.pool1);
            _updateSingleSidedTimelock(_movingAveragePrice0, uint112(pool0New), uint112(pool1New));
            // Update Pair last swap price
            pool0Last = uint112(pool0New);
            pool1Last = uint112(pool1New);
        }
        emit Swap(msg.sender, amountIn0, amountIn1, amountOut0, amountOut1, to);
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function setMovingAverageWindow(uint32 newMovingAverageWindow) external onlyFactory {
        movingAverageWindow = newMovingAverageWindow;
        emit MovingAverageWindowUpdated(newMovingAverageWindow);
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function setMaxVolatilityBps(uint16 newMaxVolatilityBps) external onlyFactory {
        maxVolatilityBps = newMaxVolatilityBps;
        emit MaxVolatilityBpsUpdated(newMaxVolatilityBps);
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function setMinTimelockDuration(uint32 newMinTimelockDuration) external onlyFactory {
        singleSidedTimelockDeadline =
            uint120(Math.max(block.timestamp + uint256(minTimelockDuration), uint256(singleSidedTimelockDeadline)));
        minTimelockDuration = newMinTimelockDuration;
        emit MinTimelockDurationUpdated(newMinTimelockDuration);
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function setMaxTimelockDuration(uint32 newMaxTimelockDuration) external onlyFactory {
        singleSidedTimelockDeadline =
            uint120(Math.min(block.timestamp + uint256(newMaxTimelockDuration), uint256(singleSidedTimelockDeadline)));
        maxTimelockDuration = newMaxTimelockDuration;
        emit MaxTimelockDurationUpdated(newMaxTimelockDuration);
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function setMaxSwappableReservoirLimitBps(uint16 newMaxSwappableReservoirLimitBps) external onlyFactory {
        maxSwappableReservoirLimitBps = newMaxSwappableReservoirLimitBps;
        emit MaxSwappableReservoirLimitBpsUpdated(newMaxSwappableReservoirLimitBps);
    }

    /**
     * @inheritdoc IButtonswapPair
     */
    function setSwappableReservoirGrowthWindow(uint32 newSwappableReservoirGrowthWindow) external onlyFactory {
        uint256 oldSwappableReservoirLimitReachesMaxDeadline = uint256(swappableReservoirLimitReachesMaxDeadline);
        if (oldSwappableReservoirLimitReachesMaxDeadline > block.timestamp) {
            swappableReservoirLimitReachesMaxDeadline = uint120(
                block.timestamp
                    + (
                        uint256(newSwappableReservoirGrowthWindow)
                            * ((oldSwappableReservoirLimitReachesMaxDeadline - block.timestamp))
                            / uint256(swappableReservoirGrowthWindow)
                    )
            );
        }
        swappableReservoirGrowthWindow = newSwappableReservoirGrowthWindow;
        emit SwappableReservoirGrowthWindowUpdated(newSwappableReservoirGrowthWindow);
    }
}

File 2 of 18 : 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 3 of 18 : 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 4 of 18 : ButtonswapERC20.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

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

contract ButtonswapERC20 is IButtonswapERC20 {
    /**
     * @inheritdoc IButtonswapERC20
     */
    uint8 public constant decimals = 18;

    /**
     * @inheritdoc IButtonswapERC20
     */
    uint256 public totalSupply;

    /**
     * @inheritdoc IButtonswapERC20
     */
    mapping(address => uint256) public balanceOf;

    /**
     * @inheritdoc IButtonswapERC20
     */
    mapping(address => mapping(address => uint256)) public allowance;

    /**
     * @inheritdoc IButtonswapERC20
     */
    bytes32 public immutable DOMAIN_SEPARATOR;

    /**
     * @inheritdoc IButtonswapERC20
     * @dev Value should equal `0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9` but it
     *   is recommended to verify this by checking the public method on-chain.
     */
    bytes32 public constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    /**
     * @inheritdoc IButtonswapERC20
     */
    mapping(address => uint256) public nonces;

    constructor() {
        uint256 chainId;
        assembly {
            chainId := chainid()
        }
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                keccak256(bytes("Buttonswap")),
                keccak256(bytes("1")),
                chainId,
                address(this)
            )
        );
    }

    /**
     * @dev Mints `value` tokens to `to`.
     *
     * Emits a {IButtonswapERC20Events-Transfer} event.
     * @param to The account that is receiving the tokens
     * @param value The amount of tokens being created
     */
    function _mint(address to, uint256 value) internal {
        totalSupply = totalSupply + value;
        balanceOf[to] = balanceOf[to] + value;
        emit Transfer(address(0), to, value);
    }

    /**
     * @dev Burns `value` tokens from `from`.
     *
     * Emits a {IButtonswapERC20Events-Transfer} event.
     * @param from The account that is sending the tokens
     * @param value The amount of tokens being destroyed
     */
    function _burn(address from, uint256 value) internal {
        balanceOf[from] = balanceOf[from] - value;
        totalSupply = totalSupply - value;
        emit Transfer(from, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the caller's tokens.
     *
     * Emits a {IButtonswapERC20Events-Approval} event.
     * @param owner The account whose tokens are being approved
     * @param spender The account that is granted permission to spend the tokens
     * @param value The amount of tokens that can be spent
     */
    function _approve(address owner, address spender, uint256 value) private {
        allowance[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    /**
     * @dev Moves `value` tokens from `from` to `to`.
     *
     * 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
     */
    function _transfer(address from, address to, uint256 value) internal {
        balanceOf[from] = balanceOf[from] - value;
        balanceOf[to] = balanceOf[to] + value;
        emit Transfer(from, to, value);
    }

    /**
     * @inheritdoc IButtonswapERC20
     */
    function name() external view virtual override returns (string memory _name) {
        _name = "Buttonswap";
    }

    /**
     * @inheritdoc IButtonswapERC20
     */
    function symbol() external view virtual override returns (string memory _symbol) {
        _symbol = "BTNSWP";
    }

    /**
     * @inheritdoc IButtonswapERC20
     */
    function approve(address spender, uint256 value) external returns (bool success) {
        _approve(msg.sender, spender, value);
        success = true;
    }

    /**
     * @inheritdoc IButtonswapERC20
     */
    function transfer(address to, uint256 value) external returns (bool success) {
        _transfer(msg.sender, to, value);
        success = true;
    }

    /**
     * @inheritdoc IButtonswapERC20
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool success) {
        uint256 allowanceFromSender = allowance[from][msg.sender];
        if (allowanceFromSender != type(uint256).max) {
            _approve(from, msg.sender, allowanceFromSender - value);
        }
        _transfer(from, to, value);
        success = true;
    }

    /**
     * @inheritdoc IButtonswapERC20
     */
    function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
        external
    {
        if (block.timestamp > deadline) {
            revert PermitExpired();
        }
        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
            )
        );
        address recoveredAddress = ecrecover(digest, v, r, s);
        if (recoveredAddress == address(0) || recoveredAddress != owner) {
            revert PermitInvalidSignature();
        }
        _approve(owner, spender, value);
    }
}

File 5 of 18 : 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 6 of 18 : PairMath.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

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

library PairMath {
    /// @dev Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#dual-sided-mint) for more detail.
    function getDualSidedMintLiquidityOutAmount(
        uint256 totalLiquidity,
        uint256 amountInA,
        uint256 amountInB,
        uint256 totalA,
        uint256 totalB
    ) internal pure returns (uint256 liquidityOut) {
        liquidityOut = Math.min((totalLiquidity * amountInA) / totalA, (totalLiquidity * amountInB) / totalB);
    }

    /// @dev Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#single-sided-mint) for more detail.
    function getSingleSidedMintLiquidityOutAmountA(
        uint256 totalLiquidity,
        uint256 mintAmountA,
        uint256 totalA,
        uint256 totalB,
        uint256 movingAveragePriceA
    ) internal pure returns (uint256 liquidityOut, uint256 swappedReservoirAmountB) {
        // movingAveragePriceA is a UQ112x112 and so is a uint224 that needs to be divided by 2^112 after being multiplied.
        // Here we risk `movingAveragePriceA * (totalA + mintAmountA)` overflowing since we multiple a uint224 by the sum
        //   of two uint112s, however:
        //   - `totalA + mintAmountA` don't exceed 2^112 without violating max pool size.
        //   - 2^256/2^112 = 144 bits spare for movingAveragePriceA
        //   - 2^144/2^112 = 2^32 is the maximum price ratio that can be expressed without overflowing
        // Is 2^32 sufficient? Consider a pair with 1 WBTC (8 decimals) and 30,000 USDX (18 decimals)
        // log2((30000*1e18)/1e8) = 48 and as such a greater price ratio that can be handled.
        // Consequently we require a mulDiv that can handle phantom overflow.
        uint256 tokenAToSwap =
            (mintAmountA * totalB) / (Math.mulDiv(movingAveragePriceA, (totalA + mintAmountA), 2 ** 112) + totalB);
        // Here we don't risk undesired overflow because if `tokenAToSwap * movingAveragePriceA` exceeded 2^256 then it
        //   would necessarily mean `swappedReservoirAmountB` exceeded 2^112, which would result in breaking the poolX uint112 limits.
        swappedReservoirAmountB = (tokenAToSwap * movingAveragePriceA) / 2 ** 112;
        // Update totals to account for the fixed price swap
        totalA += tokenAToSwap;
        totalB -= swappedReservoirAmountB;
        uint256 tokenARemaining = mintAmountA - tokenAToSwap;
        liquidityOut =
            getDualSidedMintLiquidityOutAmount(totalLiquidity, tokenARemaining, swappedReservoirAmountB, totalA, totalB);
    }

    /// @dev Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#single-sided-mint) for more detail.
    function getSingleSidedMintLiquidityOutAmountB(
        uint256 totalLiquidity,
        uint256 mintAmountB,
        uint256 totalA,
        uint256 totalB,
        uint256 movingAveragePriceA
    ) internal pure returns (uint256 liquidityOut, uint256 swappedReservoirAmountA) {
        // `movingAveragePriceA` is a UQ112x112 and so is a uint224 that needs to be divided by 2^112 after being multiplied.
        // Here we need to use the inverse price however, which means we multiply the numerator by 2^112 and then divide that
        //   by movingAveragePriceA to get the result, all without risk of overflow.
        uint256 tokenBToSwap =
            (mintAmountB * totalA) / (((2 ** 112 * (totalB + mintAmountB)) / movingAveragePriceA) + totalA);
        // Inverse price so again we can use it without overflow risk
        swappedReservoirAmountA = (tokenBToSwap * (2 ** 112)) / movingAveragePriceA;
        // Update totals to account for the fixed price swap
        totalA -= swappedReservoirAmountA;
        totalB += tokenBToSwap;
        uint256 tokenBRemaining = mintAmountB - tokenBToSwap;
        liquidityOut =
            getDualSidedMintLiquidityOutAmount(totalLiquidity, swappedReservoirAmountA, tokenBRemaining, totalA, totalB);
    }

    /// @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.
    function getDualSidedBurnOutputAmounts(uint256 totalLiquidity, uint256 liquidityIn, uint256 totalA, uint256 totalB)
        internal
        pure
        returns (uint256 amountOutA, uint256 amountOutB)
    {
        amountOutA = (totalA * liquidityIn) / totalLiquidity;
        amountOutB = (totalB * liquidityIn) / totalLiquidity;
    }

    /// @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.
    function getSingleSidedBurnOutputAmountA(
        uint256 totalLiquidity,
        uint256 liquidityIn,
        uint256 totalA,
        uint256 totalB,
        uint256 movingAveragePriceA
    ) internal pure returns (uint256 amountOutA, uint256 swappedReservoirAmountA) {
        // Calculate what the liquidity is worth in terms of both tokens
        uint256 amountOutB;
        (amountOutA, amountOutB) = getDualSidedBurnOutputAmounts(totalLiquidity, liquidityIn, totalA, totalB);

        // Here we need to use the inverse price however, which means we multiply the numerator by 2^112 and then divide that
        //   by movingAveragePriceA to get the result, all without risk of overflow (because amountOutB must be less than 2*2^112)
        swappedReservoirAmountA = (amountOutB * (2 ** 112)) / movingAveragePriceA;
        amountOutA = amountOutA + swappedReservoirAmountA;
    }

    /// @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.
    function getSingleSidedBurnOutputAmountB(
        uint256 totalLiquidity,
        uint256 liquidityIn,
        uint256 totalA,
        uint256 totalB,
        uint256 movingAveragePriceA
    ) internal pure returns (uint256 amountOutB, uint256 swappedReservoirAmountB) {
        // Calculate what the liquidity is worth in terms of both tokens
        uint256 amountOutA;
        (amountOutA, amountOutB) = getDualSidedBurnOutputAmounts(totalLiquidity, liquidityIn, totalA, totalB);

        // Whilst we appear to risk overflow here, the final `swappedReservoirAmountB` needs to be smaller than the reservoir
        //   which soft-caps it at 2^112.
        // As such, any combination of amountOutA and movingAveragePriceA that would overflow would violate the next
        //   check anyway, and we can therefore safely ignore the overflow potential.
        swappedReservoirAmountB = (amountOutA * movingAveragePriceA) / 2 ** 112;
        amountOutB = amountOutB + swappedReservoirAmountB;
    }

    /// @dev Refer to [fee-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/fee-math.md) for more detail.
    function getProtocolFeeLiquidityMinted(uint256 totalLiquidity, uint256 kLast, uint256 k)
        internal
        pure
        returns (uint256 liquidityOut)
    {
        uint256 rootKLast = Math.sqrt(kLast);
        uint256 rootK = Math.sqrt(k);
        liquidityOut = (totalLiquidity * (rootK - rootKLast)) / ((5 * rootK) + rootKLast);
    }
}

File 7 of 18 : UQ112x112.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.13;

// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))

// range: [0, 2**112 - 1]
// resolution: 1 / 2**112

library UQ112x112 {
    uint224 constant Q112 = 2 ** 112;

    // encode a uint112 as a UQ112x112
    function encode(uint112 y) internal pure returns (uint224 z) {
        z = uint224(y) * Q112; // never overflows
    }

    // divide a UQ112x112 by a uint112, returning a UQ112x112
    function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
        z = x / uint224(y);
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

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

File 10 of 18 : 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 11 of 18 : 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 12 of 18 : 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 13 of 18 : 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 14 of 18 : 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 15 of 18 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

File 17 of 18 : 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 18 of 18 : 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
    );
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Forbidden","type":"error"},{"inputs":[],"name":"InsufficientInputAmount","type":"error"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[],"name":"InsufficientLiquidityAdded","type":"error"},{"inputs":[],"name":"InsufficientLiquidityBurned","type":"error"},{"inputs":[],"name":"InsufficientLiquidityMinted","type":"error"},{"inputs":[],"name":"InsufficientOutputAmount","type":"error"},{"inputs":[],"name":"InsufficientReservoir","type":"error"},{"inputs":[],"name":"InvalidFinalPrice","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"KInvariant","type":"error"},{"inputs":[],"name":"Locked","type":"error"},{"inputs":[],"name":"Overflow","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"PermitExpired","type":"error"},{"inputs":[],"name":"PermitInvalidSignature","type":"error"},{"inputs":[],"name":"SingleSidedTimelock","type":"error"},{"inputs":[],"name":"SwappableReservoirExceeded","type":"error"},{"inputs":[],"name":"Uninitialized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"newMaxSwappableReservoirLimitBps","type":"uint16"}],"name":"MaxSwappableReservoirLimitBpsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newMaxTimelockDuration","type":"uint32"}],"name":"MaxTimelockDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"newMaxVolatilityBps","type":"uint16"}],"name":"MaxVolatilityBpsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newMinTimelockDuration","type":"uint32"}],"name":"MinTimelockDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newMovingAverageWindow","type":"uint32"}],"name":"MovingAverageWindowUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount0Out","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1Out","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newSwappableReservoirGrowthWindow","type":"uint32"}],"name":"SwappableReservoirGrowthWindowUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINIMUM_LIQUIDITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidityIn","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amountOut0","type":"uint256"},{"internalType":"uint256","name":"amountOut1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidityIn","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"burnFromReservoir","outputs":[{"internalType":"uint256","name":"amountOut0","type":"uint256"},{"internalType":"uint256","name":"amountOut1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getIsPaused","outputs":[{"internalType":"bool","name":"_isPaused","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidityBalances","outputs":[{"internalType":"uint112","name":"_pool0","type":"uint112"},{"internalType":"uint112","name":"_pool1","type":"uint112"},{"internalType":"uint112","name":"_reservoir0","type":"uint112"},{"internalType":"uint112","name":"_reservoir1","type":"uint112"},{"internalType":"uint32","name":"_blockTimestampLast","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSwappableReservoirLimit","outputs":[{"internalType":"uint256","name":"swappableReservoirLimit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSwappableReservoirLimitBps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTimelockDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxVolatilityBps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minTimelockDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn0","type":"uint256"},{"internalType":"uint256","name":"amountIn1","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"liquidityOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"mintWithReservoir","outputs":[{"internalType":"uint256","name":"liquidityOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"movingAveragePrice0","outputs":[{"internalType":"uint256","name":"_movingAveragePrice0","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"movingAverageWindow","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"_name","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"price0CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price1CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"isPausedNew","type":"bool"}],"name":"setIsPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newMaxSwappableReservoirLimitBps","type":"uint16"}],"name":"setMaxSwappableReservoirLimitBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newMaxTimelockDuration","type":"uint32"}],"name":"setMaxTimelockDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newMaxVolatilityBps","type":"uint16"}],"name":"setMaxVolatilityBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newMinTimelockDuration","type":"uint32"}],"name":"setMinTimelockDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newMovingAverageWindow","type":"uint32"}],"name":"setMovingAverageWindow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newSwappableReservoirGrowthWindow","type":"uint32"}],"name":"setSwappableReservoirGrowthWindow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"singleSidedTimelockDeadline","outputs":[{"internalType":"uint120","name":"","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn0","type":"uint256"},{"internalType":"uint256","name":"amountIn1","type":"uint256"},{"internalType":"uint256","name":"amountOut0","type":"uint256"},{"internalType":"uint256","name":"amountOut1","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swappableReservoirGrowthWindow","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swappableReservoirLimitReachesMaxDeadline","outputs":[{"internalType":"uint120","name":"","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

610100604052600980546001600160f81b0316600160f81b1790553480156200002757600080fd5b50604080518082018252600a8152690427574746f6e737761760b41b6020918201528151808301835260018152603160f81b9082015281517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818301527f3410c89869cea2edd8d127839826a19c8dfa48f3496e84bc0e7070ad9bdb521d818401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc66060820152466080808301919091523060a08084019190915284518084038201815260c0840180875281519190950120909152339081905263340b2ca160e01b9092529151909163340b2ca19160c48083019261010092919082900301816000875af11580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000165919062000261565b6004805465ffffffffffff60701b1916600160801b63ffffffff9384160261ffff60701b191617600160701b61ffff9485160217600160301b600160701b0319166a01000000000000000000009483169490940263ffffffff60301b1916939093176601000000000000948216949094029390931765ffffffffffff1916640100000000949091169390930263ffffffff19169290921792169190911790556001600160a01b0390811660e0521660c05262000309565b80516001600160a01b03811681146200023457600080fd5b919050565b805163ffffffff811681146200023457600080fd5b805161ffff811681146200023457600080fd5b600080600080600080600080610100898b0312156200027f57600080fd5b6200028a896200021c565b97506200029a60208a016200021c565b9650620002aa60408a0162000239565b9550620002ba60608a016200024e565b9450620002ca60808a0162000239565b9350620002da60a08a0162000239565b9250620002ea60c08a016200024e565b9150620002fa60e08a0162000239565b90509295985092959890939650565b60805160a05160c05160e0516141ab620004936000396000818161061d01528181610e91015281816110ae015281816110ec015281816113cb015281816114cb015281816115b601528181611619015281816116df01528181611ad001528181611f47015281816121500152818161266f0152818161270d015281816127ea01528181612a6401528181612d0b0152612dc30152600081816102b401528181610dfb01528181610f4c01528181610f8a015281816113900152818161143501528181611584015281816115e80152818161165401528181611a4201528181611eb10152818161211a015281816125d9015281816126e10152818161274b015281816129d601528181612c750152612d980152600081816105e3015281816106db015281816107850152818161096b01528181610a2601528181610b0701528181610ccf015281816119bc01528181611b9701528181611c3201528181611dc70152818161220a015281816124ef0152612b6a0152600081816103dd01526122cd01526141ab6000f3fe608060405234801561001057600080fd5b50600436106102695760003560e01c806370a0823111610151578063bd3a5c2d116100c3578063dd62ed3e11610087578063dd62ed3e14610652578063e7d3fe6b1461067d578063f2a6f21514610690578063f69bd428146106a5578063f75bc6b5146106bc578063fcd3533c146106c457600080fd5b8063bd3a5c2d146105b6578063c45a0155146105de578063cbbcd1cc14610605578063d21220a714610618578063d505accf1461063f57600080fd5b806395d89b411161011557806395d89b4114610524578063976bf4161461052c578063a9059cbb14610574578063b0c94e9914610587578063b25200ce1461059a578063ba9a7a56146105ad57600080fd5b806370a08231146104a5578063751f66ba146104c55780637d5eea90146104da5780637ecebe00146104f1578063914e6dfb1461051157600080fd5b806326d18b5a116101ea57806343b6b289116101ae57806343b6b289146104285780634ac93bd51461045a578063501ca4881461046d578063562e19df146104805780635909c0d5146104935780635a3d54931461049c57600080fd5b806326d18b5a1461038057806330adf81f14610397578063313ce567146103be5780633644e515146103d857806340a0b2bc146103ff57600080fd5b8063187fba5511610231578063187fba551461031a5780632025070a1461033f57806323b872dd14610347578063240976bf1461035a578063249d13a11461036d57600080fd5b806306fdde031461026e578063095ea7b31461028c5780630dfe1681146102af578063134e8577146102ee57806318160ddd14610303575b600080fd5b6102766106d7565b6040516102839190613cbc565b60405180910390f35b61029f61029a366004613d04565b610764565b6040519015158152602001610283565b6102d67f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610283565b6103016102fc366004613d30565b61077a565b005b61030c60005481565b604051908152602001610283565b60045461032a9063ffffffff1681565b60405163ffffffff9091168152602001610283565b61030c61081a565b61029f610355366004613d54565b610909565b610301610368366004613da3565b610960565b61030161037b366004613dc0565b610a1b565b60045461032a90600160801b900463ffffffff1681565b61030c7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6103c6601281565b60405160ff9091168152602001610283565b61030c7f000000000000000000000000000000000000000000000000000000000000000081565b60045461041590640100000000900461ffff1681565b60405161ffff9091168152602001610283565b60095461044290600160781b90046001600160781b031681565b6040516001600160781b039091168152602001610283565b610301610468366004613dc0565b610afc565b61030c61047b366004613de6565b610c20565b61030161048e366004613e16565b6112fc565b61030c60065481565b61030c60075481565b61030c6104b3366004613e63565b60016020526000908152604090205481565b60045461041590600160701b900461ffff1681565b60045461032a90600160301b900463ffffffff1681565b61030c6104ff366004613e63565b60036020526000908152604090205481565b600954610442906001600160781b031681565b6102766119b8565b610534611a18565b604080516001600160701b039687168152948616602086015292851692840192909252909216606082015263ffffffff909116608082015260a001610283565b61029f610582366004613d04565b611b7f565b610301610595366004613d30565b611b8c565b6103016105a8366004613dc0565b611c27565b61030c6103e881565b6105c96105c4366004613de6565b611d16565b60408051928352602083019190915201610283565b6102d67f000000000000000000000000000000000000000000000000000000000000000081565b610301610613366004613dc0565b6121ff565b6102d67f000000000000000000000000000000000000000000000000000000000000000081565b61030161064d366004613e80565b612290565b61030c610660366004613ef7565b600260209081526000928352604080842090915290825290205481565b61030c61068b366004613f25565b61246c565b600954600160f01b900460ff1660011461029f565b60045461032a90600160501b900463ffffffff1681565b61030c6129b4565b6105c96106d2366004613de6565b612b11565b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636c02a9316040518163ffffffff1660e01b8152600401600060405180830381865afa158015610737573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261075f9190810190613f74565b905090565b6000610771338484612e58565b50600192915050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146107c357604051631dd2188d60e31b815260040160405180910390fd5b6004805461ffff60701b1916600160701b61ffff8416908102919091179091556040519081527f5132865a5f3289cd175a05a50735aafb1a7e1bbbfb0c269dae3985c1eb93c893906020015b60405180910390a150565b60008061082c6401000000004261402c565b60055490915063ffffffff600160e01b820416820390600090610876906001600160701b038082169161086791600160701b90910416612eba565b6001600160e01b031690612ed9565b6001600160e01b031690508163ffffffff16600003610899576008549350610903565b60045463ffffffff908116908316106108b457809350610903565b60045463ffffffff9081169081906108ce90851684614056565b6108d8858461406d565b63ffffffff166008546108eb9190614056565b6108f59190614091565b6108ff91906140a4565b9450505b50505090565b6001600160a01b0383166000908152600260209081526040808320338452909152812054600019811461094a5761094a853361094586856140b8565b612e58565b610955858585612ef5565b506001949350505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109a957604051631dd2188d60e31b815260040160405180910390fd5b80156109c5576009805460ff60f01b1916600160f01b17905550565b6004546109df90600160501b900463ffffffff1642614091565b600980547fff00ffffffffffffffffffffffffffffff000000000000000000000000000000166001600160781b03929092169190911790555b50565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610a6457604051631dd2188d60e31b815260040160405180910390fd5b610a88610a7763ffffffff831642614091565b6009546001600160781b0316612f9d565b600980546001600160781b0319166001600160781b03929092169190911790556004805463ffffffff60501b1916600160501b63ffffffff8416908102919091179091556040519081527fb40c7a0f3fe456f123e9206acea9eb8b229f25ef0b7c28dcde5683f75a1f70659060200161080f565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610b4557604051631dd2188d60e31b815260040160405180910390fd5b600954600160781b90046001600160781b031642811115610bc557600454600160801b900463ffffffff16610b7a42836140b8565b610b8a9063ffffffff8516614056565b610b9491906140a4565b610b9e9042614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b031602179055505b6004805463ffffffff60801b1916600160801b63ffffffff8516908102919091179091556040519081527f35c743ab7de62a674476813485ff91b6930daf72ea8c37e41fe2f747d36298cb9060200160405180910390a15050565b600954600090600160f81b900460ff168103610c4f576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103610c8a576040516313d0ff5960e31b815260040160405180910390fd5b6009546001600160781b0316421015610cb6576040516303a6c09960e11b815260040160405180910390fd5b3060009081526001602052604090205415610d9d5760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4f91906140cb565b90506001600160a01b03811615610d815730600081815260016020526040902054610d7c91908390612ef5565b610d9b565b30600081815260016020526040902054610d9b9190612fb3565b505b82600003610dbe576040516347c3655560e01b815260040160405180910390fd5b6000805490819003610de35760405163071cbeb560e21b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610e4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6e91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610ed8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610efc91906140e8565b90506000610f0a8383613047565b80519091501580610f1d57506020810151155b15610f3b5760405163bb55fd2760e01b815260040160405180910390fd5b80604001516000036110a957610f737f000000000000000000000000000000000000000000000000000000000000000033308a613192565b6040516370a0823160e01b815230600482015283907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610fd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ffd91906140e8565b61100791906140b8565b9650600061101e6110188986614091565b84613047565b60408101519091501561104457604051630e7a760760e41b815260040160405180910390fd5b600061105a868a878761105561081a565b613203565b602085015191985091506000906110709061329c565b9050808211156110935760405163bacea60f60e01b815260040160405180910390fd5b6110a1846020015183613342565b505050611202565b6110d57f000000000000000000000000000000000000000000000000000000000000000033308a613192565b6040516370a0823160e01b815230600482015282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561113b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115f91906140e8565b61116991906140b8565b965060006111808461117b8a86614091565b613047565b6060810151909150156111a657604051630e7a760760e41b815260040160405180910390fd5b60006111bc868a87876111b761081a565b61343d565b845191985091506000906111cf9061329c565b9050808211156111f25760405163bacea60f60e01b815260040160405180910390fd5b83516111fe9083613342565b5050505b8460000361122357604051633489be7560e21b815260040160405180910390fd5b61122d86866134d3565b806040015160000361128c5760408051888152600060208201529081018690526001600160a01b0387169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a36112dc565b6040805160008152602081018990529081018690526001600160a01b0387169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a35b5050600980546001600160f81b0316600160f81b17905550909392505050565b600954600160f81b900460ff16600003611329576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103611364576040516313d0ff5960e31b815260040160405180910390fd5b82158015611370575081155b1561138e576040516342301c2360e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614806113ff57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b0316145b1561141d57604051634e46966960e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611484573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a891906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611512573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061153691906140e8565b905060006115448383613047565b80519091508610158061155b575080602001518510155b156115795760405163bb55fd2760e01b815260040160405180910390fd5b87156115ab576115ab7f000000000000000000000000000000000000000000000000000000000000000033308b613192565b86156115dd576115dd7f000000000000000000000000000000000000000000000000000000000000000033308a613192565b851561160e5761160e7f00000000000000000000000000000000000000000000000000000000000000008588613558565b841561163f5761163f7f00000000000000000000000000000000000000000000000000000000000000008587613558565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156116a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116c791906140e8565b6040516370a0823160e01b81523060048201529093507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561172e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175291906140e8565b9150600081604001518461176691906140b8565b9050600082606001518461177a91906140b8565b9050811580611787575080155b156117a5576040516323da237d60e01b815260040160405180910390fd5b82518211156117c55782516117ba90836140b8565b9950600097506117d9565b825160009a506117d69083906140b8565b97505b82602001518111156117ff5760208301516117f490826140b8565b985060009650611816565b6000985080836020015161181391906140b8565b96505b89158015611822575088155b156118405760405163098fb56160e01b815260040160405180910390fd5b600061184d8b6003614056565b611859846103e8614056565b61186391906140b8565b905060006118728b6003614056565b61187e846103e8614056565b61188891906140b8565b6020860151865191925061189b91614056565b6118a890620f4240614056565b6118b28284614056565b10156118d15760405163bf6056e760e01b815260040160405180910390fd5b60006118db61081a565b6008819055865160208801519192506118f591878761358d565b611907866000015187602001516135c0565b611912818686613692565b5050600580546001600160701b039485166001600160e01b031990911617600160701b939094169290920292909217905550506040805188815260208101889052908101869052606081018590526001600160a01b03841692503391507fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229060800160405180910390a35050600980546001600160f81b0316600160f81b179055505050565b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637b61c3206040518163ffffffff1660e01b8152600401600060405180830381865afa158015610737573d6000803e3d6000fd5b6040516370a0823160e01b8152306004820152600090819081908190819081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aad91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611b17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3b91906140e8565b90506000611b498383613047565b805160208201516040830151606090930151600554929c919b50929950919750600160e01b900463ffffffff1695509350505050565b6000610771338484612ef5565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611bd557604051631dd2188d60e31b815260040160405180910390fd5b6004805465ffff00000000191664010000000061ffff8416908102919091179091556040519081527fc9e0eeef32bc2c2d7bf9a6830def15f44953dd73a3229bd87a87b12bed187b8d9060200161080f565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611c7057604051631dd2188d60e31b815260040160405180910390fd5b600454611c9f90611c8e90600160301b900463ffffffff1642614091565b6009546001600160781b03166137ab565b600980546001600160781b0319166001600160781b03929092169190911790556004805469ffffffff0000000000001916600160301b63ffffffff8416908102919091179091556040519081527ffdc6d94bf7b4eaa8656351b49ed4d2aa06e1d903bf053c9a8e17b8c8cff415469060200161080f565b6009546000908190600160f81b900460ff168103611d47576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103611d82576040516313d0ff5960e31b815260040160405180910390fd5b6009546001600160781b0316421015611dae576040516303a6c09960e11b815260040160405180910390fd5b3060009081526001602052604090205415611e955760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e4791906140cb565b90506001600160a01b03811615611e795730600081815260016020526040902054611e7491908390612ef5565b611e93565b30600081815260016020526040902054611e939190612fb3565b505b600080546040516370a0823160e01b81523060048201529091907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611f00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f2491906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611f8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fb291906140e8565b90506000611fc08383613047565b80519091501580611fd357506020810151155b15611ff15760405163bb55fd2760e01b815260040160405180910390fd5b8060400151600003612084576000612013858a868661200e61081a565b6137ba565b6060840151919750915086111561203d57604051630e7a760760e41b815260040160405180910390fd5b600061204c836020015161329c565b90508082111561206f5760405163bacea60f60e01b815260040160405180910390fd5b61207d836020015183613342565b5050612105565b600061209a858a868661209561081a565b613801565b604084015191985091508711156120c457604051630e7a760760e41b815260040160405180910390fd5b60006120d3836000015161329c565b9050808211156120f65760405163bacea60f60e01b815260040160405180910390fd5b82516121029083613342565b50505b61210f3389612fb3565b8515612145576121407f00000000000000000000000000000000000000000000000000000000000000008888613558565b61218f565b8415612176576121407f00000000000000000000000000000000000000000000000000000000000000008887613558565b60405163749383ad60e01b815260040160405180910390fd5b60408051898152602081018890529081018690526001600160a01b0388169033907fd175a80c109434bb89948928ab2475a6647c94244cb70002197896423c8833639060600160405180910390a35050600980546001600160f81b0316600160f81b179055509194909350915050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461224857604051631dd2188d60e31b815260040160405180910390fd5b6004805463ffffffff191663ffffffff83169081179091556040519081527ff905287cbb43751efa1a33cb1f6b357b3b5c574bc270934153d2c96ff5dfb7ef9060200161080f565b834211156122b15760405163068568f360e21b815260040160405180910390fd5b6001600160a01b038716600090815260036020526040812080547f0000000000000000000000000000000000000000000000000000000000000000917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b918761231f83614101565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e0016040516020818303038152906040528051906020012060405160200161239892919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015612403573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615806124385750886001600160a01b0316816001600160a01b031614155b156124565760405163b6ea5e6160e01b815260040160405180910390fd5b612461898989612e58565b505050505050505050565b600954600090600160f81b900460ff16810361249b576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff166001036124d6576040516313d0ff5960e31b815260040160405180910390fd5b30600090815260016020526040902054156125bd5760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa15801561254b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061256f91906140cb565b90506001600160a01b038116156125a1573060008181526001602052604090205461259c91908390612ef5565b6125bb565b306000818152600160205260409020546125bb9190612fb3565b505b600080546040516370a0823160e01b81523060048201529091907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612628573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061264c91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156126b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126da91906140e8565b90506127087f000000000000000000000000000000000000000000000000000000000000000033308a613192565b6127347f0000000000000000000000000000000000000000000000000000000000000000333089613192565b6040516370a0823160e01b815230600482015282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561279a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127be91906140e8565b6127c891906140b8565b6040516370a0823160e01b815230600482015290975081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612831573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285591906140e8565b61285f91906140b8565b95508260000361290c576103e861287e612879888a614056565b613826565b61288891906140b8565b935061289760006103e86134d3565b600580546001600160701b03888116600160701b026001600160e01b0319909216908a16171790556128ce6401000000004261402c565b6005601c6101000a81548163ffffffff021916908363ffffffff1602179055506128fb8761086788612eba565b6001600160e01b031660085561291c565b61291983888885856138ca565b93505b8360000361293d57604051633489be7560e21b815260040160405180910390fd5b61294785856134d3565b60408051888152602081018890529081018590526001600160a01b0386169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a35050600980546001600160f81b0316600160f81b179055509392505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612a1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a4191906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612aab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612acf91906140e8565b90506000612add8383613047565b604081015190915015612afc578051612af59061329c565b9350610903565b612b09816020015161329c565b935050505090565b6009546000908190600160f81b900460ff168103612b42576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690553060009081526001602052604090205415612c385760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bea91906140cb565b90506001600160a01b03811615612c1c5730600081815260016020526040902054612c1791908390612ef5565b612c36565b30600081815260016020526040902054612c369190612fb3565b505b83600003612c595760405163749383ad60e01b815260040160405180910390fd5b600080546040516370a0823160e01b81523060048201529091907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612cc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ce891906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612d52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d7691906140e8565b9050612d8483888484613908565b9095509350612d933388612fb3565b612dbe7f00000000000000000000000000000000000000000000000000000000000000008787613558565b612de97f00000000000000000000000000000000000000000000000000000000000000008786613558565b60408051888152602081018790529081018590526001600160a01b0387169033907fd175a80c109434bb89948928ab2475a6647c94244cb70002197896423c8833639060600160405180910390a35050600980546001600160f81b0316600160f81b1790555090939092509050565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000612ed3600160701b6001600160701b03841661411a565b92915050565b6000612eee6001600160701b0383168461414c565b9392505050565b6001600160a01b038316600090815260016020526040902054612f199082906140b8565b6001600160a01b038085166000908152600160205260408082209390935590841681522054612f49908290614091565b6001600160a01b0380841660008181526001602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612ead9085815260200190565b6000818310612fac5781612eee565b5090919050565b6001600160a01b038216600090815260016020526040902054612fd79082906140b8565b6001600160a01b03831660009081526001602052604081209190915554612fff9082906140b8565b60009081556040518281526001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6130726040518060800160405280600081526020016000815260200160008152602001600081525090565b6005546001600160701b0380821691600160701b900416811580613094575080155b61318a578415806130a3575083155b61318a576130b18285614056565b6130bb8287614056565b101561310957848352816130cf8287614056565b6130d991906140a4565b6020840181905283516130ee91908385613942565b602084018190526130ff90856140b8565b606084015261314c565b602083018490528061311b8386614056565b61312591906140a4565b808452602084015161313991908484613942565b80845261314690866140b8565b60408401525b82516001600160701b03108061316c575060208301516001600160701b03105b1561318a57604051631a93c68960e11b815260040160405180910390fd5b505092915050565b6040516001600160a01b03808516602483015283166044820152606481018290526131fd9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261399e565b50505050565b6000808084613220856132168a8a614091565b600160701b613a78565b61322a9190614091565b6132348689614056565b61323e91906140a4565b9050600160701b61324f8583614056565b61325991906140a4565b91506132658187614091565b955061327182866140b8565b9450600061327f82896140b8565b905061328e8982858a8a6138ca565b935050509550959350505050565b6004546000908190612710906132bd90600160701b900461ffff1685614056565b6132c791906140a4565b600954909150600160781b90046001600160781b03164281111561333757600454600160801b900463ffffffff16600061330142846140b8565b6133119063ffffffff84166140b8565b905063ffffffff82166133248286614056565b61332e91906140a4565b9450505061333b565b8192505b5050919050565b6004546000906127109061336190600160701b900461ffff1685614056565b61336b91906140a4565b9050600081156133a3576004548290613392908590600160801b900463ffffffff16614056565b61339c91906140a4565b90506133b5565b50600454600160801b900463ffffffff165b600954600160781b90046001600160781b031642811115613405576133da8282614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b03160217905550613436565b61340f8242614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b031602179055505b5050505050565b60008080858461344d8988614091565b61345b90600160701b614056565b61346591906140a4565b61346f9190614091565b6134798789614056565b61348391906140a4565b90508361349482600160701b614056565b61349e91906140a4565b91506134aa82876140b8565b95506134b68186614091565b945060006134c482896140b8565b905061328e8984838a8a6138ca565b806000546134e19190614091565b60009081556001600160a01b038316815260016020526040902054613507908290614091565b6001600160a01b0383166000818152600160205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061303b9085815260200190565b6040516001600160a01b03831660248201526044810182905261358890849063a9059cbb60e01b906064016131c6565b505050565b600080546135ae9061359f8688614056565b6135a98587614056565b613b26565b905080156134365761343630826134d3565b818160006135d36401000000004261402c565b60055490915063ffffffff600160e01b90910481168203908116158015906135fa57508515155b801561360557508415155b1561368a57836001600160701b03168163ffffffff16607087901b028161362e5761362e614016565b600680549290910490910190556001600160701b03831663ffffffff8216607088901b028161365f5761365f614016565b60078054929091049091019055600580546001600160e01b0316600160e01b63ffffffff8516021790555b505050505050565b60006136a18361086784612eba565b6001600160e01b031690506000848211156136c7576136c085836140b8565b90506136d4565b6136d182866140b8565b90505b60045463ffffffff600160301b8204811691600160501b81049091169060009061375b9061370f9061ffff640100000000909104168a614056565b613719858561406d565b63ffffffff1661372b61271088614056565b6137359190614056565b61373f91906140a4565b61374f9063ffffffff8616614091565b8363ffffffff16612f9d565b905060006137698242614091565b6009549091506001600160781b03908116908216111561246157600980546001600160781b0383166001600160781b0319909116179055505050505050505050565b6000818311612fac5781612eee565b60008060006137cb88888888613908565b93509050600160701b6137de8583614056565b6137e891906140a4565b91506137f48284614091565b9250509550959350505050565b600080600061381288888888613908565b9093509050836137de82600160701b614056565b60b581600160881b811061383f5760409190911b9060801c5b6901000000000000000000811061385b5760209190911b9060401c5b6501000000000081106138735760109190911b9060201c5b630100000081106138895760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b60006138fe836138da8789614056565b6138e491906140a4565b836138ef878a614056565b6138f991906140a4565b612f9d565b9695505050505050565b600080856139168686614056565b61392091906140a4565b91508561392d8685614056565b61393791906140a4565b905094509492505050565b600083613950846002614056565b61395a9190614056565b8280613967886002614056565b6139719190614056565b61397b9190614091565b10156139935761398c856001614091565b9050613996565b50835b949350505050565b60006139f3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613b759092919063ffffffff16565b9050805160001480613a14575080806020019051810190613a149190614172565b6135885760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084015b60405180910390fd5b6000808060001985870985870292508281108382030391505080600003613ab257838281613aa857613aa8614016565b0492505050612eee565b808411613abe57600080fd5b600084868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203026000889003889004909101858311909403939093029303949094049190911702949350505050565b600080613b3284613826565b90506000613b3f84613826565b905081613b4d826005614056565b613b579190614091565b613b6183836140b8565b613b6b9088614056565b6138fe91906140a4565b6060613996848460008585600080866001600160a01b03168587604051613b9c919061418f565b60006040518083038185875af1925050503d8060008114613bd9576040519150601f19603f3d011682016040523d82523d6000602084013e613bde565b606091505b5091509150613bef87838387613bfa565b979650505050505050565b60608315613c69578251600003613c62576001600160a01b0385163b613c625760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401613a6f565b5081613996565b6139968383815115613c7e5781518083602001fd5b8060405162461bcd60e51b8152600401613a6f9190613cbc565b60005b83811015613cb3578181015183820152602001613c9b565b50506000910152565b6020815260008251806020840152613cdb816040850160208701613c98565b601f01601f19169190910160400192915050565b6001600160a01b0381168114610a1857600080fd5b60008060408385031215613d1757600080fd5b8235613d2281613cef565b946020939093013593505050565b600060208284031215613d4257600080fd5b813561ffff81168114612eee57600080fd5b600080600060608486031215613d6957600080fd5b8335613d7481613cef565b92506020840135613d8481613cef565b929592945050506040919091013590565b8015158114610a1857600080fd5b600060208284031215613db557600080fd5b8135612eee81613d95565b600060208284031215613dd257600080fd5b813563ffffffff81168114612eee57600080fd5b60008060408385031215613df957600080fd5b823591506020830135613e0b81613cef565b809150509250929050565b600080600080600060a08688031215613e2e57600080fd5b853594506020860135935060408601359250606086013591506080860135613e5581613cef565b809150509295509295909350565b600060208284031215613e7557600080fd5b8135612eee81613cef565b600080600080600080600060e0888a031215613e9b57600080fd5b8735613ea681613cef565b96506020880135613eb681613cef565b95506040880135945060608801359350608088013560ff81168114613eda57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215613f0a57600080fd5b8235613f1581613cef565b91506020830135613e0b81613cef565b600080600060608486031215613f3a57600080fd5b83359250602084013591506040840135613f5381613cef565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b600060208284031215613f8657600080fd5b815167ffffffffffffffff80821115613f9e57600080fd5b818401915084601f830112613fb257600080fd5b815181811115613fc457613fc4613f5e565b604051601f8201601f19908116603f01168101908382118183101715613fec57613fec613f5e565b8160405282815287602084870101111561400557600080fd5b613bef836020830160208801613c98565b634e487b7160e01b600052601260045260246000fd5b60008261403b5761403b614016565b500690565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417612ed357612ed3614040565b63ffffffff82811682821603908082111561408a5761408a614040565b5092915050565b80820180821115612ed357612ed3614040565b6000826140b3576140b3614016565b500490565b81810381811115612ed357612ed3614040565b6000602082840312156140dd57600080fd5b8151612eee81613cef565b6000602082840312156140fa57600080fd5b5051919050565b60006001820161411357614113614040565b5060010190565b6001600160e01b0382811682821681810283169291811582850482141761414357614143614040565b50505092915050565b60006001600160e01b038381168061416657614166614016565b92169190910492915050565b60006020828403121561418457600080fd5b8151612eee81613d95565b600082516141a1818460208701613c98565b919091019291505056

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102695760003560e01c806370a0823111610151578063bd3a5c2d116100c3578063dd62ed3e11610087578063dd62ed3e14610652578063e7d3fe6b1461067d578063f2a6f21514610690578063f69bd428146106a5578063f75bc6b5146106bc578063fcd3533c146106c457600080fd5b8063bd3a5c2d146105b6578063c45a0155146105de578063cbbcd1cc14610605578063d21220a714610618578063d505accf1461063f57600080fd5b806395d89b411161011557806395d89b4114610524578063976bf4161461052c578063a9059cbb14610574578063b0c94e9914610587578063b25200ce1461059a578063ba9a7a56146105ad57600080fd5b806370a08231146104a5578063751f66ba146104c55780637d5eea90146104da5780637ecebe00146104f1578063914e6dfb1461051157600080fd5b806326d18b5a116101ea57806343b6b289116101ae57806343b6b289146104285780634ac93bd51461045a578063501ca4881461046d578063562e19df146104805780635909c0d5146104935780635a3d54931461049c57600080fd5b806326d18b5a1461038057806330adf81f14610397578063313ce567146103be5780633644e515146103d857806340a0b2bc146103ff57600080fd5b8063187fba5511610231578063187fba551461031a5780632025070a1461033f57806323b872dd14610347578063240976bf1461035a578063249d13a11461036d57600080fd5b806306fdde031461026e578063095ea7b31461028c5780630dfe1681146102af578063134e8577146102ee57806318160ddd14610303575b600080fd5b6102766106d7565b6040516102839190613cbc565b60405180910390f35b61029f61029a366004613d04565b610764565b6040519015158152602001610283565b6102d67f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe81565b6040516001600160a01b039091168152602001610283565b6103016102fc366004613d30565b61077a565b005b61030c60005481565b604051908152602001610283565b60045461032a9063ffffffff1681565b60405163ffffffff9091168152602001610283565b61030c61081a565b61029f610355366004613d54565b610909565b610301610368366004613da3565b610960565b61030161037b366004613dc0565b610a1b565b60045461032a90600160801b900463ffffffff1681565b61030c7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6103c6601281565b60405160ff9091168152602001610283565b61030c7f62732c8b657c622f8f8c0e3993169125026a519229c7f6bdeb367cb58083ab1e81565b60045461041590640100000000900461ffff1681565b60405161ffff9091168152602001610283565b60095461044290600160781b90046001600160781b031681565b6040516001600160781b039091168152602001610283565b610301610468366004613dc0565b610afc565b61030c61047b366004613de6565b610c20565b61030161048e366004613e16565b6112fc565b61030c60065481565b61030c60075481565b61030c6104b3366004613e63565b60016020526000908152604090205481565b60045461041590600160701b900461ffff1681565b60045461032a90600160301b900463ffffffff1681565b61030c6104ff366004613e63565b60036020526000908152604090205481565b600954610442906001600160781b031681565b6102766119b8565b610534611a18565b604080516001600160701b039687168152948616602086015292851692840192909252909216606082015263ffffffff909116608082015260a001610283565b61029f610582366004613d04565b611b7f565b610301610595366004613d30565b611b8c565b6103016105a8366004613dc0565b611c27565b61030c6103e881565b6105c96105c4366004613de6565b611d16565b60408051928352602083019190915201610283565b6102d67f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5081565b610301610613366004613dc0565b6121ff565b6102d67f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16181565b61030161064d366004613e80565b612290565b61030c610660366004613ef7565b600260209081526000928352604080842090915290825290205481565b61030c61068b366004613f25565b61246c565b600954600160f01b900460ff1660011461029f565b60045461032a90600160501b900463ffffffff1681565b61030c6129b4565b6105c96106d2366004613de6565b612b11565b60607f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b0316636c02a9316040518163ffffffff1660e01b8152600401600060405180830381865afa158015610737573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261075f9190810190613f74565b905090565b6000610771338484612e58565b50600192915050565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5016146107c357604051631dd2188d60e31b815260040160405180910390fd5b6004805461ffff60701b1916600160701b61ffff8416908102919091179091556040519081527f5132865a5f3289cd175a05a50735aafb1a7e1bbbfb0c269dae3985c1eb93c893906020015b60405180910390a150565b60008061082c6401000000004261402c565b60055490915063ffffffff600160e01b820416820390600090610876906001600160701b038082169161086791600160701b90910416612eba565b6001600160e01b031690612ed9565b6001600160e01b031690508163ffffffff16600003610899576008549350610903565b60045463ffffffff908116908316106108b457809350610903565b60045463ffffffff9081169081906108ce90851684614056565b6108d8858461406d565b63ffffffff166008546108eb9190614056565b6108f59190614091565b6108ff91906140a4565b9450505b50505090565b6001600160a01b0383166000908152600260209081526040808320338452909152812054600019811461094a5761094a853361094586856140b8565b612e58565b610955858585612ef5565b506001949350505050565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce5016146109a957604051631dd2188d60e31b815260040160405180910390fd5b80156109c5576009805460ff60f01b1916600160f01b17905550565b6004546109df90600160501b900463ffffffff1642614091565b600980547fff00ffffffffffffffffffffffffffffff000000000000000000000000000000166001600160781b03929092169190911790555b50565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce501614610a6457604051631dd2188d60e31b815260040160405180910390fd5b610a88610a7763ffffffff831642614091565b6009546001600160781b0316612f9d565b600980546001600160781b0319166001600160781b03929092169190911790556004805463ffffffff60501b1916600160501b63ffffffff8416908102919091179091556040519081527fb40c7a0f3fe456f123e9206acea9eb8b229f25ef0b7c28dcde5683f75a1f70659060200161080f565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce501614610b4557604051631dd2188d60e31b815260040160405180910390fd5b600954600160781b90046001600160781b031642811115610bc557600454600160801b900463ffffffff16610b7a42836140b8565b610b8a9063ffffffff8516614056565b610b9491906140a4565b610b9e9042614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b031602179055505b6004805463ffffffff60801b1916600160801b63ffffffff8516908102919091179091556040519081527f35c743ab7de62a674476813485ff91b6930daf72ea8c37e41fe2f747d36298cb9060200160405180910390a15050565b600954600090600160f81b900460ff168103610c4f576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103610c8a576040516313d0ff5960e31b815260040160405180910390fd5b6009546001600160781b0316421015610cb6576040516303a6c09960e11b815260040160405180910390fd5b3060009081526001602052604090205415610d9d5760007f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4f91906140cb565b90506001600160a01b03811615610d815730600081815260016020526040902054610d7c91908390612ef5565b610d9b565b30600081815260016020526040902054610d9b9190612fb3565b505b82600003610dbe576040516347c3655560e01b815260040160405180910390fd5b6000805490819003610de35760405163071cbeb560e21b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015610e4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6e91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015610ed8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610efc91906140e8565b90506000610f0a8383613047565b80519091501580610f1d57506020810151155b15610f3b5760405163bb55fd2760e01b815260040160405180910390fd5b80604001516000036110a957610f737f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe33308a613192565b6040516370a0823160e01b815230600482015283907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015610fd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ffd91906140e8565b61100791906140b8565b9650600061101e6110188986614091565b84613047565b60408101519091501561104457604051630e7a760760e41b815260040160405180910390fd5b600061105a868a878761105561081a565b613203565b602085015191985091506000906110709061329c565b9050808211156110935760405163bacea60f60e01b815260040160405180910390fd5b6110a1846020015183613342565b505050611202565b6110d57f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16133308a613192565b6040516370a0823160e01b815230600482015282907f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1616001600160a01b0316906370a0823190602401602060405180830381865afa15801561113b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115f91906140e8565b61116991906140b8565b965060006111808461117b8a86614091565b613047565b6060810151909150156111a657604051630e7a760760e41b815260040160405180910390fd5b60006111bc868a87876111b761081a565b61343d565b845191985091506000906111cf9061329c565b9050808211156111f25760405163bacea60f60e01b815260040160405180910390fd5b83516111fe9083613342565b5050505b8460000361122357604051633489be7560e21b815260040160405180910390fd5b61122d86866134d3565b806040015160000361128c5760408051888152600060208201529081018690526001600160a01b0387169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a36112dc565b6040805160008152602081018990529081018690526001600160a01b0387169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a35b5050600980546001600160f81b0316600160f81b17905550909392505050565b600954600160f81b900460ff16600003611329576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103611364576040516313d0ff5960e31b815260040160405180910390fd5b82158015611370575081155b1561138e576040516342301c2360e01b815260040160405180910390fd5b7f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316816001600160a01b031614806113ff57507f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1616001600160a01b0316816001600160a01b0316145b1561141d57604051634e46966960e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015611484573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a891906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015611512573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061153691906140e8565b905060006115448383613047565b80519091508610158061155b575080602001518510155b156115795760405163bb55fd2760e01b815260040160405180910390fd5b87156115ab576115ab7f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe33308b613192565b86156115dd576115dd7f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16133308a613192565b851561160e5761160e7f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe8588613558565b841561163f5761163f7f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1618587613558565b6040516370a0823160e01b81523060048201527f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa1580156116a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116c791906140e8565b6040516370a0823160e01b81523060048201529093507f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1616001600160a01b0316906370a0823190602401602060405180830381865afa15801561172e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175291906140e8565b9150600081604001518461176691906140b8565b9050600082606001518461177a91906140b8565b9050811580611787575080155b156117a5576040516323da237d60e01b815260040160405180910390fd5b82518211156117c55782516117ba90836140b8565b9950600097506117d9565b825160009a506117d69083906140b8565b97505b82602001518111156117ff5760208301516117f490826140b8565b985060009650611816565b6000985080836020015161181391906140b8565b96505b89158015611822575088155b156118405760405163098fb56160e01b815260040160405180910390fd5b600061184d8b6003614056565b611859846103e8614056565b61186391906140b8565b905060006118728b6003614056565b61187e846103e8614056565b61188891906140b8565b6020860151865191925061189b91614056565b6118a890620f4240614056565b6118b28284614056565b10156118d15760405163bf6056e760e01b815260040160405180910390fd5b60006118db61081a565b6008819055865160208801519192506118f591878761358d565b611907866000015187602001516135c0565b611912818686613692565b5050600580546001600160701b039485166001600160e01b031990911617600160701b939094169290920292909217905550506040805188815260208101889052908101869052606081018590526001600160a01b03841692503391507fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229060800160405180910390a35050600980546001600160f81b0316600160f81b179055505050565b60607f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b0316637b61c3206040518163ffffffff1660e01b8152600401600060405180830381865afa158015610737573d6000803e3d6000fd5b6040516370a0823160e01b8152306004820152600090819081908190819081906001600160a01b037f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe16906370a0823190602401602060405180830381865afa158015611a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aad91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015611b17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3b91906140e8565b90506000611b498383613047565b805160208201516040830151606090930151600554929c919b50929950919750600160e01b900463ffffffff1695509350505050565b6000610771338484612ef5565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce501614611bd557604051631dd2188d60e31b815260040160405180910390fd5b6004805465ffff00000000191664010000000061ffff8416908102919091179091556040519081527fc9e0eeef32bc2c2d7bf9a6830def15f44953dd73a3229bd87a87b12bed187b8d9060200161080f565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce501614611c7057604051631dd2188d60e31b815260040160405180910390fd5b600454611c9f90611c8e90600160301b900463ffffffff1642614091565b6009546001600160781b03166137ab565b600980546001600160781b0319166001600160781b03929092169190911790556004805469ffffffff0000000000001916600160301b63ffffffff8416908102919091179091556040519081527ffdc6d94bf7b4eaa8656351b49ed4d2aa06e1d903bf053c9a8e17b8c8cff415469060200161080f565b6009546000908190600160f81b900460ff168103611d47576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff16600103611d82576040516313d0ff5960e31b815260040160405180910390fd5b6009546001600160781b0316421015611dae576040516303a6c09960e11b815260040160405180910390fd5b3060009081526001602052604090205415611e955760007f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e4791906140cb565b90506001600160a01b03811615611e795730600081815260016020526040902054611e7491908390612ef5565b611e93565b30600081815260016020526040902054611e939190612fb3565b505b600080546040516370a0823160e01b81523060048201529091907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015611f00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f2491906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015611f8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fb291906140e8565b90506000611fc08383613047565b80519091501580611fd357506020810151155b15611ff15760405163bb55fd2760e01b815260040160405180910390fd5b8060400151600003612084576000612013858a868661200e61081a565b6137ba565b6060840151919750915086111561203d57604051630e7a760760e41b815260040160405180910390fd5b600061204c836020015161329c565b90508082111561206f5760405163bacea60f60e01b815260040160405180910390fd5b61207d836020015183613342565b5050612105565b600061209a858a868661209561081a565b613801565b604084015191985091508711156120c457604051630e7a760760e41b815260040160405180910390fd5b60006120d3836000015161329c565b9050808211156120f65760405163bacea60f60e01b815260040160405180910390fd5b82516121029083613342565b50505b61210f3389612fb3565b8515612145576121407f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe8888613558565b61218f565b8415612176576121407f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1618887613558565b60405163749383ad60e01b815260040160405180910390fd5b60408051898152602081018890529081018690526001600160a01b0388169033907fd175a80c109434bb89948928ab2475a6647c94244cb70002197896423c8833639060600160405180910390a35050600980546001600160f81b0316600160f81b179055509194909350915050565b336001600160a01b037f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce50161461224857604051631dd2188d60e31b815260040160405180910390fd5b6004805463ffffffff191663ffffffff83169081179091556040519081527ff905287cbb43751efa1a33cb1f6b357b3b5c574bc270934153d2c96ff5dfb7ef9060200161080f565b834211156122b15760405163068568f360e21b815260040160405180910390fd5b6001600160a01b038716600090815260036020526040812080547f62732c8b657c622f8f8c0e3993169125026a519229c7f6bdeb367cb58083ab1e917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b918761231f83614101565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e0016040516020818303038152906040528051906020012060405160200161239892919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015612403573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615806124385750886001600160a01b0316816001600160a01b031614155b156124565760405163b6ea5e6160e01b815260040160405180910390fd5b612461898989612e58565b505050505050505050565b600954600090600160f81b900460ff16810361249b576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690819055600160f01b900460ff166001036124d6576040516313d0ff5960e31b815260040160405180910390fd5b30600090815260016020526040902054156125bd5760007f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa15801561254b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061256f91906140cb565b90506001600160a01b038116156125a1573060008181526001602052604090205461259c91908390612ef5565b6125bb565b306000818152600160205260409020546125bb9190612fb3565b505b600080546040516370a0823160e01b81523060048201529091907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015612628573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061264c91906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa1580156126b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126da91906140e8565b90506127087f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe33308a613192565b6127347f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a161333089613192565b6040516370a0823160e01b815230600482015282907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa15801561279a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127be91906140e8565b6127c891906140b8565b6040516370a0823160e01b815230600482015290975081906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015612831573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285591906140e8565b61285f91906140b8565b95508260000361290c576103e861287e612879888a614056565b613826565b61288891906140b8565b935061289760006103e86134d3565b600580546001600160701b03888116600160701b026001600160e01b0319909216908a16171790556128ce6401000000004261402c565b6005601c6101000a81548163ffffffff021916908363ffffffff1602179055506128fb8761086788612eba565b6001600160e01b031660085561291c565b61291983888885856138ca565b93505b8360000361293d57604051633489be7560e21b815260040160405180910390fd5b61294785856134d3565b60408051888152602081018890529081018590526001600160a01b0386169033907fa8137fff86647d8a402117b9c5dbda627f721d3773338fb9678c83e54ed390809060600160405180910390a35050600980546001600160f81b0316600160f81b179055509392505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b037f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe16906370a0823190602401602060405180830381865afa158015612a1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a4191906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015612aab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612acf91906140e8565b90506000612add8383613047565b604081015190915015612afc578051612af59061329c565b9350610903565b612b09816020015161329c565b935050505090565b6009546000908190600160f81b900460ff168103612b42576040516303cb96db60e21b815260040160405180910390fd5b600980546001600160f81b031690553060009081526001602052604090205415612c385760007f000000000000000000000000b8de4ab6c65e274630f5279f74eb69b66327ce506001600160a01b031663017e7e586040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bea91906140cb565b90506001600160a01b03811615612c1c5730600081815260016020526040902054612c1791908390612ef5565b612c36565b30600081815260016020526040902054612c369190612fb3565b505b83600003612c595760405163749383ad60e01b815260040160405180910390fd5b600080546040516370a0823160e01b81523060048201529091907f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe6001600160a01b0316906370a0823190602401602060405180830381865afa158015612cc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ce891906140e8565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a16116906370a0823190602401602060405180830381865afa158015612d52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d7691906140e8565b9050612d8483888484613908565b9095509350612d933388612fb3565b612dbe7f000000000000000000000000c1f33e0cf7e40a67375007104b929e49a581bafe8787613558565b612de97f000000000000000000000000d46ba6d942050d489dbd938a2c909a5d5039a1618786613558565b60408051888152602081018790529081018590526001600160a01b0387169033907fd175a80c109434bb89948928ab2475a6647c94244cb70002197896423c8833639060600160405180910390a35050600980546001600160f81b0316600160f81b1790555090939092509050565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000612ed3600160701b6001600160701b03841661411a565b92915050565b6000612eee6001600160701b0383168461414c565b9392505050565b6001600160a01b038316600090815260016020526040902054612f199082906140b8565b6001600160a01b038085166000908152600160205260408082209390935590841681522054612f49908290614091565b6001600160a01b0380841660008181526001602052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612ead9085815260200190565b6000818310612fac5781612eee565b5090919050565b6001600160a01b038216600090815260016020526040902054612fd79082906140b8565b6001600160a01b03831660009081526001602052604081209190915554612fff9082906140b8565b60009081556040518281526001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6130726040518060800160405280600081526020016000815260200160008152602001600081525090565b6005546001600160701b0380821691600160701b900416811580613094575080155b61318a578415806130a3575083155b61318a576130b18285614056565b6130bb8287614056565b101561310957848352816130cf8287614056565b6130d991906140a4565b6020840181905283516130ee91908385613942565b602084018190526130ff90856140b8565b606084015261314c565b602083018490528061311b8386614056565b61312591906140a4565b808452602084015161313991908484613942565b80845261314690866140b8565b60408401525b82516001600160701b03108061316c575060208301516001600160701b03105b1561318a57604051631a93c68960e11b815260040160405180910390fd5b505092915050565b6040516001600160a01b03808516602483015283166044820152606481018290526131fd9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261399e565b50505050565b6000808084613220856132168a8a614091565b600160701b613a78565b61322a9190614091565b6132348689614056565b61323e91906140a4565b9050600160701b61324f8583614056565b61325991906140a4565b91506132658187614091565b955061327182866140b8565b9450600061327f82896140b8565b905061328e8982858a8a6138ca565b935050509550959350505050565b6004546000908190612710906132bd90600160701b900461ffff1685614056565b6132c791906140a4565b600954909150600160781b90046001600160781b03164281111561333757600454600160801b900463ffffffff16600061330142846140b8565b6133119063ffffffff84166140b8565b905063ffffffff82166133248286614056565b61332e91906140a4565b9450505061333b565b8192505b5050919050565b6004546000906127109061336190600160701b900461ffff1685614056565b61336b91906140a4565b9050600081156133a3576004548290613392908590600160801b900463ffffffff16614056565b61339c91906140a4565b90506133b5565b50600454600160801b900463ffffffff165b600954600160781b90046001600160781b031642811115613405576133da8282614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b03160217905550613436565b61340f8242614091565b6009600f6101000a8154816001600160781b0302191690836001600160781b031602179055505b5050505050565b60008080858461344d8988614091565b61345b90600160701b614056565b61346591906140a4565b61346f9190614091565b6134798789614056565b61348391906140a4565b90508361349482600160701b614056565b61349e91906140a4565b91506134aa82876140b8565b95506134b68186614091565b945060006134c482896140b8565b905061328e8984838a8a6138ca565b806000546134e19190614091565b60009081556001600160a01b038316815260016020526040902054613507908290614091565b6001600160a01b0383166000818152600160205260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061303b9085815260200190565b6040516001600160a01b03831660248201526044810182905261358890849063a9059cbb60e01b906064016131c6565b505050565b600080546135ae9061359f8688614056565b6135a98587614056565b613b26565b905080156134365761343630826134d3565b818160006135d36401000000004261402c565b60055490915063ffffffff600160e01b90910481168203908116158015906135fa57508515155b801561360557508415155b1561368a57836001600160701b03168163ffffffff16607087901b028161362e5761362e614016565b600680549290910490910190556001600160701b03831663ffffffff8216607088901b028161365f5761365f614016565b60078054929091049091019055600580546001600160e01b0316600160e01b63ffffffff8516021790555b505050505050565b60006136a18361086784612eba565b6001600160e01b031690506000848211156136c7576136c085836140b8565b90506136d4565b6136d182866140b8565b90505b60045463ffffffff600160301b8204811691600160501b81049091169060009061375b9061370f9061ffff640100000000909104168a614056565b613719858561406d565b63ffffffff1661372b61271088614056565b6137359190614056565b61373f91906140a4565b61374f9063ffffffff8616614091565b8363ffffffff16612f9d565b905060006137698242614091565b6009549091506001600160781b03908116908216111561246157600980546001600160781b0383166001600160781b0319909116179055505050505050505050565b6000818311612fac5781612eee565b60008060006137cb88888888613908565b93509050600160701b6137de8583614056565b6137e891906140a4565b91506137f48284614091565b9250509550959350505050565b600080600061381288888888613908565b9093509050836137de82600160701b614056565b60b581600160881b811061383f5760409190911b9060801c5b6901000000000000000000811061385b5760209190911b9060401c5b6501000000000081106138735760109190911b9060201c5b630100000081106138895760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b60006138fe836138da8789614056565b6138e491906140a4565b836138ef878a614056565b6138f991906140a4565b612f9d565b9695505050505050565b600080856139168686614056565b61392091906140a4565b91508561392d8685614056565b61393791906140a4565b905094509492505050565b600083613950846002614056565b61395a9190614056565b8280613967886002614056565b6139719190614056565b61397b9190614091565b10156139935761398c856001614091565b9050613996565b50835b949350505050565b60006139f3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613b759092919063ffffffff16565b9050805160001480613a14575080806020019051810190613a149190614172565b6135885760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084015b60405180910390fd5b6000808060001985870985870292508281108382030391505080600003613ab257838281613aa857613aa8614016565b0492505050612eee565b808411613abe57600080fd5b600084868809600260036001881981018916988990049182028318808302840302808302840302808302840302808302840302808302840302918202909203026000889003889004909101858311909403939093029303949094049190911702949350505050565b600080613b3284613826565b90506000613b3f84613826565b905081613b4d826005614056565b613b579190614091565b613b6183836140b8565b613b6b9088614056565b6138fe91906140a4565b6060613996848460008585600080866001600160a01b03168587604051613b9c919061418f565b60006040518083038185875af1925050503d8060008114613bd9576040519150601f19603f3d011682016040523d82523d6000602084013e613bde565b606091505b5091509150613bef87838387613bfa565b979650505050505050565b60608315613c69578251600003613c62576001600160a01b0385163b613c625760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401613a6f565b5081613996565b6139968383815115613c7e5781518083602001fd5b8060405162461bcd60e51b8152600401613a6f9190613cbc565b60005b83811015613cb3578181015183820152602001613c9b565b50506000910152565b6020815260008251806020840152613cdb816040850160208701613c98565b601f01601f19169190910160400192915050565b6001600160a01b0381168114610a1857600080fd5b60008060408385031215613d1757600080fd5b8235613d2281613cef565b946020939093013593505050565b600060208284031215613d4257600080fd5b813561ffff81168114612eee57600080fd5b600080600060608486031215613d6957600080fd5b8335613d7481613cef565b92506020840135613d8481613cef565b929592945050506040919091013590565b8015158114610a1857600080fd5b600060208284031215613db557600080fd5b8135612eee81613d95565b600060208284031215613dd257600080fd5b813563ffffffff81168114612eee57600080fd5b60008060408385031215613df957600080fd5b823591506020830135613e0b81613cef565b809150509250929050565b600080600080600060a08688031215613e2e57600080fd5b853594506020860135935060408601359250606086013591506080860135613e5581613cef565b809150509295509295909350565b600060208284031215613e7557600080fd5b8135612eee81613cef565b600080600080600080600060e0888a031215613e9b57600080fd5b8735613ea681613cef565b96506020880135613eb681613cef565b95506040880135945060608801359350608088013560ff81168114613eda57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215613f0a57600080fd5b8235613f1581613cef565b91506020830135613e0b81613cef565b600080600060608486031215613f3a57600080fd5b83359250602084013591506040840135613f5381613cef565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b600060208284031215613f8657600080fd5b815167ffffffffffffffff80821115613f9e57600080fd5b818401915084601f830112613fb257600080fd5b815181811115613fc457613fc4613f5e565b604051601f8201601f19908116603f01168101908382118183101715613fec57613fec613f5e565b8160405282815287602084870101111561400557600080fd5b613bef836020830160208801613c98565b634e487b7160e01b600052601260045260246000fd5b60008261403b5761403b614016565b500690565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417612ed357612ed3614040565b63ffffffff82811682821603908082111561408a5761408a614040565b5092915050565b80820180821115612ed357612ed3614040565b6000826140b3576140b3614016565b500490565b81810381811115612ed357612ed3614040565b6000602082840312156140dd57600080fd5b8151612eee81613cef565b6000602082840312156140fa57600080fd5b5051919050565b60006001820161411357614113614040565b5060010190565b6001600160e01b0382811682821681810283169291811582850482141761414357614143614040565b50505092915050565b60006001600160e01b038381168061416657614166614016565b92169190910492915050565b60006020828403121561418457600080fd5b8151612eee81613d95565b600082516141a1818460208701613c98565b919091019291505056

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.