creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/ButtonswapPair.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\nimport {IButtonswapPair} from \"./interfaces/IButtonswapPair/IButtonswapPair.sol\";\nimport {IButtonswapERC20} from \"./interfaces/IButtonswapERC20/IButtonswapERC20.sol\";\nimport {ButtonswapERC20} from \"./ButtonswapERC20.sol\";\nimport {Math} from \"./libraries/Math.sol\";\nimport {PairMath} from \"./libraries/PairMath.sol\";\nimport {UQ112x112} from \"./libraries/UQ112x112.sol\";\nimport {IERC20} from \"buttonswap-core_@openzeppelin-contracts/token/ERC20/IERC20.sol\";\nimport {SafeERC20} from \"buttonswap-core_@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {IButtonswapFactory} from \"./interfaces/IButtonswapFactory/IButtonswapFactory.sol\";\n\ncontract ButtonswapPair is IButtonswapPair, ButtonswapERC20 {\n    using UQ112x112 for uint224;\n\n    /**\n     * @dev A set of liquidity values.\n     * @param pool0 The active `token0` liquidity\n     * @param pool1 The active `token1` liquidity\n     * @param reservoir0 The inactive `token0` liquidity\n     * @param reservoir1 The inactive `token1` liquidity\n     */\n    struct LiquidityBalances {\n        uint256 pool0;\n        uint256 pool1;\n        uint256 reservoir0;\n        uint256 reservoir1;\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    uint256 public constant MINIMUM_LIQUIDITY = 10 ** 3;\n\n    /**\n     * @dev Denominator for basis points.\n     */\n    uint256 private constant BPS = 10_000;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    uint32 public movingAverageWindow;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    uint16 public maxVolatilityBps;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    uint32 public minTimelockDuration;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    uint32 public maxTimelockDuration;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    uint16 public maxSwappableReservoirLimitBps;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    uint32 public swappableReservoirGrowthWindow;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    address public immutable factory;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    address public immutable token0;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    address public immutable token1;\n\n    /**\n     * @dev The active `token0` liquidity amount following the last swap.\n     * This value is used to determine active liquidity balances after potential rebases until the next future swap.\n     */\n    uint112 internal pool0Last;\n\n    /**\n     * @dev The active `token1` liquidity amount following the last swap.\n     * This value is used to determine active liquidity balances after potential rebases until the next future swap.\n     */\n    uint112 internal pool1Last;\n\n    /**\n     * @dev The timestamp of the block that the last swap occurred in.\n     */\n    uint32 internal blockTimestampLast;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    uint256 public price0CumulativeLast;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    uint256 public price1CumulativeLast;\n\n    /**\n     * @dev The value of `movingAveragePrice0` at the time of the last swap.\n     */\n    uint256 internal movingAveragePrice0Last;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    uint120 public singleSidedTimelockDeadline;\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    uint120 public swappableReservoirLimitReachesMaxDeadline;\n\n    /**\n     * @dev Whether or not the pair is isPaused (paused = 1, unPaused = 0).\n     * When paused, all operations other than dual-sided burning LP tokens are disabled.\n     */\n    uint8 internal isPaused;\n\n    /**\n     * @dev Value to track the state of the re-entrancy guard.\n     */\n    uint8 private unlocked = 1;\n\n    /**\n     * @dev Guards against re-entrancy.\n     */\n    modifier lock() {\n        if (unlocked == 0) {\n            revert Locked();\n        }\n        unlocked = 0;\n        _;\n        unlocked = 1;\n    }\n\n    /**\n     * @dev Prevents certain operations from being executed if the price volatility induced timelock has yet to conclude.\n     */\n    modifier singleSidedTimelock() {\n        if (block.timestamp < singleSidedTimelockDeadline) {\n            revert SingleSidedTimelock();\n        }\n        _;\n    }\n\n    /**\n     * @dev Prevents operations from being executed if the Pair is currently paused.\n     */\n    modifier checkPaused() {\n        if (isPaused == 1) {\n            revert Paused();\n        }\n        _;\n    }\n\n    /**\n     * @dev Called whenever an LP wants to burn their LP tokens to make sure they get their fair share of fees.\n     * If `feeTo` is defined, `balanceOf(address(this))` gets transferred to `feeTo`.\n     * If `feeTo` is not defined, `balanceOf(address(this))` gets burned and the LP tokens all grow in value.\n     */\n    modifier sendOrRefundFee() {\n        if (balanceOf[address(this)] > 0) {\n            address feeTo = IButtonswapFactory(factory).feeTo();\n            if (feeTo != address(0)) {\n                _transfer(address(this), feeTo, balanceOf[address(this)]);\n            } else {\n                _burn(address(this), balanceOf[address(this)]);\n            }\n        }\n        _;\n    }\n\n    /**\n     * @dev Prevents operations from being executed if the caller is not the factory.\n     */\n    modifier onlyFactory() {\n        if (msg.sender != factory) {\n            revert Forbidden();\n        }\n        _;\n    }\n\n    constructor() {\n        factory = msg.sender;\n        (\n            token0,\n            token1,\n            movingAverageWindow,\n            maxVolatilityBps,\n            minTimelockDuration,\n            maxTimelockDuration,\n            maxSwappableReservoirLimitBps,\n            swappableReservoirGrowthWindow\n        ) = IButtonswapFactory(factory).lastCreatedTokensAndParameters();\n    }\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    function name() external view override(ButtonswapERC20, IButtonswapERC20) returns (string memory _name) {\n        _name = IButtonswapFactory(factory).tokenName();\n    }\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    function symbol() external view override(ButtonswapERC20, IButtonswapERC20) returns (string memory _symbol) {\n        _symbol = IButtonswapFactory(factory).tokenSymbol();\n    }\n\n    /**\n     * @dev Always mints liquidity equivalent to 1/6th of the growth in sqrt(k) and allocates to address(this)\n     * If there isn't a `feeTo` address defined, these LP tokens will get burned this 1/6th gets reallocated to LPs\n     * @param pool0 The `token0` active liquidity balance at the start of the ongoing swap\n     * @param pool1 The `token1` active liquidity balance at the start of the ongoing swap\n     * @param pool0New The `token0` active liquidity balance at the end of the ongoing swap\n     * @param pool1New The `token1` active liquidity balance at the end of the ongoing swap\n     */\n    function _mintFee(uint256 pool0, uint256 pool1, uint256 pool0New, uint256 pool1New) internal {\n        uint256 liquidityOut = PairMath.getProtocolFeeLiquidityMinted(totalSupply, pool0 * pool1, pool0New * pool1New);\n        if (liquidityOut > 0) {\n            _mint(address(this), liquidityOut);\n        }\n    }\n\n    /**\n     * @dev Updates `price0CumulativeLast` and `price1CumulativeLast` based on the current timestamp.\n     * @param pool0 The `token0` active liquidity balance at the start of the ongoing swap\n     * @param pool1 The `token1` active liquidity balance at the start of the ongoing swap\n     */\n    function _updatePriceCumulative(uint256 pool0, uint256 pool1) internal {\n        uint112 _pool0 = uint112(pool0);\n        uint112 _pool1 = uint112(pool1);\n        uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32);\n        uint32 timeElapsed;\n        unchecked {\n            // underflow is desired\n            timeElapsed = blockTimestamp - blockTimestampLast;\n        }\n        if (timeElapsed > 0 && pool0 != 0 && pool1 != 0) {\n            // * never overflows, and + overflow is desired\n            unchecked {\n                price0CumulativeLast += ((pool1 << 112) * timeElapsed) / _pool0;\n                price1CumulativeLast += ((pool0 << 112) * timeElapsed) / _pool1;\n            }\n            blockTimestampLast = blockTimestamp;\n        }\n    }\n\n    /**\n     * @dev Refer to [closest-bound-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/closest-bound-math.md) for more detail.\n     * @param poolALower The lower bound for the active liquidity balance of the non-fixed token\n     * @param poolB The active liquidity balance of the fixed token\n     * @param _poolALast The active liquidity balance at the end of the last swap for the non-fixed token\n     * @param _poolBLast The active liquidity balance at the end of the last swap for the fixed token\n     * @return closestBound The bound for the active liquidity balance of the non-fixed token that produces a price ratio closest to last swap price\n     */\n    function _closestBound(uint256 poolALower, uint256 poolB, uint256 _poolALast, uint256 _poolBLast)\n        internal\n        pure\n        returns (uint256 closestBound)\n    {\n        if ((2 * poolALower * _poolBLast) + _poolBLast < 2 * _poolALast * poolB) {\n            closestBound = poolALower + 1;\n        } else {\n            closestBound = poolALower;\n        }\n    }\n\n    /**\n     * @dev Refer to [liquidity-balances-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/liquidity-balances-math.md) for more detail.\n     * @param total0 The total amount of `token0` held by the Pair\n     * @param total1 The total amount of `token1` held by the Pair\n     * @return lb The current active and inactive liquidity balances\n     */\n    function _getLiquidityBalances(uint256 total0, uint256 total1)\n        internal\n        view\n        returns (LiquidityBalances memory lb)\n    {\n        uint256 _pool0Last = uint256(pool0Last);\n        uint256 _pool1Last = uint256(pool1Last);\n        if (_pool0Last == 0 || _pool1Last == 0) {\n            // Before Pair is initialized by first dual mint just return zeroes\n        } else if (total0 == 0 || total1 == 0) {\n            // Save the extra calculations and just return zeroes\n        } else {\n            if (total0 * _pool1Last < total1 * _pool0Last) {\n                lb.pool0 = total0;\n                // pool0Last/pool1Last == pool0/pool1 => pool1 == (pool0*pool1Last)/pool0Last\n                // pool1Last/pool0Last == pool1/pool0 => pool1 == (pool0*pool1Last)/pool0Last\n                lb.pool1 = (lb.pool0 * _pool1Last) / _pool0Last;\n                lb.pool1 = _closestBound(lb.pool1, lb.pool0, _pool1Last, _pool0Last);\n                // reservoir0 is zero, so no need to set it\n                lb.reservoir1 = total1 - lb.pool1;\n            } else {\n                lb.pool1 = total1;\n                // pool0Last/pool1Last == pool0/pool1 => pool0 == (pool1*pool0Last)/pool1Last\n                // pool1Last/pool0Last == pool1/pool0 => pool0 == (pool1*pool0Last)/pool1Last\n                lb.pool0 = (lb.pool1 * _pool0Last) / _pool1Last;\n                lb.pool0 = _closestBound(lb.pool0, lb.pool1, _pool0Last, _pool1Last);\n                // reservoir1 is zero, so no need to set it\n                lb.reservoir0 = total0 - lb.pool0;\n            }\n            if (lb.pool0 > type(uint112).max || lb.pool1 > type(uint112).max) {\n                revert Overflow();\n            }\n        }\n    }\n\n    /**\n     * @dev Calculates current price volatility and initiates a timelock scaled to the volatility size.\n     * This timelock prohibits single-sided operations from being executed until enough time has passed for the timelock\n     *   to conclude.\n     * This protects against attempts to manipulate the price that the reservoir is valued at during single-sided operations.\n     * @param _movingAveragePrice0 The current `movingAveragePrice0` value\n     * @param pool0New The `token0` active liquidity balance at the end of the ongoing swap\n     * @param pool1New The `token1` active liquidity balance at the end of the ongoing swap\n     */\n    function _updateSingleSidedTimelock(uint256 _movingAveragePrice0, uint112 pool0New, uint112 pool1New) internal {\n        uint256 newPrice0 = uint256(UQ112x112.encode(pool1New).uqdiv(pool0New));\n        uint256 priceDifference;\n        if (newPrice0 > _movingAveragePrice0) {\n            priceDifference = newPrice0 - _movingAveragePrice0;\n        } else {\n            priceDifference = _movingAveragePrice0 - newPrice0;\n        }\n        // priceDifference / ((_movingAveragePrice0 * maxVolatilityBps)/BPS)\n        uint32 _minTimelockDuration = minTimelockDuration;\n        uint32 _maxTimelockDuration = maxTimelockDuration;\n        uint256 timelock = Math.min(\n            _minTimelockDuration\n                + (\n                    (priceDifference * BPS * (_maxTimelockDuration - _minTimelockDuration))\n                        / (_movingAveragePrice0 * maxVolatilityBps)\n                ),\n            _maxTimelockDuration\n        );\n        uint120 timelockDeadline = uint120(block.timestamp + timelock);\n        if (timelockDeadline > singleSidedTimelockDeadline) {\n            singleSidedTimelockDeadline = timelockDeadline;\n        }\n    }\n\n    /**\n     * @dev Calculates the current limit on the number of reservoir tokens that can be exchanged during a single-sided\n     *   operation.\n     * This is based on corresponding active liquidity size and time since and size of the last single-sided operation.\n     * @param poolA The active liquidity balance for the non-zero reservoir token\n     * @return swappableReservoir The amount of non-zero reservoir token that can be exchanged as part of a single-sided operation\n     */\n    function _getSwappableReservoirLimit(uint256 poolA) internal view returns (uint256 swappableReservoir) {\n        // Calculate the maximum the limit can be as a fraction of the corresponding active liquidity\n        uint256 maxSwappableReservoirLimit = (poolA * maxSwappableReservoirLimitBps) / BPS;\n        uint256 _swappableReservoirLimitReachesMaxDeadline = swappableReservoirLimitReachesMaxDeadline;\n        if (_swappableReservoirLimitReachesMaxDeadline > block.timestamp) {\n            // If the current deadline is still active then calculate the progress towards reaching it\n            uint32 _swappableReservoirGrowthWindow = swappableReservoirGrowthWindow;\n            uint256 progress =\n                _swappableReservoirGrowthWindow - (_swappableReservoirLimitReachesMaxDeadline - block.timestamp);\n            // The greater the progress, the closer to the max limit we get\n            swappableReservoir = (maxSwappableReservoirLimit * progress) / _swappableReservoirGrowthWindow;\n        } else {\n            // If the current deadline has expired then the full limit is available\n            swappableReservoir = maxSwappableReservoirLimit;\n        }\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function getSwappableReservoirLimit() external view returns (uint256 swappableReservoirLimit) {\n        uint256 total0 = IERC20(token0).balanceOf(address(this));\n        uint256 total1 = IERC20(token1).balanceOf(address(this));\n        LiquidityBalances memory lb = _getLiquidityBalances(total0, total1);\n\n        if (lb.reservoir0 > 0) {\n            swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool0);\n        } else {\n            swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool1);\n        }\n    }\n\n    /**\n     * @dev Updates the value of `swappableReservoirLimitReachesMaxDeadline` which is the time at which the maximum\n     *   amount of inactive liquidity tokens can be exchanged during a single-sided operation.\n     * @dev Assumes `swappedAmountA` is less than or equal to `maxSwappableReservoirLimit`\n     * @param poolA The active liquidity balance for the non-zero reservoir token\n     * @param swappedAmountA The amount of non-zero reservoir tokens that were exchanged during the ongoing single-sided\n     *   operation\n     */\n    function _updateSwappableReservoirDeadline(uint256 poolA, uint256 swappedAmountA) internal {\n        // Calculate the maximum the limit can be as a fraction of the corresponding active liquidity\n        uint256 maxSwappableReservoirLimit = (poolA * maxSwappableReservoirLimitBps) / BPS;\n        // Calculate how much time delay the swap instigates\n        uint256 delay;\n        // Check non-zero to avoid div by zero error\n        if (maxSwappableReservoirLimit > 0) {\n            // Since `swappedAmountA/maxSwappableReservoirLimit <= 1`, `delay <= swappableReservoirGrowthWindow`\n            delay = (swappableReservoirGrowthWindow * swappedAmountA) / maxSwappableReservoirLimit;\n        } else {\n            // If it is zero then it's in an extreme condition and a delay is most appropriate way to handle it\n            delay = swappableReservoirGrowthWindow;\n        }\n        // Apply the delay\n        uint256 _swappableReservoirLimitReachesMaxDeadline = swappableReservoirLimitReachesMaxDeadline;\n        if (_swappableReservoirLimitReachesMaxDeadline > block.timestamp) {\n            // If the current deadline hasn't expired yet then add the delay to it\n            swappableReservoirLimitReachesMaxDeadline = uint120(_swappableReservoirLimitReachesMaxDeadline + delay);\n        } else {\n            // If the current deadline has expired already then add the delay to the current time, so that the full\n            //   delay is still applied\n            swappableReservoirLimitReachesMaxDeadline = uint120(block.timestamp + delay);\n        }\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function getIsPaused() external view returns (bool _isPaused) {\n        _isPaused = isPaused == 1;\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function setIsPaused(bool isPausedNew) external onlyFactory {\n        if (isPausedNew) {\n            isPaused = 1;\n        } else {\n            singleSidedTimelockDeadline = uint120(block.timestamp + maxTimelockDuration);\n            isPaused = 0;\n        }\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function getLiquidityBalances()\n        external\n        view\n        returns (uint112 _pool0, uint112 _pool1, uint112 _reservoir0, uint112 _reservoir1, uint32 _blockTimestampLast)\n    {\n        uint256 total0 = IERC20(token0).balanceOf(address(this));\n        uint256 total1 = IERC20(token1).balanceOf(address(this));\n        LiquidityBalances memory lb = _getLiquidityBalances(total0, total1);\n        _pool0 = uint112(lb.pool0);\n        _pool1 = uint112(lb.pool1);\n        _reservoir0 = uint112(lb.reservoir0);\n        _reservoir1 = uint112(lb.reservoir1);\n        _blockTimestampLast = blockTimestampLast;\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function movingAveragePrice0() public view returns (uint256 _movingAveragePrice0) {\n        uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32);\n        uint32 timeElapsed;\n        unchecked {\n            // overflow is desired\n            timeElapsed = blockTimestamp - blockTimestampLast;\n        }\n        uint256 currentPrice0 = uint256(UQ112x112.encode(pool1Last).uqdiv(pool0Last));\n        if (timeElapsed == 0) {\n            _movingAveragePrice0 = movingAveragePrice0Last;\n        } else if (timeElapsed >= movingAverageWindow) {\n            _movingAveragePrice0 = currentPrice0;\n        } else {\n            uint32 _movingAverageWindow = movingAverageWindow;\n            _movingAveragePrice0 = (\n                (movingAveragePrice0Last * (_movingAverageWindow - timeElapsed)) + (currentPrice0 * timeElapsed)\n            ) / _movingAverageWindow;\n        }\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function mint(uint256 amountIn0, uint256 amountIn1, address to)\n        external\n        lock\n        checkPaused\n        sendOrRefundFee\n        returns (uint256 liquidityOut)\n    {\n        uint256 _totalSupply = totalSupply;\n        uint256 total0 = IERC20(token0).balanceOf(address(this));\n        uint256 total1 = IERC20(token1).balanceOf(address(this));\n        SafeERC20.safeTransferFrom(IERC20(token0), msg.sender, address(this), amountIn0);\n        SafeERC20.safeTransferFrom(IERC20(token1), msg.sender, address(this), amountIn1);\n        // Use the balance delta as input amounts to ensure feeOnTransfer or similar tokens don't disrupt Pair math\n        amountIn0 = IERC20(token0).balanceOf(address(this)) - total0;\n        amountIn1 = IERC20(token1).balanceOf(address(this)) - total1;\n\n        if (_totalSupply == 0) {\n            liquidityOut = Math.sqrt(amountIn0 * amountIn1) - MINIMUM_LIQUIDITY;\n            // permanently lock the first MINIMUM_LIQUIDITY tokens\n            _mint(address(0), MINIMUM_LIQUIDITY);\n            // Initialize Pair last swap price\n            pool0Last = uint112(amountIn0);\n            pool1Last = uint112(amountIn1);\n            // Initialize timestamp so first price update is accurate\n            blockTimestampLast = uint32(block.timestamp % 2 ** 32);\n            // Initialize moving average to price from initial amounts\n            movingAveragePrice0Last = uint256(UQ112x112.encode(uint112(amountIn1)).uqdiv(uint112(amountIn0)));\n        } else {\n            // Don't need to check that amountIn{0,1} are in the right ratio because the least generous ratio is used\n            //   to determine the liquidityOut value, meaning any tokens that exceed that ratio are donated.\n            // If total0 or total1 are zero (eg. due to negative rebases) then the function call reverts with div by zero\n            liquidityOut =\n                PairMath.getDualSidedMintLiquidityOutAmount(_totalSupply, amountIn0, amountIn1, total0, total1);\n        }\n\n        if (liquidityOut == 0) {\n            revert InsufficientLiquidityMinted();\n        }\n        _mint(to, liquidityOut);\n        emit Mint(msg.sender, amountIn0, amountIn1, liquidityOut, to);\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function mintWithReservoir(uint256 amountIn, address to)\n        external\n        lock\n        checkPaused\n        singleSidedTimelock\n        sendOrRefundFee\n        returns (uint256 liquidityOut)\n    {\n        if (amountIn == 0) {\n            revert InsufficientLiquidityAdded();\n        }\n        uint256 _totalSupply = totalSupply;\n        if (_totalSupply == 0) {\n            revert Uninitialized();\n        }\n        uint256 total0 = IERC20(token0).balanceOf(address(this));\n        uint256 total1 = IERC20(token1).balanceOf(address(this));\n        // Determine current pool liquidity\n        LiquidityBalances memory lb = _getLiquidityBalances(total0, total1);\n        if (lb.pool0 == 0 || lb.pool1 == 0) {\n            revert InsufficientLiquidity();\n        }\n        if (lb.reservoir0 == 0) {\n            // If reservoir0 is empty then we're adding token0 to pair with token1 reservoir liquidity\n            SafeERC20.safeTransferFrom(IERC20(token0), msg.sender, address(this), amountIn);\n            // Use the balance delta as input amounts to ensure feeOnTransfer or similar tokens don't disrupt Pair math\n            amountIn = IERC20(token0).balanceOf(address(this)) - total0;\n\n            // Ensure there's enough reservoir1 liquidity to do this without growing reservoir0\n            LiquidityBalances memory lbNew = _getLiquidityBalances(total0 + amountIn, total1);\n            if (lbNew.reservoir0 > 0) {\n                revert InsufficientReservoir();\n            }\n\n            uint256 swappedReservoirAmount1;\n            (liquidityOut, swappedReservoirAmount1) = PairMath.getSingleSidedMintLiquidityOutAmountA(\n                _totalSupply, amountIn, total0, total1, movingAveragePrice0()\n            );\n\n            uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool1);\n            if (swappedReservoirAmount1 > swappableReservoirLimit) {\n                revert SwappableReservoirExceeded();\n            }\n            _updateSwappableReservoirDeadline(lb.pool1, swappedReservoirAmount1);\n        } else {\n            // If reservoir1 is empty then we're adding token1 to pair with token0 reservoir liquidity\n            SafeERC20.safeTransferFrom(IERC20(token1), msg.sender, address(this), amountIn);\n            // Use the balance delta as input amounts to ensure feeOnTransfer or similar tokens don't disrupt Pair math\n            amountIn = IERC20(token1).balanceOf(address(this)) - total1;\n\n            // Ensure there's enough reservoir0 liquidity to do this without growing reservoir1\n            LiquidityBalances memory lbNew = _getLiquidityBalances(total0, total1 + amountIn);\n            if (lbNew.reservoir1 > 0) {\n                revert InsufficientReservoir();\n            }\n\n            uint256 swappedReservoirAmount0;\n            (liquidityOut, swappedReservoirAmount0) = PairMath.getSingleSidedMintLiquidityOutAmountB(\n                _totalSupply, amountIn, total0, total1, movingAveragePrice0()\n            );\n\n            uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool0);\n            if (swappedReservoirAmount0 > swappableReservoirLimit) {\n                revert SwappableReservoirExceeded();\n            }\n            _updateSwappableReservoirDeadline(lb.pool0, swappedReservoirAmount0);\n        }\n\n        if (liquidityOut == 0) {\n            revert InsufficientLiquidityMinted();\n        }\n        _mint(to, liquidityOut);\n        if (lb.reservoir0 == 0) {\n            emit Mint(msg.sender, amountIn, 0, liquidityOut, to);\n        } else {\n            emit Mint(msg.sender, 0, amountIn, liquidityOut, to);\n        }\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function burn(uint256 liquidityIn, address to)\n        external\n        lock\n        sendOrRefundFee\n        returns (uint256 amountOut0, uint256 amountOut1)\n    {\n        if (liquidityIn == 0) {\n            revert InsufficientLiquidityBurned();\n        }\n        uint256 _totalSupply = totalSupply;\n        uint256 total0 = IERC20(token0).balanceOf(address(this));\n        uint256 total1 = IERC20(token1).balanceOf(address(this));\n\n        (amountOut0, amountOut1) = PairMath.getDualSidedBurnOutputAmounts(_totalSupply, liquidityIn, total0, total1);\n\n        _burn(msg.sender, liquidityIn);\n        SafeERC20.safeTransfer(IERC20(token0), to, amountOut0);\n        SafeERC20.safeTransfer(IERC20(token1), to, amountOut1);\n        emit Burn(msg.sender, liquidityIn, amountOut0, amountOut1, to);\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function burnFromReservoir(uint256 liquidityIn, address to)\n        external\n        lock\n        checkPaused\n        singleSidedTimelock\n        sendOrRefundFee\n        returns (uint256 amountOut0, uint256 amountOut1)\n    {\n        uint256 _totalSupply = totalSupply;\n        uint256 total0 = IERC20(token0).balanceOf(address(this));\n        uint256 total1 = IERC20(token1).balanceOf(address(this));\n        // Determine current pool liquidity\n        LiquidityBalances memory lb = _getLiquidityBalances(total0, total1);\n        if (lb.pool0 == 0 || lb.pool1 == 0) {\n            revert InsufficientLiquidity();\n        }\n        if (lb.reservoir0 == 0) {\n            // If reservoir0 is empty then we're swapping amountOut0 for token1 from reservoir1\n            uint256 swappedReservoirAmount1;\n            (amountOut1, swappedReservoirAmount1) = PairMath.getSingleSidedBurnOutputAmountB(\n                _totalSupply, liquidityIn, total0, total1, movingAveragePrice0()\n            );\n            // Check there's enough reservoir liquidity to withdraw from\n            // If `amountOut1` exceeds reservoir1 then it will result in reservoir0 growing from excess token0\n            if (amountOut1 > lb.reservoir1) {\n                revert InsufficientReservoir();\n            }\n\n            uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool1);\n            if (swappedReservoirAmount1 > swappableReservoirLimit) {\n                revert SwappableReservoirExceeded();\n            }\n            _updateSwappableReservoirDeadline(lb.pool1, swappedReservoirAmount1);\n        } else {\n            // If reservoir0 isn't empty then we're swapping amountOut1 for token0 from reservoir0\n            uint256 swappedReservoirAmount0;\n            (amountOut0, swappedReservoirAmount0) = PairMath.getSingleSidedBurnOutputAmountA(\n                _totalSupply, liquidityIn, total0, total1, movingAveragePrice0()\n            );\n            // Check there's enough reservoir liquidity to withdraw from\n            // If `amountOut0` exceeds reservoir0 then it will result in reservoir1 growing from excess token1\n            if (amountOut0 > lb.reservoir0) {\n                revert InsufficientReservoir();\n            }\n\n            uint256 swappableReservoirLimit = _getSwappableReservoirLimit(lb.pool0);\n            if (swappedReservoirAmount0 > swappableReservoirLimit) {\n                revert SwappableReservoirExceeded();\n            }\n            _updateSwappableReservoirDeadline(lb.pool0, swappedReservoirAmount0);\n        }\n        _burn(msg.sender, liquidityIn);\n        if (amountOut0 > 0) {\n            SafeERC20.safeTransfer(IERC20(token0), to, amountOut0);\n        } else if (amountOut1 > 0) {\n            SafeERC20.safeTransfer(IERC20(token1), to, amountOut1);\n        } else {\n            revert InsufficientLiquidityBurned();\n        }\n        emit Burn(msg.sender, liquidityIn, amountOut0, amountOut1, to);\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function swap(uint256 amountIn0, uint256 amountIn1, uint256 amountOut0, uint256 amountOut1, address to)\n        external\n        lock\n        checkPaused\n    {\n        {\n            if (amountOut0 == 0 && amountOut1 == 0) {\n                revert InsufficientOutputAmount();\n            }\n            if (to == token0 || to == token1) {\n                revert InvalidRecipient();\n            }\n            uint256 total0 = IERC20(token0).balanceOf(address(this));\n            uint256 total1 = IERC20(token1).balanceOf(address(this));\n            // Determine current pool liquidity\n            LiquidityBalances memory lb = _getLiquidityBalances(total0, total1);\n            if (amountOut0 >= lb.pool0 || amountOut1 >= lb.pool1) {\n                revert InsufficientLiquidity();\n            }\n            // Transfer in the specified input\n            if (amountIn0 > 0) {\n                SafeERC20.safeTransferFrom(IERC20(token0), msg.sender, address(this), amountIn0);\n            }\n            if (amountIn1 > 0) {\n                SafeERC20.safeTransferFrom(IERC20(token1), msg.sender, address(this), amountIn1);\n            }\n            // Optimistically transfer output\n            if (amountOut0 > 0) {\n                SafeERC20.safeTransfer(IERC20(token0), to, amountOut0);\n            }\n            if (amountOut1 > 0) {\n                SafeERC20.safeTransfer(IERC20(token1), to, amountOut1);\n            }\n\n            // Refresh balances\n            total0 = IERC20(token0).balanceOf(address(this));\n            total1 = IERC20(token1).balanceOf(address(this));\n            // The reservoir balances must remain unchanged during a swap, so all balance changes impact the pool balances\n            uint256 pool0New = total0 - lb.reservoir0;\n            uint256 pool1New = total1 - lb.reservoir1;\n            if (pool0New == 0 || pool1New == 0) {\n                revert InvalidFinalPrice();\n            }\n            // Update to the actual amount of tokens the user sent in based on the delta between old and new pool balances\n            if (pool0New > lb.pool0) {\n                amountIn0 = pool0New - lb.pool0;\n                amountOut0 = 0;\n            } else {\n                amountIn0 = 0;\n                amountOut0 = lb.pool0 - pool0New;\n            }\n            if (pool1New > lb.pool1) {\n                amountIn1 = pool1New - lb.pool1;\n                amountOut1 = 0;\n            } else {\n                amountIn1 = 0;\n                amountOut1 = lb.pool1 - pool1New;\n            }\n            // If after accounting for input and output cancelling one another out, fee on transfer, etc there is no\n            //   input tokens in real terms then revert.\n            if (amountIn0 == 0 && amountIn1 == 0) {\n                revert InsufficientInputAmount();\n            }\n            uint256 pool0NewAdjusted = (pool0New * 1000) - (amountIn0 * 3);\n            uint256 pool1NewAdjusted = (pool1New * 1000) - (amountIn1 * 3);\n            // After account for 0.3% fees, the new K must not be less than the old K\n            if (pool0NewAdjusted * pool1NewAdjusted < (lb.pool0 * lb.pool1 * 1000 ** 2)) {\n                revert KInvariant();\n            }\n            // Update moving average before `_updatePriceCumulative` updates `blockTimestampLast` and the new `poolXLast` values are set\n            uint256 _movingAveragePrice0 = movingAveragePrice0();\n            movingAveragePrice0Last = _movingAveragePrice0;\n            _mintFee(lb.pool0, lb.pool1, pool0New, pool1New);\n            _updatePriceCumulative(lb.pool0, lb.pool1);\n            _updateSingleSidedTimelock(_movingAveragePrice0, uint112(pool0New), uint112(pool1New));\n            // Update Pair last swap price\n            pool0Last = uint112(pool0New);\n            pool1Last = uint112(pool1New);\n        }\n        emit Swap(msg.sender, amountIn0, amountIn1, amountOut0, amountOut1, to);\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function setMovingAverageWindow(uint32 newMovingAverageWindow) external onlyFactory {\n        movingAverageWindow = newMovingAverageWindow;\n        emit MovingAverageWindowUpdated(newMovingAverageWindow);\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function setMaxVolatilityBps(uint16 newMaxVolatilityBps) external onlyFactory {\n        maxVolatilityBps = newMaxVolatilityBps;\n        emit MaxVolatilityBpsUpdated(newMaxVolatilityBps);\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function setMinTimelockDuration(uint32 newMinTimelockDuration) external onlyFactory {\n        singleSidedTimelockDeadline =\n            uint120(Math.max(block.timestamp + uint256(minTimelockDuration), uint256(singleSidedTimelockDeadline)));\n        minTimelockDuration = newMinTimelockDuration;\n        emit MinTimelockDurationUpdated(newMinTimelockDuration);\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function setMaxTimelockDuration(uint32 newMaxTimelockDuration) external onlyFactory {\n        singleSidedTimelockDeadline =\n            uint120(Math.min(block.timestamp + uint256(newMaxTimelockDuration), uint256(singleSidedTimelockDeadline)));\n        maxTimelockDuration = newMaxTimelockDuration;\n        emit MaxTimelockDurationUpdated(newMaxTimelockDuration);\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function setMaxSwappableReservoirLimitBps(uint16 newMaxSwappableReservoirLimitBps) external onlyFactory {\n        maxSwappableReservoirLimitBps = newMaxSwappableReservoirLimitBps;\n        emit MaxSwappableReservoirLimitBpsUpdated(newMaxSwappableReservoirLimitBps);\n    }\n\n    /**\n     * @inheritdoc IButtonswapPair\n     */\n    function setSwappableReservoirGrowthWindow(uint32 newSwappableReservoirGrowthWindow) external onlyFactory {\n        uint256 oldSwappableReservoirLimitReachesMaxDeadline = uint256(swappableReservoirLimitReachesMaxDeadline);\n        if (oldSwappableReservoirLimitReachesMaxDeadline > block.timestamp) {\n            swappableReservoirLimitReachesMaxDeadline = uint120(\n                block.timestamp\n                    + (\n                        uint256(newSwappableReservoirGrowthWindow)\n                            * ((oldSwappableReservoirLimitReachesMaxDeadline - block.timestamp))\n                            / uint256(swappableReservoirGrowthWindow)\n                    )\n            );\n        }\n        swappableReservoirGrowthWindow = newSwappableReservoirGrowthWindow;\n        emit SwappableReservoirGrowthWindowUpdated(newSwappableReservoirGrowthWindow);\n    }\n}\n"
      },
      "src/interfaces/IButtonswapPair/IButtonswapPair.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\nimport {IButtonswapPairErrors} from \"./IButtonswapPairErrors.sol\";\nimport {IButtonswapPairEvents} from \"./IButtonswapPairEvents.sol\";\nimport {IButtonswapERC20} from \"../IButtonswapERC20/IButtonswapERC20.sol\";\n\ninterface IButtonswapPair is IButtonswapPairErrors, IButtonswapPairEvents, IButtonswapERC20 {\n    /**\n     * @notice The smallest value that {IButtonswapERC20-totalSupply} can be.\n     * @dev After the first mint the total liquidity (represented by the liquidity token total supply) can never drop below this value.\n     *\n     * 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.\n     * This results in future minters receiving no liquidity tokens when they deposit.\n     * By enforcing a minimum liquidity value this attack becomes prohibitively expensive to execute.\n     * @return MINIMUM_LIQUIDITY The MINIMUM_LIQUIDITY value\n     */\n    function MINIMUM_LIQUIDITY() external pure returns (uint256 MINIMUM_LIQUIDITY);\n\n    /**\n     * @notice The duration for which the moving average is calculated for.\n     * @return _movingAverageWindow The value of movingAverageWindow\n     */\n    function movingAverageWindow() external view returns (uint32 _movingAverageWindow);\n\n    /**\n     * @notice Updates the movingAverageWindow parameter of the pair.\n     * This can only be called by the Factory address.\n     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#movingaveragewindow) for more detail.\n     * @param newMovingAverageWindow The new value for movingAverageWindow\n     */\n    function setMovingAverageWindow(uint32 newMovingAverageWindow) external;\n\n    /**\n     * @notice Numerator (over 10_000) of the threshold when price volatility triggers maximum single-sided timelock duration.\n     * @return _maxVolatilityBps The value of maxVolatilityBps\n     */\n    function maxVolatilityBps() external view returns (uint16 _maxVolatilityBps);\n\n    /**\n     * @notice Updates the maxVolatilityBps parameter of the pair.\n     * This can only be called by the Factory address.\n     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#maxvolatilitybps) for more detail.\n     * @param newMaxVolatilityBps The new value for maxVolatilityBps\n     */\n    function setMaxVolatilityBps(uint16 newMaxVolatilityBps) external;\n\n    /**\n     * @notice How long the minimum singled-sided timelock lasts for.\n     * @return _minTimelockDuration The value of minTimelockDuration\n     */\n    function minTimelockDuration() external view returns (uint32 _minTimelockDuration);\n\n    /**\n     * @notice Updates the minTimelockDuration parameter of the pair.\n     * This can only be called by the Factory address.\n     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#mintimelockduration) for more detail.\n     * @param newMinTimelockDuration The new value for minTimelockDuration\n     */\n    function setMinTimelockDuration(uint32 newMinTimelockDuration) external;\n\n    /**\n     * @notice How long the maximum singled-sided timelock lasts for.\n     * @return _maxTimelockDuration The value of maxTimelockDuration\n     */\n    function maxTimelockDuration() external view returns (uint32 _maxTimelockDuration);\n\n    /**\n     * @notice Updates the maxTimelockDuration parameter of the pair.\n     * This can only be called by the Factory address.\n     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#maxtimelockduration) for more detail.\n     * @param newMaxTimelockDuration The new value for maxTimelockDuration\n     */\n    function setMaxTimelockDuration(uint32 newMaxTimelockDuration) external;\n\n    /**\n     * @notice Numerator (over 10_000) of the fraction of the pool balance that acts as the maximum limit on how much of the reservoir\n     * can be swapped in a given timeframe.\n     * @return _maxSwappableReservoirLimitBps The value of maxSwappableReservoirLimitBps\n     */\n    function maxSwappableReservoirLimitBps() external view returns (uint16 _maxSwappableReservoirLimitBps);\n\n    /**\n     * @notice Updates the maxSwappableReservoirLimitBps parameter of the pair.\n     * This can only be called by the Factory address.\n     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#maxswappablereservoirlimitbps) for more detail.\n     * @param newMaxSwappableReservoirLimitBps The new value for maxSwappableReservoirLimitBps\n     */\n    function setMaxSwappableReservoirLimitBps(uint16 newMaxSwappableReservoirLimitBps) external;\n\n    /**\n     * @notice How much time it takes for the swappable reservoir value to grow from nothing to its maximum value.\n     * @return _swappableReservoirGrowthWindow The value of swappableReservoirGrowthWindow\n     */\n    function swappableReservoirGrowthWindow() external view returns (uint32 _swappableReservoirGrowthWindow);\n\n    /**\n     * @notice Updates the swappableReservoirGrowthWindow parameter of the pair.\n     * This can only be called by the Factory address.\n     * Refer to [parameters.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/parameters.md#swappablereservoirgrowthwindow) for more detail.\n     * @param newSwappableReservoirGrowthWindow The new value for swappableReservoirGrowthWindow\n     */\n    function setSwappableReservoirGrowthWindow(uint32 newSwappableReservoirGrowthWindow) external;\n\n    /**\n     * @notice The address of the {ButtonswapFactory} instance used to create this Pair.\n     * @dev Set to `msg.sender` in the Pair constructor.\n     * @return factory The factory address\n     */\n    function factory() external view returns (address factory);\n\n    /**\n     * @notice The address of the first sorted token.\n     * @return token0 The token address\n     */\n    function token0() external view returns (address token0);\n\n    /**\n     * @notice The address of the second sorted token.\n     * @return token1 The token address\n     */\n    function token1() external view returns (address token1);\n\n    /**\n     * @notice The time-weighted average price of the Pair.\n     * The price is of `token0` in terms of `token1`.\n     * @dev The price is represented as a [UQ112x112](https://en.wikipedia.org/wiki/Q_(number_format)) to maintain precision.\n     * Consequently this value must be divided by `2^112` to get the actual price.\n     *\n     * Because of the time weighting, `price0CumulativeLast` must also be divided by the total Pair lifetime to get the average price over that time period.\n     * @return price0CumulativeLast The current cumulative `token0` price\n     */\n    function price0CumulativeLast() external view returns (uint256 price0CumulativeLast);\n\n    /**\n     * @notice The time-weighted average price of the Pair.\n     * The price is of `token1` in terms of `token0`.\n     * @dev The price is represented as a [UQ112x112](https://en.wikipedia.org/wiki/Q_(number_format)) to maintain precision.\n     * Consequently this value must be divided by `2^112` to get the actual price.\n     *\n     * Because of the time weighting, `price1CumulativeLast` must also be divided by the total Pair lifetime to get the average price over that time period.\n     * @return price1CumulativeLast The current cumulative `token1` price\n     */\n    function price1CumulativeLast() external view returns (uint256 price1CumulativeLast);\n\n    /**\n     * @notice The timestamp for when the single-sided timelock concludes.\n     * The timelock is initiated based on price volatility of swaps over the last `movingAverageWindow`, and can be\n     *   extended by new swaps if they are sufficiently volatile.\n     * The timelock protects against attempts to manipulate the price that is used to valuate the reservoir tokens during\n     *   single-sided operations.\n     * It also guards against general legitimate volatility, as it is preferable to defer single-sided operations until\n     *   it is clearer what the market considers the price to be.\n     * @return singleSidedTimelockDeadline The current deadline timestamp\n     */\n    function singleSidedTimelockDeadline() external view returns (uint120 singleSidedTimelockDeadline);\n\n    /**\n     * @notice The timestamp by which the amount of reservoir tokens that can be exchanged during a single-sided operation\n     *   reaches its maximum value.\n     * This maximum value is not necessarily the entirety of the reservoir, instead being calculated as a fraction of the\n     *   corresponding token's active liquidity.\n     * @return swappableReservoirLimitReachesMaxDeadline The current deadline timestamp\n     */\n    function swappableReservoirLimitReachesMaxDeadline()\n        external\n        view\n        returns (uint120 swappableReservoirLimitReachesMaxDeadline);\n\n    /**\n     * @notice Returns the current limit on the number of reservoir tokens that can be exchanged during a single-sided mint/burn operation.\n     * @return swappableReservoirLimit The amount of reservoir token that can be exchanged\n     */\n    function getSwappableReservoirLimit() external view returns (uint256 swappableReservoirLimit);\n\n    /**\n     * @notice Whether the Pair is currently paused\n     * @return _isPaused The paused state\n     */\n    function getIsPaused() external view returns (bool _isPaused);\n\n    /**\n     * @notice Updates the pause state.\n     * This can only be called by the Factory address.\n     * @param isPausedNew The new value for isPaused\n     */\n    function setIsPaused(bool isPausedNew) external;\n\n    /**\n     * @notice Get the current liquidity values.\n     * @return _pool0 The active `token0` liquidity\n     * @return _pool1 The active `token1` liquidity\n     * @return _reservoir0 The inactive `token0` liquidity\n     * @return _reservoir1 The inactive `token1` liquidity\n     * @return _blockTimestampLast The timestamp of when the price was last updated\n     */\n    function getLiquidityBalances()\n        external\n        view\n        returns (uint112 _pool0, uint112 _pool1, uint112 _reservoir0, uint112 _reservoir1, uint32 _blockTimestampLast);\n\n    /**\n     * @notice The current `movingAveragePrice0` value, based on the current block timestamp.\n     * @dev This is the `token0` price, time weighted to prevent manipulation.\n     * Refer to [reservoir-valuation.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/reservoir-valuation.md#price-stability) for more detail.\n     *\n     * The price is represented as a [UQ112x112](https://en.wikipedia.org/wiki/Q_(number_format)) to maintain precision.\n     * It is used to valuate the reservoir tokens that are exchanged during single-sided operations.\n     * @return _movingAveragePrice0 The current `movingAveragePrice0` value\n     */\n    function movingAveragePrice0() external view returns (uint256 _movingAveragePrice0);\n\n    /**\n     * @notice Mints new liquidity tokens to `to` based on `amountIn0` of `token0` and `amountIn1  of`token1` deposited.\n     * Expects both tokens to be deposited in a ratio that matches the current Pair price.\n     * @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.\n     * Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#dual-sided-mint) for more detail.\n     * @param amountIn0 The amount of `token0` that should be transferred in from the user\n     * @param amountIn1 The amount of `token1` that should be transferred in from the user\n     * @param to The account that receives the newly minted liquidity tokens\n     * @return liquidityOut THe amount of liquidity tokens minted\n     */\n    function mint(uint256 amountIn0, uint256 amountIn1, address to) external returns (uint256 liquidityOut);\n\n    /**\n     * @notice Mints new liquidity tokens to `to` based on how much `token0` or `token1` has been deposited.\n     * The token transferred is the one that the Pair does not have a non-zero inactive liquidity balance for.\n     * Expects only one token to be deposited, so that it can be paired with the other token's inactive liquidity.\n     * @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.\n     * Refer to [mint-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/mint-math.md#single-sided-mint) for more detail.\n     * @param amountIn The amount of tokens that should be transferred in from the user\n     * @param to The account that receives the newly minted liquidity tokens\n     * @return liquidityOut THe amount of liquidity tokens minted\n     */\n    function mintWithReservoir(uint256 amountIn, address to) external returns (uint256 liquidityOut);\n\n    /**\n     * @notice Burns `liquidityIn` liquidity tokens to redeem to `to` the corresponding `amountOut0` of `token0` and `amountOut1` of `token1`.\n     * @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.\n     * @param liquidityIn The amount of liquidity tokens to burn\n     * @param to The account that receives the redeemed tokens\n     * @return amountOut0 The amount of `token0` that the liquidity tokens are redeemed for\n     * @return amountOut1 The amount of `token1` that the liquidity tokens are redeemed for\n     */\n    function burn(uint256 liquidityIn, address to) external returns (uint256 amountOut0, uint256 amountOut1);\n\n    /**\n     * @notice Burns `liquidityIn` liquidity tokens to redeem to `to` the corresponding `amountOut0` of `token0` and `amountOut1` of `token1`.\n     * Only returns tokens from the non-zero inactive liquidity balance, meaning one of `amountOut0` and `amountOut1` will be zero.\n     * @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.\n     * @param liquidityIn The amount of liquidity tokens to burn\n     * @param to The account that receives the redeemed tokens\n     * @return amountOut0 The amount of `token0` that the liquidity tokens are redeemed for\n     * @return amountOut1 The amount of `token1` that the liquidity tokens are redeemed for\n     */\n    function burnFromReservoir(uint256 liquidityIn, address to)\n        external\n        returns (uint256 amountOut0, uint256 amountOut1);\n\n    /**\n     * @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`.\n     * The price of the swap is determined by maintaining the \"K Invariant\".\n     * A 0.3% fee is collected to distribute between liquidity providers and the protocol.\n     * @dev The token deposits are deduced to be the delta between the current Pair contract token balances and the last stored balances.\n     * 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.\n     * Refer to [swap-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/swap-math.md) for more detail.\n     * @param amountIn0 The amount of `token0` that the sender sends\n     * @param amountIn1 The amount of `token1` that the sender sends\n     * @param amountOut0 The amount of `token0` that the recipient receives\n     * @param amountOut1 The amount of `token1` that the recipient receives\n     * @param to The account that receives the swap output\n     */\n    function swap(uint256 amountIn0, uint256 amountIn1, uint256 amountOut0, uint256 amountOut1, address to) external;\n}\n"
      },
      "src/interfaces/IButtonswapERC20/IButtonswapERC20.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\nimport {IButtonswapERC20Errors} from \"./IButtonswapERC20Errors.sol\";\nimport {IButtonswapERC20Events} from \"./IButtonswapERC20Events.sol\";\n\ninterface IButtonswapERC20 is IButtonswapERC20Errors, IButtonswapERC20Events {\n    /**\n     * @notice Returns the name of the token.\n     * @return _name The token name\n     */\n    function name() external view returns (string memory _name);\n\n    /**\n     * @notice Returns the symbol of the token, usually a shorter version of the name.\n     * @return _symbol The token symbol\n     */\n    function symbol() external view returns (string memory _symbol);\n\n    /**\n     * @notice Returns the number of decimals used to get its user representation.\n     * For example, if `decimals` equals `2`, a balance of `505` tokens should be displayed to a user as `5.05` (`505 / 10 ** 2`).\n     * @dev This information is only used for _display_ purposes: it in no way affects any of the arithmetic of the contract.\n     * @return decimals The number of decimals\n     */\n    function decimals() external pure returns (uint8 decimals);\n\n    /**\n     * @notice Returns the amount of tokens in existence.\n     * @return totalSupply The amount of tokens in existence\n     */\n    function totalSupply() external view returns (uint256 totalSupply);\n\n    /**\n     * @notice Returns the amount of tokens owned by `account`.\n     * @param owner The account the balance is being checked for\n     * @return balance The amount of tokens owned by `owner`\n     */\n    function balanceOf(address owner) external view returns (uint256 balance);\n\n    /**\n     * @notice Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}.\n     * This is zero by default.\n     * This value changes when {approve} or {transferFrom} are called.\n     * @param owner The account that owns the tokens\n     * @param spender The account that can spend the tokens\n     * @return allowance The amount of tokens owned by `owner` that the `spender` can transfer\n     */\n    function allowance(address owner, address spender) external view returns (uint256 allowance);\n\n    /**\n     * @notice Sets `value` as the allowance of `spender` over the caller's tokens.\n     * @dev IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {IButtonswapERC20Events-Approval} event.\n     * @param spender The account that is granted permission to spend the tokens\n     * @param value The amount of tokens that can be spent\n     * @return success Whether the operation succeeded\n     */\n    function approve(address spender, uint256 value) external returns (bool success);\n\n    /**\n     * @notice Moves `value` tokens from the caller's account to `to`.\n     * @dev Emits a {IButtonswapERC20Events-Transfer} event.\n     * @param to The account that is receiving the tokens\n     * @param value The amount of tokens being sent\n     * @return success Whether the operation succeeded\n     */\n    function transfer(address to, uint256 value) external returns (bool success);\n\n    /**\n     * @notice Moves `value` tokens from `from` to `to` using the allowance mechanism.\n     * `value` is then deducted from the caller's allowance.\n     * @dev Emits a {IButtonswapERC20Events-Transfer} event.\n     * @param from The account that is sending the tokens\n     * @param to The account that is receiving the tokens\n     * @param value The amount of tokens being sent\n     * @return success Whether the operation succeeded\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n\n    /**\n     * @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).\n     * @return DOMAIN_SEPARATOR The `DOMAIN_SEPARATOR` value\n     */\n    function DOMAIN_SEPARATOR() external view returns (bytes32 DOMAIN_SEPARATOR);\n\n    /**\n     * @notice Returns the typehash used in the encoding of the signature for {permit}, as defined by [EIP712](https://eips.ethereum.org/EIPS/eip-712).\n     * @return PERMIT_TYPEHASH The `PERMIT_TYPEHASH` value\n     */\n    function PERMIT_TYPEHASH() external pure returns (bytes32 PERMIT_TYPEHASH);\n\n    /**\n     * @notice Returns the current nonce for `owner`.\n     * This value must be included whenever a signature is generated for {permit}.\n     * @dev Every successful call to {permit} increases `owner`'s nonce by one.\n     * This prevents a signature from being used multiple times.\n     * @param owner The account to get the nonce for\n     * @return nonce The current nonce for the given `owner`\n     */\n    function nonces(address owner) external view returns (uint256 nonce);\n\n    /**\n     * @notice Sets `value` as the allowance of `spender` over `owner`'s tokens, given `owner`'s signed approval.\n     * @dev IMPORTANT: The same issues {approve} has related to transaction ordering also apply here.\n     *\n     * Emits an {IButtonswapERC20Events-Approval} event.\n     *\n     * Requirements:\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` over the EIP712-formatted function arguments.\n     * - the signature must use `owner`'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the [relevant EIP section](https://eips.ethereum.org/EIPS/eip-2612#specification).\n     * @param owner The account that owns the tokens\n     * @param spender The account that can spend the tokens\n     * @param value The amount of `owner`'s tokens that `spender` can transfer\n     * @param deadline The future time after which the permit is no longer valid\n     * @param v Part of the signature\n     * @param r Part of the signature\n     * @param s Part of the signature\n     */\n    function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)\n        external;\n}\n"
      },
      "src/ButtonswapERC20.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\nimport {IButtonswapERC20} from \"./interfaces/IButtonswapERC20/IButtonswapERC20.sol\";\n\ncontract ButtonswapERC20 is IButtonswapERC20 {\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    uint8 public constant decimals = 18;\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    uint256 public totalSupply;\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    mapping(address => uint256) public balanceOf;\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    bytes32 public immutable DOMAIN_SEPARATOR;\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     * @dev Value should equal `0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9` but it\n     *   is recommended to verify this by checking the public method on-chain.\n     */\n    bytes32 public constant PERMIT_TYPEHASH =\n        keccak256(\"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\");\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    mapping(address => uint256) public nonces;\n\n    constructor() {\n        uint256 chainId;\n        assembly {\n            chainId := chainid()\n        }\n        DOMAIN_SEPARATOR = keccak256(\n            abi.encode(\n                keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n                keccak256(bytes(\"Buttonswap\")),\n                keccak256(bytes(\"1\")),\n                chainId,\n                address(this)\n            )\n        );\n    }\n\n    /**\n     * @dev Mints `value` tokens to `to`.\n     *\n     * Emits a {IButtonswapERC20Events-Transfer} event.\n     * @param to The account that is receiving the tokens\n     * @param value The amount of tokens being created\n     */\n    function _mint(address to, uint256 value) internal {\n        totalSupply = totalSupply + value;\n        balanceOf[to] = balanceOf[to] + value;\n        emit Transfer(address(0), to, value);\n    }\n\n    /**\n     * @dev Burns `value` tokens from `from`.\n     *\n     * Emits a {IButtonswapERC20Events-Transfer} event.\n     * @param from The account that is sending the tokens\n     * @param value The amount of tokens being destroyed\n     */\n    function _burn(address from, uint256 value) internal {\n        balanceOf[from] = balanceOf[from] - value;\n        totalSupply = totalSupply - value;\n        emit Transfer(from, address(0), value);\n    }\n\n    /**\n     * @dev Sets `value` as the allowance of `spender` over the caller's tokens.\n     *\n     * Emits a {IButtonswapERC20Events-Approval} event.\n     * @param owner The account whose tokens are being approved\n     * @param spender The account that is granted permission to spend the tokens\n     * @param value The amount of tokens that can be spent\n     */\n    function _approve(address owner, address spender, uint256 value) private {\n        allowance[owner][spender] = value;\n        emit Approval(owner, spender, value);\n    }\n\n    /**\n     * @dev Moves `value` tokens from `from` to `to`.\n     *\n     * Emits a {IButtonswapERC20Events-Transfer} event.\n     * @param from The account that is sending the tokens\n     * @param to The account that is receiving the tokens\n     * @param value The amount of tokens being sent\n     */\n    function _transfer(address from, address to, uint256 value) internal {\n        balanceOf[from] = balanceOf[from] - value;\n        balanceOf[to] = balanceOf[to] + value;\n        emit Transfer(from, to, value);\n    }\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    function name() external view virtual override returns (string memory _name) {\n        _name = \"Buttonswap\";\n    }\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    function symbol() external view virtual override returns (string memory _symbol) {\n        _symbol = \"BTNSWP\";\n    }\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    function approve(address spender, uint256 value) external returns (bool success) {\n        _approve(msg.sender, spender, value);\n        success = true;\n    }\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    function transfer(address to, uint256 value) external returns (bool success) {\n        _transfer(msg.sender, to, value);\n        success = true;\n    }\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool success) {\n        uint256 allowanceFromSender = allowance[from][msg.sender];\n        if (allowanceFromSender != type(uint256).max) {\n            _approve(from, msg.sender, allowanceFromSender - value);\n        }\n        _transfer(from, to, value);\n        success = true;\n    }\n\n    /**\n     * @inheritdoc IButtonswapERC20\n     */\n    function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)\n        external\n    {\n        if (block.timestamp > deadline) {\n            revert PermitExpired();\n        }\n        bytes32 digest = keccak256(\n            abi.encodePacked(\n                \"\\x19\\x01\",\n                DOMAIN_SEPARATOR,\n                keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))\n            )\n        );\n        address recoveredAddress = ecrecover(digest, v, r, s);\n        if (recoveredAddress == address(0) || recoveredAddress != owner) {\n            revert PermitInvalidSignature();\n        }\n        _approve(owner, spender, value);\n    }\n}\n"
      },
      "src/libraries/Math.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity ^0.8.13;\n\n// a library for performing various math operations\n\nlibrary Math {\n    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {\n        z = x < y ? x : y;\n    }\n\n    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {\n        z = x > y ? x : y;\n    }\n\n    // Borrowed implementation from solmate\n    // https://github.com/transmissions11/solmate/blob/2001af43aedb46fdc2335d2a7714fb2dae7cfcd1/src/utils/FixedPointMathLib.sol#L164\n    function sqrt(uint256 x) internal pure returns (uint256 z) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let y := x // We start y at x, which will help us make our initial estimate.\n\n            z := 181 // The \"correct\" value is 1, but this saves a multiplication later.\n\n            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad\n            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.\n\n            // We check y >= 2^(k + 8) but shift right by k bits\n            // each branch to ensure that if x >= 256, then y >= 256.\n            if iszero(lt(y, 0x10000000000000000000000000000000000)) {\n                y := shr(128, y)\n                z := shl(64, z)\n            }\n            if iszero(lt(y, 0x1000000000000000000)) {\n                y := shr(64, y)\n                z := shl(32, z)\n            }\n            if iszero(lt(y, 0x10000000000)) {\n                y := shr(32, y)\n                z := shl(16, z)\n            }\n            if iszero(lt(y, 0x1000000)) {\n                y := shr(16, y)\n                z := shl(8, z)\n            }\n\n            // Goal was to get z*z*y within a small factor of x. More iterations could\n            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).\n            // We ensured y >= 256 so that the relative difference between y and y+1 is small.\n            // That's not possible if x < 256 but we can just verify those cases exhaustively.\n\n            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.\n            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.\n            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.\n\n            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range\n            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.\n\n            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate\n            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.\n\n            // There is no overflow risk here since y < 2^136 after the first branch above.\n            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.\n\n            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n            z := shr(1, add(z, div(x, z)))\n\n            // If x+1 is a perfect square, the Babylonian method cycles between\n            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.\n            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division\n            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.\n            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.\n            z := sub(z, lt(div(x, z), z))\n        }\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n     * with further edits by Uniswap Labs also under MIT license.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod0 := mul(x, y)\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            require(denominator > prod1);\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n            // See https://cs.stackexchange.com/q/138556/92363.\n\n            // Does not overflow because the denominator cannot be zero at this stage in the function.\n            uint256 twos = denominator & (~denominator + 1);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n            // in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n}\n"
      },
      "src/libraries/PairMath.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\nimport {Math} from \"./Math.sol\";\n\nlibrary PairMath {\n    /// @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.\n    function getDualSidedMintLiquidityOutAmount(\n        uint256 totalLiquidity,\n        uint256 amountInA,\n        uint256 amountInB,\n        uint256 totalA,\n        uint256 totalB\n    ) internal pure returns (uint256 liquidityOut) {\n        liquidityOut = Math.min((totalLiquidity * amountInA) / totalA, (totalLiquidity * amountInB) / totalB);\n    }\n\n    /// @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.\n    function getSingleSidedMintLiquidityOutAmountA(\n        uint256 totalLiquidity,\n        uint256 mintAmountA,\n        uint256 totalA,\n        uint256 totalB,\n        uint256 movingAveragePriceA\n    ) internal pure returns (uint256 liquidityOut, uint256 swappedReservoirAmountB) {\n        // movingAveragePriceA is a UQ112x112 and so is a uint224 that needs to be divided by 2^112 after being multiplied.\n        // Here we risk `movingAveragePriceA * (totalA + mintAmountA)` overflowing since we multiple a uint224 by the sum\n        //   of two uint112s, however:\n        //   - `totalA + mintAmountA` don't exceed 2^112 without violating max pool size.\n        //   - 2^256/2^112 = 144 bits spare for movingAveragePriceA\n        //   - 2^144/2^112 = 2^32 is the maximum price ratio that can be expressed without overflowing\n        // Is 2^32 sufficient? Consider a pair with 1 WBTC (8 decimals) and 30,000 USDX (18 decimals)\n        // log2((30000*1e18)/1e8) = 48 and as such a greater price ratio that can be handled.\n        // Consequently we require a mulDiv that can handle phantom overflow.\n        uint256 tokenAToSwap =\n            (mintAmountA * totalB) / (Math.mulDiv(movingAveragePriceA, (totalA + mintAmountA), 2 ** 112) + totalB);\n        // Here we don't risk undesired overflow because if `tokenAToSwap * movingAveragePriceA` exceeded 2^256 then it\n        //   would necessarily mean `swappedReservoirAmountB` exceeded 2^112, which would result in breaking the poolX uint112 limits.\n        swappedReservoirAmountB = (tokenAToSwap * movingAveragePriceA) / 2 ** 112;\n        // Update totals to account for the fixed price swap\n        totalA += tokenAToSwap;\n        totalB -= swappedReservoirAmountB;\n        uint256 tokenARemaining = mintAmountA - tokenAToSwap;\n        liquidityOut =\n            getDualSidedMintLiquidityOutAmount(totalLiquidity, tokenARemaining, swappedReservoirAmountB, totalA, totalB);\n    }\n\n    /// @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.\n    function getSingleSidedMintLiquidityOutAmountB(\n        uint256 totalLiquidity,\n        uint256 mintAmountB,\n        uint256 totalA,\n        uint256 totalB,\n        uint256 movingAveragePriceA\n    ) internal pure returns (uint256 liquidityOut, uint256 swappedReservoirAmountA) {\n        // `movingAveragePriceA` is a UQ112x112 and so is a uint224 that needs to be divided by 2^112 after being multiplied.\n        // Here we need to use the inverse price however, which means we multiply the numerator by 2^112 and then divide that\n        //   by movingAveragePriceA to get the result, all without risk of overflow.\n        uint256 tokenBToSwap =\n            (mintAmountB * totalA) / (((2 ** 112 * (totalB + mintAmountB)) / movingAveragePriceA) + totalA);\n        // Inverse price so again we can use it without overflow risk\n        swappedReservoirAmountA = (tokenBToSwap * (2 ** 112)) / movingAveragePriceA;\n        // Update totals to account for the fixed price swap\n        totalA -= swappedReservoirAmountA;\n        totalB += tokenBToSwap;\n        uint256 tokenBRemaining = mintAmountB - tokenBToSwap;\n        liquidityOut =\n            getDualSidedMintLiquidityOutAmount(totalLiquidity, swappedReservoirAmountA, tokenBRemaining, totalA, totalB);\n    }\n\n    /// @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.\n    function getDualSidedBurnOutputAmounts(uint256 totalLiquidity, uint256 liquidityIn, uint256 totalA, uint256 totalB)\n        internal\n        pure\n        returns (uint256 amountOutA, uint256 amountOutB)\n    {\n        amountOutA = (totalA * liquidityIn) / totalLiquidity;\n        amountOutB = (totalB * liquidityIn) / totalLiquidity;\n    }\n\n    /// @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.\n    function getSingleSidedBurnOutputAmountA(\n        uint256 totalLiquidity,\n        uint256 liquidityIn,\n        uint256 totalA,\n        uint256 totalB,\n        uint256 movingAveragePriceA\n    ) internal pure returns (uint256 amountOutA, uint256 swappedReservoirAmountA) {\n        // Calculate what the liquidity is worth in terms of both tokens\n        uint256 amountOutB;\n        (amountOutA, amountOutB) = getDualSidedBurnOutputAmounts(totalLiquidity, liquidityIn, totalA, totalB);\n\n        // Here we need to use the inverse price however, which means we multiply the numerator by 2^112 and then divide that\n        //   by movingAveragePriceA to get the result, all without risk of overflow (because amountOutB must be less than 2*2^112)\n        swappedReservoirAmountA = (amountOutB * (2 ** 112)) / movingAveragePriceA;\n        amountOutA = amountOutA + swappedReservoirAmountA;\n    }\n\n    /// @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.\n    function getSingleSidedBurnOutputAmountB(\n        uint256 totalLiquidity,\n        uint256 liquidityIn,\n        uint256 totalA,\n        uint256 totalB,\n        uint256 movingAveragePriceA\n    ) internal pure returns (uint256 amountOutB, uint256 swappedReservoirAmountB) {\n        // Calculate what the liquidity is worth in terms of both tokens\n        uint256 amountOutA;\n        (amountOutA, amountOutB) = getDualSidedBurnOutputAmounts(totalLiquidity, liquidityIn, totalA, totalB);\n\n        // Whilst we appear to risk overflow here, the final `swappedReservoirAmountB` needs to be smaller than the reservoir\n        //   which soft-caps it at 2^112.\n        // As such, any combination of amountOutA and movingAveragePriceA that would overflow would violate the next\n        //   check anyway, and we can therefore safely ignore the overflow potential.\n        swappedReservoirAmountB = (amountOutA * movingAveragePriceA) / 2 ** 112;\n        amountOutB = amountOutB + swappedReservoirAmountB;\n    }\n\n    /// @dev Refer to [fee-math.md](https://github.com/buttonwood-protocol/buttonswap-core/blob/main/notes/fee-math.md) for more detail.\n    function getProtocolFeeLiquidityMinted(uint256 totalLiquidity, uint256 kLast, uint256 k)\n        internal\n        pure\n        returns (uint256 liquidityOut)\n    {\n        uint256 rootKLast = Math.sqrt(kLast);\n        uint256 rootK = Math.sqrt(k);\n        liquidityOut = (totalLiquidity * (rootK - rootKLast)) / ((5 * rootK) + rootKLast);\n    }\n}\n"
      },
      "src/libraries/UQ112x112.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\n// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))\n\n// range: [0, 2**112 - 1]\n// resolution: 1 / 2**112\n\nlibrary UQ112x112 {\n    uint224 constant Q112 = 2 ** 112;\n\n    // encode a uint112 as a UQ112x112\n    function encode(uint112 y) internal pure returns (uint224 z) {\n        z = uint224(y) * Q112; // never overflows\n    }\n\n    // divide a UQ112x112 by a uint112, returning a UQ112x112\n    function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {\n        z = x / uint224(y);\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 amount) external returns (bool);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../extensions/IERC20Permit.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(IERC20 token, address spender, uint256 value) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n        require(\n            (value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        unchecked {\n            uint256 oldAllowance = token.allowance(address(this), spender);\n            require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to\n     * 0 before setting it to a non-zero value.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));\n            _callOptionalReturn(token, approvalCall);\n        }\n    }\n\n    /**\n     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.\n     * Revert on invalid signature.\n     */\n    function safePermit(\n        IERC20Permit token,\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal {\n        uint256 nonceBefore = token.nonces(owner);\n        token.permit(owner, spender, value, deadline, v, r, s);\n        uint256 nonceAfter = token.nonces(owner);\n        require(nonceAfter == nonceBefore + 1, \"SafeERC20: permit did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        require(returndata.length == 0 || abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n        // and not revert is the subcall reverts.\n\n        (bool success, bytes memory returndata) = address(token).call(data);\n        return\n            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));\n    }\n}\n"
      },
      "src/interfaces/IButtonswapFactory/IButtonswapFactory.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\nimport {IButtonswapFactoryErrors} from \"./IButtonswapFactoryErrors.sol\";\nimport {IButtonswapFactoryEvents} from \"./IButtonswapFactoryEvents.sol\";\n\ninterface IButtonswapFactory is IButtonswapFactoryErrors, IButtonswapFactoryEvents {\n    /**\n     * @notice Returns the current address for `feeTo`.\n     * The owner of this address receives the protocol fee as it is collected over time.\n     * @return _feeTo The `feeTo` address\n     */\n    function feeTo() external view returns (address _feeTo);\n\n    /**\n     * @notice Returns the current address for `feeToSetter`.\n     * The owner of this address has the power to update both `feeToSetter` and `feeTo`.\n     * @return _feeToSetter The `feeToSetter` address\n     */\n    function feeToSetter() external view returns (address _feeToSetter);\n\n    /**\n     * @notice The name of the ERC20 liquidity token.\n     * @return _tokenName The `tokenName`\n     */\n    function tokenName() external view returns (string memory _tokenName);\n\n    /**\n     * @notice The symbol of the ERC20 liquidity token.\n     * @return _tokenSymbol The `tokenSymbol`\n     */\n    function tokenSymbol() external view returns (string memory _tokenSymbol);\n\n    /**\n     * @notice Returns the current state of restricted creation.\n     * If true, then no new pairs, only feeToSetter can create new pairs\n     * @return _isCreationRestricted The `isCreationRestricted` state\n     */\n    function isCreationRestricted() external view returns (bool _isCreationRestricted);\n\n    /**\n     * @notice Returns the current address for `isCreationRestrictedSetter`.\n     * The owner of this address has the power to update both `isCreationRestrictedSetter` and `isCreationRestricted`.\n     * @return _isCreationRestrictedSetter The `isCreationRestrictedSetter` address\n     */\n    function isCreationRestrictedSetter() external view returns (address _isCreationRestrictedSetter);\n\n    /**\n     * @notice Get the (unique) Pair address created for the given combination of `tokenA` and `tokenB`.\n     * If the Pair does not exist then zero address is returned.\n     * @param tokenA The first unsorted token\n     * @param tokenB The second unsorted token\n     * @return pair The address of the Pair instance\n     */\n    function getPair(address tokenA, address tokenB) external view returns (address pair);\n\n    /**\n     * @notice Get the Pair address at the given `index`, ordered chronologically.\n     * @param index The index to query\n     * @return pair The address of the Pair created at the given `index`\n     */\n    function allPairs(uint256 index) external view returns (address pair);\n\n    /**\n     * @notice Get the current total number of Pairs created\n     * @return count The total number of Pairs created\n     */\n    function allPairsLength() external view returns (uint256 count);\n\n    /**\n     * @notice Creates a new {ButtonswapPair} instance for the given unsorted tokens `tokenA` and `tokenB`.\n     * @dev The tokens are sorted later, but can be provided to this method in either order.\n     * @param tokenA The first unsorted token address\n     * @param tokenB The second unsorted token address\n     * @return pair The address of the new {ButtonswapPair} instance\n     */\n    function createPair(address tokenA, address tokenB) external returns (address pair);\n\n    /**\n     * @notice Updates the address that receives the protocol fee.\n     * This can only be called by the `feeToSetter` address.\n     * @param _feeTo The new address\n     */\n    function setFeeTo(address _feeTo) external;\n\n    /**\n     * @notice Updates the address that has the power to set the `feeToSetter` and `feeTo` addresses.\n     * This can only be called by the `feeToSetter` address.\n     * @param _feeToSetter The new address\n     */\n    function setFeeToSetter(address _feeToSetter) external;\n\n    /**\n     * @notice Updates the state of restricted creation.\n     * This can only be called by the `feeToSetter` address.\n     * @param _isCreationRestricted The new state\n     */\n    function setIsCreationRestricted(bool _isCreationRestricted) external;\n\n    /**\n     * @notice Updates the address that has the power to set the `isCreationRestrictedSetter` and `isCreationRestricted`.\n     * This can only be called by the `isCreationRestrictedSetter` address.\n     * @param _isCreationRestrictedSetter The new address\n     */\n    function setIsCreationRestrictedSetter(address _isCreationRestrictedSetter) external;\n\n    /**\n     * @notice Returns the current address for `isPausedSetter`.\n     * The owner of this address has the power to update both `isPausedSetter` and call `setIsPaused`.\n     * @return _isPausedSetter The `isPausedSetter` address\n     */\n    function isPausedSetter() external view returns (address _isPausedSetter);\n\n    /**\n     * @notice Updates the address that has the power to set the `isPausedSetter` and call `setIsPaused`.\n     * This can only be called by the `isPausedSetter` address.\n     * @param _isPausedSetter The new address\n     */\n    function setIsPausedSetter(address _isPausedSetter) external;\n\n    /**\n     * @notice Updates the pause state of given Pairs.\n     * This can only be called by the `feeToSetter` address.\n     * @param pairs A list of addresses for the pairs that should be updated\n     * @param isPausedNew The new pause state\n     */\n    function setIsPaused(address[] calldata pairs, bool isPausedNew) external;\n\n    /**\n     * @notice Returns the current address for `paramSetter`.\n     * The owner of this address has the power to update `paramSetter`, default parameters, and current parameters on existing pairs\n     * @return _paramSetter The `paramSetter` address\n     */\n    function paramSetter() external view returns (address _paramSetter);\n\n    /**\n     * @notice Updates the address that has the power to set the `paramSetter` and update the default params.\n     * This can only be called by the `paramSetter` address.\n     * @param _paramSetter The new address\n     */\n    function setParamSetter(address _paramSetter) external;\n\n    /**\n     * @notice Returns the default value of `movingAverageWindow` used for new pairs.\n     * @return _defaultMovingAverageWindow The `defaultMovingAverageWindow` value\n     */\n    function defaultMovingAverageWindow() external view returns (uint32 _defaultMovingAverageWindow);\n\n    /**\n     * @notice Returns the default value of `maxVolatilityBps` used for new pairs.\n     * @return _defaultMaxVolatilityBps The `defaultMaxVolatilityBps` value\n     */\n    function defaultMaxVolatilityBps() external view returns (uint16 _defaultMaxVolatilityBps);\n\n    /**\n     * @notice Returns the default value of `minTimelockDuration` used for new pairs.\n     * @return _defaultMinTimelockDuration The `defaultMinTimelockDuration` value\n     */\n    function defaultMinTimelockDuration() external view returns (uint32 _defaultMinTimelockDuration);\n\n    /**\n     * @notice Returns the default value of `maxTimelockDuration` used for new pairs.\n     * @return _defaultMaxTimelockDuration The `defaultMaxTimelockDuration` value\n     */\n    function defaultMaxTimelockDuration() external view returns (uint32 _defaultMaxTimelockDuration);\n\n    /**\n     * @notice Returns the default value of `maxSwappableReservoirLimitBps` used for new pairs.\n     * @return _defaultMaxSwappableReservoirLimitBps The `defaultMaxSwappableReservoirLimitBps` value\n     */\n    function defaultMaxSwappableReservoirLimitBps()\n        external\n        view\n        returns (uint16 _defaultMaxSwappableReservoirLimitBps);\n\n    /**\n     * @notice Returns the default value of `swappableReservoirGrowthWindow` used for new pairs.\n     * @return _defaultSwappableReservoirGrowthWindow The `defaultSwappableReservoirGrowthWindow` value\n     */\n    function defaultSwappableReservoirGrowthWindow()\n        external\n        view\n        returns (uint32 _defaultSwappableReservoirGrowthWindow);\n\n    /**\n     * @notice Updates the default parameters used for new pairs.\n     * This can only be called by the `paramSetter` address.\n     * @param newDefaultMovingAverageWindow The new defaultMovingAverageWindow\n     * @param newDefaultMaxVolatilityBps The new defaultMaxVolatilityBps\n     * @param newDefaultMinTimelockDuration The new defaultMinTimelockDuration\n     * @param newDefaultMaxTimelockDuration The new defaultMaxTimelockDuration\n     * @param newDefaultMaxSwappableReservoirLimitBps The new defaultMaxSwappableReservoirLimitBps\n     * @param newDefaultSwappableReservoirGrowthWindow The new defaultSwappableReservoirGrowthWindow\n     */\n    function setDefaultParameters(\n        uint32 newDefaultMovingAverageWindow,\n        uint16 newDefaultMaxVolatilityBps,\n        uint32 newDefaultMinTimelockDuration,\n        uint32 newDefaultMaxTimelockDuration,\n        uint16 newDefaultMaxSwappableReservoirLimitBps,\n        uint32 newDefaultSwappableReservoirGrowthWindow\n    ) external;\n\n    /**\n     * @notice Updates the `movingAverageWindow` value of given Pairs.\n     * This can only be called by the `paramSetter` address.\n     * @param pairs A list of addresses for the pairs that should be updated\n     * @param newMovingAverageWindow The new `movingAverageWindow` value\n     */\n    function setMovingAverageWindow(address[] calldata pairs, uint32 newMovingAverageWindow) external;\n\n    /**\n     * @notice Updates the `maxVolatilityBps` value of given Pairs.\n     * This can only be called by the `paramSetter` address.\n     * @param pairs A list of addresses for the pairs that should be updated\n     * @param newMaxVolatilityBps The new `maxVolatilityBps` value\n     */\n    function setMaxVolatilityBps(address[] calldata pairs, uint16 newMaxVolatilityBps) external;\n\n    /**\n     * @notice Updates the `minTimelockDuration` value of given Pairs.\n     * This can only be called by the `paramSetter` address.\n     * @param pairs A list of addresses for the pairs that should be updated\n     * @param newMinTimelockDuration The new `minTimelockDuration` value\n     */\n    function setMinTimelockDuration(address[] calldata pairs, uint32 newMinTimelockDuration) external;\n\n    /**\n     * @notice Updates the `maxTimelockDuration` value of given Pairs.\n     * This can only be called by the `paramSetter` address.\n     * @param pairs A list of addresses for the pairs that should be updated\n     * @param newMaxTimelockDuration The new `maxTimelockDuration` value\n     */\n    function setMaxTimelockDuration(address[] calldata pairs, uint32 newMaxTimelockDuration) external;\n\n    /**\n     * @notice Updates the `maxSwappableReservoirLimitBps` value of given Pairs.\n     * This can only be called by the `paramSetter` address.\n     * @param pairs A list of addresses for the pairs that should be updated\n     * @param newMaxSwappableReservoirLimitBps The new `maxSwappableReservoirLimitBps` value\n     */\n    function setMaxSwappableReservoirLimitBps(address[] calldata pairs, uint16 newMaxSwappableReservoirLimitBps)\n        external;\n\n    /**\n     * @notice Updates the `swappableReservoirGrowthWindow` value of given Pairs.\n     * This can only be called by the `paramSetter` address.\n     * @param pairs A list of addresses for the pairs that should be updated\n     * @param newSwappableReservoirGrowthWindow The new `swappableReservoirGrowthWindow` value\n     */\n    function setSwappableReservoirGrowthWindow(address[] calldata pairs, uint32 newSwappableReservoirGrowthWindow)\n        external;\n\n    /**\n     * @notice Returns the last token pair created and the parameters used.\n     * @return token0 The first token address\n     * @return token1 The second token address\n     * @return movingAverageWindow The moving average window\n     * @return maxVolatilityBps The max volatility bps\n     * @return minTimelockDuration The minimum time lock duration\n     * @return maxTimelockDuration The maximum time lock duration\n     * @return maxSwappableReservoirLimitBps The max swappable reservoir limit bps\n     * @return swappableReservoirGrowthWindow The swappable reservoir growth window\n     */\n    function lastCreatedTokensAndParameters()\n        external\n        returns (\n            address token0,\n            address token1,\n            uint32 movingAverageWindow,\n            uint16 maxVolatilityBps,\n            uint32 minTimelockDuration,\n            uint32 maxTimelockDuration,\n            uint16 maxSwappableReservoirLimitBps,\n            uint32 swappableReservoirGrowthWindow\n        );\n}\n"
      },
      "src/interfaces/IButtonswapPair/IButtonswapPairErrors.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\nimport {IButtonswapERC20Errors} from \"../IButtonswapERC20/IButtonswapERC20Errors.sol\";\n\ninterface IButtonswapPairErrors is IButtonswapERC20Errors {\n    /**\n     * @notice Re-entrancy guard prevented method call\n     */\n    error Locked();\n\n    /**\n     * @notice User does not have permission for the attempted operation\n     */\n    error Forbidden();\n\n    /**\n     * @notice Integer maximums exceeded\n     */\n    error Overflow();\n\n    /**\n     * @notice Initial deposit not yet made\n     */\n    error Uninitialized();\n\n    /**\n     * @notice There was not enough liquidity in the reservoir\n     */\n    error InsufficientReservoir();\n\n    /**\n     * @notice Not enough liquidity was created during mint\n     */\n    error InsufficientLiquidityMinted();\n\n    /**\n     * @notice Not enough funds added to mint new liquidity\n     */\n    error InsufficientLiquidityAdded();\n\n    /**\n     * @notice More liquidity must be burned to be redeemed for non-zero amounts\n     */\n    error InsufficientLiquidityBurned();\n\n    /**\n     * @notice Swap was attempted with zero input\n     */\n    error InsufficientInputAmount();\n\n    /**\n     * @notice Swap was attempted with zero output\n     */\n    error InsufficientOutputAmount();\n\n    /**\n     * @notice Pool doesn't have the liquidity to service the swap\n     */\n    error InsufficientLiquidity();\n\n    /**\n     * @notice The specified \"to\" address is invalid\n     */\n    error InvalidRecipient();\n\n    /**\n     * @notice The product of pool balances must not change during a swap (save for accounting for fees)\n     */\n    error KInvariant();\n\n    /**\n     * @notice The new price ratio after a swap is invalid (one or more of the price terms are zero)\n     */\n    error InvalidFinalPrice();\n\n    /**\n     * @notice Single sided operations are not executable at this point in time\n     */\n    error SingleSidedTimelock();\n\n    /**\n     * @notice The attempted operation would have swapped reservoir tokens above the current limit\n     */\n    error SwappableReservoirExceeded();\n\n    /**\n     * @notice All operations on the pair other than dual-sided burning are currently paused\n     */\n    error Paused();\n}\n"
      },
      "src/interfaces/IButtonswapPair/IButtonswapPairEvents.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\nimport {IButtonswapERC20Events} from \"../IButtonswapERC20/IButtonswapERC20Events.sol\";\n\ninterface IButtonswapPairEvents is IButtonswapERC20Events {\n    /**\n     * @notice Emitted when a {IButtonswapPair-mint} is performed.\n     * Some `token0` and `token1` are deposited in exchange for liquidity tokens representing a claim on them.\n     * @param from The account that supplied the tokens for the mint\n     * @param amount0 The amount of `token0` that was deposited\n     * @param amount1 The amount of `token1` that was deposited\n     * @param amountOut The amount of liquidity tokens that were minted\n     * @param to The account that received the tokens from the mint\n     */\n    event Mint(address indexed from, uint256 amount0, uint256 amount1, uint256 amountOut, address indexed to);\n\n    /**\n     * @notice Emitted when a {IButtonswapPair-burn} is performed.\n     * Liquidity tokens are redeemed for underlying `token0` and `token1`.\n     * @param from The account that supplied the tokens for the burn\n     * @param amountIn The amount of liquidity tokens that were burned\n     * @param amount0 The amount of `token0` that was received\n     * @param amount1 The amount of `token1` that was received\n     * @param to The account that received the tokens from the burn\n     */\n    event Burn(address indexed from, uint256 amountIn, uint256 amount0, uint256 amount1, address indexed to);\n\n    /**\n     * @notice Emitted when a {IButtonswapPair-swap} is performed.\n     * @param from The account that supplied the tokens for the swap\n     * @param amount0In The amount of `token0` that went into the swap\n     * @param amount1In The amount of `token1` that went into the swap\n     * @param amount0Out The amount of `token0` that came out of the swap\n     * @param amount1Out The amount of `token1` that came out of the swap\n     * @param to The account that received the tokens from the swap\n     */\n    event Swap(\n        address indexed from,\n        uint256 amount0In,\n        uint256 amount1In,\n        uint256 amount0Out,\n        uint256 amount1Out,\n        address indexed to\n    );\n\n    /**\n     * @notice Emitted when the movingAverageWindow parameter for the pair has been updated.\n     * @param newMovingAverageWindow The new movingAverageWindow value\n     */\n    event MovingAverageWindowUpdated(uint32 newMovingAverageWindow);\n\n    /**\n     * @notice Emitted when the maxVolatilityBps parameter for the pair has been updated.\n     * @param newMaxVolatilityBps The new maxVolatilityBps value\n     */\n    event MaxVolatilityBpsUpdated(uint16 newMaxVolatilityBps);\n\n    /**\n     * @notice Emitted when the minTimelockDuration parameter for the pair has been updated.\n     * @param newMinTimelockDuration The new minTimelockDuration value\n     */\n    event MinTimelockDurationUpdated(uint32 newMinTimelockDuration);\n\n    /**\n     * @notice Emitted when the maxTimelockDuration parameter for the pair has been updated.\n     * @param newMaxTimelockDuration The new maxTimelockDuration value\n     */\n    event MaxTimelockDurationUpdated(uint32 newMaxTimelockDuration);\n\n    /**\n     * @notice Emitted when the maxSwappableReservoirLimitBps parameter for the pair has been updated.\n     * @param newMaxSwappableReservoirLimitBps The new maxSwappableReservoirLimitBps value\n     */\n    event MaxSwappableReservoirLimitBpsUpdated(uint16 newMaxSwappableReservoirLimitBps);\n\n    /**\n     * @notice Emitted when the swappableReservoirGrowthWindow parameter for the pair has been updated.\n     * @param newSwappableReservoirGrowthWindow The new swappableReservoirGrowthWindow value\n     */\n    event SwappableReservoirGrowthWindowUpdated(uint32 newSwappableReservoirGrowthWindow);\n}\n"
      },
      "src/interfaces/IButtonswapERC20/IButtonswapERC20Errors.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\ninterface IButtonswapERC20Errors {\n    /**\n     * @notice Permit deadline was exceeded\n     */\n    error PermitExpired();\n\n    /**\n     * @notice Permit signature invalid\n     */\n    error PermitInvalidSignature();\n}\n"
      },
      "src/interfaces/IButtonswapERC20/IButtonswapERC20Events.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\ninterface IButtonswapERC20Events {\n    /**\n     * @notice Emitted when the allowance of a `spender` for an `owner` is set by a call to {IButtonswapERC20-approve}.\n     * `value` is the new allowance.\n     * @param owner The account that has granted approval\n     * @param spender The account that has been given approval\n     * @param value The amount the spender can transfer\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @notice Emitted when `value` tokens are moved from one account (`from`) to another (`to`).\n     * @param from The account that sent the tokens\n     * @param to The account that received the tokens\n     * @param value The amount of tokens transferred\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     *\n     * Furthermore, `isContract` will also return true if the target contract within\n     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,\n     * which only has an effect at the end of a transaction.\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"
      },
      "src/interfaces/IButtonswapFactory/IButtonswapFactoryErrors.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\ninterface IButtonswapFactoryErrors {\n    /**\n     * @notice The given token addresses are the same\n     */\n    error TokenIdenticalAddress();\n\n    /**\n     * @notice The given token address is the zero address\n     */\n    error TokenZeroAddress();\n\n    /**\n     * @notice The given tokens already have a {ButtonswapPair} instance\n     */\n    error PairExists();\n\n    /**\n     * @notice User does not have permission for the attempted operation\n     */\n    error Forbidden();\n\n    /**\n     * @notice There was an attempt to update a parameter to an invalid value\n     */\n    error InvalidParameter();\n}\n"
      },
      "src/interfaces/IButtonswapFactory/IButtonswapFactoryEvents.sol": {
        "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.8.13;\n\ninterface IButtonswapFactoryEvents {\n    /**\n     * @notice Emitted when a new Pair is created.\n     * @param token0 The first sorted token\n     * @param token1 The second sorted token\n     * @param pair The address of the new {ButtonswapPair} contract\n     * @param count The new total number of Pairs created\n     */\n    event PairCreated(address indexed token0, address indexed token1, address pair, uint256 count);\n\n    /**\n     * @notice Emitted when the default parameters for a new pair have been updated.\n     * @param paramSetter The address that changed the parameters\n     * @param newDefaultMovingAverageWindow The new movingAverageWindow default value\n     * @param newDefaultMaxVolatilityBps The new maxVolatilityBps default value\n     * @param newDefaultMinTimelockDuration The new minTimelockDuration default value\n     * @param newDefaultMaxTimelockDuration The new maxTimelockDuration default value\n     * @param newDefaultMaxSwappableReservoirLimitBps The new maxSwappableReservoirLimitBps default value\n     * @param newDefaultSwappableReservoirGrowthWindow The new swappableReservoirGrowthWindow default value\n     */\n    event DefaultParametersUpdated(\n        address indexed paramSetter,\n        uint32 newDefaultMovingAverageWindow,\n        uint16 newDefaultMaxVolatilityBps,\n        uint32 newDefaultMinTimelockDuration,\n        uint32 newDefaultMaxTimelockDuration,\n        uint16 newDefaultMaxSwappableReservoirLimitBps,\n        uint32 newDefaultSwappableReservoirGrowthWindow\n    );\n}\n"
      }
    },
    "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": {}
    }
  }}