ETH Price: $2,985.15 (+1.06%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Remove Liquidity238199662025-11-17 16:05:4732 days ago1763395547IN
0x0000000C...6EeC0cE7d
0 ETH0.000036970.94597171
Remove Liquidity238199572025-11-17 16:03:5932 days ago1763395439IN
0x0000000C...6EeC0cE7d
0 ETH0.000026330.90160858
Remove Liquidity238198242025-11-17 15:37:1132 days ago1763393831IN
0x0000000C...6EeC0cE7d
0 ETH0.000031570.85894725
Remove Liquidity236204642025-10-20 18:01:4760 days ago1760983307IN
0x0000000C...6EeC0cE7d
0 ETH0.000026910.10879072
Remove Liquidity236204342025-10-20 17:55:4760 days ago1760982947IN
0x0000000C...6EeC0cE7d
0 ETH0.00002720.10421401
Remove Liquidity236203232025-10-20 17:33:2360 days ago1760981603IN
0x0000000C...6EeC0cE7d
0 ETH0.000026560.10172513
Remove Liquidity233999442025-09-19 21:55:5991 days ago1758318959IN
0x0000000C...6EeC0cE7d
0 ETH0.000074230.30004034
Remove Liquidity232627002025-08-31 17:44:23110 days ago1756662263IN
0x0000000C...6EeC0cE7d
0 ETH0.000010110.35520736
Remove Liquidity232398582025-08-28 13:17:23113 days ago1756387043IN
0x0000000C...6EeC0cE7d
0 ETH0.000093140.37643479
Remove Liquidity232034772025-08-23 11:25:59118 days ago1755948359IN
0x0000000C...6EeC0cE7d
0 ETH0.000079760.30548777
Remove Liquidity231036472025-08-09 12:58:23132 days ago1754744303IN
0x0000000C...6EeC0cE7d
0 ETH0.000239580.96831352
Remove Liquidity230634732025-08-03 22:17:59138 days ago1754259479IN
0x0000000C...6EeC0cE7d
0 ETH0.000006350.20207554
Remove Liquidity230614222025-08-03 15:26:23138 days ago1754234783IN
0x0000000C...6EeC0cE7d
0 ETH0.000058690.25115783
Remove Liquidity230613192025-08-03 15:05:23138 days ago1754233523IN
0x0000000C...6EeC0cE7d
0 ETH0.000066820.27012265
Remove Liquidity230031592025-07-26 11:55:35146 days ago1753530935IN
0x0000000C...6EeC0cE7d
0 ETH0.000582722.23181624
Remove Liquidity229899552025-07-24 15:37:47148 days ago1753371467IN
0x0000000C...6EeC0cE7d
0 ETH0.000823163.32698598
Remove Liquidity229438512025-07-18 4:58:59155 days ago1752814739IN
0x0000000C...6EeC0cE7d
0 ETH0.00100663.85525986
Remove Liquidity227806912025-06-25 9:54:59177 days ago1750845299IN
0x0000000C...6EeC0cE7d
0 ETH0.001617876.19571139
Remove Liquidity227806652025-06-25 9:49:47177 days ago1750844987IN
0x0000000C...6EeC0cE7d
0 ETH0.000231136.3317387
Remove Liquidity227565622025-06-22 0:58:59181 days ago1750553939IN
0x0000000C...6EeC0cE7d
0 ETH0.000298921.20817301
Remove Liquidity226603702025-06-08 14:09:47194 days ago1749391787IN
0x0000000C...6EeC0cE7d
0 ETH0.000331961.34171474
Remove Liquidity226583202025-06-08 7:16:23195 days ago1749366983IN
0x0000000C...6EeC0cE7d
0 ETH0.000139120.5328279
Remove Liquidity226515662025-06-07 8:38:23196 days ago1749285503IN
0x0000000C...6EeC0cE7d
0 ETH0.000359991.37876445
Remove Liquidity226509202025-06-07 6:28:35196 days ago1749277715IN
0x0000000C...6EeC0cE7d
0 ETH0.000308851.18288023
Remove Liquidity226506172025-06-07 5:27:59196 days ago1749274079IN
0x0000000C...6EeC0cE7d
0 ETH0.000274991.11145502
View all transactions

Latest 13 internal transactions

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer207427042024-09-13 15:51:47462 days ago1726242707
0x0000000C...6EeC0cE7d
0.00046056 ETH
Deposit207427022024-09-13 15:51:23462 days ago1726242683
0x0000000C...6EeC0cE7d
4.99953943 ETH
Transfer207426802024-09-13 15:46:59462 days ago1726242419
0x0000000C...6EeC0cE7d
0.01 ETH
Transfer207426732024-09-13 15:45:35462 days ago1726242335
0x0000000C...6EeC0cE7d
0.05709122 ETH
Deposit207426732024-09-13 15:45:35462 days ago1726242335
0x0000000C...6EeC0cE7d
4.94290877 ETH
Transfer207426522024-09-13 15:41:23462 days ago1726242083
0x0000000C...6EeC0cE7d
0.06539437 ETH
Deposit207426512024-09-13 15:41:11462 days ago1726242071
0x0000000C...6EeC0cE7d
4.93460562 ETH
Transfer207426422024-09-13 15:39:11462 days ago1726241951
0x0000000C...6EeC0cE7d
0.00136698 ETH
Deposit207426412024-09-13 15:38:59462 days ago1726241939
0x0000000C...6EeC0cE7d
0.09863301 ETH
Transfer207426352024-09-13 15:37:47462 days ago1726241867
0x0000000C...6EeC0cE7d
0.01 ETH
Transfer207426302024-09-13 15:36:47462 days ago1726241807
0x0000000C...6EeC0cE7d
0.00195354 ETH
Deposit207426292024-09-13 15:36:35462 days ago1726241795
0x0000000C...6EeC0cE7d
0.09804645 ETH
0x60e06040202121692024-07-01 14:09:11536 days ago1719842951  Contract Creation0 ETH
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
NapierRouter

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 500 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;

// external interfaces
import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";
import {IWETH9} from "@napier/v1-tranche/src/interfaces/IWETH9.sol";
import {ITranche} from "@napier/v1-tranche/src/interfaces/ITranche.sol";
import {IBaseAdapter} from "@napier/v1-tranche/src/interfaces/IBaseAdapter.sol";
import {CurveTricryptoOptimizedWETH} from "./interfaces/external/CurveTricryptoOptimizedWETH.sol";
import {INapierPool} from "./interfaces/INapierPool.sol";
import {IPoolFactory} from "./interfaces/IPoolFactory.sol";
// implements
import {INapierRouter} from "./interfaces/INapierRouter.sol";
import {INapierMintCallback} from "./interfaces/INapierMintCallback.sol";
import {INapierSwapCallback} from "./interfaces/INapierSwapCallback.sol";

// libraries
import {SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";
import {CallbackDataTypes, CallbackType} from "./libs/CallbackDataTypes.sol";
import {PoolAddress} from "./libs/PoolAddress.sol";
import {ApproxParams} from "./interfaces/ApproxParams.sol";
import {TrancheMathHelper} from "./libs/TrancheMathHelper.sol";
import {Errors} from "./libs/Errors.sol";

// inherits
import {ReentrancyGuard} from "@openzeppelin/[email protected]/security/ReentrancyGuard.sol";
import {PeripheryImmutableState} from "./base/PeripheryImmutableState.sol";
import {PeripheryPayments} from "./base/PeripheryPayments.sol";
import {Multicallable} from "./base/Multicallable.sol";

/// @notice Router for Napier pools
/// @dev This contract provides a single entry point for Napier pools. Accepts native ETH.
/// @dev Multicallable is used to batch multiple operations. E.g. Swap Principal Tokens for WETH and unwrap WETH to ETH with a single transaction.
/// See each function for more details.
contract NapierRouter is
    INapierRouter,
    INapierSwapCallback,
    INapierMintCallback,
    PeripheryPayments,
    ReentrancyGuard,
    Multicallable
{
    using SafeERC20 for IERC20;
    using SafeERC20 for ITranche;

    /// @notice Napier Pool Factory
    /// @dev pool passed as functions parameter must be deployed by this factory
    IPoolFactory public immutable factory;

    bytes32 internal immutable POOL_CREATION_HASH;

    /// @notice If the transaction is too old, revert.
    /// @param deadline Transaction deadline in unix timestamp
    modifier checkDeadline(uint256 deadline) {
        if (block.timestamp > deadline) revert Errors.RouterTransactionTooOld();
        _;
    }

    constructor(IPoolFactory _factory, IWETH9 _WETH9) PeripheryImmutableState(_WETH9) {
        factory = _factory;
        POOL_CREATION_HASH = _factory.POOL_CREATION_HASH();
    }

    /// @dev Revert if `msg.sender` is not a Napier pool.
    function _verifyCallback(address basePool, address underlying) internal view {
        if (
            PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory))
                != INapierPool(msg.sender)
        ) revert Errors.RouterCallbackNotNapierPool();
    }

    function mintCallback(uint256 underlyingDelta, uint256 baseLptDelta, bytes calldata data) external override {
        // `data` is encoded as follows:
        // [0x00: 0x20] CallbackType (uint8)
        // [0x20:  ~  ] Custom data (based on CallbackType)
        CallbackDataTypes.AddLiquidityData memory params = abi.decode(data[0x20:], (CallbackDataTypes.AddLiquidityData));
        _verifyCallback(params.basePool, params.underlying);

        // In all addLiquidity functions of router, underlying tokens are saved in payer and baseLpTokens are saved in router.
        // So in this callback function, we don't need to get CallbackTypes.
        // If this contract holds enough ETH, wrap it. Otherwise, transfer from the caller.
        _pay(params.underlying, params.payer, msg.sender, underlyingDelta);
        IERC20(params.basePool).safeTransfer(msg.sender, baseLptDelta);
    }

    function swapCallback(int256 underlyingDelta, int256 ptDelta, bytes calldata data) external override {
        // `data` is encoded as follows:
        // [0x00: 0x20] CallbackType (uint8)
        // [0x20: 0x40] Underlying (address)
        // [0x40: 0x60] BasePool (address)
        // [0x60:  ~  ] Custom data (based on CallbackType)
        (address underlying, address basePool) = abi.decode(data[0x20:0x60], (address, address));
        _verifyCallback(basePool, underlying);

        CallbackType _type = CallbackDataTypes.getCallbackType(data);

        if (_type == CallbackType.SwapPtForUnderlying) {
            CallbackDataTypes.SwapPtForUnderlyingData memory params =
                abi.decode(data[0x60:], (CallbackDataTypes.SwapPtForUnderlyingData));
            params.pt.safeTransferFrom(params.payer, msg.sender, uint256(-ptDelta));
        } else if (_type == CallbackType.SwapUnderlyingForPt) {
            // Decode callback data
            CallbackDataTypes.SwapUnderlyingForPtData memory params =
                abi.decode(data[0x60:], (CallbackDataTypes.SwapUnderlyingForPtData));

            // Check slippage. Revert if exceeded max underlying in
            if (uint256(-underlyingDelta) > params.underlyingInMax) revert Errors.RouterExceededLimitUnderlyingIn();
            _pay(underlying, params.payer, msg.sender, uint256(-underlyingDelta));
        } else if (_type == CallbackType.SwapYtForUnderlying) {
            // Decode callback data
            CallbackDataTypes.SwapYtForUnderlyingData memory params =
                abi.decode(data[0x60:], (CallbackDataTypes.SwapYtForUnderlyingData));

            uint256 uRepay = uint256(-underlyingDelta); // unsafe cast is okay because always negative in this branch
            uint256 pyRedeem; // amount of PT (YT) to be redeemed
            // Assign the minimum amount of (ytIn, ptDelta) to `pyRedeem`
            if (params.ytIn >= uint256(ptDelta)) {
                // If the actual amount of PT received is less than the requested amount, use the actual amount
                pyRedeem = uint256(ptDelta);
            } else {
                pyRedeem = params.ytIn;
                // Surplus of `ptDelta` - `params.ytIn` should be refunded to the payer
                // no underflow because of the if statement above
                IERC20(params.pt).safeTransfer(params.payer, uint256(ptDelta) - params.ytIn); // non-zero
            }

            // Transfer YT from caller to this contract
            IERC20(params.pt.yieldToken()).safeTransferFrom(params.payer, address(this), pyRedeem);

            // Optimistically redeem any amount of PT and YT for underlying
            // Later, we will check if the amount of underlying redeemed is enough to cover the underlying to be repaid
            uint256 uRedeemed = params.pt.redeemWithYT({
                pyAmount: pyRedeem,
                to: address(this),
                from: address(this) // At this point, the YT is already in this contract
            });
            if (uRedeemed < uRepay) revert Errors.RouterInsufficientUnderlyingRepay();
            // Check slippage
            uint256 underlyingToRecipient = uRedeemed - uRepay; // no underflow because of the if statement above
            if (underlyingToRecipient < params.underlyingOutMin) revert Errors.RouterInsufficientUnderlyingOut();

            // Repay underlying to Napier pool and transfer the rest to recipient
            IERC20(underlying).safeTransfer(msg.sender, uRepay);
            IERC20(underlying).safeTransfer(params.recipient, underlyingToRecipient);
        } else if (_type == CallbackType.SwapUnderlyingForYt) {
            // Decode callback data
            CallbackDataTypes.SwapUnderlyingForYtData memory params =
                abi.decode(data[0x60:], (CallbackDataTypes.SwapUnderlyingForYtData));

            uint256 uReceived = uint256(underlyingDelta); // unsafe cast is okay because always positive in this branch.
            uint256 pyDesired = uint256(-ptDelta); // principal token to be repaid and yield token to be issued

            // Pull underlying from payer.
            // Economically, it's almost unlikely that the payer doesn't need to pay underlying asset.
            // But if the above case happens, it would be reverted.
            if (params.underlyingDeposit <= uReceived) revert Errors.RouterNonSituationSwapUnderlyingForYt();
            uint256 uPull = params.underlyingDeposit - uReceived;
            if (uPull > params.maxUnderlyingPull) revert Errors.RouterExceededLimitUnderlyingIn();
            _pay(underlying, params.payer, address(this), uPull);

            IERC20(underlying).forceApprove(address(params.pt), params.underlyingDeposit);
            uint256 pyIssued = params.pt.issue({to: address(this), underlyingAmount: params.underlyingDeposit});

            // Repay the PT to Napier pool
            if (pyIssued < pyDesired) revert Errors.RouterInsufficientPtRepay();
            params.pt.safeTransfer(msg.sender, pyDesired);
            // Transfer the remaining PT to payer
            params.pt.safeTransfer(params.payer, pyIssued - pyDesired);
            // Transfer the YT to recipient
            params.yt.safeTransfer(params.recipient, pyIssued);
        }
    }

    /// @inheritdoc INapierRouter
    /// @notice Swap exact amount of Principal Token (PT) for Underlying.
    /// @notice Caller must approve the router to spend PTs prior to calling this method.
    /// @notice Revert if the pool is not deployed by the factory set in the constructor.
    /// @notice Revert if maturity has passed for the pool.
    /// @notice Revert if deadline has passed.
    /// @notice Revert if the amount of underlying asset received is less than the minimum amount specified.
    /// @param pool The address of the pool.
    /// @param index The index of the PT. (0, 1, 2)
    /// @param ptInDesired The amount of PT to swap.
    /// @param underlyingOutMin The minimum amount of underlying asset to receive.
    /// @param recipient The recipient of the swapped underlying asset.
    /// @param deadline The deadline for the swap.
    /// @return The amount of underlying asset received.
    function swapPtForUnderlying(
        address pool,
        uint256 index,
        uint256 ptInDesired,
        uint256 underlyingOutMin,
        address recipient,
        uint256 deadline
    ) external override nonReentrant checkDeadline(deadline) returns (uint256) {
        // dev: Optimistically call to the `pool` provided by the untrusted caller.
        // And then verify the pool using CREATE2.
        (address underlying, address basePool) = INapierPool(pool).getAssets();

        // if `pool` doesn't matched, it would be reverted.
        if (INapierPool(pool) != PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory)))
        {
            revert Errors.RouterPoolNotFound();
        }

        address pt = address(INapierPool(pool).principalTokens()[index]);

        // Abi encode callback data to be used in swapCallback
        bytes memory data = new bytes(0xa0);

        uint256 callbackType = uint256(CallbackType.SwapPtForUnderlying);
        assembly {
            // Equivanlent to:
            // data = abi.encode(CallbackType.SwapUnderlyingForPt, underlying, basePool, CallbackDataTypes.SwapUnderlyingForPtData({payer: msg.sender, underlyingInMax: underlyingInMax}))
            mstore(add(data, 0x20), callbackType)
            mstore(add(data, 0x40), underlying)
            mstore(add(data, 0x60), basePool)
            mstore(add(data, 0x80), caller()) // dev: Ensure 'payer' is always 'msg.sender' to prevent allowance theft on callback.
            mstore(add(data, 0xa0), pt)
        }
        uint256 underlyingOut = INapierPool(pool).swapPtForUnderlying(index, ptInDesired, recipient, data);
        if (underlyingOut < underlyingOutMin) revert Errors.RouterInsufficientUnderlyingOut();

        return underlyingOut;
    }

    /// @inheritdoc INapierRouter
    /// @notice Swap underlying for PT.
    /// @notice Caller must approve the router to spend underlying asset prior to calling this method.
    /// @dev If caller calls with ether, the ether will be wrapped to WETH9.
    /// Note: the remaining ether is NOT returned automatically. Caller must call refundETH to get the remaining ether back.
    /// @dev Revert conditions are the same as swapPtForUnderlying.
    /// @param pool The address of the pool.
    /// @param index The index of the PT.
    /// @param ptOutDesired The amount of PT to receive.
    /// @param underlyingInMax The maximum amount of underlying asset to spend.
    /// @param recipient The recipient of the PT.
    /// @param deadline The deadline for the swap.
    /// @return The amount of underlying asset spent.
    function swapUnderlyingForPt(
        address pool,
        uint256 index,
        uint256 ptOutDesired,
        uint256 underlyingInMax,
        address recipient,
        uint256 deadline
    ) external payable override nonReentrant checkDeadline(deadline) returns (uint256) {
        // dev: Optimistically call to the `pool` provided by the untrusted caller.
        // And then verify the pool using CREATE2.
        (address underlying, address basePool) = INapierPool(pool).getAssets();

        // if `pool` doesn't matched, it would be reverted.
        if (INapierPool(pool) != PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory)))
        {
            revert Errors.RouterPoolNotFound();
        }

        IERC20 pt = INapierPool(pool).principalTokens()[index];

        // Abi encode callback data to be used in swapCallback
        bytes memory data = new bytes(0xa0);
        {
            uint256 callbackType = uint256(CallbackType.SwapUnderlyingForPt);
            assembly {
                // Equivanlent to:
                // data = abi.encode(CallbackType.SwapUnderlyingForPt, underlying, basePool, CallbackDataTypes.SwapUnderlyingForPtData({payer: msg.sender, underlyingInMax: underlyingInMax}))
                mstore(add(data, 0x20), callbackType)
                mstore(add(data, 0x40), underlying)
                mstore(add(data, 0x60), basePool)
                mstore(add(data, 0x80), caller()) // dev: Ensure 'payer' is always 'msg.sender' to prevent allowance theft on callback.
                mstore(add(data, 0xa0), underlyingInMax)
            }
        }

        uint256 prevBalance = pt.balanceOf(address(this));
        uint256 underlyingUsed = INapierPool(pool).swapUnderlyingForPt(
            index,
            ptOutDesired,
            address(this), // this contract will receive principal token from pool
            data
        );

        pt.safeTransfer(recipient, pt.balanceOf(address(this)) - prevBalance);
        return underlyingUsed;
    }

    /// @inheritdoc INapierRouter
    /// @notice Swap underlying asset for YT.
    /// @dev Under the hood, Router receives underlying asset from `pool` with flash swap and issues PT and YT.
    /// After that, pay back the PT to `pool` and transfer the issued YT to `recipient`.
    /// @param pool The address of the pool.
    /// @param index The index of principal token / yield token.
    /// @param ytOutDesired The amount of YT to receive. (at least `ytOutDesired` amount of PT and YT should be issued)
    /// @param underlyingInMax The maximum amount of underlying asset to spend.
    /// @param recipient The recipient of the YT.
    /// @param deadline The deadline for the swap.
    /// @return The amount of underlying asset recipient spent.
    function swapUnderlyingForYt(
        address pool,
        uint256 index,
        uint256 ytOutDesired,
        uint256 underlyingInMax,
        address recipient,
        uint256 deadline
    ) external payable returns (uint256) {
        // `swapUnderlyingForYt` with default approx parameters
        return swapUnderlyingForYt(
            pool, index, ytOutDesired, underlyingInMax, recipient, deadline, ApproxParams(0, 0, 0, 0)
        );
    }

    /// @inheritdoc INapierRouter
    /// @param approx Approximation parameters for the calculation.
    /// - `guessMin`: The minimum guess for the approximation. Underlying needed to issue PT and YT.
    /// - `guessMax`: The maximum guess for the approximation. Underlying needed to issue PT and YT.
    /// - `eps`: The maximum relative error for the approximation.
    /// - `maxIteration`: The maximum number of iterations for the approximation.
    function swapUnderlyingForYt(
        address pool,
        uint256 index,
        uint256 ytOutDesired,
        uint256 underlyingInMax,
        address recipient,
        uint256 deadline,
        ApproxParams memory approx
    ) public payable override nonReentrant checkDeadline(deadline) returns (uint256) {
        // dev: Optimistically call to the `pool` provided by the untrusted caller.
        // And then verify the pool using CREATE2.
        (address underlying, address basePool) = INapierPool(pool).getAssets();

        // if `pool` doesn't matched, it would be reverted.
        if (INapierPool(pool) != PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory)))
        {
            revert Errors.RouterPoolNotFound();
        }

        ITranche pt = ITranche(address(INapierPool(pool).principalTokens()[index]));

        // Compute underlying asset to be deposited to Tranche
        // Binary search is used to find the exact amount of underlying needed to issue PT and YT
        uint256 uDeposit = TrancheMathHelper.getApproxUnderlyingNeededByYt(pt, ytOutDesired, approx);

        // Abi encode callback data to be used in swapCallback
        bytes memory data = new bytes(0x120);
        {
            uint256 callbackType = uint256(CallbackType.SwapUnderlyingForYt);
            address yt = pt.yieldToken();
            assembly {
                // Equivanlent to:
                // abi.encode(CallbackType.SwapUnderlyingForYt, underlying, basePool, CallbackDataTypes.SwapUnderlyingForYtData({pt: pt, yt: yt, payer: msg.sender, recipient: recipient, underlyingDeposit: uDeposit, maxUnderlyingPull: underlyingInMax}))
                mstore(add(data, 0x20), callbackType)
                mstore(add(data, 0x40), underlying)
                mstore(add(data, 0x60), basePool)
                mstore(add(data, 0x80), caller()) // dev: Ensure 'payer' is always 'msg.sender' to prevent allowance theft on callback.
                mstore(add(data, 0xa0), pt)
                mstore(add(data, 0xc0), yt)
                mstore(add(data, 0xe0), recipient)
                mstore(add(data, 0x100), uDeposit)
                mstore(add(data, 0x120), underlyingInMax)
            }
        }
        uint256 received = INapierPool(pool).swapPtForUnderlying(
            index,
            ytOutDesired, // ptInDesired
            address(this), // this contract will receive underlying token from pool
            data
        );

        // Underlying pulled = underlying deposited - underlying received from swap
        return uDeposit - received;
    }

    /// @inheritdoc INapierRouter
    /// @notice Swap YT for underlying asset.
    /// @dev Under the hood, Router receives principal token from `pool` with flash swap and redeem it with YT for underlying asset.
    /// After that, pay back the underlying asset to `pool` and transfer the remaining underlying asset to `recipient`.
    /// @param pool The address of the pool.
    /// @param index The index of the YT.
    /// @param ytIn The amount of YT to swap.
    /// @param underlyingOutMin The minimum amount of underlying asset to receive.
    /// @param recipient The recipient of the underlying asset.
    /// @param deadline The deadline for the swap.
    /// @return The amount of underlying asset recipient received.
    function swapYtForUnderlying(
        address pool,
        uint256 index,
        uint256 ytIn,
        uint256 underlyingOutMin,
        address recipient,
        uint256 deadline
    ) external override nonReentrant checkDeadline(deadline) returns (uint256) {
        // dev: Optimistically call to the `pool` provided by the untrusted caller.
        // And then verify the pool using CREATE2.
        (address underlying, address basePool) = INapierPool(pool).getAssets();

        // if `pool` doesn't matched, it would be reverted.
        if (INapierPool(pool) != PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory)))
        {
            revert Errors.RouterPoolNotFound();
        }

        ITranche pt = ITranche(address(INapierPool(pool).principalTokens()[index]));

        uint256 prevBalance = IERC20(underlying).balanceOf(recipient);

        // Abi encode callback data to be used in swapCallback
        bytes memory data = new bytes(0x100);
        uint256 callbackType = uint256(CallbackType.SwapYtForUnderlying);
        assembly {
            // Equivanlent to:
            // data = abi.encode(CallbackType.SwapYtForUnderlying, underlying, basePool, CallbackDataTypes.SwapYtForUnderlyingData({pt: pt, payer: msg.sender, ytIn: ytIn, recipient: recipient, underlyingOutMin: underlyingOutMin}))
            mstore(add(data, 0x20), callbackType)
            mstore(add(data, 0x40), underlying)
            mstore(add(data, 0x60), basePool)
            mstore(add(data, 0x80), caller()) // dev: Ensure 'payer' is always 'msg.sender' to prevent allowance theft on callback.
            mstore(add(data, 0xa0), pt)
            mstore(add(data, 0xc0), ytIn)
            mstore(add(data, 0xe0), recipient)
            mstore(add(data, 0x100), underlyingOutMin)
        }
        // Note: swap for PT approximate equal to `ytIn`
        INapierPool(pool).swapUnderlyingForPt(
            index,
            ytIn, // ptOutDesired
            address(this), // this contract will receive principal token from pool
            data
        );

        // Underlying received = balance after swap - balance before swap
        return IERC20(underlying).balanceOf(recipient) - prevBalance;
    }

    /// @inheritdoc INapierRouter
    /// @notice Caller must approve the router to spend underlying asset and PTs prior to calling this method.
    /// @notice Revert if the pool is not deployed by the factory set in the constructor
    /// @notice Revert if maturity has passed for the pool
    /// @notice Revert if deadline has passed
    /// @notice Revert if the amount of liquidity tokens received is less than the minimum amount specified
    /// @notice It will refund the remaining tokens (Native ETH or Base LP token) to the caller if any.
    /// @param pool The address of the pool.
    /// @param underlyingIn The amount of underlying asset to deposit.
    /// @param ptsIn The amounts of PTs to deposit. Can be zero but at least one must be non-zero. Otherwise, revert in the Curve pool.
    /// @param minLiquidity The minimum amount of liquidity tokens to receive.
    /// @param recipient The recipient of the liquidity tokens.
    /// @param deadline The deadline for adding liquidity.
    /// @return The amount of liquidity tokens received.
    function addLiquidity(
        address pool,
        uint256 underlyingIn,
        uint256[3] calldata ptsIn,
        uint256 minLiquidity,
        address recipient,
        uint256 deadline
    ) external payable override nonReentrant checkDeadline(deadline) returns (uint256) {
        // dev: Optimistically call to the `pool` provided by the untrusted caller.
        // And then verify the pool using CREATE2.
        (address underlying, address basePool) = INapierPool(pool).getAssets();

        // if `pool` doesn't matched, it would be reverted.
        if (INapierPool(pool) != PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory)))
        {
            revert Errors.RouterPoolNotFound();
        }

        IERC20[3] memory pts = INapierPool(pool).principalTokens();

        // Loop unrolling for gas optimization
        pts[0].safeTransferFrom(msg.sender, address(this), ptsIn[0]);
        pts[1].safeTransferFrom(msg.sender, address(this), ptsIn[1]);
        pts[2].safeTransferFrom(msg.sender, address(this), ptsIn[2]);
        // approve max to Tricrypto pool
        if (pts[0].allowance(address(this), basePool) < ptsIn[0]) pts[0].approve(basePool, type(uint256).max); // dev: Principal token will revert if failed to approve
        if (pts[1].allowance(address(this), basePool) < ptsIn[1]) pts[1].approve(basePool, type(uint256).max);
        if (pts[2].allowance(address(this), basePool) < ptsIn[2]) pts[2].approve(basePool, type(uint256).max);

        uint256 baseLptIn = CurveTricryptoOptimizedWETH(basePool).add_liquidity(ptsIn, 0);
        // Add liquidity to Napier pool
        uint256 liquidity = INapierPool(pool).addLiquidity(
            underlyingIn,
            baseLptIn,
            recipient,
            abi.encode(
                CallbackType.AddLiquidityPts,
                CallbackDataTypes.AddLiquidityData({payer: msg.sender, underlying: underlying, basePool: basePool})
            )
        );
        if (liquidity < minLiquidity) revert Errors.RouterInsufficientLpOut();

        // Sweep remaining tokens if any.
        uint256 bBalance = IERC20(basePool).balanceOf(address(this));
        if (bBalance > 0) IERC20(basePool).safeTransfer(msg.sender, bBalance);
        // If WETH or ERC20 tokens are used, the exact amount is pulled from the caller. So, no need to sweep.
        // If caller sent native ETH, make sure to send remaining ETH back to caller.
        if (address(this).balance > 0) _safeTransferETH(msg.sender, address(this).balance);

        return liquidity;
    }

    /// @inheritdoc INapierRouter
    /// @notice Add liquidity to Napier pool from one principal token proportionally as possible as it can.
    /// @notice Deadline should be tightly set.
    /// @notice Caller must approve the router to spend PT prior to calling this method.
    /// @dev Caller must specify the amount of base LP token to be swapped for underlying asset using off-chain calculation.
    /// @dev Remaining base LP token and underlying asset are swept to the caller if any.
    /// @param pool The address of the pool.
    /// @param index The index of the PT.
    /// @param amountIn The amount of PT to deposit.
    /// @param minLiquidity The minimum amount of liquidity tokens to receive.
    /// @param recipient The recipient of the liquidity tokens.
    /// @param deadline The deadline for adding liquidity.
    /// @param baseLpTokenSwap The estimated baseLpt amount to swap with underlying tokens.
    /// @return The amount of liquidity tokens received.
    function addLiquidityOnePt(
        address pool,
        uint256 index,
        uint256 amountIn,
        uint256 minLiquidity,
        address recipient,
        uint256 deadline,
        uint256 baseLpTokenSwap
    ) external override nonReentrant checkDeadline(deadline) returns (uint256) {
        // dev: Optimistically call to the `pool` provided by the untrusted caller.
        // And then verify the pool using CREATE2.
        (address underlying, address basePool) = INapierPool(pool).getAssets();

        // if `pool` doesn't matched, it would be reverted.
        if (INapierPool(pool) != PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory)))
        {
            revert Errors.RouterPoolNotFound();
        }

        IERC20[3] memory pts = INapierPool(pool).principalTokens();
        pts[index].safeTransferFrom(msg.sender, address(this), amountIn);
        if (pts[index].allowance(address(this), basePool) < amountIn) pts[index].approve(basePool, type(uint256).max);

        uint256[3] memory ptsIn;
        ptsIn[index] = amountIn;
        uint256 baseLptIn = CurveTricryptoOptimizedWETH(basePool).add_liquidity(ptsIn, 0);
        IERC20(basePool).forceApprove(address(pool), baseLptIn);

        // Swap some base LP token for underlying
        uint256 underlyingIn = INapierPool(pool).swapExactBaseLpTokenForUnderlying(baseLpTokenSwap, address(this));

        // Add liquidity to Napier pool
        uint256 liquidity = INapierPool(pool).addLiquidity(
            underlyingIn,
            baseLptIn - baseLpTokenSwap,
            recipient,
            abi.encode(
                CallbackType.AddLiquidityOnePt,
                CallbackDataTypes.AddLiquidityData({
                    payer: address(this), // Router has already had both tokens at this point
                    underlying: underlying,
                    basePool: basePool
                })
            )
        );
        if (liquidity < minLiquidity) revert Errors.RouterInsufficientLpOut();

        // Sweep remaining tokens if any.
        uint256 bBalance = IERC20(basePool).balanceOf(address(this));
        if (bBalance > 0) IERC20(basePool).safeTransfer(msg.sender, bBalance);
        uint256 uBalance = IERC20(underlying).balanceOf(address(this));
        if (uBalance > 0) IERC20(underlying).safeTransfer(msg.sender, uBalance);

        return liquidity;
    }

    /// @inheritdoc INapierRouter
    /// @notice Add liquidity to NapierPool with one underlying asset.
    /// @notice Deadline should be tightly set.
    /// @notice Caller must approve the router to spend underlying asset prior to calling this method.
    /// @dev Under the hood, router swap some underlying asset for Base pool LP token.
    /// @dev Caller must specify the amount of base LP token to be swapped for underlying asset using off-chain calculation.
    /// @dev Remaining base LP token and underlying asset are swept to the caller if any.
    /// @param pool The address of the pool.
    /// @param underlyingIn The amount of underlying asset to deposit.
    /// @param minLiquidity The minimum amount of liquidity tokens to receive.
    /// @param recipient The recipient of the liquidity tokens.
    /// @param deadline The deadline for adding liquidity.
    /// @param baseLpTokenSwap The estimated baseLpTokenSwap amount to swap with underlying tokens.
    /// @return The amount of liquidity tokens received.
    function addLiquidityOneUnderlying(
        address pool,
        uint256 underlyingIn,
        uint256 minLiquidity,
        address recipient,
        uint256 deadline,
        uint256 baseLpTokenSwap
    ) external override nonReentrant checkDeadline(deadline) returns (uint256) {
        // dev: Optimistically call to the `pool` provided by the untrusted caller.
        // And then verify the pool using CREATE2.
        (address underlying, address basePool) = INapierPool(pool).getAssets();

        // if `pool` doesn't matched, it would be reverted.
        if (INapierPool(pool) != PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory)))
        {
            revert Errors.RouterPoolNotFound();
        }

        IERC20(underlying).safeTransferFrom(msg.sender, address(this), underlyingIn);

        // Swap some underlying for baseLpt
        // At this point, Router doesn't know how much underlying is needed to get `baseLpTokenSwap` amount of base LP token.
        // So, Router just approve pool to spend all underlying asset and let pool to spend as much as it needs.
        // approve max
        if (IERC20(underlying).allowance(address(this), pool) < underlyingIn) {
            IERC20(underlying).forceApprove(pool, type(uint256).max);
        }
        uint256 uSpent = INapierPool(pool).swapUnderlyingForExactBaseLpToken(baseLpTokenSwap, address(this));

        // Add liquidity to Napier pool
        uint256 liquidity = INapierPool(pool).addLiquidity(
            underlyingIn - uSpent, // remaining underlying asset
            baseLpTokenSwap, // base LP token from swap
            recipient,
            abi.encode(
                CallbackType.AddLiquidityOneUnderlying,
                CallbackDataTypes.AddLiquidityData({
                    payer: address(this), // Router has already had both tokens at this point
                    underlying: underlying,
                    basePool: basePool
                })
            )
        );
        if (liquidity < minLiquidity) revert Errors.RouterInsufficientLpOut();

        // Sweep remaining tokens if any. WETH is not unwrapped.
        uint256 bBalance = IERC20(basePool).balanceOf(address(this));
        if (bBalance > 0) IERC20(basePool).safeTransfer(msg.sender, bBalance);
        uint256 uBalance = IERC20(underlying).balanceOf(address(this));
        if (uBalance > 0) IERC20(underlying).safeTransfer(msg.sender, uBalance);

        return liquidity;
    }

    /// @inheritdoc INapierRouter
    /// @notice Add liquidity to NapierPool with one underlying asset.
    /// @notice Deadline should be tightly set.
    /// @notice Caller must approve the router to spend underlying asset prior to calling this method.
    /// @dev Under the hood, router issues PT and YT with some of underlying asset and deposit the PT to Tricrypto pool.
    /// After that, router add liquidity to Napier pool with the remaining underlying asset and the minted base LP token.
    /// The caller will receive the issued YT and the remaining base LP token if any.
    /// @dev Caller must specify the amount of underlying asset to be spent to issue PT and YT using off-chain calculation.
    /// @param pool The address of the pool.
    /// @param index The index of the issued PT / YT.
    /// @param underlyingIn The amount of underlying asset to deposit.
    /// @param minLiquidity The minimum amount of liquidity tokens to receive.
    /// @param ytOutMin The minimum amount of YT to receive.
    /// @param recipient The recipient of the liquidity tokens.
    /// @param deadline The deadline for adding liquidity.
    /// @param underlyingDeposit The estimated underlying amount spent to issue PT and YT.
    /// @return The amount of liquidity tokens received.
    function addLiquidityOneUnderlyingKeepYt(
        address pool,
        uint256 index,
        uint256 underlyingIn,
        uint256 minLiquidity,
        uint256 ytOutMin,
        address recipient,
        uint256 deadline,
        uint256 underlyingDeposit
    ) external nonReentrant checkDeadline(deadline) returns (uint256) {
        // dev: Optimistically call to the `pool` provided by the untrusted caller.
        // And then verify the pool using CREATE2.
        (address underlying, address basePool) = INapierPool(pool).getAssets();

        // if `pool` doesn't matched, it would be reverted.
        if (INapierPool(pool) != PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory)))
        {
            revert Errors.RouterPoolNotFound();
        }

        IERC20(underlying).safeTransferFrom(msg.sender, address(this), underlyingIn);

        uint256[3] memory amounts; // amounts of PTs to be added to Tricrypto pool
        // avoid stack too deep
        {
            // Issue principal token and yield token with some of the underlying asset.
            address pt = address(INapierPool(pool).principalTokens()[index]);
            if (IERC20(underlying).allowance(address(this), pt) < underlyingDeposit) {
                IERC20(underlying).forceApprove(pt, type(uint256).max);
            }
            uint256 pyIssued = ITranche(pt).issue({to: address(this), underlyingAmount: underlyingDeposit});
            if (pyIssued < ytOutMin) revert Errors.RouterInsufficientYtOut();

            // Send the remaining yield token to the caller.
            IERC20(ITranche(pt).yieldToken()).safeTransfer(msg.sender, pyIssued);

            // Deposit the issued principal token to Tricrypto.
            if (IERC20(pt).allowance(address(this), basePool) < pyIssued) {
                IERC20(pt).forceApprove(basePool, type(uint256).max);
            }
            amounts[index] = pyIssued;
        }
        uint256 baseLptIn = CurveTricryptoOptimizedWETH(basePool).add_liquidity(amounts, 0);

        // Add liquidity to Napier pool
        uint256 liquidity = INapierPool(pool).addLiquidity(
            underlyingIn - underlyingDeposit, // remaining underlying asset
            baseLptIn,
            recipient,
            abi.encode(
                CallbackType.AddLiquidityOneUnderlying,
                CallbackDataTypes.AddLiquidityData({
                    payer: address(this), // Router has already had both tokens at this point
                    underlying: underlying,
                    basePool: basePool
                })
            )
        );
        if (liquidity < minLiquidity) revert Errors.RouterInsufficientLpOut();

        // Sweep remaining tokens if any. WETH is not unwrapped.
        uint256 bBalance = IERC20(basePool).balanceOf(address(this));
        if (bBalance > 0) IERC20(basePool).safeTransfer(msg.sender, bBalance);
        uint256 uBalance = IERC20(underlying).balanceOf(address(this));
        if (uBalance > 0) IERC20(underlying).safeTransfer(msg.sender, uBalance);

        return liquidity;
    }

    /// @inheritdoc INapierRouter
    /// @notice Remove liquidity from NapierPool and Curve pool.
    /// @notice Caller must approve the router to spend liquidity tokens prior to calling this method.
    /// @dev Can withdraw liquidity even if maturity has passed.
    /// @dev Revert if the pool is not deployed by the factory set in the constructor.
    /// @dev Revert if deadline has passed.
    /// @dev Revert if the amount of underlying asset received is less than the minimum amount specified.
    /// @dev Revert if the amount of PTs received is less than the minimum amount specified.
    /// @param pool The address of the pool.
    /// @param liquidity The amount of liquidity tokens to burn.
    /// @param underlyingOutMin The minimum amount of underlying asset to receive.
    /// @param ptsOutMin The minimum amounts of PTs to receive.
    /// @param recipient The recipient of the PTs and underlying asset.
    /// @param deadline The deadline for removing liquidity.
    /// @return The amounts of PTs and underlying asset received.
    function removeLiquidity(
        address pool,
        uint256 liquidity,
        uint256 underlyingOutMin,
        uint256[3] calldata ptsOutMin,
        address recipient,
        uint256 deadline
    ) external override nonReentrant checkDeadline(deadline) returns (uint256, uint256[3] memory) {
        // dev: Optimistically call to the `pool` provided by the untrusted caller.
        // And then verify the pool using CREATE2.
        (address underlying, address basePool) = INapierPool(pool).getAssets();

        // if `pool` doesn't matched, it would be reverted.
        if (INapierPool(pool) != PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory)))
        {
            revert Errors.RouterPoolNotFound();
        }

        IERC20(pool).safeTransferFrom(msg.sender, pool, liquidity);
        (uint256 underlyingOut, uint256 baseLptOut) = INapierPool(pool).removeLiquidity(address(this));

        // Check slippage for underlying
        if (underlyingOut < underlyingOutMin) revert Errors.RouterInsufficientUnderlyingOut();

        // Note: Curve Tricrypto Optimized WETH pool doesn't cause clear error messages
        // Curve pool would check slippage for principal tokens
        uint256[3] memory ptsOut = CurveTricryptoOptimizedWETH(basePool).remove_liquidity(
            baseLptOut,
            ptsOutMin, // min_amounts
            false,
            recipient,
            false
        );

        // Transfer underlying to recipient
        IERC20(underlying).safeTransfer(recipient, underlyingOut);

        return (underlyingOut, ptsOut);
    }

    /// @inheritdoc INapierRouter
    /// @notice Remove liquidity from NapierPool and Curve pool with one underlying asset.
    /// @notice Caller must approve the router to spend liquidity tokens prior to calling this method.
    /// @dev Can withdraw liquidity even if maturity has passed.
    /// @dev Revert conditions are the same as removeLiquidity.
    /// @param pool Address of the pool to remove liquidity from.
    /// @param index The index of PT to be withdrawn when removing liquidity from Base pool. Ignored if maturity has not passed.
    /// @param liquidity Liquidity to be removed from Napier pool.
    /// @param underlyingOutMin Minimum amount of underlying asset to receive.
    /// @param recipient Recipient of the underlying asset.
    /// @param deadline Deadline for removing liquidity. Revert if deadline has passed when the transaction is executed.
    function removeLiquidityOneUnderlying(
        address pool,
        uint256 index,
        uint256 liquidity,
        uint256 underlyingOutMin,
        address recipient,
        uint256 deadline
    ) external override nonReentrant checkDeadline(deadline) returns (uint256) {
        // dev: Optimistically call to the `pool` provided by the untrusted caller.
        // And then verify the pool using CREATE2.
        (address underlying, address basePool) = INapierPool(pool).getAssets();

        // if `pool` doesn't matched, it would be reverted.
        if (INapierPool(pool) != PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory)))
        {
            revert Errors.RouterPoolNotFound();
        }

        IERC20[3] memory pts = INapierPool(pool).principalTokens();
        // Remove liquidity from Napier pool and get base LP token and underlying back
        IERC20(pool).safeTransferFrom(msg.sender, pool, liquidity);
        (uint256 underlyingOut, uint256 baseLptOut) = INapierPool(pool).removeLiquidity(address(this));

        // The withdrawn base LP token is exchanged for underlying in two different ways depending on the maturity.
        // If maturity has passed, redeem else swap for underlying.
        // 1. Swapping is used when maturity hasn't passed because redeeming is disabled before maturity.
        // 2. Redeeming is preferred because it doesn't cause slippage.
        if (block.timestamp < INapierPool(pool).maturity()) {
            // Swap base LP token for underlying
            // approve baseLptOut
            if (baseLptOut > IERC20(basePool).allowance(address(this), pool)) {
                IERC20(basePool).forceApprove(pool, type(uint256).max);
            }

            uint256 removed = INapierPool(pool).swapExactBaseLpTokenForUnderlying(baseLptOut, address(this));
            underlyingOut += removed;
        } else {
            // Withdraw the pt from base pool in return for base LP token
            // Allow unlimited slippage. Check slippage later
            uint256 ptWithdrawn = CurveTricryptoOptimizedWETH(basePool).remove_liquidity_one_coin(
                baseLptOut, index, 0, false, address(this)
            );
            // Redeem the pt for underlying
            uint256 redeemed = ITranche(address(pts[index])).redeem(ptWithdrawn, address(this), address(this));
            underlyingOut += redeemed;
        }

        // Check slippage
        if (underlyingOut < underlyingOutMin) revert Errors.RouterInsufficientUnderlyingOut();
        IERC20(underlying).safeTransfer(recipient, underlyingOut);

        return underlyingOut;
    }

    /// @notice Remove liquidity from the pool and receive a single PT.
    /// @notice Caller must approve the router to spend liquidity tokens prior to calling this method.
    /// @dev Revert conditions are the same as removeLiquidity.
    /// @dev Caller must specify the amount of base LP token to be swapped with underlying asset using off-chain calculation.
    /// @dev Remaining base LP token and underlying asset are swept to the caller if any.
    /// @param pool Address of the pool to remove liquidity from.
    /// @param index The index of PT.
    /// @param liquidity The amount of liquidity tokens to remove.
    /// @param ptOutMin The minimum amount of PT to receive.
    /// @param recipient The recipient of the PT.
    /// @param deadline Deadline for removing liquidity. Revert if deadline has passed when the transaction is executed.
    /// @param baseLpTokenSwap The estimated baseLpt amount to swap with underlying tokens.
    /// @return The amount of PT received.
    function removeLiquidityOnePt(
        address pool,
        uint256 index,
        uint256 liquidity,
        uint256 ptOutMin,
        address recipient,
        uint256 deadline,
        uint256 baseLpTokenSwap
    ) external override nonReentrant checkDeadline(deadline) returns (uint256) {
        // dev: Optimistically call to the `pool` provided by the untrusted caller.
        // And then verify the pool using CREATE2.
        (address underlying, address basePool) = INapierPool(pool).getAssets();

        // if `pool` doesn't matched, it would be reverted.
        if (INapierPool(pool) != PoolAddress.computeAddress(basePool, underlying, POOL_CREATION_HASH, address(factory)))
        {
            revert Errors.RouterPoolNotFound();
        }

        // Remove liquidity from Napier pool and get base LP token and underlying back
        IERC20(pool).safeTransferFrom(msg.sender, pool, liquidity);
        (uint256 underlyingIn, uint256 baseLptOut) = INapierPool(pool).removeLiquidity(address(this));

        // Swap underlying for baseLpt
        // At this point, we don't know how much actual underlying amount will be needed to swap.
        // `baseLpTokenSwap` should be set so that the actual underlying amount would be less than `underlyingIn` otherwise revert in the pool.
        IERC20(underlying).forceApprove(pool, underlyingIn);
        INapierPool(pool).swapUnderlyingForExactBaseLpToken(baseLpTokenSwap, address(this));

        // Withdraw liquidity in a one principal token
        uint256 ptWithdrawn = CurveTricryptoOptimizedWETH(basePool).remove_liquidity_one_coin(
            baseLptOut + baseLpTokenSwap, index, ptOutMin, false, recipient
        );

        // Sweep remaining tokens if any.
        uint256 bBalance = IERC20(basePool).balanceOf(address(this));
        if (bBalance > 0) IERC20(basePool).safeTransfer(msg.sender, bBalance);
        uint256 uBalance = IERC20(underlying).balanceOf(address(this));
        if (uBalance > 0) IERC20(underlying).safeTransfer(msg.sender, uBalance);

        return ptWithdrawn;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";

/// @notice WETH9 interface
interface IWETH9 is IERC20 {
    function deposit() external payable;

    function withdraw(uint256 wad) external;
}

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

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

/// @notice Tranche interface
/// @dev Tranche divides a yield-bearing token into two tokens: Principal and Yield tokens
///      Unspecific types: Simply avoiding dependencies on other interfaces from our interfaces
interface ITranche is IERC5095 {
    /* ==================== ERRORS ===================== */

    error TimestampBeforeMaturity();
    error TimestampAfterMaturity();
    error ProtectedToken();
    error Unauthorized();
    error OnlyYT();
    error ReentrancyGuarded();
    error ZeroAddress();
    error NoAccruedYield();

    /* ==================== EVENTS ===================== */

    /// @param adapter the address of the adapter
    /// @param maturity timestamp of maturity (seconds since Unix epoch)
    /// @param issuanceFee fee for issuing PT and YT
    event SeriesCreated(address indexed adapter, uint256 indexed maturity, uint256 issuanceFee);

    /// @param from the sender of the underlying token
    /// @param to the recipient of the PT and YT
    /// @param underlyingUsed the amount of underlying token used to issue PT and YT
    /// @param sharesUsed the amount of target token used to issue PT and YT (before deducting issuance fee)
    event Issue(address indexed from, address indexed to, uint256 underlyingUsed, uint256 sharesUsed);

    /// @param owner the address of the owner of the PT and YT (address that called collect())
    /// @param shares the amount of Target token collected
    event Collect(address indexed owner, uint256 shares);

    /// @param owner the address of the owner of the PT and YT
    /// @param to the recipient of the underlying token redeemed
    /// @param underlyingRedeemed the amount of underlying token redeemed
    event RedeemWithYT(address indexed owner, address indexed to, uint256 underlyingRedeemed);

    /* ==================== STRUCTS ===================== */

    /// @notice Series is a struct that contains all the information about a series.
    /// @param underlying the address of the underlying token
    /// @param target the address of the target token
    /// @param yt the address of the Yield Token
    /// @param adapter the address of the adapter
    /// @param mscale scale value at maturity
    /// @param maxscale max scale value from this series' lifetime
    /// @param issuanceFee fee for issuing PT and YT
    /// @param maturity timestamp of maturity (seconds since Unix epoch)
    struct Series {
        address underlying;
        address target;
        address yt;
        address adapter;
        uint256 mscale;
        uint256 maxscale;
        uint64 issuanceFee;
        uint64 maturity;
    }

    /// @notice GlobalScales is a struct that contains scale values that are used in multiple functions throughout the Tranche contract.
    /// @param mscale scale value at maturity. before maturity and settlement, this value is 0.
    /// @param maxscale max scale value from this series' lifetime.
    struct GlobalScales {
        uint128 mscale;
        uint128 maxscale;
    }

    /* ================== MUTATIVE METHODS =================== */

    /// @notice deposit an `underlyingAmount` of underlying token into the yield source, receiving PT and YT.
    ///         amount of PT and YT issued are the same.
    /// @param   to the address to receive PT and YT
    /// @param   underlyingAmount the amount of underlying token to deposit
    /// @return  principalAmount the amount of PT and YT issued
    function issue(address to, uint256 underlyingAmount) external returns (uint256 principalAmount);

    /// @notice redeem an `principalAmount` of PT and YT for underlying token.
    /// @param from the address to burn PT and YT from
    /// @param to the address to receive underlying token
    /// @param pyAmount the amount of PT and YT to redeem
    /// @return underlyingAmount the amount of underlying token redeemed
    function redeemWithYT(address from, address to, uint256 pyAmount) external returns (uint256 underlyingAmount);

    /// @notice collect interest for `msg.sender` and transfer accrued interest to `msg.sender`
    /// NOTE: if the maturity has passed, all the YT balance of `msg.sender` is burned.
    /// @dev anyone can call this function to collect interest for themselves
    /// @return collected collected interest in Underlying token
    function collect() external returns (uint256 collected);

    /* ================== PERMISSIONED METHODS =================== */

    /// @notice collect interest from the yield source and distribute it
    ///         every YT transfer, this function is triggered by the Yield Token contract.
    ///         only the Yield Token contract can call this function.
    ///         NOTE: YT is not burned in this function even if the maturity has passed.
    /// @param from address to transfer the Yield Token from. i.e. the user who collects the interest.
    /// @param to address to transfer the Yield Token to (MUST NOT be zero address, CAN be the same as `from`)
    /// @param value amount of Yield Token transferred to `to` (CAN be 0)
    function updateUnclaimedYield(address from, address to, uint256 value) external;

    /* ================== VIEW METHODS =================== */

    /// @notice get the address of Yield Token associated with this Tranche.
    function yieldToken() external view returns (address);

    /// @notice get Series struct
    function getSeries() external view returns (Series memory);

    /// @notice get an accrued yield that can be claimed by `account` (in unis of Target token)
    /// @dev this is reset to 0 when `account` claims the yield.
    /// @param account the address to check
    /// @return accruedInTarget
    function unclaimedYields(address account) external view returns (uint256 accruedInTarget);

    /// @notice get an accrued yield that can be claimed by `account` (in unis of Underlying token)
    /// @param account the address to check
    /// @return accruedInUnderlying accrued yield in underlying token
    function previewCollect(address account) external view returns (uint256 accruedInUnderlying);
}

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

interface IBaseAdapter {
    /* ============== MUTATIVE METHODS =============== */

    /// @notice update adapter's scale value and return it
    ///         Underlying decimals: `u`, Target decimals: `t`, Target conversion rate: 10^u / 10^t
    ///         => Scale = 10^(u-t) * 10^18 = 10^(u-t+18)
    ///         e.g. WstETH (t=18,u=18) price: 1.2 WETH => scale = 1.2*10^18
    ///              eUSDC (t=18,u=6) price: 1.01 USDC => scale = 1.01*10^(6-18+18) = 1.01*10^6
    /// @dev For interest-bearing token, such as cTokens, this is simply the conversion rate
    /// @dev For other Targets, such as AMM LP shares, specialized logic will be required
    /// @return scale in units of underlying token
    function scale() external view returns (uint256);

    /// @notice deposit Underlying in return for Target.
    function deposit(uint256 underlyingUsed) external returns (uint256 shares);

    /// @notice redeem Target and receive Underlying in return.
    /// @dev no funds should be left in the contract after this call
    ///      the caller must transfer Target to this contract before calling this function.
    /// @param to recipient of Underlying
    /// @return underlyingWithdrawn amount of Underlying returned
    /// @return sharesRedeemed amount of Target redeemed
    function prefundedRedeem(address to) external returns (uint256 underlyingWithdrawn, uint256 sharesRedeemed);

    /* =============== VIEW METHODS ================ */

    /// @notice return Underlying token address (eg USDC, DAI)
    /// @return Underlying address
    function underlying() external view returns (address);

    /// @notice return yield-bearing token address (eg cUSDC, wstETH, AMM LP shares)
    /// @return Target address (yield-bearing token)
    function target() external view returns (address);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// https://github.com/curvefi/tricrypto-ng/blob/0bc1191b6097c8854e4f09e385f6c2c79a5bb773/contracts/main/CurveTricryptoOptimizedWETH.vy

import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";

interface CurveTricryptoOptimizedWETH is IERC20 {
    /// @notice Exchange using wrapped native token by default
    /// @param i Index value for the input coin
    /// @param j Index value for the output coin
    /// @param dx Amount of input coin being swapped in
    /// @param min_dy Minimum amount of output coin to receive
    /// @param use_eth True if the input coin is native token, False otherwise
    /// @param receiver Address to send the output coin to. Default is msg.sender
    /// @return uint256 Amount of tokens at index j received by the `receiver
    function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy, bool use_eth, address receiver)
        external
        payable
        returns (uint256);

    /// @notice Exchange with callback method.
    /// @dev This method does not allow swapping in native token, but does allow
    /// swaps that transfer out native token from the pool.
    /// @dev Does not allow flashloans
    /// @dev One use-case is to reduce the number of redundant ERC20 token
    /// transfers in zaps.
    /// @param i Index value for the input coin
    /// @param j Index value for the output coin
    /// @param dx Amount of input coin being swapped in
    /// @param min_dy Minimum amount of output coin to receive
    /// @param use_eth True if output is native token, False otherwise
    /// @param sender Address to transfer input coin from
    /// @param receiver Address to send the output coin to
    /// @param cb Callback signature
    /// @return uint256 Amount of tokens at index j received by the `receiver`
    function exchange_extended(
        uint256 i,
        uint256 j,
        uint256 dx,
        uint256 min_dy,
        bool use_eth,
        address sender,
        address receiver,
        bytes32 cb
    ) external returns (uint256);

    /// @notice Adds liquidity into the pool.
    /// @param amounts Amounts of each coin to add.
    /// @param min_mint_amount Minimum amount of LP to mint.
    function add_liquidity(uint256[3] calldata amounts, uint256 min_mint_amount) external payable returns (uint256);

    /// @notice Adds liquidity into the pool.
    /// @param amounts Amounts of each coin to add.
    /// @param min_mint_amount Minimum amount of LP to mint.
    /// @return uint256 Amount of LP tokens received by the `receiver
    /// @param use_eth True if native token is being added to the pool.
    /// @param receiver Address to send the LP tokens to. Default is msg.sender
    function add_liquidity(uint256[3] calldata amounts, uint256 min_mint_amount, bool use_eth, address receiver)
        external
        payable
        returns (uint256);

    /// @notice This withdrawal method is very safe, does no complex math since
    ///         tokens are withdrawn in balanced proportions. No fees are charged.
    /// @param amount Amount of LP tokens to burn
    /// @param min_amounts Minimum amounts of tokens to withdraw
    /// @param use_eth Whether to withdraw ETH or not
    /// @param receiver Address to send the withdrawn tokens to
    /// @param claim_admin_fees If True, call self._claim_admin_fees(). Default is True.
    /// @return uint256[3] Amount of pool tokens received by the `receiver`
    function remove_liquidity(
        uint256 amount,
        uint256[3] calldata min_amounts,
        bool use_eth,
        address receiver,
        bool claim_admin_fees
    ) external returns (uint256[3] memory);

    /// @notice Withdraw liquidity in a single token.
    /// Involves fees (lower than swap fees).
    /// @dev This operation also involves an admin fee claim.
    /// @param token_amount Amount of LP tokens to burn
    /// @param i Index of the token to withdraw
    /// @param min_amount Minimum amount of token to withdraw.
    /// @param use_eth Whether to withdraw ETH or not
    /// @param receiver Address to send the withdrawn tokens to
    /// @return Amount of tokens at index i received by the `receiver`
    function remove_liquidity_one_coin(
        uint256 token_amount,
        uint256 i,
        uint256 min_amount,
        bool use_eth,
        address receiver
    ) external returns (uint256);

    ///////////////////////////////////////////////////////////
    // View methods
    ///////////////////////////////////////////////////////////

    /// @notice Returns the balance of the coin at index `i`
    function balances(uint256 i) external view returns (uint256);

    /// @notice Calculate LP tokens minted or to be burned for depositing or
    ///         removing `amounts` of coins
    /// @dev Includes fee.
    /// @param amounts Amounts of tokens being deposited or withdrawn
    /// @param deposit True if it is a deposit action, False if withdrawn.
    /// @return uint256 Amount of LP tokens deposited or withdrawn.
    function calc_token_amount(uint256[3] calldata amounts, bool deposit) external view returns (uint256);

    function get_dy(uint256 i, uint256 j, uint256 dx) external view returns (uint256);

    function get_dx(uint256 i, uint256 j, uint256 dy) external view returns (uint256);

    /// @notice Calculates the current price of the LP token with respect to the coin at the 0th index
    /// @dev This function should be implemented to return the LP price
    /// @return The current LP price as a uint256
    function lp_price() external view returns (uint256);

    /// @notice calculate the current virtual price of the pool's LP token (in 18 decimals)
    /// @dev Non read-reenrant.
    /// @dev https://docs.curve.fi/cryptoswap-exchange/tricrypto-ng/pools/tricrypto/?h=virtual#get_virtual_price
    function get_virtual_price() external view returns (uint256);

    /// @notice Returns the oracle price of the coin at index `k` with respect to the coin at index 0
    /// @dev The oracle is an exponential moving average, with a periodicity determined internally.
    ///      The aggregated prices are cached state prices (dy/dx) calculated AFTER the latest trade.
    /// @param k The index of the coin for which the oracle price is needed (k = 0 or 1)
    /// @return The oracle price of the coin at index `k` as a uint256
    function price_oracle(uint256 k) external view returns (uint256);

    /// @notice Calculates output tokens with fee
    /// @param token_amount LP Token amount to burn
    /// @param i token in which liquidity is withdrawn
    /// @return uint256 Amount of ith tokens received for burning token_amount LP tokens.
    function calc_withdraw_one_coin(uint256 token_amount, uint256 i) external view returns (uint256);

    function calc_token_fee(uint256[3] calldata amounts, uint256[3] calldata xp) external view returns (uint256);

    function fee_calc(uint256[3] calldata xp) external view returns (uint256);

    /// @notice Returns i-th coin address.
    /// @param i Index of the coin. i must be 0, 1 or 2.
    function coins(uint256 i) external view returns (address);

    /// @dev Returns the address of the factory that created the pool.
    /// @return address The factory address.
    function factory() external view returns (address);

    function D() external view returns (uint256);

    /// @dev Returns the cached virtual price of the pool.
    function virtual_price() external view returns (uint256);

    /// @dev Returns the current pool amplification parameter.
    /// @return uint256 The A parameter.
    function A() external view returns (uint256);

    /// @dev Returns the current pool gamma parameter.
    /// @return uint256 The gamma parameter.
    function gamma() external view returns (uint256);

    ///////////////////////////////////////////////////////////
    // Protected methods
    ///////////////////////////////////////////////////////////

    /// @notice Initialise Ramping A and gamma parameter values linearly.
    /// @dev Only accessible by factory admin, and only
    /// @param future_A The future A value.
    /// @param future_gamma The future gamma value.
    /// @param future_time The timestamp at which the ramping will end.
    function ramp_A_gamma(uint256 future_A, uint256 future_gamma, uint256 future_time) external;
}

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

import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";
import {CurveTricryptoOptimizedWETH} from "./external/CurveTricryptoOptimizedWETH.sol";

import {PoolState} from "../libs/PoolMath.sol";

interface INapierPool {
    event Mint(address indexed receiver, uint256 liquidity, uint256 underlyingUsed, uint256 baseLptUsed);

    event Burn(address indexed receiver, uint256 liquidity, uint256 underlyingOut, uint256 baseLptOut);

    event Swap(
        address indexed caller,
        address indexed receiver,
        int256 netUnderlying,
        uint256 index,
        int256 netPt,
        uint256 swapFee,
        uint256 protocolFee
    );

    event SwapBaseLpt(
        address indexed caller,
        address indexed receiver,
        int256 netUnderlying,
        int256 netBaseLpt,
        uint256 swapFee,
        uint256 protocolFee
    );

    event UpdateLnImpliedRate(uint256 lnImpliedRate);

    /**
     * @notice Add liquidity to the pool with Underlying and base lp token.
     * Caller have to transfer tokens to this contract before calling this function.
     * @param underlyingInDesired The desired amount of underlying asset to add.
     * @param baseLptInDesired The desired amount of base lp token to add.
     * @param recipient The recipient of the liquidity tokens.
     * @param data Additional data for callback.
     * @return The amount of liquidity tokens received.
     */
    function addLiquidity(uint256 underlyingInDesired, uint256 baseLptInDesired, address recipient, bytes memory data)
        external
        returns (uint256);

    /**
     * @notice Remove liquidity from the pool.
     * Caller have to transfer Lp token to this contract before calling this function.
     * @param recipient The recipient of the assets.
     * @return The amounts of base lp token and underlying asset received.
     */
    function removeLiquidity(address recipient) external returns (uint256, uint256);

    /**
     * @notice Swap exact amount of PT for Underlying asset.
     * It supports flash swap by specifying the callback data.
     * Flash swap enables user to receive Underlying asset before paying PT.
     * If the pool contract received enough PT after the callback, the swap is successful. Otherwise, the swap is reverted.
     * @param index The index of the PT.
     * @param ptIn The amount of PT to swap.
     * @param recipient The recipient of the swapped underlying asset.
     * @param data Additional data for the flash swap.
     * @return The amount of underlying asset received.
     */
    function swapPtForUnderlying(uint256 index, uint256 ptIn, address recipient, bytes calldata data)
        external
        returns (uint256);

    /**
     * @notice Swap Underlying asset for exact amount of PT.
     * It supports flash swap by specifying the callback data.
     * It enables user to receive PT before paying Underlying asset.
     * if the pool contract received enough Underlying asset after the callback, the swap is successful. Otherwise, the swap is reverted.
     * @param index The index of the PT.
     * @param ptOut The desired amount of PT to receive.
     * @param recipient The recipient of the PT.
     * @param data Additional data for the flash swap.
     * @return The amount of PT received.
     */
    function swapUnderlyingForPt(uint256 index, uint256 ptOut, address recipient, bytes calldata data)
        external
        returns (uint256);

    /**
     * @notice Swap Underlying asset for exact amount of Base LP token.
     * @param baseLpOut The desired amount of Base LP token to receive.
     * @param recipient The recipient of the Base LP token.
     */
    function swapUnderlyingForExactBaseLpToken(uint256 baseLpOut, address recipient) external returns (uint256);

    /**
     * @notice Swap exact amount of Base LP token for Underlying asset.
     * @param recipient The recipient of the Underlying asset.
     */
    function swapExactBaseLpTokenForUnderlying(uint256 baseLptIn, address recipient) external returns (uint256);

    /**
     * @notice Maturity of the pool, in unix timestamp.
     * @dev Maturity is same as the maturity of Principal Token in the pool.
     */
    function maturity() external view returns (uint256);

    function totalUnderlying() external view returns (uint128);

    function totalBaseLpt() external view returns (uint128);

    function getAssets() external view returns (address, address);

    /**
     * @notice State of the pool.
     * @dev This function is not expected to be called on-chain.
     */
    function readState() external view returns (PoolState memory);

    function tricrypto() external view returns (CurveTricryptoOptimizedWETH);

    function principalTokens() external view returns (IERC20[3] memory);

    function lastLnImpliedRate() external view returns (uint256);
}

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

interface IPoolFactory {
    event Deployed(address indexed basePool, address indexed underlying, address indexed pool);
    event AuthorizedCallbackReceiver(address indexed callback);
    event RevokedCallbackReceiver(address indexed callback);

    struct PoolAssets {
        address basePool;
        address underlying;
        address[3] principalTokens;
    }

    struct PoolConfig {
        int256 initialAnchor;
        uint256 scalarRoot;
        uint80 lnFeeRateRoot;
        uint8 protocolFeePercent;
        address feeRecipient;
    }

    struct InitArgs {
        PoolAssets assets;
        PoolConfig configs;
    }

    /// @notice Deploy a new NapierPool contract.
    /// @dev Only the factory owner can call this function.
    /// @param basePool Base pool contract
    /// @param underlying underlying asset
    function deploy(address basePool, address underlying, PoolConfig calldata poolConfig) external returns (address);

    /// @notice Authorize swap callback
    /// @dev Only the factory owner can call this function.
    /// @param callback Callback receiver
    function authorizeCallbackReceiver(address callback) external;

    /// @notice Revoke swap callback authorization
    /// @dev Only the factory owner can call this function.
    /// @param callback Callback receiver
    function revokeCallbackReceiver(address callback) external;

    function isCallbackReceiverAuthorized(address callback) external view returns (bool);

    /// @notice calculate the address of a tranche with CREATE2 using the adapter and maturity as salt
    function poolFor(address basePool, address underlying) external view returns (address);

    /// @param pool a pool address
    /// @dev returns the pool parameters used to deploy the pool
    /// this function doesn't revert even if the pool doesn't exist. It returns the default values in that case.
    /// @return the pool parameters
    function getPoolAssets(address pool) external view returns (PoolAssets memory);

    /// @notice Owner of this contract
    function owner() external view returns (address);

    function args() external view returns (InitArgs memory);

    function POOL_CREATION_HASH() external view returns (bytes32);
}

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

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

/// @title Napier Router Interface
/// @dev Interface for the Router contract
/// @notice Router contract for interacting with Napier Yield Metapool and Base pool.
interface INapierRouter {
    ////////////////////////////////////////////////////////////////
    // PT-Underlying swap functions
    ////////////////////////////////////////////////////////////////

    /// @notice Swap exact amount of Principal Token (PT) for Underlying.
    /// @param pool The address of the pool.
    /// @param index The index of the PT.
    /// @param ptInDesired The amount of PT to swap.
    /// @param underlyingOutMin The minimum amount of underlying asset to receive.
    /// @param recipient The recipient of the swapped underlying asset.
    /// @param deadline The deadline for the swap.
    /// @return The amount of underlying asset received.
    function swapPtForUnderlying(
        address pool,
        uint256 index,
        uint256 ptInDesired,
        uint256 underlyingOutMin,
        address recipient,
        uint256 deadline
    ) external returns (uint256);

    /// @notice Swap underlying asset for exact amount of Principal Token (PT).
    /// @dev This function pulls underlying asset from msg.sender on callback because we don't know the exact amount of underlying asset to pull before the swap.
    /// @param pool The address of the pool.
    /// @param index The index of the PT.
    /// @param ptOutDesired The amount of PT to receive.
    /// @param underlyingInMax The maximum amount of underlying asset to spend.
    /// @param recipient The recipient of the PT token.
    /// @param deadline The deadline for the swap.
    /// @return The amount of PT token received.
    function swapUnderlyingForPt(
        address pool,
        uint256 index,
        uint256 ptOutDesired,
        uint256 underlyingInMax,
        address recipient,
        uint256 deadline
    ) external payable returns (uint256);

    ////////////////////////////////////////////////////////////////
    // YT-Underlying swap functions
    ////////////////////////////////////////////////////////////////

    /// @notice Variant of `swapUnderlyingForYt` with `ApproxParams` as an additional parameter.
    function swapUnderlyingForYt(
        address pool,
        uint256 index,
        uint256 ytOutDesired,
        uint256 underlyingInMax,
        address recipient,
        uint256 deadline,
        ApproxParams calldata approx
    ) external payable returns (uint256);

    /**
     * @notice Swap exact amount of Underlying for YT.
     * @param pool The address of the pool.
     * @param index The index of the YT.
     * @param ytOutDesired The amount of YT to receive.
     * @param underlyingInMax The maximum amount of underlying asset to spend.
     * @param recipient The recipient of the YT.
     * @param deadline The deadline for the swap.
     * @return The amount of YT received.
     */
    function swapUnderlyingForYt(
        address pool,
        uint256 index,
        uint256 ytOutDesired,
        uint256 underlyingInMax,
        address recipient,
        uint256 deadline
    ) external payable returns (uint256);

    /**
     * @notice Swap exact amount of YT for Underlying.
     * @param pool The address of the pool.
     * @param index The index of the YT.
     * @param ytIn The amount of YT to swap.
     * @param underlyingOutMin The minimum amount of underlying asset to receive.
     * @param recipient The recipient of the underlying asset.
     * @param deadline The deadline for the swap.
     * @return The amount of underlying asset received.
     */
    function swapYtForUnderlying(
        address pool,
        uint256 index,
        uint256 ytIn,
        uint256 underlyingOutMin,
        address recipient,
        uint256 deadline
    ) external returns (uint256);

    ////////////////////////////////////////////////////////////////
    // Liquidity-related functions
    ////////////////////////////////////////////////////////////////

    /// @notice Add liquidity to NapierPool and Curve Tricrypto pool.
    /// @param pool The address of the pool.
    /// @param underlyingIn The amount of underlying asset to deposit.
    /// @param ptsIn The amounts of PTs to deposit.
    /// @param minLiquidity The minimum amount of liquidity tokens to receive.
    /// @param recipient The recipient of the liquidity tokens.
    /// @param deadline The deadline for adding liquidity.
    /// @return The amount of liquidity tokens received.
    function addLiquidity(
        address pool,
        uint256 underlyingIn,
        uint256[3] calldata ptsIn,
        uint256 minLiquidity,
        address recipient,
        uint256 deadline
    ) external payable returns (uint256);

    /// @notice Add liquidity to the pool using a single Principal Token.
    /// @dev Swap some amount of PT for underlying asset so that the user can deposit tokens proportionally to the reserves.
    /// @param pool The address of the pool.
    /// @param index The index of the PT.
    /// @param amountIn The amount of PT to deposit.
    /// @param minLiquidity The minimum amount of liquidity tokens to receive.
    /// @param recipient The recipient of the liquidity tokens.
    /// @param deadline The deadline for adding liquidity.
    /// @param baseLpTokenSwap The estimated baseLpt amount to swap with underlying tokens.
    /// @return The amount of liquidity tokens received.
    function addLiquidityOnePt(
        address pool,
        uint256 index,
        uint256 amountIn,
        uint256 minLiquidity,
        address recipient,
        uint256 deadline,
        uint256 baseLpTokenSwap
    ) external returns (uint256);

    /// @notice Add liquidity to the pool using a single underlying asset.
    /// @dev Swap some underlying asset for Base pool token so that the user can deposit tokens proportionally to the reserves.
    /// @param pool The address of the pool.
    /// @param underlyingIn The amount of underlying asset to deposit.
    /// @param minLiquidity The minimum amount of liquidity tokens to receive.
    /// @param recipient The recipient of the liquidity tokens.
    /// @param deadline The deadline for adding liquidity.
    /// @param baseLpTokenSwap The estimated baseLpt amount to get from underlying tokens.
    /// @return The amount of liquidity tokens received.
    function addLiquidityOneUnderlying(
        address pool,
        uint256 underlyingIn,
        uint256 minLiquidity,
        address recipient,
        uint256 deadline,
        uint256 baseLpTokenSwap
    ) external returns (uint256);

    /// @notice Add liquidity to NapierPool with one underlying asset.
    /// @notice Caller must approve the router to spend underlying asset prior to calling this method.
    /// @dev Caller must specify the amount of underlying asset to be spent to issue PT and YT using off-chain calculation.
    /// @param pool The address of the pool.
    /// @param index The index of the issued PT / YT.
    /// @param underlyingIn The amount of underlying asset to deposit.
    /// @param minLiquidity The minimum amount of liquidity tokens to receive.
    /// @param recipient The recipient of the liquidity tokens.
    /// @param deadline The deadline for adding liquidity.
    /// @param underlyingDeposit The estimated underlying amount spent to issue PT and YT.
    /// @return The amount of liquidity tokens received.
    function addLiquidityOneUnderlyingKeepYt(
        address pool,
        uint256 index,
        uint256 underlyingIn,
        uint256 minLiquidity,
        uint256 ytOutMin,
        address recipient,
        uint256 deadline,
        uint256 underlyingDeposit
    ) external returns (uint256);

    /// @notice Remove liquidity from NapierPool and Curve Tricrypto pool.
    /// @param pool The address of the pool.
    /// @param liquidity The amount of liquidity tokens to burn.
    /// @param underlyingOutMin The minimum amount of underlying asset to receive.
    /// @param ptsOutMin The minimum amounts of PTs to receive.
    /// @param recipient The recipient of the PTs and underlying asset.
    /// @param deadline The deadline for removing liquidity.
    /// @return The amounts of PTs and underlying asset received.
    function removeLiquidity(
        address pool,
        uint256 liquidity,
        uint256 underlyingOutMin,
        uint256[3] calldata ptsOutMin,
        address recipient,
        uint256 deadline
    ) external returns (uint256, uint256[3] memory);

    /**
     * @notice Remove liquidity from the metapool and receive a single underlying asset.
     * @param pool The address of the pool.
     * @param index The index of PT to be withdrawn when removing liquidity from Base pool. Ignored if maturity has not passed.
     * @param liquidity The amount of liquidity tokens to remove.
     * @param underlyingOutMin The minimum amount of underlying asset to receive.
     * @param recipient The recipient of the underlying asset.
     * @param deadline The deadline for removing liquidity.
     * @return The amount of underlying asset received by `recipient`.
     */
    function removeLiquidityOneUnderlying(
        address pool,
        uint256 index,
        uint256 liquidity,
        uint256 underlyingOutMin,
        address recipient,
        uint256 deadline
    ) external returns (uint256);

    /**
     * @notice Remove liquidity from the metapool and receive a single PT.
     * @param pool The address of the pool.
     * @param index The index of the PT.
     * @param liquidity The amount of liquidity tokens to remove.
     * @param ptOutMin The minimum amount of PT to receive.
     * @param recipient The recipient of the PT.
     * @param deadline The deadline for removing liquidity.
     * @param baseLpTokenSwap The estimated baseLpt amount to swap with underlying tokens.
     * @return The amount of PT received by `recipient`.
     */
    function removeLiquidityOnePt(
        address pool,
        uint256 index,
        uint256 liquidity,
        uint256 ptOutMin,
        address recipient,
        uint256 deadline,
        uint256 baseLpTokenSwap
    ) external returns (uint256);
}

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

interface INapierMintCallback {
    /**
     * @notice Callback function to handle the add liquidity.
     * @param underlyingDelta The change in underlying.
     * @param baseLptDelta The change in Base pool LP token.
     * @param data Additional data passed to the callback. Can be used to pass context-specific information.
     */
    function mintCallback(uint256 underlyingDelta, uint256 baseLptDelta, bytes calldata data) external;
}

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

interface INapierSwapCallback {
    /**
     * @notice Callback function to handle the token swap.
     * @param underlyingDelta The change in underlying after the swap.
     * @param ptDelta The change in Principal token after the swap.
     * @param data Additional data passed to the callback. Can be used to pass context-specific information.
     */
    function swapCallback(int256 underlyingDelta, int256 ptDelta, bytes calldata data) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";
import {ITranche} from "@napier/v1-tranche/src/interfaces/ITranche.sol";

enum CallbackType {
    SwapPtForUnderlying,
    SwapUnderlyingForPt,
    SwapYtForUnderlying,
    SwapUnderlyingForYt,
    AddLiquidityPts,
    AddLiquidityOnePt,
    AddLiquidityOneUnderlying
}

library CallbackDataTypes {
    function getCallbackType(bytes calldata data) internal pure returns (CallbackType callbackType) {
        // Read the first 32 bytes of the calldata
        assembly {
            callbackType := calldataload(data.offset)
        }
    }

    struct AddLiquidityData {
        address payer;
        address underlying;
        address basePool;
    }

    struct SwapPtForUnderlyingData {
        address payer;
        IERC20 pt;
    }

    struct SwapUnderlyingForPtData {
        address payer;
        uint256 underlyingInMax;
    }

    struct SwapYtForUnderlyingData {
        address payer;
        ITranche pt;
        uint256 ytIn;
        address recipient;
        uint256 underlyingOutMin;
    }

    struct SwapUnderlyingForYtData {
        address payer;
        ITranche pt;
        IERC20 yt;
        address recipient;
        uint256 underlyingDeposit;
        uint256 maxUnderlyingPull;
    }
}

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

import {INapierPool} from "../interfaces/INapierPool.sol";
import {Create2} from "@openzeppelin/[email protected]/utils/Create2.sol";

library PoolAddress {
    function computeAddress(address basePool, address underlying, bytes32 initHash, address factory)
        internal
        pure
        returns (INapierPool pool)
    {
        // Optimize salt computation
        // https://www.rareskills.io/post/gas-optimization#viewer-ed7oh
        // https://github.com/dragonfly-xyz/useful-solidity-patterns/tree/main/patterns/assembly-tricks-1#hash-two-words
        bytes32 salt;
        assembly {
            // Clean the upper 96 bits of `basePool` in case they are dirty.
            mstore(0x00, shr(96, shl(96, basePool)))
            mstore(0x20, shr(96, shl(96, underlying)))
            salt := keccak256(0x00, 0x40)
        }
        pool = INapierPool(Create2.computeAddress(salt, initHash, factory));
    }
}

File 15 of 59 : ApproxParams.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;

// Taken from: Pendle finance v2

/// @notice Parameters for controlling the approximation process
/// @dev The approximation process is a binary search algorithm that finds the value that satisfies the provided function `f`.
/// By default, NapierPool defines swap formula in terms of Principal token (Base Pool LP token). To swap a given amount of Underlying token,
/// it's necessary to run an approximation algorithm to find the corresponding amount of Principal token to swap in/out
/// because computing inverse of the swap function is very hard.
/// The approximation algorithm will run as follows:
/// Let f(x) be the function that calculates difference between the desired value and the computed value for a given x (the amount of Base pool LP token to swap in/out)
/// The algorithm will find the value x that satisfies f(x) ~= ε, where ε is the relative error tolerance.
/// for a given range [a, b],
/// ```
///     mid = (a + b) / 2
///     error_mid = f(mid)
///     if error_mid <= eps
///         return mid
///     if error_mid > 0
///         a = mid
///     else
///         b = mid
/// ```
/// The algorithm will run for `maxIteration` times, or until the relative error tolerance `eps` is satisfied.
/// @param guessMin The lower bound of the guess range
/// @param guessMax The upper bound of the guess range
/// @param maxIteration The maximum number of iterations to run the approximation algorithm
/// @param eps The maximum relative error tolerance (in 18 decimals) between the desired value and the computed value. 0.1% = 1e15 (1e18/1000)
struct ApproxParams {
    uint256 guessMin;
    uint256 guessMax;
    uint256 maxIteration;
    uint256 eps;
}

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

import {ERC20} from "@openzeppelin/[email protected]/token/ERC20/ERC20.sol";
import {ITranche} from "@napier/v1-tranche/src/interfaces/ITranche.sol";
import {IBaseAdapter} from "@napier/v1-tranche/src/interfaces/IBaseAdapter.sol";
import {ApproxParams} from "..//interfaces/ApproxParams.sol";

import {FixedPointMathLib} from "@napier/v1-tranche/src/utils/FixedPointMathLib.sol";
import {Math} from "@openzeppelin/[email protected]/utils/math/Math.sol";
import {SafeCast} from "@openzeppelin/[email protected]/utils/math/SafeCast.sol";
import {MAX_BPS} from "@napier/v1-tranche/src/Constants.sol";
import {Errors} from "./Errors.sol";

library TrancheMathHelper {
    using SafeCast for uint256;

    uint256 constant DEFAULT_MAX_ITERATION = 100;
    uint256 constant MAX_ISSUANCE_FEE_BPS = 500; // 5%

    function getApproxUnderlyingNeededByYt(ITranche pt, uint256 ytDesired) internal view returns (uint256) {
        return getApproxUnderlyingNeededByYt(pt, ytDesired, ApproxParams(0, 0, 0, 0));
    }

    /// @notice This section of code aims to calculate the amount of underlying asset (`uDeposit`) required to issue a specific amount of PT and YT (`ytOutDesired`).
    /// The calculations are based on the formula used in the `Tranche.issue` function.
    function getApproxUnderlyingNeededByYt(ITranche pt, uint256 ytDesired, ApproxParams memory approx)
        internal
        view
        returns (uint256)
    {
        // Default approx parameters if not set
        if (approx.guessMax < approx.guessMin) revert Errors.ApproxBinarySearchInputInvalid();
        if (approx.eps == 0) approx.eps = 0.05 * 1e18; // 5%
        if (approx.maxIteration == 0) approx.maxIteration = DEFAULT_MAX_ITERATION;

        ITranche.Series memory series = pt.getSeries();
        uint256 cscale = IBaseAdapter(series.adapter).scale();
        IssueParams memory params = IssueParams({
            decimals: ERC20(address(pt)).decimals(),
            cscale: cscale,
            maxscale: Math.max(series.maxscale, cscale), // Update maxscale if current scale is greater than maxscale
            issuanceFeeBps: series.issuanceFee
        });
        // Variable Definitions:
        // - `uDeposit`: The amount of underlying asset that needs to be deposited to issue PT and YT.
        // - `ytOutDesired`: The desired amount of PT and YT to be issued.
        // - `cscale`: Current scale of the Tranche.
        // - `maxscale`: Maximum scale of the Tranche (denoted as 'S' in the formula).
        // - `issuanceFee`: Issuance fee in basis points. (10000 =100%).
        // `uDeposit` amount of underlying should issue at least `ytOutDesired` amount of PT and YT.
        // Issuance fee is charged in units of underlying token.
        // Formula for `Tranche.issue`:
        // ```
        // fee = uDeposit * issuanceFeeBps
        // shares = (uDeposit - fee) / s
        // pyIssue = shares * S
        // ```
        // Solving for `uDeposit`:
        // ```
        // uDeposit = pyIssue * s / S / (1 - issuanceFeeBps)
        //          => pyIssue * s * MAX_BPS / (S * (MAX_BPS - issuanceFeeBps))
        // ```
        // However, we can't get correct `uDeposit` due to the precision loss, probably indirectly caused by the issuance fee mechanism.

        // Estimate the maximum amount of underlying token
        uint256 uDepositMax = FixedPointMathLib.mulDivUp(
            // cscale is basically a share price which is usually rounded down.
            // So, we need to add 1 to cscale to round up the share price
            ytDesired * (cscale + 1),
            MAX_BPS,
            params.maxscale * (MAX_BPS - MAX_ISSUANCE_FEE_BPS)
        );
        // We use bisection as a workaround.
        return
            _bisectUnderlyingNeeded({params: params, ytDesired: ytDesired, uDepositGuess: uDepositMax, approx: approx});
    }

    /// @notice Variables to be cached
    struct IssueParams {
        uint256 decimals;
        uint256 cscale;
        uint256 maxscale;
        uint256 issuanceFeeBps;
    }

    /// @notice This function uses bisection to find [uDeposit] such that `Tranche::issue` would mint at least `ytDesired` YT.
    /// @param params - Variables to be cached for gas saving
    /// @param ytDesired - A desired amount of YT to issue
    /// @param uDepositGuess - An amount of underlying token that would issue less than `ytDesired` YT.
    function _bisectUnderlyingNeeded(
        IssueParams memory params,
        uint256 ytDesired,
        uint256 uDepositGuess,
        ApproxParams memory approx
    ) internal pure returns (uint256) {
        uint256 stepSize = 10 ** params.decimals; // 1 Underlying token
        uint256 a = FixedPointMathLib.mulDivUp(ytDesired, params.cscale, params.maxscale);
        uint256 b = uDepositGuess + stepSize; // upper bound

        if (approx.guessMin != 0) a = Math.max(approx.guessMin, a);
        if (approx.guessMax != 0) b = Math.min(approx.guessMax, b);

        // Try to find an min amount of underlying token such that the issuing at least `ytDesired`.
        // Bisect the interval [a, b].
        uint256 midpoint;
        for (uint256 i = 0; i != approx.maxIteration;) {
            midpoint = (a + b) / 2;
            uint256 preview = _previewIssue(params, midpoint);
            int256 err_mid = 1e18 - (preview * 1e18 / ytDesired).toInt256(); // v_desired - v_approx
            // Check if the relative error is less than the tolerance
            if (preview >= ytDesired && -(approx.eps).toInt256() < err_mid) {
                return midpoint;
            }
            // a == b ---> midpoint is `b` forever
            // a+1 == b ---> midpoint is `b` forever
            // Exit the loop if midpoin doesn't change
            if (a == b || (a + 1 == b)) break;

            if (err_mid > 0) {
                // bound interval [midpoint, b]
                a = midpoint;
            } else {
                // bound interval [a, midpoint]
                b = midpoint;
            }
            unchecked {
                ++i;
            }
        }
        // If the function hasn't returned by now, it means it didn't find a solution within the tolerance.
        // Try changing the tolerance.
        revert Errors.ApproxFail();
    }

    /// @notice A copy of `Tranche::issue` math
    function _previewIssue(IssueParams memory params, uint256 underlyingAmount) internal pure returns (uint256) {
        uint256 fee = FixedPointMathLib.mulDivUp(underlyingAmount, params.issuanceFeeBps, MAX_BPS);
        uint256 shares = FixedPointMathLib.divWadDown(underlyingAmount - fee, params.cscale);
        return FixedPointMathLib.mulWadDown(shares, params.maxscale);
    }
}

File 17 of 59 : Errors.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;

library Errors {
    // Approx
    error ApproxFail();
    error ApproxBinarySearchInputInvalid();

    // Quoter
    error ApproxFailWithHint(bytes hint);

    // Factory
    error FactoryPoolAlreadyExists();
    error FactoryUnderlyingMismatch();
    error FactoryMaturityMismatch();

    // Pool
    error PoolOnlyOwner();
    error PoolInvalidParamName();
    error PoolUnauthorizedCallback();
    error PoolExpired();
    error PoolInvariantViolated();
    error PoolZeroAmountsInput();
    error PoolZeroAmountsOutput();
    error PoolZeroLnImpliedRate();
    error PoolInsufficientBaseLptForTrade();
    error PoolInsufficientBaseLptReceived();
    error PoolInsufficientUnderlyingReceived();
    error PoolExchangeRateBelowOne(int256 exchangeRate);
    error PoolProportionMustNotEqualOne();
    error PoolRateScalarZero();
    error PoolProportionTooHigh();

    // Router
    error RouterInsufficientWETH();
    error RouterInconsistentWETHPayment();
    error RouterPoolNotFound();
    error RouterTransactionTooOld();
    error RouterInsufficientLpOut();
    error RouterInsufficientTokenBalance();
    error RouterInsufficientUnderlyingOut();
    error RouterInsufficientYtOut();
    error RouterExceededLimitUnderlyingIn();
    error RouterInsufficientUnderlyingRepay();
    error RouterInsufficientPtRepay();
    error RouterCallbackNotNapierPool();
    error RouterNonSituationSwapUnderlyingForYt();
    error RouterInsufficientPyIssue();

    // Generic
    error FailedToSendEther();
    error NotWETH();

    // Config
    error LnFeeRateRootTooHigh();
    error ProtocolFeePercentTooHigh();
    error InitialAnchorTooLow();
}

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

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

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

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

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

File 19 of 59 : PeripheryImmutableState.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;

import {IWETH9} from "@napier/v1-tranche/src/interfaces/IWETH9.sol";

/// @title Periphery Immutable State
/// @notice Common immutable state used by periphery contracts
abstract contract PeripheryImmutableState {
    /// @notice Wrapped Ether
    IWETH9 public immutable WETH9;

    constructor(IWETH9 _WETH9) {
        WETH9 = _WETH9;
    }
}

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

// interfaces
import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";
// libraries
import {SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";
import {Errors} from "../libs/Errors.sol";
// inherits
import {PeripheryImmutableState} from "./PeripheryImmutableState.sol";

/// @notice Payments utility contract for periphery contracts
/// @dev Taken and modified from Uniswap v3 periphery PeripheryPayments: https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/PeripheryPayments.sol
abstract contract PeripheryPayments is PeripheryImmutableState {
    using SafeERC20 for IERC20;

    receive() external payable {
        if (msg.sender != address(WETH9)) revert Errors.NotWETH();
    }

    /// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH.
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
    /// @notice Unwrap WETH9 and send to recipient
    /// @param amountMinimum The minimum amount of WETH9 to unwrap
    /// @param recipient The entity that will receive the ether
    function unwrapWETH9(uint256 amountMinimum, address recipient) external payable {
        uint256 balanceWETH9 = WETH9.balanceOf(address(this));
        if (balanceWETH9 < amountMinimum) revert Errors.RouterInsufficientWETH();

        if (balanceWETH9 > 0) {
            WETH9.withdraw(balanceWETH9);
            _safeTransferETH(recipient, balanceWETH9);
        }
    }

    /// @notice Transfers the full amount of a token held by this contract to recipient
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
    /// @param token The contract address of the token which will be transferred to `recipient`
    /// @param amountMinimum The minimum amount of token required for a transfer
    /// @param recipient The destination address of the token
    function sweepToken(address token, uint256 amountMinimum, address recipient) public payable {
        uint256 balanceToken = IERC20(token).balanceOf(address(this));
        if (balanceToken < amountMinimum) revert Errors.RouterInsufficientTokenBalance();

        if (balanceToken > 0) {
            IERC20(token).safeTransfer(recipient, balanceToken);
        }
    }

    /// @notice Transfers the full amount of multiple tokens held by this contract to recipient
    /// @dev Batched version of `sweepToken`
    function sweepTokens(address[] calldata tokens, uint256[] calldata amountMinimums, address recipient)
        external
        payable
    {
        require(tokens.length == amountMinimums.length);
        for (uint256 i; i < tokens.length;) {
            // Not caching length saves gas in this case.
            sweepToken(tokens[i], amountMinimums[i], recipient);
            unchecked {
                ++i;
            }
        }
    }

    /// @notice Refund ether to sender
    function refundETH() external payable {
        if (address(this).balance > 0) _safeTransferETH(msg.sender, address(this).balance);
    }

    /// @notice transfer ether safely
    function _safeTransferETH(address to, uint256 value) internal {
        (bool success,) = to.call{value: value}(new bytes(0));
        if (!success) revert Errors.FailedToSendEther();
    }

    /// @dev Pay with token or WEH9 if the contract has enough ether
    /// @param token The token to pay
    /// @param payer The entity that must pay
    /// @param recipient The entity that will receive payment
    /// @param value The amount to pay
    function _pay(address token, address payer, address recipient, uint256 value) internal {
        if (token == address(WETH9) && address(this).balance >= value) {
            // pay with WETH9
            WETH9.deposit{value: value}(); // wrap only what is needed to pay
            WETH9.transfer(recipient, value);
        } else if (payer == address(this)) {
            IERC20(token).safeTransfer(recipient, value);
        } else {
            // pull payment

            // note: Check value sent to this contract is zero if token is WETH9
            // Corner case: A situation where the `msg.value` sent is not enough to satisfy `address(this).balance >= value`.
            // In such conditions, if we wouldn't revert, `IERC20(WETH).safeTransferFrom(payer, recipient, value)` will be executed,
            // and the `msg.value` will remain in the Router, potentially allowing the attacker to claim it.
            // This is why we ensure that the `msg.value` is zero for pulling WETH.

            // note: NapierRouter inherits from PeripheryPayments and Multicallable.
            // Basically, using `msg.value` in a loop can be dangerous but in this case, `msg.value` is not used for accounting purposes.
            if (token == address(WETH9) && msg.value > 0) revert Errors.RouterInconsistentWETHPayment();
            IERC20(token).safeTransferFrom(payer, recipient, value);
        }
    }
}

File 21 of 59 : Multicallable.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.19;

/// @title Multicallable
/// @notice Enables calling multiple methods in a single call to the contract
/// @dev Forked from Uniswap v3 periphery: https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/Multicallable.sol
/// @dev Apply `DELEGATECALL` with the current contract to each calldata in `data`,
/// and store the `abi.encode` formatted results of each `DELEGATECALL` into `results`.
/// If any of the `DELEGATECALL`s reverts, the entire context is reverted,
/// and the error is bubbled up.
///
// Combining Multicallable with msg.value can cause double spending issues.
/// (See: https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong)
abstract contract Multicallable {
    function multicall(bytes[] calldata data) external payable returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length;) {
            (bool success, bytes memory returndata) = address(this).delegatecall(data[i]);

            if (!success) {
                // Bubble up the revert message.
                // https://github.com/dragonfly-xyz/useful-solidity-patterns/tree/main/patterns/error-handling
                // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/36bf1e46fa811f0f07d38eb9cfbc69a955f300ce/contracts/utils/Address.sol#L151-L154
                assembly {
                    revert(
                        // Start of revert data bytes.
                        add(returndata, 0x20),
                        // Length of revert data.
                        mload(returndata)
                    )
                }
            }

            results[i] = returndata;

            unchecked {
                ++i;
            }
        }
    }
}

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

import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";

/// @notice Principal tokens (zero-coupon tokens) are redeemable for a single underlying EIP-20 token at a future timestamp.
///         https://eips.ethereum.org/EIPS/eip-5095
interface IERC5095 is IERC20 {
    event Redeem(address indexed from, address indexed to, uint256 underlyingAmount);

    /// @dev Asset that is returned on redemption.
    function underlying() external view returns (address underlyingAddress);

    /// @dev Unix time at which redemption of fyToken for underlying are possible
    function maturity() external view returns (uint256 timestamp);

    /// @dev Converts a specified amount of principal to underlying
    function convertToUnderlying(uint256 principalAmount) external view returns (uint256 underlyingAmount);

    /// @dev Converts a specified amount of underlying to principal
    function convertToPrincipal(uint256 underlyingAmount) external view returns (uint256 principalAmount);

    /// @dev Gives the maximum amount an address holder can redeem in terms of the principal
    function maxRedeem(address holder) external view returns (uint256 maxPrincipalAmount);

    /// @dev Gives the amount in terms of underlying that the princiapl amount can be redeemed for plus accrual
    function previewRedeem(uint256 principalAmount) external view returns (uint256 underlyingAmount);

    /// @dev Burn fyToken after maturity for an amount of principal.
    function redeem(uint256 principalAmount, address to, address from) external returns (uint256 underlyingAmount);

    /// @dev Gives the maximum amount an address holder can withdraw in terms of the underlying
    function maxWithdraw(address holder) external view returns (uint256 maxUnderlyingAmount);

    /// @dev Gives the amount in terms of principal that the underlying amount can be withdrawn for plus accrual
    function previewWithdraw(uint256 underlyingAmount) external view returns (uint256 principalAmount);

    /// @dev Burn fyToken after maturity for an amount of underlying.
    function withdraw(uint256 underlyingAmount, address to, address from) external returns (uint256 principalAmount);
}

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

/// @notice This library contains the math used in NapierPool.
/// @dev Taken and modified from Pendle V2: https://github.com/pendle-finance/pendle-core-v2-public/blob/163783b09014e515b645b83936fec32c5731d092/contracts/core/Market/MarketMathCore.sol
/// @dev Taken and modified from Notional : https://github.com/notional-finance/contracts-v2/blob/1845605ab0d9eec9b5dd374cf7c246957b534f85/contracts/internal/markets/Market.sol
/// @dev Naming convention:
/// - `pt` => baseLpt: BasePool LP token
/// - `asset` => `underlying`: underlying asset
/// - `totalPt` => `totalBaseLptTimesN`: total BasePool LP token reserve in the pool multiplied by 3 * virtual_price where virtual_price is the share price of the Tricrypto LP token
/// See NapierPool.sol for more details.
/// - `totalAsset` => `totalUnderlying`: total underlying asset reserve in the pool
/// - `executeTradeCore` function =>  `executeSwap` function
/// - `calculateTrade` function => `calculateSwap` function
/// - `getMarketPreCompute` function => `computeAmmParameters` function
/// - `setNewMarketStateTrade` function => `_setPostPoolState` function
/// @dev All functions in this library are view functions.
/// @dev Changes:
///  1) Math library dependency from LogExpMath to PRBMath etc.
///  2) Swap functions multiply the parameter `exactPtToAccount` by N(=3) to make it equivalent to the amount of PT being swapped.
///  3) Swap functions divide the computed underlying swap result by N.
///  3) Remove some redundant checks (e.g. check for maturity)
///  4) Remove some redundant variables (e.g. `totalAsset` in `MarketPreCompute`)
///  5) Remove some redundant functions (`addLiquidity` and `removeLiquidity`)

// libraries
import {Math} from "@openzeppelin/[email protected]/utils/math/Math.sol";
import {SafeCast} from "@openzeppelin/[email protected]/utils/math/SafeCast.sol";
import {FixedPointMathLib} from "@napier/v1-tranche/src/utils/FixedPointMathLib.sol";
import {SignedMath} from "./SignedMath.sol";
import {sd, ln, intoInt256} from "@prb/math/SD59x18.sol"; // used for logarithm operation
import {ud, exp, intoUint256} from "@prb/math/UD60x18.sol"; // used for exp operation
import {Errors} from "./Errors.sol";

/// @param totalBaseLptTimesN - Reserve Curve v2 Tricrypto 3PrincipalToken Pool LP token x times N(=# of Curve v2 Pool assets) in 18 decimals
/// @param totalUnderlying18 - Reserve underlying asset in 18 decimals
/// @param scalarRoot - Scalar root for NapierPool (See whitepaper)
/// @param maturity - Expiry of NapierPool (Unix timestamp)
/// @param lnFeeRateRoot - Logarithmic fee rate root
/// @param protocolFeePercent - Protocol fee percent (base 100)
/// @param lastLnImpliedRate - Last ln implied rate
struct PoolState {
    uint256 totalBaseLptTimesN;
    uint256 totalUnderlying18;
    // Tricrypto pool LP token virtual price
    uint256 virtualPrice;
    /// immutable variables ///
    uint256 scalarRoot;
    uint256 maturity;
    /// fee data ///
    uint256 lnFeeRateRoot;
    uint256 protocolFeePercent; // 100=100%
    /// last trade data ///
    uint256 lastLnImpliedRate;
}

/// @notice Variables that are used to compute the swap result
/// @dev params that are expensive to compute, therefore we pre-compute them
struct PoolPreCompute {
    int256 rateScalar;
    int256 rateAnchor;
    int256 feeRate;
}

/// @title PoolMath - library for calculating swaps
/// @notice Taken and modified from Pendle V2: https://github.com/pendle-finance/pendle-core-v2-public/blob/163783b09014e515b645b83936fec32c5731d092/contracts/core/Market/MarketMathCore.sol
/// @dev Swaps take place between the BasePool LP token and the underlying asset.
/// The BasePool LP token is basket of 3 principal tokens.
/// @dev The AMM formula is defined in terms of the amount of PT being swapped.
/// @dev The math assumes two tokens (pt and underlying) have same decimals. Need to convert if they have different decimals.
/// @dev All functions in this library are view functions.
library PoolMath {
    /// @notice Minimum liquidity in the pool
    uint256 internal constant MINIMUM_LIQUIDITY = 10 ** 3;
    /// @notice Percentage base (100=100%)
    int256 internal constant FULL_PERCENTAGE = 100;
    /// @notice Day in seconds in Unix timestamp
    uint256 internal constant DAY = 86400;
    /// @notice Year in seconds in Unix timestamp
    uint256 internal constant IMPLIED_RATE_TIME = 365 * DAY;

    /// @notice Max proportion of BasePool LP token / (BasePool LP token + underlying asset) in the pool
    uint256 internal constant MAX_POOL_PROPORTION = 0.96 * 1e18; // 96%

    int256 internal constant N_COINS = 3;

    using FixedPointMathLib for uint256;
    using SignedMath for int256;
    using SignedMath for uint256;
    using SafeCast for uint256;
    using SafeCast for int256;

    /// @param pool State - pool state of the pool
    /// @param exactBaseLptIn - exact amount of Base Pool LP tokens to be swapped in
    /// @return underlyingOut18 - underlying tokens to be swapped out (18 decimals)
    /// @return swapFee18 - swap fee in underlying (18 decimals)
    /// @return protocolFee18 - protocol fee in underlying (18 decimals)
    function swapExactBaseLpTokenForUnderlying(PoolState memory pool, uint256 exactBaseLptIn)
        internal
        view
        returns (uint256 underlyingOut18, uint256 swapFee18, uint256 protocolFee18)
    {
        (int256 _netUnderlyingToAccount18, int256 _netUnderlyingFee18, int256 _netUnderlyingToProtocol18) = executeSwap(
            pool,
            // Note: sign is defined from the perspective of the swapper.
            // negative because the swapper is selling pt
            // Note: Here we are multiplying by virtualPrice * N_COINS because the swap formula is defined in terms of the amount of PT being swapped.
            // Basically BaseLpt is equivalent to more than 3 times the amount of PT due to the initial deposit of 1:1:1:1=pt1:pt2:pt3:Lp share in Curve pool.
            // The LP token accrues trade fees on the Tricrypto pool and the virtual price’s value would increase over time.
            FixedPointMathLib.mulWadDown(exactBaseLptIn, pool.virtualPrice * uint256(N_COINS)).neg() // user would get smaller amount of underlying due to the rounding down
        );

        underlyingOut18 = _netUnderlyingToAccount18.toUint256();
        swapFee18 = _netUnderlyingFee18.toUint256();
        protocolFee18 = _netUnderlyingToProtocol18.toUint256();
    }

    /// @param pool State - pool state of the pool
    /// @param exactBaseLptOut exact amount of Base Pool LP tokens to be swapped out
    /// @return underlyingIn18 - underlying tokens to be swapped in (18 decimals)
    /// @return swapFee18 - swap fee in underlying (18 decimals)
    /// @return protocolFee18 - protocol fee in underlying (18 decimals)
    function swapUnderlyingForExactBaseLpToken(PoolState memory pool, uint256 exactBaseLptOut)
        internal
        view
        returns (uint256 underlyingIn18, uint256 swapFee18, uint256 protocolFee18)
    {
        (int256 _netUnderlyingToAccount18, int256 _netUnderlyingFee18, int256 _netUnderlyingToProtocol18) = executeSwap(
            pool,
            // Note: sign is defined from the perspective of the swapper.
            // positive because the swapper is buying pt
            FixedPointMathLib.mulWadUp(exactBaseLptOut, pool.virtualPrice * uint256(N_COINS)).toInt256() // user would need to pay more underlying due to the rounding up
        );

        underlyingIn18 = _netUnderlyingToAccount18.neg().toUint256();
        swapFee18 = _netUnderlyingFee18.toUint256();
        protocolFee18 = _netUnderlyingToProtocol18.toUint256();
    }

    /// @notice Compute swap result given the amount of base pool LP tokens to be swapped in.
    /// @dev This function is used to compute the swap result before the swap is executed.
    /// @param pool State - pool state of the pool
    /// @param netBaseLptToAccount (int256) amount of base pool LP tokens to be swapped in (negative if selling pt) multiplied by the number of BasePool assets
    /// Note: sign is defined from the perspective of the swapper. positive if the swapper is buying pt.
    /// @return netUnderlyingToAccount18 (int256) amount of underlying tokens to be swapped out
    /// @return netUnderlyingFee18 (int256) total fee. including protocol fee.
    /// `netUnderlyingFee18 - netUnderlyingToProtocol` will be distributed to LP holders.
    /// @return netUnderlyingToProtocol18 (int256) Protocol fee
    function executeSwap(PoolState memory pool, int256 netBaseLptToAccount)
        internal
        view
        returns (int256 netUnderlyingToAccount18, int256 netUnderlyingFee18, int256 netUnderlyingToProtocol18)
    {
        if (pool.totalBaseLptTimesN.toInt256() <= netBaseLptToAccount) {
            revert Errors.PoolInsufficientBaseLptForTrade();
        }

        /// ------------------------------------------------------------
        /// MATH
        /// ------------------------------------------------------------
        PoolPreCompute memory comp = computeAmmParameters(pool);

        (netUnderlyingToAccount18, netUnderlyingFee18, netUnderlyingToProtocol18) =
            calculateSwap(pool, comp, netBaseLptToAccount);
        /// ------------------------------------------------------------
        /// WRITE
        /// ------------------------------------------------------------
        _setPostPoolState(pool, comp, netBaseLptToAccount, netUnderlyingToAccount18, netUnderlyingToProtocol18);
    }

    /// @notice Compute the pseudo invariant of the pool.
    /// @dev The pseudo invariant is computed every swap before the swap is executed.
    /// @param pool State - pool state of the pool
    function computeAmmParameters(PoolState memory pool) internal view returns (PoolPreCompute memory cache) {
        uint256 timeToExpiry = pool.maturity - block.timestamp;

        cache.rateScalar = _getRateScalar(pool, timeToExpiry);
        cache.rateAnchor = _getRateAnchor(
            pool.totalBaseLptTimesN, pool.lastLnImpliedRate, pool.totalUnderlying18, cache.rateScalar, timeToExpiry
        );
        cache.feeRate = _getExchangeRateFromImpliedRate(pool.lnFeeRateRoot, timeToExpiry);
    }

    /// @notice Calculate the new `RateAnchor(t)` based on the pre-trade implied rate, `lastImpliedRate`, before the swap.
    /// To ensure interest rate continuity, we adjust the `rateAnchor(t)` such that the pre-trade implied rate at t* remains the same as `lastImpliedRate`.
    ///
    /// Formulas for `rateAnchor(t)`:
    /// ----------------------------
    /// yearsToExpiry(t) = timeToExpiry / 365 days
    ///
    /// portion(t*) = totalBaseLptTimesN / (totalBaseLptTimesN + totalUnderlying18)
    ///
    /// extRate(t*) = lastImpliedRate^(yearsToExpiry(t))
    ///              = e^(ln(lastImpliedRate) * yearsToExpiry(t))
    ///
    /// rateAnchor(t) = extRate(t*) - ln(portion(t*)) / rateScalar(t)
    /// ----------------------------
    /// Where `portion(t*)` represents the portion of the pool that is BasePool LP token at t* and `extRate(t*)` is the exchange rate at t*.
    ///
    /// @param totalBaseLptTimesN total Base Lp token in the pool
    /// @param lastLnImpliedRate the implied rate for the last trade that occurred at t_last.
    /// @param totalUnderlying18 total underlying in the pool
    /// @param rateScalar a parameter of swap formula. Calculated as  `scalarRoot` divided by `yearsToExpiry`
    /// @param timeToExpiry time to maturity in seconds
    /// @return rateAnchor the new rate anchor
    function _getRateAnchor(
        uint256 totalBaseLptTimesN,
        uint256 lastLnImpliedRate,
        uint256 totalUnderlying18,
        int256 rateScalar,
        uint256 timeToExpiry
    ) internal pure returns (int256 rateAnchor) {
        // `extRate(t*) = e^(lastLnImpliedRate * yearsToExpiry(t))`
        // Get pre-trade exchange rate with zero-fee
        int256 preTradeExchangeRate = _getExchangeRateFromImpliedRate(lastLnImpliedRate, timeToExpiry);
        // exchangeRate should not be below 1.
        // But it is mathematically almost impossible to happen because `exp(x) < 1` is satisfied for all `x < 0`.
        // Here x = lastLnImpliedRate * yearsToExpiry(t), which is very unlikely to be negative.(or
        // more accurately the natural log rounds down to zero). `lastLnImpliedRate` is guaranteed to be positive when it is set
        // and `yearsToExpiry(t)` is guaranteed to be positive because swap can only happen before maturity.
        // We still check for this case to be safe.
        require(preTradeExchangeRate >= SignedMath.WAD);
        uint256 proportion = totalBaseLptTimesN.divWadDown(totalBaseLptTimesN + totalUnderlying18);
        int256 lnProportion = _logProportion(proportion);

        // Compute `rateAnchor(t) = extRate(t*) - ln(portion(t*)) / rateScalar(t)`
        rateAnchor = preTradeExchangeRate - lnProportion.divWadDown(rateScalar);
    }

    /// @notice Converts an implied rate to an exchange rate given a time to maturity. The
    /// @dev Formula: `E = e^rt`
    /// @return exchangeRate the price of underlying token in Base LP token. Guaranteed to be positive or zero.
    function _getExchangeRateFromImpliedRate(uint256 lnImpliedRate, uint256 timeToExpiry)
        internal
        pure
        returns (int256 exchangeRate)
    {
        uint256 rt = (lnImpliedRate * timeToExpiry) / IMPLIED_RATE_TIME;
        exchangeRate = exp(ud(rt)).intoUint256().toInt256();
    }

    /// @notice Compute swap result given the delta of baseLpt an swapper wants to swap.
    /// @param pool State - pool state of the pool
    /// @param comp PreCompute - pre-computed values of the pool
    /// @param netBaseLptToAccount the delta of baseLpt the swapper wants to swap.
    /// @dev Note: Ensure that abs(`netBaseLptToAccount`) is not greater than `totalBaseLptTimesN`.
    /// @return netUnderlyingToAccount18 the amount of underlying the swapper will receive
    /// negative if the swapper is selling BaseLpt and positive if the swapper is buying BaseLpt.
    /// @return underlyingFee18 the amount of underlying charged as swap fee
    /// this includes `underlyingToProtocol18`
    /// @return underlyingToProtocol18 the amount of underlying the Pool fee recipient will receive as fee
    /// Protocol accrues fee in underlying.
    function calculateSwap(
        PoolState memory pool,
        PoolPreCompute memory comp,
        int256 netBaseLptToAccount // d_pt
    ) internal pure returns (int256, int256, int256) {
        // Calculates the exchange rate from underlying to baseLpt before any fees are applied
        // Note: The exchange rate is int type but it must be always strictly gt 1.
        // Note: `netBaseLptToAccount` should be checked prior to calling this function
        int256 preFeeExchangeRate = _getExchangeRate(
            pool.totalBaseLptTimesN, pool.totalUnderlying18, comp.rateScalar, comp.rateAnchor, netBaseLptToAccount
        ).toInt256();

        // Basically swap formula is:
        //                                 netBaseLptToAccount
        // netUnderlyingToAccount18 = -1 * ────────────────────────
        //                                       extRate
        // where `netBaseLptToAccount` is the delta of baseLpt (`d_pt`) and `netUnderlyingToAccount18` is the delta of underlying (`d_u`).
        // because if `d_pt > 0`, then `d_u < 0` and vice versa.
        // fees can be applied to the `extRate`.
        // `postFeeExchangeRate = preFeeExchangeRate / feeRate` if `netBaseLptToAccount > 0` else `postFeeExchangeRate = preFeeExchangeRate * feeRate`
        int256 netUnderlying18 = netBaseLptToAccount.divWadDown(preFeeExchangeRate).neg();

        // See whitepaper for the formula:
        // fee is calculated as the difference between the underlying amount before and after the fee is applied:
        // fee = underlyingNoFee - underlyingWithFee
        // where `underlyingNoFee = - (ptToAccount / preFeeExchangeRate)`
        // and `underlyingWithFee = - (ptToAccount / postFeeExchangeRate)`
        //
        // Therefore:
        // fee = - (ptToAccount / preFeeExchangeRate) + (ptToAccount / postFeeExchangeRate)
        int256 underlyingFee18;
        if (netBaseLptToAccount > 0) {
            // User swap underlying for baseLpt
            // Exchange rate after fee is applied is:
            //  `postFeeExchangeRate := preFeeExchangeRate / feeRate`
            //  `postFeeExchangeRate` must be strictly gt 1.
            // It's possible that the fee pushes the implied rate into negative territory. This is not allowed.
            int256 postFeeExchangeRate = preFeeExchangeRate.divWadDown(comp.feeRate);
            if (postFeeExchangeRate < SignedMath.WAD) revert Errors.PoolExchangeRateBelowOne(postFeeExchangeRate);

            // fee = - (ptToAccount / preFeeExchangeRate) + (ptToAccount / postFeeExchangeRate)
            //     = (ptToAccount / preFeeExchangeRate) * (feeRate - 1)
            //     = netUnderlying18 * (feeRate - 1)
            underlyingFee18 = netUnderlying18.mulWadDown(SignedMath.WAD - comp.feeRate);
        } else {
            // User swap baseLpt for underlying
            // Exchange rate after fee is applied is:
            //  `postFeeExchangeRate := preFeeExchangeRate * feeRate`
            // In this case, `postFeeExchangeRate` can't be below 1 unlike the case above.

            // fee = - (ptToAccount / preFeeExchangeRate) + (ptToAccount / postFeeExchangeRate)
            //     = - (ptToAccount / preFeeExchangeRate) + (ptToAccount / (preFeeExchangeRate * feeRate))
            //     = - (ptToAccount / preFeeExchangeRate) * (1 - 1 / feeRate)
            //     = - (ptToAccount / preFeeExchangeRate) * (feeRate - 1) / feeRate
            // Note: ptToAccount is negative in this branch so we negate it to ensure that fee is a positive number
            underlyingFee18 = ((netUnderlying18 * (SignedMath.WAD - comp.feeRate)) / comp.feeRate).neg();
        }

        // Subtract swap fee
        // underlyingWithFee = underlyingNoFee - fee
        int256 netUnderlyingToAccount18 = netUnderlying18 - underlyingFee18;
        // Charge protocol fee on swap fee
        // This underlying will be removed from the pool reserve
        int256 underlyingToProtocol18 = (underlyingFee18 * pool.protocolFeePercent.toInt256()) / FULL_PERCENTAGE;

        return (netUnderlyingToAccount18, underlyingFee18, underlyingToProtocol18);
    }

    /// @notice Update pool state cache after swap is executed
    /// @param pool pool state of the pool
    /// @param comp swap formula pre-computed values
    /// @param netBaseLptToAccount net Base Lpt to account. negative if the swapper is selling BaseLpt
    /// @param netUnderlyingToAccount18 net underlying to account. positive if the swapper is selling BaseLpt.
    /// @param netUnderlyingToProtocol18 should be removed from the pool reserve `totalUnderlying18`. must be positive
    function _setPostPoolState(
        PoolState memory pool,
        PoolPreCompute memory comp,
        int256 netBaseLptToAccount,
        int256 netUnderlyingToAccount18,
        int256 netUnderlyingToProtocol18
    ) internal view {
        // update pool state
        // Note safe because pre-trade check ensures totalBaseLptTimesN >= netBaseLptToAccount
        pool.totalBaseLptTimesN = (pool.totalBaseLptTimesN.toInt256() - netBaseLptToAccount).toUint256();
        pool.totalUnderlying18 = (pool.totalUnderlying18).toInt256().subNoNeg(
            netUnderlyingToAccount18 + netUnderlyingToProtocol18
        ).toUint256();
        // compute post-trade implied rate
        // this will be used to compute the new rateAnchor for the next trade
        uint256 timeToExpiry = pool.maturity - block.timestamp;
        pool.lastLnImpliedRate = _getLnImpliedRate(
            pool.totalBaseLptTimesN, pool.totalUnderlying18, comp.rateScalar, comp.rateAnchor, timeToExpiry
        );
        // It's technically unlikely that the implied rate is actually exactly zero but we will still fail
        // in this case.
        if (pool.lastLnImpliedRate == 0) revert Errors.PoolZeroLnImpliedRate();
    }

    /// @notice Get rate scalar given the pool state and time to maturity.
    /// @dev Formula: `scalarRoot * ONE_YEAR / yearsToExpiry`
    function _getRateScalar(PoolState memory pool, uint256 timeToExpiry) internal pure returns (int256) {
        uint256 rateScalar = (pool.scalarRoot * IMPLIED_RATE_TIME) / timeToExpiry;
        if (rateScalar == 0) revert Errors.PoolRateScalarZero();
        return rateScalar.toInt256();
    }

    /// @notice Calculates the current pool implied rate.
    /// ln(extRate) * ONE_YEAR / timeToExpiry
    /// @return lnImpliedRate the implied rate
    function _getLnImpliedRate(
        uint256 totalBaseLptTimesN,
        uint256 totalUnderlying18,
        int256 rateScalar,
        int256 rateAnchor,
        uint256 timeToExpiry
    ) internal pure returns (uint256 lnImpliedRate) {
        // This should ensure that exchange rate < FixedPointMathLib.WAD
        int256 exchangeRate =
            _getExchangeRate(totalBaseLptTimesN, totalUnderlying18, rateScalar, rateAnchor, 0).toInt256();

        // exchangeRate >= 1 so its ln(extRate) >= 0
        int256 lnRate = ln(sd(exchangeRate)).intoInt256();

        lnImpliedRate = (uint256(lnRate) * IMPLIED_RATE_TIME) / timeToExpiry;
    }

    /// @notice Calculates exchange rate given the total baseLpt and total underlying.
    ///     (1 / rateScalar) * ln(proportion / (1 - proportion)) + rateAnchor
    /// where:
    ///     proportion = totalPt / (totalPt + totalUnderlying)
    ///
    /// @dev Revert if the exchange rate is below 1. Prevent users from swapping when 1 baseLpt is worth more than 1 underlying.
    /// @dev Revert if the proportion of baseLpt to total is greater than MAX_POOL_PROPORTION.
    /// @param totalBaseLptTimesN the total baseLpt in the pool
    /// @param totalUnderlying18 the total underlying in the pool
    /// @param rateScalar the scalar used to compute the exchange rate
    /// @param rateAnchor the anchor used to compute the exchange rate
    /// @param netBaseLptToAccount the net baseLpt to the account (negative if account is swapping baseLpt for underlying)
    /// @return exchangeRate the price of underlying token in terms of Base LP token
    function _getExchangeRate(
        uint256 totalBaseLptTimesN,
        uint256 totalUnderlying18,
        int256 rateScalar,
        int256 rateAnchor,
        int256 netBaseLptToAccount
    ) internal pure returns (uint256) {
        // Revert if there is not enough baseLpt to support this swap.
        // Note: Ensure that abs(`netBaseLptToAccount`) is not greater than `totalBaseLptTimesN` before calling this function
        uint256 numerator = (totalBaseLptTimesN.toInt256() - netBaseLptToAccount).toUint256();
        uint256 proportion = numerator.divWadDown(totalBaseLptTimesN + totalUnderlying18);

        if (proportion > MAX_POOL_PROPORTION) {
            revert Errors.PoolProportionTooHigh();
        }

        int256 lnProportion = _logProportion(proportion);

        int256 exchangeRate = lnProportion.divWadDown(rateScalar) + rateAnchor;
        if (exchangeRate < int256(FixedPointMathLib.WAD)) revert Errors.PoolExchangeRateBelowOne(exchangeRate);
        return exchangeRate.toUint256();
    }

    /// @notice Compute Logit function (log(p/(1-p)) given a proportion `p`.
    /// @param proportion the proportion of baseLpt to (baseLpt + underlying) (0 <= proportion <= 1e18)
    function _logProportion(uint256 proportion) internal pure returns (int256 logitP) {
        if (proportion == FixedPointMathLib.WAD) revert Errors.PoolProportionMustNotEqualOne();

        // input = p/(1-p)
        int256 input = proportion.divWadDown(FixedPointMathLib.WAD - proportion).toInt256();
        // logit(p) = log(input) = ln(p/(1-p))
        logitP = ln(sd(input)).intoInt256();
    }

    /// @notice Compute the initial implied rate of the pool.
    /// @dev This function is expected to be called only once when initial liquidity is added.
    /// @param pool pool state of the pool
    /// @param initialAnchor initial anchor of the pool
    /// @return initialLnImpliedRate the initial implied rate
    function computeInitialLnImpliedRate(PoolState memory pool, int256 initialAnchor) internal view returns (uint256) {
        uint256 timeToExpiry = pool.maturity - block.timestamp;
        int256 rateScalar = _getRateScalar(pool, timeToExpiry);

        return
            _getLnImpliedRate(pool.totalBaseLptTimesN, pool.totalUnderlying18, rateScalar, initialAnchor, timeToExpiry);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

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

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

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

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
        require(address(this).balance >= amount, "Create2: insufficient balance");
        require(bytecode.length != 0, "Create2: bytecode length is zero");
        /// @solidity memory-safe-assembly
        assembly {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
        }
        require(addr != address(0), "Create2: Failed on deploy");
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40) // Get free memory pointer

            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
            // |-------------------|---------------------------------------------------------------------------|
            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
            // | salt              |                                      BBBBBBBBBBBBB...BB                   |
            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
            // | 0xFF              |            FF                                                             |
            // |-------------------|---------------------------------------------------------------------------|
            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |

            mstore(add(ptr, 0x40), bytecodeHash)
            mstore(add(ptr, 0x20), salt)
            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
            mstore8(start, 0xff)
            addr := keccak256(start, 85)
        }
    }
}

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

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

// SPDX-License-Identifier: AGPL-3.0-only
/// @notice Taken from: https://github.com/transmissions11/solmate/blob/2001af43aedb46fdc2335d2a7714fb2dae7cfcd1/src/utils/FixedPointMathLib.sol

pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(uint256 x, uint256 n, uint256 scalar) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

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

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

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

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

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

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

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

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

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

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

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

File 30 of 59 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.2._
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v2.5._
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.2._
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v2.5._
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v2.5._
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v2.5._
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v2.5._
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 31 of 59 : Constants.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;

uint256 constant WAD = 1e18;

// @notice 100% in basis points. 10_000 = 100%s
uint256 constant MAX_BPS = 10_000;

/* =============== ADDRESSES ================ */

// @notice WETH address on mainnet
address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

// @notice stETH address on mainnet
address constant STETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;

// @notice wstETH address on mainnet
address constant WSTETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;

// @notice WithdrawalQueueERC721 of LIDO address on mainnet
address constant LIDO_WITHDRAWAL_QUEUE = 0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1;

// @notice rETH address on mainnet
address constant RETH = 0xae78736Cd615f374D3085123A210448E74Fc6393;

// @notice eETH address on mainnet
address constant EETH = 0x35fA164735182de50811E8e2E824cFb9B6118ac2;

// @notice cETH address on mainnet
address constant CETH = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;

// @notice CDAI address on mainnet
address constant CDAI = 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643;

// @notice DAI address on mainnet
address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;

// @notice COMPTROLLER address on mainnet
address constant COMPTROLLER = 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B;

// @notice COMP address on mainnet
address constant COMP = 0xc00e94Cb662C3520282E6f5717214004A7f26888;

// @notice AWETH address on mainnet
address constant AWETH = 0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8;

// @notice LendingAAVEV3_POOL_ADDRESSES_PROVIDER address on mainnet
address constant AAVEV3_POOL_ADDRESSES_PROVIDER = 0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e;

// @notice ma3WETH ERC 4626 Vault address on mainnet
address constant MA3WETH = 0x39Dd7790e75C6F663731f7E1FdC0f35007D3879b;

// @notice Morpho Aave v3 optimizer contract address on mainnet
address constant MORPHO_AAVE_V3 = 0x33333aea097c193e66081E930c33020272b33333;

// @notice MORPHO token address on mainnet
address constant MORPHO = 0x9994E35Db50125E0DF82e4c2dde62496CE330999;

// @notice Frax Ether address on mainnet
address constant FRXETH = 0x5E8422345238F34275888049021821E8E08CAa1f;

// @notice Staked Frax Ether address on mainnet
address constant STAKED_FRXETH = 0xac3E018457B222d93114458476f3E3416Abbe38F;

// @notice EtherFi LiquidityPool
address constant ETHERFI_LP = 0x308861A430be4cce5502d0A12724771Fc6DaF216;

// @notice EtherFi WETH
address constant ETHERFI_WEETH = 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee;

// @notice EtherFi WithdrawRequestNFT
address constant ETHERFI_WITHDRAW_REQUEST = 0x7d5706f6ef3F89B3951E23e557CDFBC3239D4E2c;

// @notice BedRock uniETH address on mainnet
address constant UNIETH = 0xF1376bceF0f78459C0Ed0ba5ddce976F1ddF51F4;

// @notice BedRock Staking address on mainnet
address constant BEDROCK_STAKING = 0x4beFa2aA9c305238AA3E0b5D17eB20C045269E9d;

// @notice Inception inETH address on mainnet
address constant INETH = 0xf073bAC22DAb7FaF4a3Dd6c6189a70D54110525C;

// @notice Origin Protocol OETH address
address constant OETH = 0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3;

// @notice Origin Protocol Vault address
address constant OETH_VAULT = 0x39254033945AA2E4809Cc2977E7087BEE48bd7Ab;

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

import {SafeCast} from "@openzeppelin/[email protected]/utils/math/SafeCast.sol";

library SignedMath {
    int256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulDivDown(int256 x, int256 y, int256 z) internal pure returns (int256) {
        int256 xy = x * y;
        unchecked {
            return xy / z;
        }
    }

    function subNoNeg(int256 a, int256 b) internal pure returns (int256) {
        require(a >= b, "negative");
        return a - b; // no unchecked since if b is very negative, a - b might overflow
    }

    function mulWadDown(int256 a, int256 b) internal pure returns (int256) {
        return mulDivDown(a, b, WAD);
    }

    function divWadDown(int256 a, int256 b) internal pure returns (int256) {
        return mulDivDown(a, WAD, b);
    }

    function neg(int256 x) internal pure returns (int256) {
        return x * (-1);
    }

    function neg(uint256 x) internal pure returns (int256) {
        return SafeCast.toInt256(x) * (-1);
    }
}

File 33 of 59 : SD59x18.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

/*

██████╗ ██████╗ ██████╗ ███╗   ███╗ █████╗ ████████╗██╗  ██╗
██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║  ██║
██████╔╝██████╔╝██████╔╝██╔████╔██║███████║   ██║   ███████║
██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║   ██║   ██╔══██║
██║     ██║  ██║██████╔╝██║ ╚═╝ ██║██║  ██║   ██║   ██║  ██║
╚═╝     ╚═╝  ╚═╝╚═════╝ ╚═╝     ╚═╝╚═╝  ╚═╝   ╚═╝   ╚═╝  ╚═╝

███████╗██████╗ ███████╗ █████╗ ██╗  ██╗ ██╗ █████╗
██╔════╝██╔══██╗██╔════╝██╔══██╗╚██╗██╔╝███║██╔══██╗
███████╗██║  ██║███████╗╚██████║ ╚███╔╝ ╚██║╚█████╔╝
╚════██║██║  ██║╚════██║ ╚═══██║ ██╔██╗  ██║██╔══██╗
███████║██████╔╝███████║ █████╔╝██╔╝ ██╗ ██║╚█████╔╝
╚══════╝╚═════╝ ╚══════╝ ╚════╝ ╚═╝  ╚═╝ ╚═╝ ╚════╝

*/

import "./sd59x18/Casting.sol";
import "./sd59x18/Constants.sol";
import "./sd59x18/Conversions.sol";
import "./sd59x18/Errors.sol";
import "./sd59x18/Helpers.sol";
import "./sd59x18/Math.sol";
import "./sd59x18/ValueType.sol";

File 34 of 59 : UD60x18.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

/*

██████╗ ██████╗ ██████╗ ███╗   ███╗ █████╗ ████████╗██╗  ██╗
██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║  ██║
██████╔╝██████╔╝██████╔╝██╔████╔██║███████║   ██║   ███████║
██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║   ██║   ██╔══██║
██║     ██║  ██║██████╔╝██║ ╚═╝ ██║██║  ██║   ██║   ██║  ██║
╚═╝     ╚═╝  ╚═╝╚═════╝ ╚═╝     ╚═╝╚═╝  ╚═╝   ╚═╝   ╚═╝  ╚═╝

██╗   ██╗██████╗  ██████╗  ██████╗ ██╗  ██╗ ██╗ █████╗
██║   ██║██╔══██╗██╔════╝ ██╔═████╗╚██╗██╔╝███║██╔══██╗
██║   ██║██║  ██║███████╗ ██║██╔██║ ╚███╔╝ ╚██║╚█████╔╝
██║   ██║██║  ██║██╔═══██╗████╔╝██║ ██╔██╗  ██║██╔══██╗
╚██████╔╝██████╔╝╚██████╔╝╚██████╔╝██╔╝ ██╗ ██║╚█████╔╝
 ╚═════╝ ╚═════╝  ╚═════╝  ╚═════╝ ╚═╝  ╚═╝ ╚═╝ ╚════╝

*/

import "./ud60x18/Casting.sol";
import "./ud60x18/Constants.sol";
import "./ud60x18/Conversions.sol";
import "./ud60x18/Errors.sol";
import "./ud60x18/Helpers.sol";
import "./ud60x18/Math.sol";
import "./ud60x18/ValueType.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

File 37 of 59 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Errors.sol" as CastingErrors;
import { MAX_UINT128, MAX_UINT40 } from "../Common.sol";
import { uMAX_SD1x18, uMIN_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { SD59x18 } from "./ValueType.sol";

/// @notice Casts an SD59x18 number into int256.
/// @dev This is basically a functional alias for {unwrap}.
function intoInt256(SD59x18 x) pure returns (int256 result) {
    result = SD59x18.unwrap(x);
}

/// @notice Casts an SD59x18 number into SD1x18.
/// @dev Requirements:
/// - x must be greater than or equal to `uMIN_SD1x18`.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(SD59x18 x) pure returns (SD1x18 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < uMIN_SD1x18) {
        revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Underflow(x);
    }
    if (xInt > uMAX_SD1x18) {
        revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Overflow(x);
    }
    result = SD1x18.wrap(int64(xInt));
}

/// @notice Casts an SD59x18 number into UD2x18.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(SD59x18 x) pure returns (UD2x18 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Underflow(x);
    }
    if (xInt > int256(uint256(uMAX_UD2x18))) {
        revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Overflow(x);
    }
    result = UD2x18.wrap(uint64(uint256(xInt)));
}

/// @notice Casts an SD59x18 number into UD60x18.
/// @dev Requirements:
/// - x must be positive.
function intoUD60x18(SD59x18 x) pure returns (UD60x18 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUD60x18_Underflow(x);
    }
    result = UD60x18.wrap(uint256(xInt));
}

/// @notice Casts an SD59x18 number into uint256.
/// @dev Requirements:
/// - x must be positive.
function intoUint256(SD59x18 x) pure returns (uint256 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint256_Underflow(x);
    }
    result = uint256(xInt);
}

/// @notice Casts an SD59x18 number into uint128.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `uMAX_UINT128`.
function intoUint128(SD59x18 x) pure returns (uint128 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint128_Underflow(x);
    }
    if (xInt > int256(uint256(MAX_UINT128))) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint128_Overflow(x);
    }
    result = uint128(uint256(xInt));
}

/// @notice Casts an SD59x18 number into uint40.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(SD59x18 x) pure returns (uint40 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint40_Underflow(x);
    }
    if (xInt > int256(uint256(MAX_UINT40))) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint40_Overflow(x);
    }
    result = uint40(uint256(xInt));
}

/// @notice Alias for {wrap}.
function sd(int256 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(x);
}

/// @notice Alias for {wrap}.
function sd59x18(int256 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(x);
}

/// @notice Unwraps an SD59x18 number into int256.
function unwrap(SD59x18 x) pure returns (int256 result) {
    result = SD59x18.unwrap(x);
}

/// @notice Wraps an int256 number into SD59x18.
function wrap(int256 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(x);
}

File 38 of 59 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD59x18 } from "./ValueType.sol";

// NOTICE: the "u" prefix stands for "unwrapped".

/// @dev Euler's number as an SD59x18 number.
SD59x18 constant E = SD59x18.wrap(2_718281828459045235);

/// @dev The maximum input permitted in {exp}.
int256 constant uEXP_MAX_INPUT = 133_084258667509499440;
SD59x18 constant EXP_MAX_INPUT = SD59x18.wrap(uEXP_MAX_INPUT);

/// @dev The maximum input permitted in {exp2}.
int256 constant uEXP2_MAX_INPUT = 192e18 - 1;
SD59x18 constant EXP2_MAX_INPUT = SD59x18.wrap(uEXP2_MAX_INPUT);

/// @dev Half the UNIT number.
int256 constant uHALF_UNIT = 0.5e18;
SD59x18 constant HALF_UNIT = SD59x18.wrap(uHALF_UNIT);

/// @dev $log_2(10)$ as an SD59x18 number.
int256 constant uLOG2_10 = 3_321928094887362347;
SD59x18 constant LOG2_10 = SD59x18.wrap(uLOG2_10);

/// @dev $log_2(e)$ as an SD59x18 number.
int256 constant uLOG2_E = 1_442695040888963407;
SD59x18 constant LOG2_E = SD59x18.wrap(uLOG2_E);

/// @dev The maximum value an SD59x18 number can have.
int256 constant uMAX_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_792003956564819967;
SD59x18 constant MAX_SD59x18 = SD59x18.wrap(uMAX_SD59x18);

/// @dev The maximum whole value an SD59x18 number can have.
int256 constant uMAX_WHOLE_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_000000000000000000;
SD59x18 constant MAX_WHOLE_SD59x18 = SD59x18.wrap(uMAX_WHOLE_SD59x18);

/// @dev The minimum value an SD59x18 number can have.
int256 constant uMIN_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_792003956564819968;
SD59x18 constant MIN_SD59x18 = SD59x18.wrap(uMIN_SD59x18);

/// @dev The minimum whole value an SD59x18 number can have.
int256 constant uMIN_WHOLE_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_000000000000000000;
SD59x18 constant MIN_WHOLE_SD59x18 = SD59x18.wrap(uMIN_WHOLE_SD59x18);

/// @dev PI as an SD59x18 number.
SD59x18 constant PI = SD59x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of SD59x18.
int256 constant uUNIT = 1e18;
SD59x18 constant UNIT = SD59x18.wrap(1e18);

/// @dev The unit number squared.
int256 constant uUNIT_SQUARED = 1e36;
SD59x18 constant UNIT_SQUARED = SD59x18.wrap(uUNIT_SQUARED);

/// @dev Zero as an SD59x18 number.
SD59x18 constant ZERO = SD59x18.wrap(0);

File 39 of 59 : Conversions.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { uMAX_SD59x18, uMIN_SD59x18, uUNIT } from "./Constants.sol";
import { PRBMath_SD59x18_Convert_Overflow, PRBMath_SD59x18_Convert_Underflow } from "./Errors.sol";
import { SD59x18 } from "./ValueType.sol";

/// @notice Converts a simple integer to SD59x18 by multiplying it by `UNIT`.
///
/// @dev Requirements:
/// - x must be greater than or equal to `MIN_SD59x18 / UNIT`.
/// - x must be less than or equal to `MAX_SD59x18 / UNIT`.
///
/// @param x The basic integer to convert.
/// @param result The same number converted to SD59x18.
function convert(int256 x) pure returns (SD59x18 result) {
    if (x < uMIN_SD59x18 / uUNIT) {
        revert PRBMath_SD59x18_Convert_Underflow(x);
    }
    if (x > uMAX_SD59x18 / uUNIT) {
        revert PRBMath_SD59x18_Convert_Overflow(x);
    }
    unchecked {
        result = SD59x18.wrap(x * uUNIT);
    }
}

/// @notice Converts an SD59x18 number to a simple integer by dividing it by `UNIT`.
/// @dev The result is rounded toward zero.
/// @param x The SD59x18 number to convert.
/// @return result The same number as a simple integer.
function convert(SD59x18 x) pure returns (int256 result) {
    result = SD59x18.unwrap(x) / uUNIT;
}

File 40 of 59 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD59x18 } from "./ValueType.sol";

/// @notice Thrown when taking the absolute value of `MIN_SD59x18`.
error PRBMath_SD59x18_Abs_MinSD59x18();

/// @notice Thrown when ceiling a number overflows SD59x18.
error PRBMath_SD59x18_Ceil_Overflow(SD59x18 x);

/// @notice Thrown when converting a basic integer to the fixed-point format overflows SD59x18.
error PRBMath_SD59x18_Convert_Overflow(int256 x);

/// @notice Thrown when converting a basic integer to the fixed-point format underflows SD59x18.
error PRBMath_SD59x18_Convert_Underflow(int256 x);

/// @notice Thrown when dividing two numbers and one of them is `MIN_SD59x18`.
error PRBMath_SD59x18_Div_InputTooSmall();

/// @notice Thrown when dividing two numbers and one of the intermediary unsigned results overflows SD59x18.
error PRBMath_SD59x18_Div_Overflow(SD59x18 x, SD59x18 y);

/// @notice Thrown when taking the natural exponent of a base greater than 133_084258667509499441.
error PRBMath_SD59x18_Exp_InputTooBig(SD59x18 x);

/// @notice Thrown when taking the binary exponent of a base greater than 192e18.
error PRBMath_SD59x18_Exp2_InputTooBig(SD59x18 x);

/// @notice Thrown when flooring a number underflows SD59x18.
error PRBMath_SD59x18_Floor_Underflow(SD59x18 x);

/// @notice Thrown when taking the geometric mean of two numbers and their product is negative.
error PRBMath_SD59x18_Gm_NegativeProduct(SD59x18 x, SD59x18 y);

/// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows SD59x18.
error PRBMath_SD59x18_Gm_Overflow(SD59x18 x, SD59x18 y);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_SD59x18_IntoSD1x18_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_SD59x18_IntoSD1x18_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_SD59x18_IntoUD2x18_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_SD59x18_IntoUD2x18_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD60x18.
error PRBMath_SD59x18_IntoUD60x18_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_SD59x18_IntoUint128_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_SD59x18_IntoUint128_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint256.
error PRBMath_SD59x18_IntoUint256_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_SD59x18_IntoUint40_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_SD59x18_IntoUint40_Underflow(SD59x18 x);

/// @notice Thrown when taking the logarithm of a number less than or equal to zero.
error PRBMath_SD59x18_Log_InputTooSmall(SD59x18 x);

/// @notice Thrown when multiplying two numbers and one of the inputs is `MIN_SD59x18`.
error PRBMath_SD59x18_Mul_InputTooSmall();

/// @notice Thrown when multiplying two numbers and the intermediary absolute result overflows SD59x18.
error PRBMath_SD59x18_Mul_Overflow(SD59x18 x, SD59x18 y);

/// @notice Thrown when raising a number to a power and hte intermediary absolute result overflows SD59x18.
error PRBMath_SD59x18_Powu_Overflow(SD59x18 x, uint256 y);

/// @notice Thrown when taking the square root of a negative number.
error PRBMath_SD59x18_Sqrt_NegativeInput(SD59x18 x);

/// @notice Thrown when the calculating the square root overflows SD59x18.
error PRBMath_SD59x18_Sqrt_Overflow(SD59x18 x);

File 41 of 59 : Helpers.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { wrap } from "./Casting.sol";
import { SD59x18 } from "./ValueType.sol";

/// @notice Implements the checked addition operation (+) in the SD59x18 type.
function add(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    return wrap(x.unwrap() + y.unwrap());
}

/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and(SD59x18 x, int256 bits) pure returns (SD59x18 result) {
    return wrap(x.unwrap() & bits);
}

/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and2(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    return wrap(x.unwrap() & y.unwrap());
}

/// @notice Implements the equal (=) operation in the SD59x18 type.
function eq(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() == y.unwrap();
}

/// @notice Implements the greater than operation (>) in the SD59x18 type.
function gt(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() > y.unwrap();
}

/// @notice Implements the greater than or equal to operation (>=) in the SD59x18 type.
function gte(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() >= y.unwrap();
}

/// @notice Implements a zero comparison check function in the SD59x18 type.
function isZero(SD59x18 x) pure returns (bool result) {
    result = x.unwrap() == 0;
}

/// @notice Implements the left shift operation (<<) in the SD59x18 type.
function lshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() << bits);
}

/// @notice Implements the lower than operation (<) in the SD59x18 type.
function lt(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() < y.unwrap();
}

/// @notice Implements the lower than or equal to operation (<=) in the SD59x18 type.
function lte(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() <= y.unwrap();
}

/// @notice Implements the unchecked modulo operation (%) in the SD59x18 type.
function mod(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() % y.unwrap());
}

/// @notice Implements the not equal operation (!=) in the SD59x18 type.
function neq(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() != y.unwrap();
}

/// @notice Implements the NOT (~) bitwise operation in the SD59x18 type.
function not(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(~x.unwrap());
}

/// @notice Implements the OR (|) bitwise operation in the SD59x18 type.
function or(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() | y.unwrap());
}

/// @notice Implements the right shift operation (>>) in the SD59x18 type.
function rshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() >> bits);
}

/// @notice Implements the checked subtraction operation (-) in the SD59x18 type.
function sub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() - y.unwrap());
}

/// @notice Implements the checked unary minus operation (-) in the SD59x18 type.
function unary(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(-x.unwrap());
}

/// @notice Implements the unchecked addition operation (+) in the SD59x18 type.
function uncheckedAdd(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    unchecked {
        result = wrap(x.unwrap() + y.unwrap());
    }
}

/// @notice Implements the unchecked subtraction operation (-) in the SD59x18 type.
function uncheckedSub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    unchecked {
        result = wrap(x.unwrap() - y.unwrap());
    }
}

/// @notice Implements the unchecked unary minus operation (-) in the SD59x18 type.
function uncheckedUnary(SD59x18 x) pure returns (SD59x18 result) {
    unchecked {
        result = wrap(-x.unwrap());
    }
}

/// @notice Implements the XOR (^) bitwise operation in the SD59x18 type.
function xor(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() ^ y.unwrap());
}

File 42 of 59 : Math.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import {
    uEXP_MAX_INPUT,
    uEXP2_MAX_INPUT,
    uHALF_UNIT,
    uLOG2_10,
    uLOG2_E,
    uMAX_SD59x18,
    uMAX_WHOLE_SD59x18,
    uMIN_SD59x18,
    uMIN_WHOLE_SD59x18,
    UNIT,
    uUNIT,
    uUNIT_SQUARED,
    ZERO
} from "./Constants.sol";
import { wrap } from "./Helpers.sol";
import { SD59x18 } from "./ValueType.sol";

/// @notice Calculates the absolute value of x.
///
/// @dev Requirements:
/// - x must be greater than `MIN_SD59x18`.
///
/// @param x The SD59x18 number for which to calculate the absolute value.
/// @param result The absolute value of x as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function abs(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt == uMIN_SD59x18) {
        revert Errors.PRBMath_SD59x18_Abs_MinSD59x18();
    }
    result = xInt < 0 ? wrap(-xInt) : x;
}

/// @notice Calculates the arithmetic average of x and y.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as an SD59x18 number.
/// @param y The second operand as an SD59x18 number.
/// @return result The arithmetic average as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();

    unchecked {
        // This operation is equivalent to `x / 2 +  y / 2`, and it can never overflow.
        int256 sum = (xInt >> 1) + (yInt >> 1);

        if (sum < 0) {
            // If at least one of x and y is odd, add 1 to the result, because shifting negative numbers to the right
            // rounds toward negative infinity. The right part is equivalent to `sum + (x % 2 == 1 || y % 2 == 1)`.
            assembly ("memory-safe") {
                result := add(sum, and(or(xInt, yInt), 1))
            }
        } else {
            // Add 1 if both x and y are odd to account for the double 0.5 remainder truncated after shifting.
            result = wrap(sum + (xInt & yInt & 1));
        }
    }
}

/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_SD59x18`.
///
/// @param x The SD59x18 number to ceil.
/// @param result The smallest whole number greater than or equal to x, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt > uMAX_WHOLE_SD59x18) {
        revert Errors.PRBMath_SD59x18_Ceil_Overflow(x);
    }

    int256 remainder = xInt % uUNIT;
    if (remainder == 0) {
        result = x;
    } else {
        unchecked {
            // Solidity uses C fmod style, which returns a modulus with the same sign as x.
            int256 resultInt = xInt - remainder;
            if (xInt > 0) {
                resultInt += uUNIT;
            }
            result = wrap(resultInt);
        }
    }
}

/// @notice Divides two SD59x18 numbers, returning a new SD59x18 number.
///
/// @dev This is an extension of {Common.mulDiv} for signed numbers, which works by computing the signs and the absolute
/// values separately.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
/// - None of the inputs can be `MIN_SD59x18`.
/// - The denominator must not be zero.
/// - The result must fit in SD59x18.
///
/// @param x The numerator as an SD59x18 number.
/// @param y The denominator as an SD59x18 number.
/// @param result The quotient as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();
    if (xInt == uMIN_SD59x18 || yInt == uMIN_SD59x18) {
        revert Errors.PRBMath_SD59x18_Div_InputTooSmall();
    }

    // Get hold of the absolute values of x and y.
    uint256 xAbs;
    uint256 yAbs;
    unchecked {
        xAbs = xInt < 0 ? uint256(-xInt) : uint256(xInt);
        yAbs = yInt < 0 ? uint256(-yInt) : uint256(yInt);
    }

    // Compute the absolute value (x*UNIT÷y). The resulting value must fit in SD59x18.
    uint256 resultAbs = Common.mulDiv(xAbs, uint256(uUNIT), yAbs);
    if (resultAbs > uint256(uMAX_SD59x18)) {
        revert Errors.PRBMath_SD59x18_Div_Overflow(x, y);
    }

    // Check if x and y have the same sign using two's complement representation. The left-most bit represents the sign (1 for
    // negative, 0 for positive or zero).
    bool sameSign = (xInt ^ yInt) > -1;

    // If the inputs have the same sign, the result should be positive. Otherwise, it should be negative.
    unchecked {
        result = wrap(sameSign ? int256(resultAbs) : -int256(resultAbs));
    }
}

/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {exp2}.
///
/// Requirements:
/// - Refer to the requirements in {exp2}.
/// - x must be less than 133_084258667509499441.
///
/// @param x The exponent as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();

    // This check prevents values greater than 192e18 from being passed to {exp2}.
    if (xInt > uEXP_MAX_INPUT) {
        revert Errors.PRBMath_SD59x18_Exp_InputTooBig(x);
    }

    unchecked {
        // Inline the fixed-point multiplication to save gas.
        int256 doubleUnitProduct = xInt * uLOG2_E;
        result = exp2(wrap(doubleUnitProduct / uUNIT));
    }
}

/// @notice Calculates the binary exponent of x using the binary fraction method using the following formula:
///
/// $$
/// 2^{-x} = \frac{1}{2^x}
/// $$
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693.
///
/// Notes:
/// - If x is less than -59_794705707972522261, the result is zero.
///
/// Requirements:
/// - x must be less than 192e18.
/// - The result must fit in SD59x18.
///
/// @param x The exponent as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < 0) {
        // The inverse of any number less than this is truncated to zero.
        if (xInt < -59_794705707972522261) {
            return ZERO;
        }

        unchecked {
            // Inline the fixed-point inversion to save gas.
            result = wrap(uUNIT_SQUARED / exp2(wrap(-xInt)).unwrap());
        }
    } else {
        // Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
        if (xInt > uEXP2_MAX_INPUT) {
            revert Errors.PRBMath_SD59x18_Exp2_InputTooBig(x);
        }

        unchecked {
            // Convert x to the 192.64-bit fixed-point format.
            uint256 x_192x64 = uint256((xInt << 64) / uUNIT);

            // It is safe to cast the result to int256 due to the checks above.
            result = wrap(int256(Common.exp2(x_192x64)));
        }
    }
}

/// @notice Yields the greatest whole number less than or equal to x.
///
/// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be greater than or equal to `MIN_WHOLE_SD59x18`.
///
/// @param x The SD59x18 number to floor.
/// @param result The greatest whole number less than or equal to x, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < uMIN_WHOLE_SD59x18) {
        revert Errors.PRBMath_SD59x18_Floor_Underflow(x);
    }

    int256 remainder = xInt % uUNIT;
    if (remainder == 0) {
        result = x;
    } else {
        unchecked {
            // Solidity uses C fmod style, which returns a modulus with the same sign as x.
            int256 resultInt = xInt - remainder;
            if (xInt < 0) {
                resultInt -= uUNIT;
            }
            result = wrap(resultInt);
        }
    }
}

/// @notice Yields the excess beyond the floor of x for positive numbers and the part of the number to the right.
/// of the radix point for negative numbers.
/// @dev Based on the odd function definition. https://en.wikipedia.org/wiki/Fractional_part
/// @param x The SD59x18 number to get the fractional part of.
/// @param result The fractional part of x as an SD59x18 number.
function frac(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() % uUNIT);
}

/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x * y must fit in SD59x18.
/// - x * y must not be negative, since complex numbers are not supported.
///
/// @param x The first operand as an SD59x18 number.
/// @param y The second operand as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();
    if (xInt == 0 || yInt == 0) {
        return ZERO;
    }

    unchecked {
        // Equivalent to `xy / x != y`. Checking for overflow this way is faster than letting Solidity do it.
        int256 xyInt = xInt * yInt;
        if (xyInt / xInt != yInt) {
            revert Errors.PRBMath_SD59x18_Gm_Overflow(x, y);
        }

        // The product must not be negative, since complex numbers are not supported.
        if (xyInt < 0) {
            revert Errors.PRBMath_SD59x18_Gm_NegativeProduct(x, y);
        }

        // We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
        // during multiplication. See the comments in {Common.sqrt}.
        uint256 resultUint = Common.sqrt(uint256(xyInt));
        result = wrap(int256(resultUint));
    }
}

/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The SD59x18 number for which to calculate the inverse.
/// @return result The inverse as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(uUNIT_SQUARED / x.unwrap());
}

/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The SD59x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(SD59x18 x) pure returns (SD59x18 result) {
    // Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
    // {log2} can return is ~195_205294292027477728.
    result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
}

/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The SD59x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < 0) {
        revert Errors.PRBMath_SD59x18_Log_InputTooSmall(x);
    }

    // Note that the `mul` in this block is the standard multiplication operation, not {SD59x18.mul}.
    // prettier-ignore
    assembly ("memory-safe") {
        switch x
        case 1 { result := mul(uUNIT, sub(0, 18)) }
        case 10 { result := mul(uUNIT, sub(1, 18)) }
        case 100 { result := mul(uUNIT, sub(2, 18)) }
        case 1000 { result := mul(uUNIT, sub(3, 18)) }
        case 10000 { result := mul(uUNIT, sub(4, 18)) }
        case 100000 { result := mul(uUNIT, sub(5, 18)) }
        case 1000000 { result := mul(uUNIT, sub(6, 18)) }
        case 10000000 { result := mul(uUNIT, sub(7, 18)) }
        case 100000000 { result := mul(uUNIT, sub(8, 18)) }
        case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
        case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
        case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
        case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
        case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
        case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
        case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
        case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
        case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
        case 1000000000000000000 { result := 0 }
        case 10000000000000000000 { result := uUNIT }
        case 100000000000000000000 { result := mul(uUNIT, 2) }
        case 1000000000000000000000 { result := mul(uUNIT, 3) }
        case 10000000000000000000000 { result := mul(uUNIT, 4) }
        case 100000000000000000000000 { result := mul(uUNIT, 5) }
        case 1000000000000000000000000 { result := mul(uUNIT, 6) }
        case 10000000000000000000000000 { result := mul(uUNIT, 7) }
        case 100000000000000000000000000 { result := mul(uUNIT, 8) }
        case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
        case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
        case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
        case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
        case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
        case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
        case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
        case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
        case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
        case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
        case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
        case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
        case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
        case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
        case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
        case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
        case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
        case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
        case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
        case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
        case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
        case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
        case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
        case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
        case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
        case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
        case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
        case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
        case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
        case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
        case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
        case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
        case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
        case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
        case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
        case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
        case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
        case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
        case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
        case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
        case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
        case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
        default { result := uMAX_SD59x18 }
    }

    if (result.unwrap() == uMAX_SD59x18) {
        unchecked {
            // Inline the fixed-point division to save gas.
            result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
        }
    }
}

/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation.
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x must be greater than zero.
///
/// @param x The SD59x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt <= 0) {
        revert Errors.PRBMath_SD59x18_Log_InputTooSmall(x);
    }

    unchecked {
        int256 sign;
        if (xInt >= uUNIT) {
            sign = 1;
        } else {
            sign = -1;
            // Inline the fixed-point inversion to save gas.
            xInt = uUNIT_SQUARED / xInt;
        }

        // Calculate the integer part of the logarithm.
        uint256 n = Common.msb(uint256(xInt / uUNIT));

        // This is the integer part of the logarithm as an SD59x18 number. The operation can't overflow
        // because n is at most 255, `UNIT` is 1e18, and the sign is either 1 or -1.
        int256 resultInt = int256(n) * uUNIT;

        // Calculate $y = x * 2^{-n}$.
        int256 y = xInt >> n;

        // If y is the unit number, the fractional part is zero.
        if (y == uUNIT) {
            return wrap(resultInt * sign);
        }

        // Calculate the fractional part via the iterative approximation.
        // The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
        int256 DOUBLE_UNIT = 2e18;
        for (int256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
            y = (y * y) / uUNIT;

            // Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
            if (y >= DOUBLE_UNIT) {
                // Add the 2^{-m} factor to the logarithm.
                resultInt = resultInt + delta;

                // Halve y, which corresponds to z/2 in the Wikipedia article.
                y >>= 1;
            }
        }
        resultInt *= sign;
        result = wrap(resultInt);
    }
}

/// @notice Multiplies two SD59x18 numbers together, returning a new SD59x18 number.
///
/// @dev Notes:
/// - Refer to the notes in {Common.mulDiv18}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv18}.
/// - None of the inputs can be `MIN_SD59x18`.
/// - The result must fit in SD59x18.
///
/// @param x The multiplicand as an SD59x18 number.
/// @param y The multiplier as an SD59x18 number.
/// @return result The product as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();
    if (xInt == uMIN_SD59x18 || yInt == uMIN_SD59x18) {
        revert Errors.PRBMath_SD59x18_Mul_InputTooSmall();
    }

    // Get hold of the absolute values of x and y.
    uint256 xAbs;
    uint256 yAbs;
    unchecked {
        xAbs = xInt < 0 ? uint256(-xInt) : uint256(xInt);
        yAbs = yInt < 0 ? uint256(-yInt) : uint256(yInt);
    }

    // Compute the absolute value (x*y÷UNIT). The resulting value must fit in SD59x18.
    uint256 resultAbs = Common.mulDiv18(xAbs, yAbs);
    if (resultAbs > uint256(uMAX_SD59x18)) {
        revert Errors.PRBMath_SD59x18_Mul_Overflow(x, y);
    }

    // Check if x and y have the same sign using two's complement representation. The left-most bit represents the sign (1 for
    // negative, 0 for positive or zero).
    bool sameSign = (xInt ^ yInt) > -1;

    // If the inputs have the same sign, the result should be positive. Otherwise, it should be negative.
    unchecked {
        result = wrap(sameSign ? int256(resultAbs) : -int256(resultAbs));
    }
}

/// @notice Raises x to the power of y using the following formula:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {exp2}, {log2}, and {mul}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as an SD59x18 number.
/// @param y Exponent to raise x to, as an SD59x18 number
/// @return result x raised to power y, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();

    // If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
    if (xInt == 0) {
        return yInt == 0 ? UNIT : ZERO;
    }
    // If x is `UNIT`, the result is always `UNIT`.
    else if (xInt == uUNIT) {
        return UNIT;
    }

    // If y is zero, the result is always `UNIT`.
    if (yInt == 0) {
        return UNIT;
    }
    // If y is `UNIT`, the result is always x.
    else if (yInt == uUNIT) {
        return x;
    }

    // Calculate the result using the formula.
    result = exp2(mul(log2(x), y));
}

/// @notice Raises x (an SD59x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - Refer to the requirements in {abs} and {Common.mulDiv18}.
/// - The result must fit in SD59x18.
///
/// @param x The base as an SD59x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(SD59x18 x, uint256 y) pure returns (SD59x18 result) {
    uint256 xAbs = uint256(abs(x).unwrap());

    // Calculate the first iteration of the loop in advance.
    uint256 resultAbs = y & 1 > 0 ? xAbs : uint256(uUNIT);

    // Equivalent to `for(y /= 2; y > 0; y /= 2)`.
    uint256 yAux = y;
    for (yAux >>= 1; yAux > 0; yAux >>= 1) {
        xAbs = Common.mulDiv18(xAbs, xAbs);

        // Equivalent to `y % 2 == 1`.
        if (yAux & 1 > 0) {
            resultAbs = Common.mulDiv18(resultAbs, xAbs);
        }
    }

    // The result must fit in SD59x18.
    if (resultAbs > uint256(uMAX_SD59x18)) {
        revert Errors.PRBMath_SD59x18_Powu_Overflow(x, y);
    }

    unchecked {
        // Is the base negative and the exponent odd? If yes, the result should be negative.
        int256 resultInt = int256(resultAbs);
        bool isNegative = x.unwrap() < 0 && y & 1 == 1;
        if (isNegative) {
            resultInt = -resultInt;
        }
        result = wrap(resultInt);
    }
}

/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - Only the positive root is returned.
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x cannot be negative, since complex numbers are not supported.
/// - x must be less than `MAX_SD59x18 / UNIT`.
///
/// @param x The SD59x18 number for which to calculate the square root.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < 0) {
        revert Errors.PRBMath_SD59x18_Sqrt_NegativeInput(x);
    }
    if (xInt > uMAX_SD59x18 / uUNIT) {
        revert Errors.PRBMath_SD59x18_Sqrt_Overflow(x);
    }

    unchecked {
        // Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two SD59x18 numbers.
        // In this case, the two numbers are both the square root.
        uint256 resultUint = Common.sqrt(uint256(xInt * uUNIT));
        result = wrap(int256(resultUint));
    }
}

File 43 of 59 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;
import "./Helpers.sol" as Helpers;
import "./Math.sol" as Math;

/// @notice The signed 59.18-decimal fixed-point number representation, which can have up to 59 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type int256.
type SD59x18 is int256;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoInt256,
    Casting.intoSD1x18,
    Casting.intoUD2x18,
    Casting.intoUD60x18,
    Casting.intoUint256,
    Casting.intoUint128,
    Casting.intoUint40,
    Casting.unwrap
} for SD59x18 global;

/*//////////////////////////////////////////////////////////////////////////
                            MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

using {
    Math.abs,
    Math.avg,
    Math.ceil,
    Math.div,
    Math.exp,
    Math.exp2,
    Math.floor,
    Math.frac,
    Math.gm,
    Math.inv,
    Math.log10,
    Math.log2,
    Math.ln,
    Math.mul,
    Math.pow,
    Math.powu,
    Math.sqrt
} for SD59x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

using {
    Helpers.add,
    Helpers.and,
    Helpers.eq,
    Helpers.gt,
    Helpers.gte,
    Helpers.isZero,
    Helpers.lshift,
    Helpers.lt,
    Helpers.lte,
    Helpers.mod,
    Helpers.neq,
    Helpers.not,
    Helpers.or,
    Helpers.rshift,
    Helpers.sub,
    Helpers.uncheckedAdd,
    Helpers.uncheckedSub,
    Helpers.uncheckedUnary,
    Helpers.xor
} for SD59x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                    OPERATORS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes it possible to use these operators on the SD59x18 type.
using {
    Helpers.add as +,
    Helpers.and2 as &,
    Math.div as /,
    Helpers.eq as ==,
    Helpers.gt as >,
    Helpers.gte as >=,
    Helpers.lt as <,
    Helpers.lte as <=,
    Helpers.mod as %,
    Math.mul as *,
    Helpers.neq as !=,
    Helpers.not as ~,
    Helpers.or as |,
    Helpers.sub as -,
    Helpers.unary as -,
    Helpers.xor as ^
} for SD59x18 global;

File 44 of 59 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Errors.sol" as CastingErrors;
import { MAX_UINT128, MAX_UINT40 } from "../Common.sol";
import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_SD59x18 } from "../sd59x18/Constants.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "./ValueType.sol";

/// @notice Casts a UD60x18 number into SD1x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(UD60x18 x) pure returns (SD1x18 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > uint256(int256(uMAX_SD1x18))) {
        revert CastingErrors.PRBMath_UD60x18_IntoSD1x18_Overflow(x);
    }
    result = SD1x18.wrap(int64(uint64(xUint)));
}

/// @notice Casts a UD60x18 number into UD2x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(UD60x18 x) pure returns (UD2x18 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > uMAX_UD2x18) {
        revert CastingErrors.PRBMath_UD60x18_IntoUD2x18_Overflow(x);
    }
    result = UD2x18.wrap(uint64(xUint));
}

/// @notice Casts a UD60x18 number into SD59x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD59x18`.
function intoSD59x18(UD60x18 x) pure returns (SD59x18 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > uint256(uMAX_SD59x18)) {
        revert CastingErrors.PRBMath_UD60x18_IntoSD59x18_Overflow(x);
    }
    result = SD59x18.wrap(int256(xUint));
}

/// @notice Casts a UD60x18 number into uint128.
/// @dev This is basically an alias for {unwrap}.
function intoUint256(UD60x18 x) pure returns (uint256 result) {
    result = UD60x18.unwrap(x);
}

/// @notice Casts a UD60x18 number into uint128.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT128`.
function intoUint128(UD60x18 x) pure returns (uint128 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > MAX_UINT128) {
        revert CastingErrors.PRBMath_UD60x18_IntoUint128_Overflow(x);
    }
    result = uint128(xUint);
}

/// @notice Casts a UD60x18 number into uint40.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(UD60x18 x) pure returns (uint40 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > MAX_UINT40) {
        revert CastingErrors.PRBMath_UD60x18_IntoUint40_Overflow(x);
    }
    result = uint40(xUint);
}

/// @notice Alias for {wrap}.
function ud(uint256 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(x);
}

/// @notice Alias for {wrap}.
function ud60x18(uint256 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(x);
}

/// @notice Unwraps a UD60x18 number into uint256.
function unwrap(UD60x18 x) pure returns (uint256 result) {
    result = UD60x18.unwrap(x);
}

/// @notice Wraps a uint256 number into the UD60x18 value type.
function wrap(uint256 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(x);
}

File 45 of 59 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD60x18 } from "./ValueType.sol";

// NOTICE: the "u" prefix stands for "unwrapped".

/// @dev Euler's number as a UD60x18 number.
UD60x18 constant E = UD60x18.wrap(2_718281828459045235);

/// @dev The maximum input permitted in {exp}.
uint256 constant uEXP_MAX_INPUT = 133_084258667509499440;
UD60x18 constant EXP_MAX_INPUT = UD60x18.wrap(uEXP_MAX_INPUT);

/// @dev The maximum input permitted in {exp2}.
uint256 constant uEXP2_MAX_INPUT = 192e18 - 1;
UD60x18 constant EXP2_MAX_INPUT = UD60x18.wrap(uEXP2_MAX_INPUT);

/// @dev Half the UNIT number.
uint256 constant uHALF_UNIT = 0.5e18;
UD60x18 constant HALF_UNIT = UD60x18.wrap(uHALF_UNIT);

/// @dev $log_2(10)$ as a UD60x18 number.
uint256 constant uLOG2_10 = 3_321928094887362347;
UD60x18 constant LOG2_10 = UD60x18.wrap(uLOG2_10);

/// @dev $log_2(e)$ as a UD60x18 number.
uint256 constant uLOG2_E = 1_442695040888963407;
UD60x18 constant LOG2_E = UD60x18.wrap(uLOG2_E);

/// @dev The maximum value a UD60x18 number can have.
uint256 constant uMAX_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_584007913129639935;
UD60x18 constant MAX_UD60x18 = UD60x18.wrap(uMAX_UD60x18);

/// @dev The maximum whole value a UD60x18 number can have.
uint256 constant uMAX_WHOLE_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_000000000000000000;
UD60x18 constant MAX_WHOLE_UD60x18 = UD60x18.wrap(uMAX_WHOLE_UD60x18);

/// @dev PI as a UD60x18 number.
UD60x18 constant PI = UD60x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of UD60x18.
uint256 constant uUNIT = 1e18;
UD60x18 constant UNIT = UD60x18.wrap(uUNIT);

/// @dev The unit number squared.
uint256 constant uUNIT_SQUARED = 1e36;
UD60x18 constant UNIT_SQUARED = UD60x18.wrap(uUNIT_SQUARED);

/// @dev Zero as a UD60x18 number.
UD60x18 constant ZERO = UD60x18.wrap(0);

File 46 of 59 : Conversions.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { uMAX_UD60x18, uUNIT } from "./Constants.sol";
import { PRBMath_UD60x18_Convert_Overflow } from "./Errors.sol";
import { UD60x18 } from "./ValueType.sol";

/// @notice Converts a UD60x18 number to a simple integer by dividing it by `UNIT`.
/// @dev The result is rounded toward zero.
/// @param x The UD60x18 number to convert.
/// @return result The same number in basic integer form.
function convert(UD60x18 x) pure returns (uint256 result) {
    result = UD60x18.unwrap(x) / uUNIT;
}

/// @notice Converts a simple integer to UD60x18 by multiplying it by `UNIT`.
///
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UD60x18 / UNIT`.
///
/// @param x The basic integer to convert.
/// @param result The same number converted to UD60x18.
function convert(uint256 x) pure returns (UD60x18 result) {
    if (x > uMAX_UD60x18 / uUNIT) {
        revert PRBMath_UD60x18_Convert_Overflow(x);
    }
    unchecked {
        result = UD60x18.wrap(x * uUNIT);
    }
}

File 47 of 59 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD60x18 } from "./ValueType.sol";

/// @notice Thrown when ceiling a number overflows UD60x18.
error PRBMath_UD60x18_Ceil_Overflow(UD60x18 x);

/// @notice Thrown when converting a basic integer to the fixed-point format overflows UD60x18.
error PRBMath_UD60x18_Convert_Overflow(uint256 x);

/// @notice Thrown when taking the natural exponent of a base greater than 133_084258667509499441.
error PRBMath_UD60x18_Exp_InputTooBig(UD60x18 x);

/// @notice Thrown when taking the binary exponent of a base greater than 192e18.
error PRBMath_UD60x18_Exp2_InputTooBig(UD60x18 x);

/// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows UD60x18.
error PRBMath_UD60x18_Gm_Overflow(UD60x18 x, UD60x18 y);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_UD60x18_IntoSD1x18_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD59x18.
error PRBMath_UD60x18_IntoSD59x18_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_UD60x18_IntoUD2x18_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_UD60x18_IntoUint128_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_UD60x18_IntoUint40_Overflow(UD60x18 x);

/// @notice Thrown when taking the logarithm of a number less than 1.
error PRBMath_UD60x18_Log_InputTooSmall(UD60x18 x);

/// @notice Thrown when calculating the square root overflows UD60x18.
error PRBMath_UD60x18_Sqrt_Overflow(UD60x18 x);

File 48 of 59 : Helpers.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { wrap } from "./Casting.sol";
import { UD60x18 } from "./ValueType.sol";

/// @notice Implements the checked addition operation (+) in the UD60x18 type.
function add(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() + y.unwrap());
}

/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() & bits);
}

/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and2(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() & y.unwrap());
}

/// @notice Implements the equal operation (==) in the UD60x18 type.
function eq(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() == y.unwrap();
}

/// @notice Implements the greater than operation (>) in the UD60x18 type.
function gt(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() > y.unwrap();
}

/// @notice Implements the greater than or equal to operation (>=) in the UD60x18 type.
function gte(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() >= y.unwrap();
}

/// @notice Implements a zero comparison check function in the UD60x18 type.
function isZero(UD60x18 x) pure returns (bool result) {
    // This wouldn't work if x could be negative.
    result = x.unwrap() == 0;
}

/// @notice Implements the left shift operation (<<) in the UD60x18 type.
function lshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() << bits);
}

/// @notice Implements the lower than operation (<) in the UD60x18 type.
function lt(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() < y.unwrap();
}

/// @notice Implements the lower than or equal to operation (<=) in the UD60x18 type.
function lte(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() <= y.unwrap();
}

/// @notice Implements the checked modulo operation (%) in the UD60x18 type.
function mod(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() % y.unwrap());
}

/// @notice Implements the not equal operation (!=) in the UD60x18 type.
function neq(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() != y.unwrap();
}

/// @notice Implements the NOT (~) bitwise operation in the UD60x18 type.
function not(UD60x18 x) pure returns (UD60x18 result) {
    result = wrap(~x.unwrap());
}

/// @notice Implements the OR (|) bitwise operation in the UD60x18 type.
function or(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() | y.unwrap());
}

/// @notice Implements the right shift operation (>>) in the UD60x18 type.
function rshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() >> bits);
}

/// @notice Implements the checked subtraction operation (-) in the UD60x18 type.
function sub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() - y.unwrap());
}

/// @notice Implements the unchecked addition operation (+) in the UD60x18 type.
function uncheckedAdd(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(x.unwrap() + y.unwrap());
    }
}

/// @notice Implements the unchecked subtraction operation (-) in the UD60x18 type.
function uncheckedSub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(x.unwrap() - y.unwrap());
    }
}

/// @notice Implements the XOR (^) bitwise operation in the UD60x18 type.
function xor(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() ^ y.unwrap());
}

File 49 of 59 : Math.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { wrap } from "./Casting.sol";
import {
    uEXP_MAX_INPUT,
    uEXP2_MAX_INPUT,
    uHALF_UNIT,
    uLOG2_10,
    uLOG2_E,
    uMAX_UD60x18,
    uMAX_WHOLE_UD60x18,
    UNIT,
    uUNIT,
    uUNIT_SQUARED,
    ZERO
} from "./Constants.sol";
import { UD60x18 } from "./ValueType.sol";

/*//////////////////////////////////////////////////////////////////////////
                            MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Calculates the arithmetic average of x and y using the following formula:
///
/// $$
/// avg(x, y) = (x & y) + ((xUint ^ yUint) / 2)
/// $$
//
/// In English, this is what this formula does:
///
/// 1. AND x and y.
/// 2. Calculate half of XOR x and y.
/// 3. Add the two results together.
///
/// This technique is known as SWAR, which stands for "SIMD within a register". You can read more about it here:
/// https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The arithmetic average as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    uint256 yUint = y.unwrap();
    unchecked {
        result = wrap((xUint & yUint) + ((xUint ^ yUint) >> 1));
    }
}

/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev This is optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_UD60x18`.
///
/// @param x The UD60x18 number to ceil.
/// @param result The smallest whole number greater than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    if (xUint > uMAX_WHOLE_UD60x18) {
        revert Errors.PRBMath_UD60x18_Ceil_Overflow(x);
    }

    assembly ("memory-safe") {
        // Equivalent to `x % UNIT`.
        let remainder := mod(x, uUNIT)

        // Equivalent to `UNIT - remainder`.
        let delta := sub(uUNIT, remainder)

        // Equivalent to `x + remainder > 0 ? delta : 0`.
        result := add(x, mul(delta, gt(remainder, 0)))
    }
}

/// @notice Divides two UD60x18 numbers, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @param x The numerator as a UD60x18 number.
/// @param y The denominator as a UD60x18 number.
/// @param result The quotient as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(Common.mulDiv(x.unwrap(), uUNIT, y.unwrap()));
}

/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Requirements:
/// - x must be less than 133_084258667509499441.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    // This check prevents values greater than 192e18 from being passed to {exp2}.
    if (xUint > uEXP_MAX_INPUT) {
        revert Errors.PRBMath_UD60x18_Exp_InputTooBig(x);
    }

    unchecked {
        // Inline the fixed-point multiplication to save gas.
        uint256 doubleUnitProduct = xUint * uLOG2_E;
        result = exp2(wrap(doubleUnitProduct / uUNIT));
    }
}

/// @notice Calculates the binary exponent of x using the binary fraction method.
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693
///
/// Requirements:
/// - x must be less than 192e18.
/// - The result must fit in UD60x18.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    // Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
    if (xUint > uEXP2_MAX_INPUT) {
        revert Errors.PRBMath_UD60x18_Exp2_InputTooBig(x);
    }

    // Convert x to the 192.64-bit fixed-point format.
    uint256 x_192x64 = (xUint << 64) / uUNIT;

    // Pass x to the {Common.exp2} function, which uses the 192.64-bit fixed-point number representation.
    result = wrap(Common.exp2(x_192x64));
}

/// @notice Yields the greatest whole number less than or equal to x.
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
/// @param x The UD60x18 number to floor.
/// @param result The greatest whole number less than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(UD60x18 x) pure returns (UD60x18 result) {
    assembly ("memory-safe") {
        // Equivalent to `x % UNIT`.
        let remainder := mod(x, uUNIT)

        // Equivalent to `x - remainder > 0 ? remainder : 0)`.
        result := sub(x, mul(remainder, gt(remainder, 0)))
    }
}

/// @notice Yields the excess beyond the floor of x using the odd function definition.
/// @dev See https://en.wikipedia.org/wiki/Fractional_part.
/// @param x The UD60x18 number to get the fractional part of.
/// @param result The fractional part of x as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function frac(UD60x18 x) pure returns (UD60x18 result) {
    assembly ("memory-safe") {
        result := mod(x, uUNIT)
    }
}

/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$, rounding down.
///
/// @dev Requirements:
/// - x * y must fit in UD60x18.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    uint256 yUint = y.unwrap();
    if (xUint == 0 || yUint == 0) {
        return ZERO;
    }

    unchecked {
        // Checking for overflow this way is faster than letting Solidity do it.
        uint256 xyUint = xUint * yUint;
        if (xyUint / xUint != yUint) {
            revert Errors.PRBMath_UD60x18_Gm_Overflow(x, y);
        }

        // We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
        // during multiplication. See the comments in {Common.sqrt}.
        result = wrap(Common.sqrt(xyUint));
    }
}

/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The UD60x18 number for which to calculate the inverse.
/// @return result The inverse as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(UD60x18 x) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(uUNIT_SQUARED / x.unwrap());
    }
}

/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(UD60x18 x) pure returns (UD60x18 result) {
    unchecked {
        // Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
        // {log2} can return is ~196_205294292027477728.
        result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
    }
}

/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    if (xUint < uUNIT) {
        revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
    }

    // Note that the `mul` in this assembly block is the standard multiplication operation, not {UD60x18.mul}.
    // prettier-ignore
    assembly ("memory-safe") {
        switch x
        case 1 { result := mul(uUNIT, sub(0, 18)) }
        case 10 { result := mul(uUNIT, sub(1, 18)) }
        case 100 { result := mul(uUNIT, sub(2, 18)) }
        case 1000 { result := mul(uUNIT, sub(3, 18)) }
        case 10000 { result := mul(uUNIT, sub(4, 18)) }
        case 100000 { result := mul(uUNIT, sub(5, 18)) }
        case 1000000 { result := mul(uUNIT, sub(6, 18)) }
        case 10000000 { result := mul(uUNIT, sub(7, 18)) }
        case 100000000 { result := mul(uUNIT, sub(8, 18)) }
        case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
        case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
        case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
        case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
        case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
        case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
        case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
        case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
        case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
        case 1000000000000000000 { result := 0 }
        case 10000000000000000000 { result := uUNIT }
        case 100000000000000000000 { result := mul(uUNIT, 2) }
        case 1000000000000000000000 { result := mul(uUNIT, 3) }
        case 10000000000000000000000 { result := mul(uUNIT, 4) }
        case 100000000000000000000000 { result := mul(uUNIT, 5) }
        case 1000000000000000000000000 { result := mul(uUNIT, 6) }
        case 10000000000000000000000000 { result := mul(uUNIT, 7) }
        case 100000000000000000000000000 { result := mul(uUNIT, 8) }
        case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
        case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
        case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
        case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
        case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
        case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
        case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
        case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
        case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
        case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
        case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
        case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
        case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
        case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
        case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
        case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
        case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
        case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
        case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
        case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
        case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
        case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
        case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
        case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
        case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
        case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
        case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
        case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
        case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
        case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
        case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
        case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
        case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
        case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
        case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
        case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
        case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
        case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
        case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
        case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
        case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
        case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 59) }
        default { result := uMAX_UD60x18 }
    }

    if (result.unwrap() == uMAX_UD60x18) {
        unchecked {
            // Inline the fixed-point division to save gas.
            result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
        }
    }
}

/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x must be greater than zero.
///
/// @param x The UD60x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    if (xUint < uUNIT) {
        revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
    }

    unchecked {
        // Calculate the integer part of the logarithm.
        uint256 n = Common.msb(xUint / uUNIT);

        // This is the integer part of the logarithm as a UD60x18 number. The operation can't overflow because n
        // n is at most 255 and UNIT is 1e18.
        uint256 resultUint = n * uUNIT;

        // Calculate $y = x * 2^{-n}$.
        uint256 y = xUint >> n;

        // If y is the unit number, the fractional part is zero.
        if (y == uUNIT) {
            return wrap(resultUint);
        }

        // Calculate the fractional part via the iterative approximation.
        // The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
        uint256 DOUBLE_UNIT = 2e18;
        for (uint256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
            y = (y * y) / uUNIT;

            // Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
            if (y >= DOUBLE_UNIT) {
                // Add the 2^{-m} factor to the logarithm.
                resultUint += delta;

                // Halve y, which corresponds to z/2 in the Wikipedia article.
                y >>= 1;
            }
        }
        result = wrap(resultUint);
    }
}

/// @notice Multiplies two UD60x18 numbers together, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @dev See the documentation in {Common.mulDiv18}.
/// @param x The multiplicand as a UD60x18 number.
/// @param y The multiplier as a UD60x18 number.
/// @return result The product as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(Common.mulDiv18(x.unwrap(), y.unwrap()));
}

/// @notice Raises x to the power of y.
///
/// For $1 \leq x \leq \infty$, the following standard formula is used:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// For $0 \leq x \lt 1$, since the unsigned {log2} is undefined, an equivalent formula is used:
///
/// $$
/// i = \frac{1}{x}
/// w = 2^{log_2{i} * y}
/// x^y = \frac{1}{w}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2} and {mul}.
/// - Returns `UNIT` for 0^0.
/// - It may not perform well with very small values of x. Consider using SD59x18 as an alternative.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    uint256 yUint = y.unwrap();

    // If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
    if (xUint == 0) {
        return yUint == 0 ? UNIT : ZERO;
    }
    // If x is `UNIT`, the result is always `UNIT`.
    else if (xUint == uUNIT) {
        return UNIT;
    }

    // If y is zero, the result is always `UNIT`.
    if (yUint == 0) {
        return UNIT;
    }
    // If y is `UNIT`, the result is always x.
    else if (yUint == uUNIT) {
        return x;
    }

    // If x is greater than `UNIT`, use the standard formula.
    if (xUint > uUNIT) {
        result = exp2(mul(log2(x), y));
    }
    // Conversely, if x is less than `UNIT`, use the equivalent formula.
    else {
        UD60x18 i = wrap(uUNIT_SQUARED / xUint);
        UD60x18 w = exp2(mul(log2(i), y));
        result = wrap(uUNIT_SQUARED / w.unwrap());
    }
}

/// @notice Raises x (a UD60x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - The result must fit in UD60x18.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(UD60x18 x, uint256 y) pure returns (UD60x18 result) {
    // Calculate the first iteration of the loop in advance.
    uint256 xUint = x.unwrap();
    uint256 resultUint = y & 1 > 0 ? xUint : uUNIT;

    // Equivalent to `for(y /= 2; y > 0; y /= 2)`.
    for (y >>= 1; y > 0; y >>= 1) {
        xUint = Common.mulDiv18(xUint, xUint);

        // Equivalent to `y % 2 == 1`.
        if (y & 1 > 0) {
            resultUint = Common.mulDiv18(resultUint, xUint);
        }
    }
    result = wrap(resultUint);
}

/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must be less than `MAX_UD60x18 / UNIT`.
///
/// @param x The UD60x18 number for which to calculate the square root.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    unchecked {
        if (xUint > uMAX_UD60x18 / uUNIT) {
            revert Errors.PRBMath_UD60x18_Sqrt_Overflow(x);
        }
        // Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two UD60x18 numbers.
        // In this case, the two numbers are both the square root.
        result = wrap(Common.sqrt(xUint * uUNIT));
    }
}

File 50 of 59 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;
import "./Helpers.sol" as Helpers;
import "./Math.sol" as Math;

/// @notice The unsigned 60.18-decimal fixed-point number representation, which can have up to 60 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the Solidity type uint256.
/// @dev The value type is defined here so it can be imported in all other files.
type UD60x18 is uint256;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoSD1x18,
    Casting.intoUD2x18,
    Casting.intoSD59x18,
    Casting.intoUint128,
    Casting.intoUint256,
    Casting.intoUint40,
    Casting.unwrap
} for UD60x18 global;

/*//////////////////////////////////////////////////////////////////////////
                            MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
    Math.avg,
    Math.ceil,
    Math.div,
    Math.exp,
    Math.exp2,
    Math.floor,
    Math.frac,
    Math.gm,
    Math.inv,
    Math.ln,
    Math.log10,
    Math.log2,
    Math.mul,
    Math.pow,
    Math.powu,
    Math.sqrt
} for UD60x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
    Helpers.add,
    Helpers.and,
    Helpers.eq,
    Helpers.gt,
    Helpers.gte,
    Helpers.isZero,
    Helpers.lshift,
    Helpers.lt,
    Helpers.lte,
    Helpers.mod,
    Helpers.neq,
    Helpers.not,
    Helpers.or,
    Helpers.rshift,
    Helpers.sub,
    Helpers.uncheckedAdd,
    Helpers.uncheckedSub,
    Helpers.xor
} for UD60x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                    OPERATORS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes it possible to use these operators on the UD60x18 type.
using {
    Helpers.add as +,
    Helpers.and2 as &,
    Math.div as /,
    Helpers.eq as ==,
    Helpers.gt as >,
    Helpers.gte as >=,
    Helpers.lt as <,
    Helpers.lte as <=,
    Helpers.or as |,
    Helpers.mod as %,
    Math.mul as *,
    Helpers.neq as !=,
    Helpers.not as ~,
    Helpers.sub as -,
    Helpers.xor as ^
} for UD60x18 global;

File 51 of 59 : Common.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

// Common.sol
//
// Common mathematical functions needed by both SD59x18 and UD60x18. Note that these global functions do not
// always operate with SD59x18 and UD60x18 numbers.

/*//////////////////////////////////////////////////////////////////////////
                                CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Thrown when the resultant value in {mulDiv} overflows uint256.
error PRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);

/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.
error PRBMath_MulDiv18_Overflow(uint256 x, uint256 y);

/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.
error PRBMath_MulDivSigned_InputTooSmall();

/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.
error PRBMath_MulDivSigned_Overflow(int256 x, int256 y);

/*//////////////////////////////////////////////////////////////////////////
                                    CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

/// @dev The maximum value a uint128 number can have.
uint128 constant MAX_UINT128 = type(uint128).max;

/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;

/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;

/// @dev The unit number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;

/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant
/// bit in the binary representation of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;

/*//////////////////////////////////////////////////////////////////////////
                                    FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function exp2(uint256 x) pure returns (uint256 result) {
    unchecked {
        // Start from 0.5 in the 192.64-bit fixed-point format.
        result = 0x800000000000000000000000000000000000000000000000;

        // The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points:
        //
        // 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.
        // 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing
        // a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,
        // we know that `x & 0xFF` is also 1.
        if (x & 0xFF00000000000000 > 0) {
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
        }

        if (x & 0xFF000000000000 > 0) {
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
        }

        if (x & 0xFF0000000000 > 0) {
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
        }

        if (x & 0xFF00000000 > 0) {
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
        }

        if (x & 0xFF000000 > 0) {
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
        }

        if (x & 0xFF0000 > 0) {
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
        }

        if (x & 0xFF00 > 0) {
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
        }

        if (x & 0xFF > 0) {
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
        }

        // In the code snippet below, two operations are executed simultaneously:
        //
        // 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1
        // accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.
        // 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.
        //
        // The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,
        // integer part, $2^n$.
        result *= UNIT;
        result >>= (191 - (x >> 64));
    }
}

/// @notice Finds the zero-based index of the first 1 in the binary representation of x.
///
/// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set
///
/// Each step in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
///     x >>= 128;
///     result += 128;
/// }
/// ```
///
/// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948
///
/// The Yul instructions used below are:
///
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as a uint256.
/// @custom:smtchecker abstract-function-nondet
function msb(uint256 x) pure returns (uint256 result) {
    // 2^128
    assembly ("memory-safe") {
        let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^64
    assembly ("memory-safe") {
        let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^32
    assembly ("memory-safe") {
        let factor := shl(5, gt(x, 0xFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^16
    assembly ("memory-safe") {
        let factor := shl(4, gt(x, 0xFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^8
    assembly ("memory-safe") {
        let factor := shl(3, gt(x, 0xFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^4
    assembly ("memory-safe") {
        let factor := shl(2, gt(x, 0xF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^2
    assembly ("memory-safe") {
        let factor := shl(1, gt(x, 0x3))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^1
    // No need to shift x any more.
    assembly ("memory-safe") {
        let factor := gt(x, 0x1)
        result := or(result, factor)
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
    // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
    // use the Chinese Remainder Theorem to reconstruct the 512-bit result. The result is stored in two 256
    // variables such that product = prod1 * 2^256 + prod0.
    uint256 prod0; // Least significant 256 bits of the product
    uint256 prod1; // Most significant 256 bits of the product
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

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

    // Make sure the result is less than 2^256. Also prevents denominator == 0.
    if (prod1 >= denominator) {
        revert PRBMath_MulDiv_Overflow(x, y, denominator);
    }

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

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

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

    unchecked {
        // Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow
        // because the denominator cannot be zero at this point in the function execution. The result is always >= 1.
        // For more detail, see https://cs.stackexchange.com/q/138556/92363.
        uint256 lpotdod = denominator & (~denominator + 1);
        uint256 flippedLpotdod;

        assembly ("memory-safe") {
            // Factor powers of two out of denominator.
            denominator := div(denominator, lpotdod)

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

            // Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.
            // `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.
            // However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
            flippedLpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
        }

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

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

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

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

/// @notice Calculates x*y÷1e18 with 512-bit precision.
///
/// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.
///
/// Notes:
/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}.
/// - The result is rounded toward zero.
/// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:
///
/// $$
/// \begin{cases}
///     x * y = MAX\_UINT256 * UNIT \\
///     (x * y) \% UNIT \geq \frac{UNIT}{2}
/// \end{cases}
/// $$
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
    uint256 prod0;
    uint256 prod1;
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    if (prod1 == 0) {
        unchecked {
            return prod0 / UNIT;
        }
    }

    if (prod1 >= UNIT) {
        revert PRBMath_MulDiv18_Overflow(x, y);
    }

    uint256 remainder;
    assembly ("memory-safe") {
        remainder := mulmod(x, y, UNIT)
        result :=
            mul(
                or(
                    div(sub(prod0, remainder), UNIT_LPOTD),
                    mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
                ),
                UNIT_INVERSE
            )
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit in int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
/// @custom:smtchecker abstract-function-nondet
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
    if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
        revert PRBMath_MulDivSigned_InputTooSmall();
    }

    // Get hold of the absolute values of x, y and the denominator.
    uint256 xAbs;
    uint256 yAbs;
    uint256 dAbs;
    unchecked {
        xAbs = x < 0 ? uint256(-x) : uint256(x);
        yAbs = y < 0 ? uint256(-y) : uint256(y);
        dAbs = denominator < 0 ? uint256(-denominator) : uint256(denominator);
    }

    // Compute the absolute value of x*y÷denominator. The result must fit in int256.
    uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
    if (resultAbs > uint256(type(int256).max)) {
        revert PRBMath_MulDivSigned_Overflow(x, y);
    }

    // Get the signs of x, y and the denominator.
    uint256 sx;
    uint256 sy;
    uint256 sd;
    assembly ("memory-safe") {
        // "sgt" is the "signed greater than" assembly instruction and "sub(0,1)" is -1 in two's complement.
        sx := sgt(x, sub(0, 1))
        sy := sgt(y, sub(0, 1))
        sd := sgt(denominator, sub(0, 1))
    }

    // XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
    // If there are, the result should be negative. Otherwise, it should be positive.
    unchecked {
        result = sx ^ sy ^ sd == 0 ? -int256(resultAbs) : int256(resultAbs);
    }
}

/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - If x is not a perfect square, the result is rounded down.
/// - Credits to OpenZeppelin for the explanations in comments below.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function sqrt(uint256 x) pure returns (uint256 result) {
    if (x == 0) {
        return 0;
    }

    // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.
    //
    // We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
    //
    // $$
    // msb(x) <= x <= 2*msb(x)$
    // $$
    //
    // We write $msb(x)$ as $2^k$, and we get:
    //
    // $$
    // k = log_2(x)
    // $$
    //
    // Thus, we can write the initial inequality as:
    //
    // $$
    // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
    // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
    // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
    // $$
    //
    // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.
    uint256 xAux = uint256(x);
    result = 1;
    if (xAux >= 2 ** 128) {
        xAux >>= 128;
        result <<= 64;
    }
    if (xAux >= 2 ** 64) {
        xAux >>= 64;
        result <<= 32;
    }
    if (xAux >= 2 ** 32) {
        xAux >>= 32;
        result <<= 16;
    }
    if (xAux >= 2 ** 16) {
        xAux >>= 16;
        result <<= 8;
    }
    if (xAux >= 2 ** 8) {
        xAux >>= 8;
        result <<= 4;
    }
    if (xAux >= 2 ** 4) {
        xAux >>= 4;
        result <<= 2;
    }
    if (xAux >= 2 ** 2) {
        result <<= 1;
    }

    // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at
    // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision
    // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of
    // precision into the expected uint128 result.
    unchecked {
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;

        // If x is not a perfect square, round the result toward zero.
        uint256 roundedResult = x / result;
        if (result >= roundedResult) {
            result = roundedResult;
        }
    }
}

File 52 of 59 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD1x18 } from "./ValueType.sol";

/// @dev Euler's number as an SD1x18 number.
SD1x18 constant E = SD1x18.wrap(2_718281828459045235);

/// @dev The maximum value an SD1x18 number can have.
int64 constant uMAX_SD1x18 = 9_223372036854775807;
SD1x18 constant MAX_SD1x18 = SD1x18.wrap(uMAX_SD1x18);

/// @dev The maximum value an SD1x18 number can have.
int64 constant uMIN_SD1x18 = -9_223372036854775808;
SD1x18 constant MIN_SD1x18 = SD1x18.wrap(uMIN_SD1x18);

/// @dev PI as an SD1x18 number.
SD1x18 constant PI = SD1x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of SD1x18.
SD1x18 constant UNIT = SD1x18.wrap(1e18);
int256 constant uUNIT = 1e18;

File 53 of 59 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;

/// @notice The signed 1.18-decimal fixed-point number representation, which can have up to 1 digit and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type int64. This is useful when end users want to use int64 to save gas, e.g. with tight variable packing in contract
/// storage.
type SD1x18 is int64;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoSD59x18,
    Casting.intoUD2x18,
    Casting.intoUD60x18,
    Casting.intoUint256,
    Casting.intoUint128,
    Casting.intoUint40,
    Casting.unwrap
} for SD1x18 global;

File 54 of 59 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD2x18 } from "./ValueType.sol";

/// @dev Euler's number as a UD2x18 number.
UD2x18 constant E = UD2x18.wrap(2_718281828459045235);

/// @dev The maximum value a UD2x18 number can have.
uint64 constant uMAX_UD2x18 = 18_446744073709551615;
UD2x18 constant MAX_UD2x18 = UD2x18.wrap(uMAX_UD2x18);

/// @dev PI as a UD2x18 number.
UD2x18 constant PI = UD2x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of UD2x18.
uint256 constant uUNIT = 1e18;
UD2x18 constant UNIT = UD2x18.wrap(1e18);

File 55 of 59 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;

/// @notice The unsigned 2.18-decimal fixed-point number representation, which can have up to 2 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type uint64. This is useful when end users want to use uint64 to save gas, e.g. with tight variable packing in contract
/// storage.
type UD2x18 is uint64;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoSD1x18,
    Casting.intoSD59x18,
    Casting.intoUD60x18,
    Casting.intoUint256,
    Casting.intoUint128,
    Casting.intoUint40,
    Casting.unwrap
} for UD2x18 global;

File 56 of 59 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as CastingErrors;
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { SD1x18 } from "./ValueType.sol";

/// @notice Casts an SD1x18 number into SD59x18.
/// @dev There is no overflow check because the domain of SD1x18 is a subset of SD59x18.
function intoSD59x18(SD1x18 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(int256(SD1x18.unwrap(x)));
}

/// @notice Casts an SD1x18 number into UD2x18.
/// - x must be positive.
function intoUD2x18(SD1x18 x) pure returns (UD2x18 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUD2x18_Underflow(x);
    }
    result = UD2x18.wrap(uint64(xInt));
}

/// @notice Casts an SD1x18 number into UD60x18.
/// @dev Requirements:
/// - x must be positive.
function intoUD60x18(SD1x18 x) pure returns (UD60x18 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUD60x18_Underflow(x);
    }
    result = UD60x18.wrap(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint256.
/// @dev Requirements:
/// - x must be positive.
function intoUint256(SD1x18 x) pure returns (uint256 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUint256_Underflow(x);
    }
    result = uint256(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint128.
/// @dev Requirements:
/// - x must be positive.
function intoUint128(SD1x18 x) pure returns (uint128 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUint128_Underflow(x);
    }
    result = uint128(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint40.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(SD1x18 x) pure returns (uint40 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUint40_Underflow(x);
    }
    if (xInt > int64(uint64(Common.MAX_UINT40))) {
        revert CastingErrors.PRBMath_SD1x18_ToUint40_Overflow(x);
    }
    result = uint40(uint64(xInt));
}

/// @notice Alias for {wrap}.
function sd1x18(int64 x) pure returns (SD1x18 result) {
    result = SD1x18.wrap(x);
}

/// @notice Unwraps an SD1x18 number into int64.
function unwrap(SD1x18 x) pure returns (int64 result) {
    result = SD1x18.unwrap(x);
}

/// @notice Wraps an int64 number into SD1x18.
function wrap(int64 x) pure returns (SD1x18 result) {
    result = SD1x18.wrap(x);
}

File 57 of 59 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { UD2x18 } from "./ValueType.sol";

/// @notice Casts a UD2x18 number into SD1x18.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(UD2x18 x) pure returns (SD1x18 result) {
    uint64 xUint = UD2x18.unwrap(x);
    if (xUint > uint64(uMAX_SD1x18)) {
        revert Errors.PRBMath_UD2x18_IntoSD1x18_Overflow(x);
    }
    result = SD1x18.wrap(int64(xUint));
}

/// @notice Casts a UD2x18 number into SD59x18.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of SD59x18.
function intoSD59x18(UD2x18 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(int256(uint256(UD2x18.unwrap(x))));
}

/// @notice Casts a UD2x18 number into UD60x18.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of UD60x18.
function intoUD60x18(UD2x18 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(UD2x18.unwrap(x));
}

/// @notice Casts a UD2x18 number into uint128.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of uint128.
function intoUint128(UD2x18 x) pure returns (uint128 result) {
    result = uint128(UD2x18.unwrap(x));
}

/// @notice Casts a UD2x18 number into uint256.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of uint256.
function intoUint256(UD2x18 x) pure returns (uint256 result) {
    result = uint256(UD2x18.unwrap(x));
}

/// @notice Casts a UD2x18 number into uint40.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(UD2x18 x) pure returns (uint40 result) {
    uint64 xUint = UD2x18.unwrap(x);
    if (xUint > uint64(Common.MAX_UINT40)) {
        revert Errors.PRBMath_UD2x18_IntoUint40_Overflow(x);
    }
    result = uint40(xUint);
}

/// @notice Alias for {wrap}.
function ud2x18(uint64 x) pure returns (UD2x18 result) {
    result = UD2x18.wrap(x);
}

/// @notice Unwrap a UD2x18 number into uint64.
function unwrap(UD2x18 x) pure returns (uint64 result) {
    result = UD2x18.unwrap(x);
}

/// @notice Wraps a uint64 number into UD2x18.
function wrap(uint64 x) pure returns (UD2x18 result) {
    result = UD2x18.wrap(x);
}

File 58 of 59 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD1x18 } from "./ValueType.sol";

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD2x18.
error PRBMath_SD1x18_ToUD2x18_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD60x18.
error PRBMath_SD1x18_ToUD60x18_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint128.
error PRBMath_SD1x18_ToUint128_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint256.
error PRBMath_SD1x18_ToUint256_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Overflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Underflow(SD1x18 x);

File 59 of 59 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD2x18 } from "./ValueType.sol";

/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in SD1x18.
error PRBMath_UD2x18_IntoSD1x18_Overflow(UD2x18 x);

/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in uint40.
error PRBMath_UD2x18_IntoUint40_Overflow(UD2x18 x);

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "@openzeppelin/[email protected]/=lib/openzeppelin-contracts/contracts/",
    "@napier/v1-tranche/=lib/napier-v1/",
    "@prb/math/=lib/prb-math/src/",
    "foundry-vyper/=lib/foundry-vyper/src/",
    "hardhat-deployer/=lib/hardhat-deployer/src/",
    "@prb/test/=lib/prb-math/lib/prb-test/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "eth-gas-reporter/=node_modules/eth-gas-reporter/",
    "forge-std/=lib/forge-std/src/",
    "hardhat/=node_modules/hardhat/",
    "napier-v1/=lib/napier-v1/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "prb-math/=lib/prb-math/src/",
    "prb-test/=lib/prb-math/lib/prb-test/src/",
    "tricrypto-ng/=lib/tricrypto-ng/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 500
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {
    "lib/napier-v1/src/Create2TrancheLib.sol": {
      "Create2TrancheLib": "0xA00274E0A7A6406f6bb5092894E9eeFBC1dbFB93"
    },
    "src/libs/Create2PoolLib.sol": {
      "Create2PoolLib": "0x34A776b2CB016dB82881B3Ac32785ACBe316d9f1"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract IPoolFactory","name":"_factory","type":"address"},{"internalType":"contract IWETH9","name":"_WETH9","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApproxBinarySearchInputInvalid","type":"error"},{"inputs":[],"name":"ApproxFail","type":"error"},{"inputs":[],"name":"FailedToSendEther","type":"error"},{"inputs":[],"name":"NotWETH","type":"error"},{"inputs":[],"name":"RouterCallbackNotNapierPool","type":"error"},{"inputs":[],"name":"RouterExceededLimitUnderlyingIn","type":"error"},{"inputs":[],"name":"RouterInconsistentWETHPayment","type":"error"},{"inputs":[],"name":"RouterInsufficientLpOut","type":"error"},{"inputs":[],"name":"RouterInsufficientPtRepay","type":"error"},{"inputs":[],"name":"RouterInsufficientTokenBalance","type":"error"},{"inputs":[],"name":"RouterInsufficientUnderlyingOut","type":"error"},{"inputs":[],"name":"RouterInsufficientUnderlyingRepay","type":"error"},{"inputs":[],"name":"RouterInsufficientWETH","type":"error"},{"inputs":[],"name":"RouterInsufficientYtOut","type":"error"},{"inputs":[],"name":"RouterNonSituationSwapUnderlyingForYt","type":"error"},{"inputs":[],"name":"RouterPoolNotFound","type":"error"},{"inputs":[],"name":"RouterTransactionTooOld","type":"error"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"contract IWETH9","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"underlyingIn","type":"uint256"},{"internalType":"uint256[3]","name":"ptsIn","type":"uint256[3]"},{"internalType":"uint256","name":"minLiquidity","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minLiquidity","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"baseLpTokenSwap","type":"uint256"}],"name":"addLiquidityOnePt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"underlyingIn","type":"uint256"},{"internalType":"uint256","name":"minLiquidity","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"baseLpTokenSwap","type":"uint256"}],"name":"addLiquidityOneUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"underlyingIn","type":"uint256"},{"internalType":"uint256","name":"minLiquidity","type":"uint256"},{"internalType":"uint256","name":"ytOutMin","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"underlyingDeposit","type":"uint256"}],"name":"addLiquidityOneUnderlyingKeepYt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract IPoolFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"underlyingDelta","type":"uint256"},{"internalType":"uint256","name":"baseLptDelta","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mintCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"refundETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"underlyingOutMin","type":"uint256"},{"internalType":"uint256[3]","name":"ptsOutMin","type":"uint256[3]"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[3]","name":"","type":"uint256[3]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"ptOutMin","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"baseLpTokenSwap","type":"uint256"}],"name":"removeLiquidityOnePt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"underlyingOutMin","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityOneUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"underlyingDelta","type":"int256"},{"internalType":"int256","name":"ptDelta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"ptInDesired","type":"uint256"},{"internalType":"uint256","name":"underlyingOutMin","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapPtForUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"ptOutDesired","type":"uint256"},{"internalType":"uint256","name":"underlyingInMax","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapUnderlyingForPt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"ytOutDesired","type":"uint256"},{"internalType":"uint256","name":"underlyingInMax","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapUnderlyingForYt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"ytOutDesired","type":"uint256"},{"internalType":"uint256","name":"underlyingInMax","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"components":[{"internalType":"uint256","name":"guessMin","type":"uint256"},{"internalType":"uint256","name":"guessMax","type":"uint256"},{"internalType":"uint256","name":"maxIteration","type":"uint256"},{"internalType":"uint256","name":"eps","type":"uint256"}],"internalType":"struct ApproxParams","name":"approx","type":"tuple"}],"name":"swapUnderlyingForYt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"ytIn","type":"uint256"},{"internalType":"uint256","name":"underlyingOutMin","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapYtForUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amountMinimums","type":"uint256[]"},{"internalType":"address","name":"recipient","type":"address"}],"name":"sweepTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"unwrapWETH9","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60e06040523480156200001157600080fd5b5060405162005a5c38038062005a5c8339810160408190526200003491620000d5565b6001600160a01b038082166080526001600055821660a081905260408051630373298760e51b81529051636e6530e0916004808201926020929091908290030181865afa1580156200008a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000b0919062000114565b60c052506200012e9050565b6001600160a01b0381168114620000d257600080fd5b50565b60008060408385031215620000e957600080fd5b8251620000f681620000bc565b60208401519092506200010981620000bc565b809150509250929050565b6000602082840312156200012757600080fd5b5051919050565b60805160a05160c05161582c620002306000396000818161060b01528181610a0601528181610c8d0152818161131d015281816116c601528181611e2101528181612311015281816126db015281816129e101528181612c4a0152818161313e0152613ea901526000818161036b0152818161062c01528181610a2701528181610cae0152818161133e015281816116e701528181611e4201528181612332015281816126fc01528181612a0201528181612c6b0152818161315f0152613eca01526000818161014e015281816101c00152818161044b015281816104fd01528181613f1b01528181613f6101528181613ff60152614098015261582c6000f3fe60806040526004361061013e5760003560e01c8063ae6b4d04116100bb578063c45a01551161007f578063df2ab5bb11610059578063df2ab5bb146103db578063e05c7969146103ee578063fa483e721461040157600080fd5b8063c45a015514610359578063c8852b471461038d578063d702a4de146103bb57600080fd5b8063ae6b4d04146102e0578063aed0b3c6146102f3578063b1c173a714610313578063b3aa527d14610333578063b64b50151461034657600080fd5b80637100dccb116101025780637100dccb1461024d5780639b0419901461026d5780639f382e9b14610280578063a4940024146102a0578063ac9650d8146102c057600080fd5b806312210e8a1461019357806349404b7c1461019b5780634aa4a4fc146101ae5780635047d214146101ff5780635b989f2b1461022d57600080fd5b3661018e57336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461018c57604051635724f38560e01b815260040160405180910390fd5b005b600080fd5b61018c610421565b61018c6101a9366004614961565b610433565b3480156101ba57600080fd5b506101e27f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561020b57600080fd5b5061021f61021a366004614991565b610570565b6040519081526020016101f6565b34801561023957600080fd5b5061021f6102483660046149f9565b61096b565b34801561025957600080fd5b5061021f610268366004614a57565b610bf2565b61021f61027b3660046149f9565b611282565b34801561028c57600080fd5b5061018c61029b366004614b11565b6115c7565b3480156102ac57600080fd5b5061021f6102bb366004614991565b61162b565b6102d36102ce366004614ba9565b611c3e565b6040516101f69190614c3b565b61021f6102ee3660046149f9565b611d51565b3480156102ff57600080fd5b5061021f61030e3660046149f9565b611d86565b34801561031f57600080fd5b5061021f61032e3660046149f9565b612276565b61018c610341366004614c9d565b6125d0565b61021f610354366004614da6565b612640565b34801561036557600080fd5b506101e27f000000000000000000000000000000000000000000000000000000000000000081565b34801561039957600080fd5b506103ad6103a8366004614e5e565b61293e565b6040516101f6929190614ee8565b3480156103c757600080fd5b5061021f6103d6366004614efc565b612baf565b61018c6103e9366004614f58565b612ff5565b61021f6103fc366004614f9a565b6130a3565b34801561040d57600080fd5b5061018c61041c366004614b11565b61374b565b4715610431576104313347613c4e565b565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561049a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104be9190614fec565b9050828110156104e15760405163b6452f1760e01b815260040160405180910390fd5b801561056b57604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561054957600080fd5b505af115801561055d573d6000803e3d6000fd5b5050505061056b8282613c4e565b505050565b600061057a613cdc565b828042111561059c576040516371db421160e11b815260040160405180910390fd5b6000808a6001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156105dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106009190615015565b9150915061065081837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b03168b6001600160a01b03161461068157604051636365838f60e11b815260040160405180910390fd5b6106966001600160a01b038c16338d8c613d5f565b604051636bcc7c3760e11b815230600482015260009081906001600160a01b038e169063d798f86e9060240160408051808303816000875af11580156106e0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107049190615044565b909250905061071d6001600160a01b0385168e84613de2565b6040516303a19e2360e41b8152600481018890523060248201526001600160a01b038e1690633a19e230906044016020604051808303816000875af115801561076a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078e9190614fec565b5060006001600160a01b0384166307329bcd6107aa8a8561507e565b8f8e60008f6040518663ffffffff1660e01b81526004016107f895949392919094855260208501939093526040840191909152151560608301526001600160a01b0316608082015260a00190565b6020604051808303816000875af1158015610817573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061083b9190614fec565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038616906370a0823190602401602060405180830381865afa158015610885573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a99190614fec565b905080156108c5576108c56001600160a01b0386163383613e71565b6040516370a0823160e01b81523060048201526000906001600160a01b038816906370a0823190602401602060405180830381865afa15801561090c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109309190614fec565b9050801561094c5761094c6001600160a01b0388163383613e71565b509096505050505050506109606001600055565b979650505050505050565b6000610975613cdc565b8180421115610997576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156109d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109fb9190615015565b91509150610a4b81837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b03168a6001600160a01b031614610a7c57604051636365838f60e11b815260040160405180910390fd5b60008a6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa158015610abc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae09190615091565b8a60038110610af157610af16150fa565b60200201516040805160a080825260c082019092529192506000919060208201818036833701905050905060008060208301528460408301528360608301523360808301528260a083015260008d6001600160a01b03166335ec597f8e8e8d876040518563ffffffff1660e01b8152600401610b709493929190615126565b6020604051808303816000875af1158015610b8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bb39190614fec565b90508a811015610bd657604051631c5de8a560e11b815260040160405180910390fd5b9650505050505050610be86001600055565b9695505050505050565b6000610bfc613cdc565b8280421115610c1e576040516371db421160e11b815260040160405180910390fd5b6000808b6001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa158015610c5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c829190615015565b91509150610cd281837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b03168c6001600160a01b031614610d0357604051636365838f60e11b815260040160405180910390fd5b610d186001600160a01b03831633308d613d5f565b610d2061492b565b60008d6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa158015610d60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d849190615091565b8d60038110610d9557610d956150fa565b6020020151604051636eb1769f60e11b81523060048201526001600160a01b038083166024830152919250889186169063dd62ed3e90604401602060405180830381865afa158015610deb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0f9190614fec565b1015610e2b57610e2b6001600160a01b03851682600019613de2565b60405163219e412d60e21b8152306004820152602481018890526000906001600160a01b0383169063867904b4906044016020604051808303816000875af1158015610e7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9f9190614fec565b90508a811015610ec2576040516343c21d2360e01b815260040160405180910390fd5b610f393382846001600160a01b03166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f299190615154565b6001600160a01b03169190613e71565b604051636eb1769f60e11b81523060048201526001600160a01b03858116602483015282919084169063dd62ed3e90604401602060405180830381865afa158015610f88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fac9190614fec565b1015610fc857610fc86001600160a01b03831685600019613de2565b80838f60038110610fdb57610fdb6150fa565b60200201525050604051634515cef360e01b81526000906001600160a01b03841690634515cef3906110139085908590600401615171565b6020604051808303816000875af1158015611032573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110569190614fec565b905060008e6001600160a01b0316634b5992df898f611075919061518c565b848d60066040518060600160405280306001600160a01b031681526020018c6001600160a01b031681526020018b6001600160a01b03168152506040516020016110c092919061519f565b6040516020818303038152906040526040518563ffffffff1660e01b81526004016110ee9493929190615126565b6020604051808303816000875af115801561110d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111319190614fec565b90508b8110156111545760405163258003bd60e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa15801561119b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111bf9190614fec565b905080156111db576111db6001600160a01b0386163383613e71565b6040516370a0823160e01b81523060048201526000906001600160a01b038816906370a0823190602401602060405180830381865afa158015611222573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112469190614fec565b90508015611262576112626001600160a01b0388163383613e71565b509096505050505050506112766001600055565b98975050505050505050565b600061128c613cdc565b81804211156112ae576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156112ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113129190615015565b9150915061136281837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b03168a6001600160a01b03161461139357604051636365838f60e11b815260040160405180910390fd5b60008a6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa1580156113d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f79190615091565b8a60038110611408576114086150fa565b60200201516040805160a080825260c0820190925291925060009190602082018180368337019050506001602082015260408082018690526060820185905233608083015260a082018b9052516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561149c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114c09190614fec565b905060008d6001600160a01b0316634157bc218e8e30876040518563ffffffff1660e01b81526004016114f69493929190615126565b6020604051808303816000875af1158015611515573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115399190614fec565b6040516370a0823160e01b8152306004820152909150610bd6908b9084906001600160a01b038816906370a0823190602401602060405180830381865afa158015611588573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ac9190614fec565b6115b6919061518c565b6001600160a01b0387169190613e71565b60006115d682602081866151f5565b8101906115e3919061521f565b90506115f781604001518260200151613ea1565b61160b816020015182600001513388613f19565b6040810151611624906001600160a01b03163386613e71565b5050505050565b6000611635613cdc565b8280421115611657576040516371db421160e11b815260040160405180910390fd5b6000808a6001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa158015611697573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116bb9190615015565b9150915061170b81837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b03168b6001600160a01b03161461173c57604051636365838f60e11b815260040160405180910390fd5b60008b6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa15801561177c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117a09190615091565b90506117d033308c848f600381106117ba576117ba6150fa565b60200201516001600160a01b0316929190613d5f565b89818c600381106117e3576117e36150fa565b6020020151604051636eb1769f60e11b81523060048201526001600160a01b0385811660248301529091169063dd62ed3e90604401602060405180830381865afa158015611835573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118599190614fec565b10156118ed57808b60038110611871576118716150fa565b602002015160405163095ea7b360e01b81526001600160a01b03848116600483015260001960248301529091169063095ea7b3906044016020604051808303816000875af11580156118c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118eb9190615291565b505b6118f561492b565b8a818d60038110611908576119086150fa565b6020020152604051634515cef360e01b81526000906001600160a01b03851690634515cef39061193e9085908590600401615171565b6020604051808303816000875af115801561195d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119819190614fec565b90506119976001600160a01b0385168f83613de2565b60008e6001600160a01b0316632390154f8a306040518363ffffffff1660e01b81526004016119d99291909182526001600160a01b0316602082015260400190565b6020604051808303816000875af11580156119f8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a1c9190614fec565b905060008f6001600160a01b0316634b5992df838c86611a3c919061518c565b8f60056040518060600160405280306001600160a01b031681526020018e6001600160a01b031681526020018d6001600160a01b0316815250604051602001611a8692919061519f565b6040516020818303038152906040526040518563ffffffff1660e01b8152600401611ab49493929190615126565b6020604051808303816000875af1158015611ad3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af79190614fec565b90508c811015611b1a5760405163258003bd60e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038816906370a0823190602401602060405180830381865afa158015611b61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b859190614fec565b90508015611ba157611ba16001600160a01b0388163383613e71565b6040516370a0823160e01b81523060048201526000906001600160a01b038a16906370a0823190602401602060405180830381865afa158015611be8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c0c9190614fec565b90508015611c2857611c286001600160a01b038a163383613e71565b5090985050505050505050506109606001600055565b60608167ffffffffffffffff811115611c5957611c59614d21565b604051908082528060200260200182016040528015611c8c57816020015b6060815260200190600190039081611c775790505b50905060005b82811015611d4a5760008030868685818110611cb057611cb06150fa565b9050602002810190611cc291906152b3565b604051611cd09291906152fa565b600060405180830381855af49150503d8060008114611d0b576040519150601f19603f3d011682016040523d82523d6000602084013e611d10565b606091505b509150915081611d2257805160208201fd5b80848481518110611d3557611d356150fa565b60209081029190910101525050600101611c92565b5092915050565b600061096087878787878760405180608001604052806000815260200160008152602001600081526020016000815250612640565b6000611d90613cdc565b8180421115611db2576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa158015611df2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e169190615015565b91509150611e6681837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b03168a6001600160a01b031614611e9757604051636365838f60e11b815260040160405180910390fd5b60008a6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa158015611ed7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611efb9190615091565b9050611f126001600160a01b038c16338d8c613d5f565b604051636bcc7c3760e11b815230600482015260009081906001600160a01b038e169063d798f86e9060240160408051808303816000875af1158015611f5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f809190615044565b915091508c6001600160a01b031663204f83f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fc2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fe69190614fec565b42101561210257604051636eb1769f60e11b81523060048201526001600160a01b038e8116602483015285169063dd62ed3e90604401602060405180830381865afa158015612039573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061205d9190614fec565b81111561207a5761207a6001600160a01b0385168e600019613de2565b604051632390154f60e01b8152600481018290523060248201526000906001600160a01b038f1690632390154f906044016020604051808303816000875af11580156120ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ee9190614fec565b90506120fa818461507e565b925050612230565b6040516307329bcd60e01b815260048101829052602481018d905260006044820181905260648201819052306084830152906001600160a01b038616906307329bcd9060a4016020604051808303816000875af1158015612167573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218b9190614fec565b90506000848e600381106121a1576121a16150fa565b6020020151604051635d043b2960e11b815260048101849052306024820181905260448201526001600160a01b039091169063ba087652906064016020604051808303816000875af11580156121fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221f9190614fec565b905061222b818561507e565b935050505b8982101561225157604051631c5de8a560e11b815260040160405180910390fd5b6122656001600160a01b0386168a84613e71565b50945050505050610be86001600055565b6000612280613cdc565b81804211156122a2576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156122e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123069190615015565b9150915061235681837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b03168a6001600160a01b03161461238757604051636365838f60e11b815260040160405180910390fd5b60008a6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa1580156123c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123eb9190615091565b8a600381106123fc576123fc6150fa565b60200201516040516370a0823160e01b81526001600160a01b0389811660048301529192506000918516906370a0823190602401602060405180830381865afa15801561244d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124719190614fec565b604080516101008082526101208201909252919250600091906020820181803683370190505090506000600290508060208301528560408301528460608301523360808301528360a08301528b60c08301528960e08301528a6101008301528d6001600160a01b0316634157bc218e8e30866040518563ffffffff1660e01b81526004016125029493929190615126565b6020604051808303816000875af1158015612521573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125459190614fec565b506040516370a0823160e01b81526001600160a01b038b811660048301528491908816906370a0823190602401602060405180830381865afa15801561258f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125b39190614fec565b6125bd919061518c565b975050505050505050610be86001600055565b8382146125dc57600080fd5b60005b84811015612638576126308686838181106125fc576125fc6150fa565b9050602002016020810190612611919061530a565b858584818110612623576126236150fa565b9050602002013584612ff5565b6001016125df565b505050505050565b600061264a613cdc565b828042111561266c576040516371db421160e11b815260040160405180910390fd5b6000808a6001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156126ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d09190615015565b9150915061272081837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b03168b6001600160a01b03161461275157604051636365838f60e11b815260040160405180910390fd5b60008b6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa158015612791573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b59190615091565b8b600381106127c6576127c66150fa565b6020020151905060006127da828c8961410a565b604080516101208082526101408201909252919250600091906020820181803683370190505090506000600390506000846001600160a01b03166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015612848573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061286c9190615154565b90508160208401528660408401528560608401523360808401528460a08401528060c08401528b60e0840152836101008401528c610120840152505060008e6001600160a01b03166335ec597f8f8f30866040518563ffffffff1660e01b81526004016128dc9493929190615126565b6020604051808303816000875af11580156128fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291f9190614fec565b905061292b818461518c565b9750505050505050506109606001600055565b600061294861492b565b612950613cdc565b8280421115612972576040516371db421160e11b815260040160405180910390fd5b6000808a6001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156129b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d69190615015565b91509150612a2681837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b03168b6001600160a01b031614612a5757604051636365838f60e11b815260040160405180910390fd5b612a6c6001600160a01b038c16338d8d613d5f565b604051636bcc7c3760e11b815230600482015260009081906001600160a01b038e169063d798f86e9060240160408051808303816000875af1158015612ab6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ada9190615044565b915091508a821015612aff57604051631c5de8a560e11b815260040160405180910390fd5b6000836001600160a01b0316635cd34780838d60008e60006040518663ffffffff1660e01b8152600401612b37959493929190615327565b6060604051808303816000875af1158015612b56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b7a919061535d565b9050612b906001600160a01b0386168b85613e71565b91965090945050505050612ba46001600055565b965096945050505050565b6000612bb9613cdc565b8280421115612bdb576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa158015612c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3f9190615015565b91509150612c8f81837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b03168a6001600160a01b031614612cc057604051636365838f60e11b815260040160405180910390fd5b612cd56001600160a01b03831633308c613d5f565b604051636eb1769f60e11b81523060048201526001600160a01b038b811660248301528a919084169063dd62ed3e90604401602060405180830381865afa158015612d24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d489190614fec565b1015612d6457612d646001600160a01b0383168b600019613de2565b6040516303a19e2360e41b8152600481018690523060248201526000906001600160a01b038c1690633a19e230906044016020604051808303816000875af1158015612db4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dd89190614fec565b905060006001600160a01b038c16634b5992df612df5848e61518c565b898c60066040518060600160405280306001600160a01b031681526020018b6001600160a01b031681526020018a6001600160a01b0316815250604051602001612e4092919061519f565b6040516020818303038152906040526040518563ffffffff1660e01b8152600401612e6e9493929190615126565b6020604051808303816000875af1158015612e8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eb19190614fec565b905089811015612ed45760405163258003bd60e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa158015612f1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f3f9190614fec565b90508015612f5b57612f5b6001600160a01b0385163383613e71565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015612fa2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fc69190614fec565b90508015612fe257612fe26001600160a01b0387163383613e71565b509095505050505050610be86001600055565b6040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa15801561303c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130609190614fec565b90508281101561308357604051637eebb25560e11b815260040160405180910390fd5b801561309d5761309d6001600160a01b0385168383613e71565b50505050565b60006130ad613cdc565b81804211156130cf576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa15801561310f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131339190615015565b9150915061318381837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b03168a6001600160a01b0316146131b457604051636365838f60e11b815260040160405180910390fd5b60008a6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa1580156131f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132189190615091565b905061322933308b358460006117ba565b61323b333060208c01358460016117ba565b61324d333060408c01358460026117ba565b8051604051636eb1769f60e11b81523060048201526001600160a01b0384811660248301528b3592169063dd62ed3e90604401602060405180830381865afa15801561329d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132c19190614fec565b101561334057805160405163095ea7b360e01b81526001600160a01b03848116600483015260001960248301529091169063095ea7b3906044016020604051808303816000875af115801561331a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061333e9190615291565b505b602081810151604051636eb1769f60e11b81523060048201526001600160a01b038581166024830152928c0135929091169063dd62ed3e90604401602060405180830381865afa158015613398573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133bc9190614fec565b101561343e57602081015160405163095ea7b360e01b81526001600160a01b03848116600483015260001960248301529091169063095ea7b3906044016020604051808303816000875af1158015613418573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061343c9190615291565b505b6040818101518151636eb1769f60e11b81523060048201526001600160a01b038581166024830152928c0135929091169063dd62ed3e90604401602060405180830381865afa158015613495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134b99190614fec565b101561353b57604080820151905163095ea7b360e01b81526001600160a01b03848116600483015260001960248301529091169063095ea7b3906044016020604051808303816000875af1158015613515573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135399190615291565b505b604051634515cef360e01b81526000906001600160a01b03841690634515cef39061356c908d9085906004016153b2565b6020604051808303816000875af115801561358b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135af9190614fec565b905060008c6001600160a01b0316634b5992df8d848c60046040518060600160405280336001600160a01b031681526020018c6001600160a01b031681526020018b6001600160a01b031681525060405160200161360e92919061519f565b6040516020818303038152906040526040518563ffffffff1660e01b815260040161363c9493929190615126565b6020604051808303816000875af115801561365b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061367f9190614fec565b9050898110156136a25760405163258003bd60e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa1580156136e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061370d9190614fec565b90508015613729576137296001600160a01b0386163383613e71565b4715613739576137393347613c4e565b5095505050505050610be86001600055565b60008061375c6060602085876151f5565b81019061376991906153c8565b915091506137778183613ea1565b8335600081600681111561378d5761378d615110565b036137dd5760006137a185606081896151f5565b8101906137ae91906153f6565b80519091506137d790336137c18a615437565b60208501516001600160a01b0316929190613d5f565b50613c45565b60018160068111156137f1576137f1615110565b0361385957600061380585606081896151f5565b8101906138129190615453565b905080602001518861382390615437565b111561384257604051631c7d7ddb60e21b815260040160405180910390fd5b80516137d7908590336138548c615437565b613f19565b600281600681111561386d5761386d615110565b03613a6a57600061388185606081896151f5565b81019061388e919061548d565b9050600061389b89615437565b90506000888360400151106138b15750876138dc565b50604082015182516138dc906138c7838c61518c565b60208601516001600160a01b03169190613e71565b61395d8360000151308386602001516001600160a01b03166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015613928573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061394c9190615154565b6001600160a01b0316929190613d5f565b602083015160405163927d795360e01b815230600482018190526024820152604481018390526000916001600160a01b03169063927d7953906064016020604051808303816000875af11580156139b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139dc9190614fec565b9050828110156139ff57604051638650a38d60e01b815260040160405180910390fd5b6000613a0b848361518c565b90508460800151811015613a3257604051631c5de8a560e11b815260040160405180910390fd5b613a466001600160a01b0389163386613e71565b6060850151613a60906001600160a01b038a169083613e71565b5050505050613c45565b6003816006811115613a7e57613a7e615110565b03613c45576000613a9285606081896151f5565b810190613a9f9190615515565b9050876000613aad89615437565b905081836080015111613ad357604051630adf5c9d60e31b815260040160405180910390fd5b6000828460800151613ae5919061518c565b90508360a00151811115613b0c57604051631c7d7ddb60e21b815260040160405180910390fd5b613b1c8785600001513084613f19565b60208401516080850151613b3a916001600160a01b038a1691613de2565b6020840151608085015160405163219e412d60e21b815230600482015260248101919091526000916001600160a01b03169063867904b4906044016020604051808303816000875af1158015613b94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bb89190614fec565b905082811015613bdb57604051630aba2d5f60e31b815260040160405180910390fd5b6020850151613bf4906001600160a01b03163385613e71565b8451613c1990613c04858461518c565b60208801516001600160a01b03169190613e71565b613c3f85606001518287604001516001600160a01b0316613e719092919063ffffffff16565b50505050505b50505050505050565b604080516000808252602082019092526001600160a01b038416908390604051613c7891906155b0565b60006040518083038185875af1925050503d8060008114613cb5576040519150601f19603f3d011682016040523d82523d6000602084013e613cba565b606091505b505090508061056b57604051630dcf35db60e41b815260040160405180910390fd5b600260005403613d335760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b6002600055565b6001600160a01b03808516600090815290841660205260408120610be8818585614331565b6040516001600160a01b038085166024830152831660448201526064810182905261309d9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261435b565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613e338482614430565b61309d576040516001600160a01b038416602482015260006044820152613e6790859063095ea7b360e01b90606401613d93565b61309d848261435b565b6040516001600160a01b03831660248201526044810182905261056b90849063a9059cbb60e01b90606401613d93565b33613eee83837f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613d3a565b6001600160a01b031614613f15576040516303ef51a160e21b815260040160405180910390fd5b5050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316148015613f5a5750804710155b1561406d577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613fba57600080fd5b505af1158015613fce573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b038681166004830152602482018690527f000000000000000000000000000000000000000000000000000000000000000016935063a9059cbb925060440190506020604051808303816000875af1158015614043573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140679190615291565b5061309d565b306001600160a01b03841603614096576140916001600160a01b0385168383613e71565b61309d565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b03161480156140d75750600034115b156140f557604051632117573960e11b815260040160405180910390fd5b61309d6001600160a01b038516848484613d5f565b600081600001518260200151101561413557604051631b26f4c960e11b815260040160405180910390fd5b816060015160000361414f5766b1a2bc2ec5000060608301525b816040015160000361416357606460408301525b6000846001600160a01b031663e6432a346040518163ffffffff1660e01b815260040161010060405180830381865afa1580156141a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141c891906155e4565b9050600081606001516001600160a01b031663f51e181a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561420e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142329190614fec565b905060006040518060800160405280886001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561427f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142a391906156a0565b60ff1681526020018381526020016142bf8560a00151856144d9565b815260c085015167ffffffffffffffff16602090910152905060006143156142e884600161507e565b6142f290896156c3565b6127106143016101f48261518c565b856040015161431091906156c3565b6144ef565b905061432382888389614515565b9450505050505b9392505050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b60006143b0826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661468c9092919063ffffffff16565b90508051600014806143d15750808060200190518101906143d19190615291565b61056b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401613d2a565b6000806000846001600160a01b03168460405161444d91906155b0565b6000604051808303816000865af19150503d806000811461448a576040519150601f19603f3d011682016040523d82523d6000602084013e61448f565b606091505b50915091508180156144b95750805115806144b95750808060200190518101906144b99190615291565b80156144ce57506001600160a01b0385163b15155b925050505b92915050565b60008183116144e8578161432a565b5090919050565b600082600019048411830215820261450657600080fd5b50910281810615159190040190565b8351600090819061452790600a6157be565b9050600061453e86886020015189604001516144ef565b9050600061454c838761507e565b85519091501561456557845161456290836144d9565b91505b6020850151156145805761457d85602001518261469b565b90505b6000805b8660400151811461466a57600261459b848661507e565b6145a591906157ca565b915060006145b38b846146aa565b905060006145dc8b6145cd84670de0b6b3a76400006156c3565b6145d791906157ca565b6146e9565b6145ee90670de0b6b3a76400006157ec565b90508a82101580156146145750806146098a606001516146e9565b61461290615437565b125b156146285783975050505050505050614684565b8486148061463f57508461463d87600161507e565b145b1561464b57505061466a565b600081131561465c57839550614660565b8394505b5050600101614584565b50604051637d388ed960e11b815260040160405180910390fd5b949350505050565b60606146848484600085614770565b60008183106144e8578161432a565b6000806146be8385606001516127106144ef565b905060006146d96146cf838661518c565b8660200151614840565b90506144ce818660400151614855565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82111561476c5760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2061604482015267371034b73a191a9b60c11b6064820152608401613d2a565b5090565b6060824710156147d15760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401613d2a565b600080866001600160a01b031685876040516147ed91906155b0565b60006040518083038185875af1925050503d806000811461482a576040519150601f19603f3d011682016040523d82523d6000602084013e61482f565b606091505b50915091506109608783838761486a565b600061432a83670de0b6b3a7640000846148e3565b600061432a8383670de0b6b3a76400006148e3565b606083156148d95782516000036148d2576001600160a01b0385163b6148d25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401613d2a565b5081614684565b6146848383614901565b60008260001904841183021582026148fa57600080fd5b5091020490565b8151156149115781518083602001fd5b8060405162461bcd60e51b8152600401613d2a919061580c565b60405180606001604052806003906020820280368337509192915050565b6001600160a01b038116811461495e57600080fd5b50565b6000806040838503121561497457600080fd5b82359150602083013561498681614949565b809150509250929050565b600080600080600080600060e0888a0312156149ac57600080fd5b87356149b781614949565b965060208801359550604088013594506060880135935060808801356149dc81614949565b9699959850939692959460a0840135945060c09093013592915050565b60008060008060008060c08789031215614a1257600080fd5b8635614a1d81614949565b95506020870135945060408701359350606087013592506080870135614a4281614949565b8092505060a087013590509295509295509295565b600080600080600080600080610100898b031215614a7457600080fd5b8835614a7f81614949565b97506020890135965060408901359550606089013594506080890135935060a0890135614aab81614949565b979a969950949793969295929450505060c08201359160e0013590565b60008083601f840112614ada57600080fd5b50813567ffffffffffffffff811115614af257600080fd5b602083019150836020828501011115614b0a57600080fd5b9250929050565b60008060008060608587031215614b2757600080fd5b8435935060208501359250604085013567ffffffffffffffff811115614b4c57600080fd5b614b5887828801614ac8565b95989497509550505050565b60008083601f840112614b7657600080fd5b50813567ffffffffffffffff811115614b8e57600080fd5b6020830191508360208260051b8501011115614b0a57600080fd5b60008060208385031215614bbc57600080fd5b823567ffffffffffffffff811115614bd357600080fd5b614bdf85828601614b64565b90969095509350505050565b60005b83811015614c06578181015183820152602001614bee565b50506000910152565b60008151808452614c27816020860160208601614beb565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015614c9057603f19888603018452614c7e858351614c0f565b94509285019290850190600101614c62565b5092979650505050505050565b600080600080600060608688031215614cb557600080fd5b853567ffffffffffffffff80821115614ccd57600080fd5b614cd989838a01614b64565b90975095506020880135915080821115614cf257600080fd5b50614cff88828901614b64565b9094509250506040860135614d1381614949565b809150509295509295909350565b634e487b7160e01b600052604160045260246000fd5b6040516080810167ffffffffffffffff81118282101715614d5a57614d5a614d21565b60405290565b6040805190810167ffffffffffffffff81118282101715614d5a57614d5a614d21565b6040516060810167ffffffffffffffff81118282101715614d5a57614d5a614d21565b6000806000806000806000878903610140811215614dc357600080fd5b8835614dce81614949565b97506020890135965060408901359550606089013594506080890135614df381614949565b935060a08901359250608060bf1982011215614e0e57600080fd5b50614e17614d37565b60c0890135815260e089013560208201526101008901356040820152610120909801356060890152509497939650919490939192565b80606081018310156144d357600080fd5b6000806000806000806101008789031215614e7857600080fd5b8635614e8381614949565b95506020870135945060408701359350614ea08860608901614e4d565b925060c0870135614eb081614949565b8092505060e087013590509295509295509295565b8060005b600381101561309d578151845260209384019390910190600101614ec9565b8281526080810161432a6020830184614ec5565b60008060008060008060c08789031215614f1557600080fd5b8635614f2081614949565b955060208701359450604087013593506060870135614f3e81614949565b9598949750929560808101359460a0909101359350915050565b600080600060608486031215614f6d57600080fd5b8335614f7881614949565b9250602084013591506040840135614f8f81614949565b809150509250925092565b6000806000806000806101008789031215614fb457600080fd5b8635614fbf81614949565b955060208701359450614fd58860408901614e4d565b935060a0870135925060c0870135614eb081614949565b600060208284031215614ffe57600080fd5b5051919050565b805161501081614949565b919050565b6000806040838503121561502857600080fd5b825161503381614949565b602084015190925061498681614949565b6000806040838503121561505757600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b808201808211156144d3576144d3615068565b6000606082840312156150a357600080fd5b82601f8301126150b257600080fd5b6150ba614d83565b8060608401858111156150cc57600080fd5b845b818110156150ef5780516150e181614949565b8452602093840193016150ce565b509095945050505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b8481528360208201526001600160a01b0383166040820152608060608201526000610be86080830184614c0f565b60006020828403121561516657600080fd5b815161432a81614949565b6080810161517f8285614ec5565b8260608301529392505050565b818103818111156144d3576144d3615068565b60808101600784106151c157634e487b7160e01b600052602160045260246000fd5b8382526001600160a01b03808451166020840152806020850151166040840152806040850151166060840152509392505050565b6000808585111561520557600080fd5b8386111561521257600080fd5b5050820193919092039150565b60006060828403121561523157600080fd5b6040516060810181811067ffffffffffffffff8211171561525457615254614d21565b604052823561526281614949565b8152602083013561527281614949565b6020820152604083013561528581614949565b60408201529392505050565b6000602082840312156152a357600080fd5b8151801515811461432a57600080fd5b6000808335601e198436030181126152ca57600080fd5b83018035915067ffffffffffffffff8211156152e557600080fd5b602001915036819003821315614b0a57600080fd5b8183823760009101908152919050565b60006020828403121561531c57600080fd5b813561432a81614949565b85815260e08101606086602084013793151560808201526001600160a01b039290921660a0830152151560c09091015292915050565b60006060828403121561536f57600080fd5b82601f83011261537e57600080fd5b615386614d83565b80606084018581111561539857600080fd5b845b818110156150ef57805184526020938401930161539a565b6080810160608483378260608301529392505050565b600080604083850312156153db57600080fd5b82356153e681614949565b9150602083013561498681614949565b60006040828403121561540857600080fd5b615410614d60565b823561541b81614949565b8152602083013561542b81614949565b60208201529392505050565b6000600160ff1b820161544c5761544c615068565b5060000390565b60006040828403121561546557600080fd5b61546d614d60565b823561547881614949565b81526020928301359281019290925250919050565b600060a0828403121561549f57600080fd5b60405160a0810181811067ffffffffffffffff821117156154c2576154c2614d21565b60405282356154d081614949565b815260208301356154e081614949565b60208201526040838101359082015260608301356154fd81614949565b60608201526080928301359281019290925250919050565b600060c0828403121561552757600080fd5b60405160c0810181811067ffffffffffffffff8211171561554a5761554a614d21565b604052823561555881614949565b8152602083013561556881614949565b6020820152604083013561557b81614949565b6040820152606083013561558e81614949565b60608201526080838101359082015260a0928301359281019290925250919050565b600082516155c2818460208701614beb565b9190910192915050565b805167ffffffffffffffff8116811461501057600080fd5b60006101008083850312156155f857600080fd5b6040519081019067ffffffffffffffff8211818310171561561b5761561b614d21565b816040528351915061562c82614949565b81815261563b60208501615005565b602082015261564c60408501615005565b604082015261565d60608501615005565b60608201526080840151608082015260a084015160a082015261568260c085016155cc565b60c082015261569360e085016155cc565b60e0820152949350505050565b6000602082840312156156b257600080fd5b815160ff8116811461432a57600080fd5b80820281158282048414176144d3576144d3615068565b600181815b808511156157155781600019048211156156fb576156fb615068565b8085161561570857918102915b93841c93908002906156df565b509250929050565b60008261572c575060016144d3565b81615739575060006144d3565b816001811461574f576002811461575957615775565b60019150506144d3565b60ff84111561576a5761576a615068565b50506001821b6144d3565b5060208310610133831016604e8410600b8410161715615798575081810a6144d3565b6157a283836156da565b80600019048211156157b6576157b6615068565b029392505050565b600061432a838361571d565b6000826157e757634e487b7160e01b600052601260045260246000fd5b500490565b8181036000831280158383131683831282161715611d4a57611d4a615068565b60208152600061432a6020830184614c0f56fea164736f6c6343000813000a00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

Deployed Bytecode

0x60806040526004361061013e5760003560e01c8063ae6b4d04116100bb578063c45a01551161007f578063df2ab5bb11610059578063df2ab5bb146103db578063e05c7969146103ee578063fa483e721461040157600080fd5b8063c45a015514610359578063c8852b471461038d578063d702a4de146103bb57600080fd5b8063ae6b4d04146102e0578063aed0b3c6146102f3578063b1c173a714610313578063b3aa527d14610333578063b64b50151461034657600080fd5b80637100dccb116101025780637100dccb1461024d5780639b0419901461026d5780639f382e9b14610280578063a4940024146102a0578063ac9650d8146102c057600080fd5b806312210e8a1461019357806349404b7c1461019b5780634aa4a4fc146101ae5780635047d214146101ff5780635b989f2b1461022d57600080fd5b3661018e57336001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2161461018c57604051635724f38560e01b815260040160405180910390fd5b005b600080fd5b61018c610421565b61018c6101a9366004614961565b610433565b3480156101ba57600080fd5b506101e27f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561020b57600080fd5b5061021f61021a366004614991565b610570565b6040519081526020016101f6565b34801561023957600080fd5b5061021f6102483660046149f9565b61096b565b34801561025957600080fd5b5061021f610268366004614a57565b610bf2565b61021f61027b3660046149f9565b611282565b34801561028c57600080fd5b5061018c61029b366004614b11565b6115c7565b3480156102ac57600080fd5b5061021f6102bb366004614991565b61162b565b6102d36102ce366004614ba9565b611c3e565b6040516101f69190614c3b565b61021f6102ee3660046149f9565b611d51565b3480156102ff57600080fd5b5061021f61030e3660046149f9565b611d86565b34801561031f57600080fd5b5061021f61032e3660046149f9565b612276565b61018c610341366004614c9d565b6125d0565b61021f610354366004614da6565b612640565b34801561036557600080fd5b506101e27f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc6181565b34801561039957600080fd5b506103ad6103a8366004614e5e565b61293e565b6040516101f6929190614ee8565b3480156103c757600080fd5b5061021f6103d6366004614efc565b612baf565b61018c6103e9366004614f58565b612ff5565b61021f6103fc366004614f9a565b6130a3565b34801561040d57600080fd5b5061018c61041c366004614b11565b61374b565b4715610431576104313347613c4e565b565b6040516370a0823160e01b81523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401602060405180830381865afa15801561049a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104be9190614fec565b9050828110156104e15760405163b6452f1760e01b815260040160405180910390fd5b801561056b57604051632e1a7d4d60e01b8152600481018290527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561054957600080fd5b505af115801561055d573d6000803e3d6000fd5b5050505061056b8282613c4e565b505050565b600061057a613cdc565b828042111561059c576040516371db421160e11b815260040160405180910390fd5b6000808a6001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156105dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106009190615015565b9150915061065081837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b03168b6001600160a01b03161461068157604051636365838f60e11b815260040160405180910390fd5b6106966001600160a01b038c16338d8c613d5f565b604051636bcc7c3760e11b815230600482015260009081906001600160a01b038e169063d798f86e9060240160408051808303816000875af11580156106e0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107049190615044565b909250905061071d6001600160a01b0385168e84613de2565b6040516303a19e2360e41b8152600481018890523060248201526001600160a01b038e1690633a19e230906044016020604051808303816000875af115801561076a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078e9190614fec565b5060006001600160a01b0384166307329bcd6107aa8a8561507e565b8f8e60008f6040518663ffffffff1660e01b81526004016107f895949392919094855260208501939093526040840191909152151560608301526001600160a01b0316608082015260a00190565b6020604051808303816000875af1158015610817573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061083b9190614fec565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038616906370a0823190602401602060405180830381865afa158015610885573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a99190614fec565b905080156108c5576108c56001600160a01b0386163383613e71565b6040516370a0823160e01b81523060048201526000906001600160a01b038816906370a0823190602401602060405180830381865afa15801561090c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109309190614fec565b9050801561094c5761094c6001600160a01b0388163383613e71565b509096505050505050506109606001600055565b979650505050505050565b6000610975613cdc565b8180421115610997576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156109d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109fb9190615015565b91509150610a4b81837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b03168a6001600160a01b031614610a7c57604051636365838f60e11b815260040160405180910390fd5b60008a6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa158015610abc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae09190615091565b8a60038110610af157610af16150fa565b60200201516040805160a080825260c082019092529192506000919060208201818036833701905050905060008060208301528460408301528360608301523360808301528260a083015260008d6001600160a01b03166335ec597f8e8e8d876040518563ffffffff1660e01b8152600401610b709493929190615126565b6020604051808303816000875af1158015610b8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bb39190614fec565b90508a811015610bd657604051631c5de8a560e11b815260040160405180910390fd5b9650505050505050610be86001600055565b9695505050505050565b6000610bfc613cdc565b8280421115610c1e576040516371db421160e11b815260040160405180910390fd5b6000808b6001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa158015610c5e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c829190615015565b91509150610cd281837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b03168c6001600160a01b031614610d0357604051636365838f60e11b815260040160405180910390fd5b610d186001600160a01b03831633308d613d5f565b610d2061492b565b60008d6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa158015610d60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d849190615091565b8d60038110610d9557610d956150fa565b6020020151604051636eb1769f60e11b81523060048201526001600160a01b038083166024830152919250889186169063dd62ed3e90604401602060405180830381865afa158015610deb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0f9190614fec565b1015610e2b57610e2b6001600160a01b03851682600019613de2565b60405163219e412d60e21b8152306004820152602481018890526000906001600160a01b0383169063867904b4906044016020604051808303816000875af1158015610e7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9f9190614fec565b90508a811015610ec2576040516343c21d2360e01b815260040160405180910390fd5b610f393382846001600160a01b03166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f299190615154565b6001600160a01b03169190613e71565b604051636eb1769f60e11b81523060048201526001600160a01b03858116602483015282919084169063dd62ed3e90604401602060405180830381865afa158015610f88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fac9190614fec565b1015610fc857610fc86001600160a01b03831685600019613de2565b80838f60038110610fdb57610fdb6150fa565b60200201525050604051634515cef360e01b81526000906001600160a01b03841690634515cef3906110139085908590600401615171565b6020604051808303816000875af1158015611032573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110569190614fec565b905060008e6001600160a01b0316634b5992df898f611075919061518c565b848d60066040518060600160405280306001600160a01b031681526020018c6001600160a01b031681526020018b6001600160a01b03168152506040516020016110c092919061519f565b6040516020818303038152906040526040518563ffffffff1660e01b81526004016110ee9493929190615126565b6020604051808303816000875af115801561110d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111319190614fec565b90508b8110156111545760405163258003bd60e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa15801561119b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111bf9190614fec565b905080156111db576111db6001600160a01b0386163383613e71565b6040516370a0823160e01b81523060048201526000906001600160a01b038816906370a0823190602401602060405180830381865afa158015611222573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112469190614fec565b90508015611262576112626001600160a01b0388163383613e71565b509096505050505050506112766001600055565b98975050505050505050565b600061128c613cdc565b81804211156112ae576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156112ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113129190615015565b9150915061136281837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b03168a6001600160a01b03161461139357604051636365838f60e11b815260040160405180910390fd5b60008a6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa1580156113d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f79190615091565b8a60038110611408576114086150fa565b60200201516040805160a080825260c0820190925291925060009190602082018180368337019050506001602082015260408082018690526060820185905233608083015260a082018b9052516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561149c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114c09190614fec565b905060008d6001600160a01b0316634157bc218e8e30876040518563ffffffff1660e01b81526004016114f69493929190615126565b6020604051808303816000875af1158015611515573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115399190614fec565b6040516370a0823160e01b8152306004820152909150610bd6908b9084906001600160a01b038816906370a0823190602401602060405180830381865afa158015611588573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ac9190614fec565b6115b6919061518c565b6001600160a01b0387169190613e71565b60006115d682602081866151f5565b8101906115e3919061521f565b90506115f781604001518260200151613ea1565b61160b816020015182600001513388613f19565b6040810151611624906001600160a01b03163386613e71565b5050505050565b6000611635613cdc565b8280421115611657576040516371db421160e11b815260040160405180910390fd5b6000808a6001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa158015611697573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116bb9190615015565b9150915061170b81837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b03168b6001600160a01b03161461173c57604051636365838f60e11b815260040160405180910390fd5b60008b6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa15801561177c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117a09190615091565b90506117d033308c848f600381106117ba576117ba6150fa565b60200201516001600160a01b0316929190613d5f565b89818c600381106117e3576117e36150fa565b6020020151604051636eb1769f60e11b81523060048201526001600160a01b0385811660248301529091169063dd62ed3e90604401602060405180830381865afa158015611835573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118599190614fec565b10156118ed57808b60038110611871576118716150fa565b602002015160405163095ea7b360e01b81526001600160a01b03848116600483015260001960248301529091169063095ea7b3906044016020604051808303816000875af11580156118c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118eb9190615291565b505b6118f561492b565b8a818d60038110611908576119086150fa565b6020020152604051634515cef360e01b81526000906001600160a01b03851690634515cef39061193e9085908590600401615171565b6020604051808303816000875af115801561195d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119819190614fec565b90506119976001600160a01b0385168f83613de2565b60008e6001600160a01b0316632390154f8a306040518363ffffffff1660e01b81526004016119d99291909182526001600160a01b0316602082015260400190565b6020604051808303816000875af11580156119f8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a1c9190614fec565b905060008f6001600160a01b0316634b5992df838c86611a3c919061518c565b8f60056040518060600160405280306001600160a01b031681526020018e6001600160a01b031681526020018d6001600160a01b0316815250604051602001611a8692919061519f565b6040516020818303038152906040526040518563ffffffff1660e01b8152600401611ab49493929190615126565b6020604051808303816000875af1158015611ad3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af79190614fec565b90508c811015611b1a5760405163258003bd60e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038816906370a0823190602401602060405180830381865afa158015611b61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b859190614fec565b90508015611ba157611ba16001600160a01b0388163383613e71565b6040516370a0823160e01b81523060048201526000906001600160a01b038a16906370a0823190602401602060405180830381865afa158015611be8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c0c9190614fec565b90508015611c2857611c286001600160a01b038a163383613e71565b5090985050505050505050506109606001600055565b60608167ffffffffffffffff811115611c5957611c59614d21565b604051908082528060200260200182016040528015611c8c57816020015b6060815260200190600190039081611c775790505b50905060005b82811015611d4a5760008030868685818110611cb057611cb06150fa565b9050602002810190611cc291906152b3565b604051611cd09291906152fa565b600060405180830381855af49150503d8060008114611d0b576040519150601f19603f3d011682016040523d82523d6000602084013e611d10565b606091505b509150915081611d2257805160208201fd5b80848481518110611d3557611d356150fa565b60209081029190910101525050600101611c92565b5092915050565b600061096087878787878760405180608001604052806000815260200160008152602001600081526020016000815250612640565b6000611d90613cdc565b8180421115611db2576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa158015611df2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e169190615015565b91509150611e6681837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b03168a6001600160a01b031614611e9757604051636365838f60e11b815260040160405180910390fd5b60008a6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa158015611ed7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611efb9190615091565b9050611f126001600160a01b038c16338d8c613d5f565b604051636bcc7c3760e11b815230600482015260009081906001600160a01b038e169063d798f86e9060240160408051808303816000875af1158015611f5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f809190615044565b915091508c6001600160a01b031663204f83f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fc2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fe69190614fec565b42101561210257604051636eb1769f60e11b81523060048201526001600160a01b038e8116602483015285169063dd62ed3e90604401602060405180830381865afa158015612039573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061205d9190614fec565b81111561207a5761207a6001600160a01b0385168e600019613de2565b604051632390154f60e01b8152600481018290523060248201526000906001600160a01b038f1690632390154f906044016020604051808303816000875af11580156120ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ee9190614fec565b90506120fa818461507e565b925050612230565b6040516307329bcd60e01b815260048101829052602481018d905260006044820181905260648201819052306084830152906001600160a01b038616906307329bcd9060a4016020604051808303816000875af1158015612167573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218b9190614fec565b90506000848e600381106121a1576121a16150fa565b6020020151604051635d043b2960e11b815260048101849052306024820181905260448201526001600160a01b039091169063ba087652906064016020604051808303816000875af11580156121fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221f9190614fec565b905061222b818561507e565b935050505b8982101561225157604051631c5de8a560e11b815260040160405180910390fd5b6122656001600160a01b0386168a84613e71565b50945050505050610be86001600055565b6000612280613cdc565b81804211156122a2576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156122e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123069190615015565b9150915061235681837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b03168a6001600160a01b03161461238757604051636365838f60e11b815260040160405180910390fd5b60008a6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa1580156123c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123eb9190615091565b8a600381106123fc576123fc6150fa565b60200201516040516370a0823160e01b81526001600160a01b0389811660048301529192506000918516906370a0823190602401602060405180830381865afa15801561244d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124719190614fec565b604080516101008082526101208201909252919250600091906020820181803683370190505090506000600290508060208301528560408301528460608301523360808301528360a08301528b60c08301528960e08301528a6101008301528d6001600160a01b0316634157bc218e8e30866040518563ffffffff1660e01b81526004016125029493929190615126565b6020604051808303816000875af1158015612521573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125459190614fec565b506040516370a0823160e01b81526001600160a01b038b811660048301528491908816906370a0823190602401602060405180830381865afa15801561258f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125b39190614fec565b6125bd919061518c565b975050505050505050610be86001600055565b8382146125dc57600080fd5b60005b84811015612638576126308686838181106125fc576125fc6150fa565b9050602002016020810190612611919061530a565b858584818110612623576126236150fa565b9050602002013584612ff5565b6001016125df565b505050505050565b600061264a613cdc565b828042111561266c576040516371db421160e11b815260040160405180910390fd5b6000808a6001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156126ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d09190615015565b9150915061272081837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b03168b6001600160a01b03161461275157604051636365838f60e11b815260040160405180910390fd5b60008b6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa158015612791573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b59190615091565b8b600381106127c6576127c66150fa565b6020020151905060006127da828c8961410a565b604080516101208082526101408201909252919250600091906020820181803683370190505090506000600390506000846001600160a01b03166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015612848573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061286c9190615154565b90508160208401528660408401528560608401523360808401528460a08401528060c08401528b60e0840152836101008401528c610120840152505060008e6001600160a01b03166335ec597f8f8f30866040518563ffffffff1660e01b81526004016128dc9493929190615126565b6020604051808303816000875af11580156128fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291f9190614fec565b905061292b818461518c565b9750505050505050506109606001600055565b600061294861492b565b612950613cdc565b8280421115612972576040516371db421160e11b815260040160405180910390fd5b6000808a6001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa1580156129b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d69190615015565b91509150612a2681837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b03168b6001600160a01b031614612a5757604051636365838f60e11b815260040160405180910390fd5b612a6c6001600160a01b038c16338d8d613d5f565b604051636bcc7c3760e11b815230600482015260009081906001600160a01b038e169063d798f86e9060240160408051808303816000875af1158015612ab6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ada9190615044565b915091508a821015612aff57604051631c5de8a560e11b815260040160405180910390fd5b6000836001600160a01b0316635cd34780838d60008e60006040518663ffffffff1660e01b8152600401612b37959493929190615327565b6060604051808303816000875af1158015612b56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b7a919061535d565b9050612b906001600160a01b0386168b85613e71565b91965090945050505050612ba46001600055565b965096945050505050565b6000612bb9613cdc565b8280421115612bdb576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa158015612c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3f9190615015565b91509150612c8f81837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b03168a6001600160a01b031614612cc057604051636365838f60e11b815260040160405180910390fd5b612cd56001600160a01b03831633308c613d5f565b604051636eb1769f60e11b81523060048201526001600160a01b038b811660248301528a919084169063dd62ed3e90604401602060405180830381865afa158015612d24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d489190614fec565b1015612d6457612d646001600160a01b0383168b600019613de2565b6040516303a19e2360e41b8152600481018690523060248201526000906001600160a01b038c1690633a19e230906044016020604051808303816000875af1158015612db4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dd89190614fec565b905060006001600160a01b038c16634b5992df612df5848e61518c565b898c60066040518060600160405280306001600160a01b031681526020018b6001600160a01b031681526020018a6001600160a01b0316815250604051602001612e4092919061519f565b6040516020818303038152906040526040518563ffffffff1660e01b8152600401612e6e9493929190615126565b6020604051808303816000875af1158015612e8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eb19190614fec565b905089811015612ed45760405163258003bd60e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa158015612f1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f3f9190614fec565b90508015612f5b57612f5b6001600160a01b0385163383613e71565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015612fa2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fc69190614fec565b90508015612fe257612fe26001600160a01b0387163383613e71565b509095505050505050610be86001600055565b6040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa15801561303c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130609190614fec565b90508281101561308357604051637eebb25560e11b815260040160405180910390fd5b801561309d5761309d6001600160a01b0385168383613e71565b50505050565b60006130ad613cdc565b81804211156130cf576040516371db421160e11b815260040160405180910390fd5b600080896001600160a01b03166367e4ac2c6040518163ffffffff1660e01b81526004016040805180830381865afa15801561310f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131339190615015565b9150915061318381837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b03168a6001600160a01b0316146131b457604051636365838f60e11b815260040160405180910390fd5b60008a6001600160a01b031663b751a0476040518163ffffffff1660e01b8152600401606060405180830381865afa1580156131f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132189190615091565b905061322933308b358460006117ba565b61323b333060208c01358460016117ba565b61324d333060408c01358460026117ba565b8051604051636eb1769f60e11b81523060048201526001600160a01b0384811660248301528b3592169063dd62ed3e90604401602060405180830381865afa15801561329d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132c19190614fec565b101561334057805160405163095ea7b360e01b81526001600160a01b03848116600483015260001960248301529091169063095ea7b3906044016020604051808303816000875af115801561331a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061333e9190615291565b505b602081810151604051636eb1769f60e11b81523060048201526001600160a01b038581166024830152928c0135929091169063dd62ed3e90604401602060405180830381865afa158015613398573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133bc9190614fec565b101561343e57602081015160405163095ea7b360e01b81526001600160a01b03848116600483015260001960248301529091169063095ea7b3906044016020604051808303816000875af1158015613418573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061343c9190615291565b505b6040818101518151636eb1769f60e11b81523060048201526001600160a01b038581166024830152928c0135929091169063dd62ed3e90604401602060405180830381865afa158015613495573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134b99190614fec565b101561353b57604080820151905163095ea7b360e01b81526001600160a01b03848116600483015260001960248301529091169063095ea7b3906044016020604051808303816000875af1158015613515573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135399190615291565b505b604051634515cef360e01b81526000906001600160a01b03841690634515cef39061356c908d9085906004016153b2565b6020604051808303816000875af115801561358b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135af9190614fec565b905060008c6001600160a01b0316634b5992df8d848c60046040518060600160405280336001600160a01b031681526020018c6001600160a01b031681526020018b6001600160a01b031681525060405160200161360e92919061519f565b6040516020818303038152906040526040518563ffffffff1660e01b815260040161363c9493929190615126565b6020604051808303816000875af115801561365b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061367f9190614fec565b9050898110156136a25760405163258003bd60e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa1580156136e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061370d9190614fec565b90508015613729576137296001600160a01b0386163383613e71565b4715613739576137393347613c4e565b5095505050505050610be86001600055565b60008061375c6060602085876151f5565b81019061376991906153c8565b915091506137778183613ea1565b8335600081600681111561378d5761378d615110565b036137dd5760006137a185606081896151f5565b8101906137ae91906153f6565b80519091506137d790336137c18a615437565b60208501516001600160a01b0316929190613d5f565b50613c45565b60018160068111156137f1576137f1615110565b0361385957600061380585606081896151f5565b8101906138129190615453565b905080602001518861382390615437565b111561384257604051631c7d7ddb60e21b815260040160405180910390fd5b80516137d7908590336138548c615437565b613f19565b600281600681111561386d5761386d615110565b03613a6a57600061388185606081896151f5565b81019061388e919061548d565b9050600061389b89615437565b90506000888360400151106138b15750876138dc565b50604082015182516138dc906138c7838c61518c565b60208601516001600160a01b03169190613e71565b61395d8360000151308386602001516001600160a01b03166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015613928573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061394c9190615154565b6001600160a01b0316929190613d5f565b602083015160405163927d795360e01b815230600482018190526024820152604481018390526000916001600160a01b03169063927d7953906064016020604051808303816000875af11580156139b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139dc9190614fec565b9050828110156139ff57604051638650a38d60e01b815260040160405180910390fd5b6000613a0b848361518c565b90508460800151811015613a3257604051631c5de8a560e11b815260040160405180910390fd5b613a466001600160a01b0389163386613e71565b6060850151613a60906001600160a01b038a169083613e71565b5050505050613c45565b6003816006811115613a7e57613a7e615110565b03613c45576000613a9285606081896151f5565b810190613a9f9190615515565b9050876000613aad89615437565b905081836080015111613ad357604051630adf5c9d60e31b815260040160405180910390fd5b6000828460800151613ae5919061518c565b90508360a00151811115613b0c57604051631c7d7ddb60e21b815260040160405180910390fd5b613b1c8785600001513084613f19565b60208401516080850151613b3a916001600160a01b038a1691613de2565b6020840151608085015160405163219e412d60e21b815230600482015260248101919091526000916001600160a01b03169063867904b4906044016020604051808303816000875af1158015613b94573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bb89190614fec565b905082811015613bdb57604051630aba2d5f60e31b815260040160405180910390fd5b6020850151613bf4906001600160a01b03163385613e71565b8451613c1990613c04858461518c565b60208801516001600160a01b03169190613e71565b613c3f85606001518287604001516001600160a01b0316613e719092919063ffffffff16565b50505050505b50505050505050565b604080516000808252602082019092526001600160a01b038416908390604051613c7891906155b0565b60006040518083038185875af1925050503d8060008114613cb5576040519150601f19603f3d011682016040523d82523d6000602084013e613cba565b606091505b505090508061056b57604051630dcf35db60e41b815260040160405180910390fd5b600260005403613d335760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b6002600055565b6001600160a01b03808516600090815290841660205260408120610be8818585614331565b6040516001600160a01b038085166024830152831660448201526064810182905261309d9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261435b565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613e338482614430565b61309d576040516001600160a01b038416602482015260006044820152613e6790859063095ea7b360e01b90606401613d93565b61309d848261435b565b6040516001600160a01b03831660248201526044810182905261056b90849063a9059cbb60e01b90606401613d93565b33613eee83837fcbbbf5b7628d3872c2bdddd8e6ae9e691089b9a49484bcabc8cc9cd97910e6a37f00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61613d3a565b6001600160a01b031614613f15576040516303ef51a160e21b815260040160405180910390fd5b5050565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316846001600160a01b0316148015613f5a5750804710155b1561406d577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613fba57600080fd5b505af1158015613fce573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b038681166004830152602482018690527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216935063a9059cbb925060440190506020604051808303816000875af1158015614043573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140679190615291565b5061309d565b306001600160a01b03841603614096576140916001600160a01b0385168383613e71565b61309d565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316846001600160a01b03161480156140d75750600034115b156140f557604051632117573960e11b815260040160405180910390fd5b61309d6001600160a01b038516848484613d5f565b600081600001518260200151101561413557604051631b26f4c960e11b815260040160405180910390fd5b816060015160000361414f5766b1a2bc2ec5000060608301525b816040015160000361416357606460408301525b6000846001600160a01b031663e6432a346040518163ffffffff1660e01b815260040161010060405180830381865afa1580156141a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141c891906155e4565b9050600081606001516001600160a01b031663f51e181a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561420e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142329190614fec565b905060006040518060800160405280886001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561427f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142a391906156a0565b60ff1681526020018381526020016142bf8560a00151856144d9565b815260c085015167ffffffffffffffff16602090910152905060006143156142e884600161507e565b6142f290896156c3565b6127106143016101f48261518c565b856040015161431091906156c3565b6144ef565b905061432382888389614515565b9450505050505b9392505050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b60006143b0826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661468c9092919063ffffffff16565b90508051600014806143d15750808060200190518101906143d19190615291565b61056b5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401613d2a565b6000806000846001600160a01b03168460405161444d91906155b0565b6000604051808303816000865af19150503d806000811461448a576040519150601f19603f3d011682016040523d82523d6000602084013e61448f565b606091505b50915091508180156144b95750805115806144b95750808060200190518101906144b99190615291565b80156144ce57506001600160a01b0385163b15155b925050505b92915050565b60008183116144e8578161432a565b5090919050565b600082600019048411830215820261450657600080fd5b50910281810615159190040190565b8351600090819061452790600a6157be565b9050600061453e86886020015189604001516144ef565b9050600061454c838761507e565b85519091501561456557845161456290836144d9565b91505b6020850151156145805761457d85602001518261469b565b90505b6000805b8660400151811461466a57600261459b848661507e565b6145a591906157ca565b915060006145b38b846146aa565b905060006145dc8b6145cd84670de0b6b3a76400006156c3565b6145d791906157ca565b6146e9565b6145ee90670de0b6b3a76400006157ec565b90508a82101580156146145750806146098a606001516146e9565b61461290615437565b125b156146285783975050505050505050614684565b8486148061463f57508461463d87600161507e565b145b1561464b57505061466a565b600081131561465c57839550614660565b8394505b5050600101614584565b50604051637d388ed960e11b815260040160405180910390fd5b949350505050565b60606146848484600085614770565b60008183106144e8578161432a565b6000806146be8385606001516127106144ef565b905060006146d96146cf838661518c565b8660200151614840565b90506144ce818660400151614855565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82111561476c5760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2061604482015267371034b73a191a9b60c11b6064820152608401613d2a565b5090565b6060824710156147d15760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401613d2a565b600080866001600160a01b031685876040516147ed91906155b0565b60006040518083038185875af1925050503d806000811461482a576040519150601f19603f3d011682016040523d82523d6000602084013e61482f565b606091505b50915091506109608783838761486a565b600061432a83670de0b6b3a7640000846148e3565b600061432a8383670de0b6b3a76400006148e3565b606083156148d95782516000036148d2576001600160a01b0385163b6148d25760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401613d2a565b5081614684565b6146848383614901565b60008260001904841183021582026148fa57600080fd5b5091020490565b8151156149115781518083602001fd5b8060405162461bcd60e51b8152600401613d2a919061580c565b60405180606001604052806003906020820280368337509192915050565b6001600160a01b038116811461495e57600080fd5b50565b6000806040838503121561497457600080fd5b82359150602083013561498681614949565b809150509250929050565b600080600080600080600060e0888a0312156149ac57600080fd5b87356149b781614949565b965060208801359550604088013594506060880135935060808801356149dc81614949565b9699959850939692959460a0840135945060c09093013592915050565b60008060008060008060c08789031215614a1257600080fd5b8635614a1d81614949565b95506020870135945060408701359350606087013592506080870135614a4281614949565b8092505060a087013590509295509295509295565b600080600080600080600080610100898b031215614a7457600080fd5b8835614a7f81614949565b97506020890135965060408901359550606089013594506080890135935060a0890135614aab81614949565b979a969950949793969295929450505060c08201359160e0013590565b60008083601f840112614ada57600080fd5b50813567ffffffffffffffff811115614af257600080fd5b602083019150836020828501011115614b0a57600080fd5b9250929050565b60008060008060608587031215614b2757600080fd5b8435935060208501359250604085013567ffffffffffffffff811115614b4c57600080fd5b614b5887828801614ac8565b95989497509550505050565b60008083601f840112614b7657600080fd5b50813567ffffffffffffffff811115614b8e57600080fd5b6020830191508360208260051b8501011115614b0a57600080fd5b60008060208385031215614bbc57600080fd5b823567ffffffffffffffff811115614bd357600080fd5b614bdf85828601614b64565b90969095509350505050565b60005b83811015614c06578181015183820152602001614bee565b50506000910152565b60008151808452614c27816020860160208601614beb565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015614c9057603f19888603018452614c7e858351614c0f565b94509285019290850190600101614c62565b5092979650505050505050565b600080600080600060608688031215614cb557600080fd5b853567ffffffffffffffff80821115614ccd57600080fd5b614cd989838a01614b64565b90975095506020880135915080821115614cf257600080fd5b50614cff88828901614b64565b9094509250506040860135614d1381614949565b809150509295509295909350565b634e487b7160e01b600052604160045260246000fd5b6040516080810167ffffffffffffffff81118282101715614d5a57614d5a614d21565b60405290565b6040805190810167ffffffffffffffff81118282101715614d5a57614d5a614d21565b6040516060810167ffffffffffffffff81118282101715614d5a57614d5a614d21565b6000806000806000806000878903610140811215614dc357600080fd5b8835614dce81614949565b97506020890135965060408901359550606089013594506080890135614df381614949565b935060a08901359250608060bf1982011215614e0e57600080fd5b50614e17614d37565b60c0890135815260e089013560208201526101008901356040820152610120909801356060890152509497939650919490939192565b80606081018310156144d357600080fd5b6000806000806000806101008789031215614e7857600080fd5b8635614e8381614949565b95506020870135945060408701359350614ea08860608901614e4d565b925060c0870135614eb081614949565b8092505060e087013590509295509295509295565b8060005b600381101561309d578151845260209384019390910190600101614ec9565b8281526080810161432a6020830184614ec5565b60008060008060008060c08789031215614f1557600080fd5b8635614f2081614949565b955060208701359450604087013593506060870135614f3e81614949565b9598949750929560808101359460a0909101359350915050565b600080600060608486031215614f6d57600080fd5b8335614f7881614949565b9250602084013591506040840135614f8f81614949565b809150509250925092565b6000806000806000806101008789031215614fb457600080fd5b8635614fbf81614949565b955060208701359450614fd58860408901614e4d565b935060a0870135925060c0870135614eb081614949565b600060208284031215614ffe57600080fd5b5051919050565b805161501081614949565b919050565b6000806040838503121561502857600080fd5b825161503381614949565b602084015190925061498681614949565b6000806040838503121561505757600080fd5b505080516020909101519092909150565b634e487b7160e01b600052601160045260246000fd5b808201808211156144d3576144d3615068565b6000606082840312156150a357600080fd5b82601f8301126150b257600080fd5b6150ba614d83565b8060608401858111156150cc57600080fd5b845b818110156150ef5780516150e181614949565b8452602093840193016150ce565b509095945050505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b8481528360208201526001600160a01b0383166040820152608060608201526000610be86080830184614c0f565b60006020828403121561516657600080fd5b815161432a81614949565b6080810161517f8285614ec5565b8260608301529392505050565b818103818111156144d3576144d3615068565b60808101600784106151c157634e487b7160e01b600052602160045260246000fd5b8382526001600160a01b03808451166020840152806020850151166040840152806040850151166060840152509392505050565b6000808585111561520557600080fd5b8386111561521257600080fd5b5050820193919092039150565b60006060828403121561523157600080fd5b6040516060810181811067ffffffffffffffff8211171561525457615254614d21565b604052823561526281614949565b8152602083013561527281614949565b6020820152604083013561528581614949565b60408201529392505050565b6000602082840312156152a357600080fd5b8151801515811461432a57600080fd5b6000808335601e198436030181126152ca57600080fd5b83018035915067ffffffffffffffff8211156152e557600080fd5b602001915036819003821315614b0a57600080fd5b8183823760009101908152919050565b60006020828403121561531c57600080fd5b813561432a81614949565b85815260e08101606086602084013793151560808201526001600160a01b039290921660a0830152151560c09091015292915050565b60006060828403121561536f57600080fd5b82601f83011261537e57600080fd5b615386614d83565b80606084018581111561539857600080fd5b845b818110156150ef57805184526020938401930161539a565b6080810160608483378260608301529392505050565b600080604083850312156153db57600080fd5b82356153e681614949565b9150602083013561498681614949565b60006040828403121561540857600080fd5b615410614d60565b823561541b81614949565b8152602083013561542b81614949565b60208201529392505050565b6000600160ff1b820161544c5761544c615068565b5060000390565b60006040828403121561546557600080fd5b61546d614d60565b823561547881614949565b81526020928301359281019290925250919050565b600060a0828403121561549f57600080fd5b60405160a0810181811067ffffffffffffffff821117156154c2576154c2614d21565b60405282356154d081614949565b815260208301356154e081614949565b60208201526040838101359082015260608301356154fd81614949565b60608201526080928301359281019290925250919050565b600060c0828403121561552757600080fd5b60405160c0810181811067ffffffffffffffff8211171561554a5761554a614d21565b604052823561555881614949565b8152602083013561556881614949565b6020820152604083013561557b81614949565b6040820152606083013561558e81614949565b60608201526080838101359082015260a0928301359281019290925250919050565b600082516155c2818460208701614beb565b9190910192915050565b805167ffffffffffffffff8116811461501057600080fd5b60006101008083850312156155f857600080fd5b6040519081019067ffffffffffffffff8211818310171561561b5761561b614d21565b816040528351915061562c82614949565b81815261563b60208501615005565b602082015261564c60408501615005565b604082015261565d60608501615005565b60608201526080840151608082015260a084015160a082015261568260c085016155cc565b60c082015261569360e085016155cc565b60e0820152949350505050565b6000602082840312156156b257600080fd5b815160ff8116811461432a57600080fd5b80820281158282048414176144d3576144d3615068565b600181815b808511156157155781600019048211156156fb576156fb615068565b8085161561570857918102915b93841c93908002906156df565b509250929050565b60008261572c575060016144d3565b81615739575060006144d3565b816001811461574f576002811461575957615775565b60019150506144d3565b60ff84111561576a5761576a615068565b50506001821b6144d3565b5060208310610133831016604e8410600b8410161715615798575081810a6144d3565b6157a283836156da565b80600019048211156157b6576157b6615068565b029392505050565b600061432a838361571d565b6000826157e757634e487b7160e01b600052601260045260246000fd5b500490565b8181036000831280158383131683831282161715611d4a57611d4a615068565b60208152600061432a6020830184614c0f56fea164736f6c6343000813000a

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

00000000000000000000000017354e8e7518599c7f6b7095a6706766e4e4dc61000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

-----Decoded View---------------
Arg [0] : _factory (address): 0x17354e8e7518599c7f6B7095a6706766e4e4dC61
Arg [1] : _WETH9 (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

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


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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