ETH Price: $3,153.70 (+1.75%)
 

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 Liquidity...217521942025-02-01 14:13:35316 days ago1738419215IN
0x2aAAb14F...f3457c78A
0 ETH0.002005774.33895193
Remove Liquidity...217521842025-02-01 14:11:35316 days ago1738419095IN
0x2aAAb14F...f3457c78A
0 ETH0.002170614.69552678
Remove Liquidity...217521692025-02-01 14:08:35316 days ago1738418915IN
0x2aAAb14F...f3457c78A
0 ETH0.001989664.30905472
Remove Liquidity...217250652025-01-28 19:18:59320 days ago1738091939IN
0x2aAAb14F...f3457c78A
0 ETH0.003223817.05424943
Remove Liquidity...216041682025-01-11 22:16:47337 days ago1736633807IN
0x2aAAb14F...f3457c78A
0 ETH0.001512463.30959694
Remove Liquidity...215926722025-01-10 7:45:35339 days ago1736495135IN
0x2aAAb14F...f3457c78A
0 ETH0.002097134.58377817
Remove Liquidity...215925582025-01-10 7:22:47339 days ago1736493767IN
0x2aAAb14F...f3457c78A
0 ETH0.001784893.90634914
Remove Liquidity...215833202025-01-09 0:23:35340 days ago1736382215IN
0x2aAAb14F...f3457c78A
0 ETH0.002736096.02003048
Remove Liquidity...215781822025-01-08 7:12:23341 days ago1736320343IN
0x2aAAb14F...f3457c78A
0 ETH0.002694315.98994297
Remove Liquidity...215781652025-01-08 7:08:59341 days ago1736320139IN
0x2aAAb14F...f3457c78A
0 ETH0.002586465.75003236
Remove Liquidity...215781382025-01-08 7:03:35341 days ago1736319815IN
0x2aAAb14F...f3457c78A
0 ETH0.00358847.97746749
Remove Liquidity...215781252025-01-08 7:00:59341 days ago1736319659IN
0x2aAAb14F...f3457c78A
0 ETH0.003642858.09871531
Remove Liquidity...215779342025-01-08 6:22:35341 days ago1736317355IN
0x2aAAb14F...f3457c78A
0 ETH0.001917434.26269901
Remove Liquidity...215779012025-01-08 6:15:59341 days ago1736316959IN
0x2aAAb14F...f3457c78A
0 ETH0.002282564.58382375
Remove Liquidity...215644182025-01-06 9:05:59343 days ago1736154359IN
0x2aAAb14F...f3457c78A
0 ETH0.003715797.46223097
Remove Liquidity...215230262024-12-31 14:23:47348 days ago1735655027IN
0x2aAAb14F...f3457c78A
0 ETH0.0062980814.00225372
Remove Liquidity...215230192024-12-31 14:22:23348 days ago1735654943IN
0x2aAAb14F...f3457c78A
0 ETH0.007453414.25138461
Swap Pt For ETH208992532024-10-05 12:11:47436 days ago1728130307IN
0x2aAAb14F...f3457c78A
0 ETH0.001235664.78142246
Swap Pt For ETH208477782024-09-28 7:56:23443 days ago1727510183IN
0x2aAAb14F...f3457c78A
0 ETH0.00235347.64959528
Swap Pt For ETH208381212024-09-26 23:37:59444 days ago1727393879IN
0x2aAAb14F...f3457c78A
0 ETH0.0058058618.74558293
Swap Pt For ETH207836972024-09-19 9:18:47452 days ago1726737527IN
0x2aAAb14F...f3457c78A
0 ETH0.0032022510.34784908
Swap Pt For ETH207795082024-09-18 19:16:23452 days ago1726686983IN
0x2aAAb14F...f3457c78A
0 ETH0.0005549719.86935094
Swap Pt For ETH207700662024-09-17 11:35:23454 days ago1726572923IN
0x2aAAb14F...f3457c78A
0 ETH0.000812722.62635207
Swap Pt For ETH207622352024-09-16 9:17:11455 days ago1726478231IN
0x2aAAb14F...f3457c78A
0 ETH0.002850339.38268908
Swap ETH For Pt207537212024-09-15 4:47:47456 days ago1726375667IN
0x2aAAb14F...f3457c78A
0.29096654 ETH0.000638271.57416801
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer217521942025-02-01 14:13:35316 days ago1738419215
0x2aAAb14F...f3457c78A
0.04547334 ETH
Transfer217521942025-02-01 14:13:35316 days ago1738419215
0x2aAAb14F...f3457c78A
0.04547334 ETH
Transfer217521842025-02-01 14:11:35316 days ago1738419095
0x2aAAb14F...f3457c78A
0.04774473 ETH
Transfer217521842025-02-01 14:11:35316 days ago1738419095
0x2aAAb14F...f3457c78A
0.04774473 ETH
Transfer217521692025-02-01 14:08:35316 days ago1738418915
0x2aAAb14F...f3457c78A
0.05867189 ETH
Transfer217521692025-02-01 14:08:35316 days ago1738418915
0x2aAAb14F...f3457c78A
0.05867189 ETH
Transfer217250652025-01-28 19:18:59320 days ago1738091939
0x2aAAb14F...f3457c78A
0.03831304 ETH
Transfer217250652025-01-28 19:18:59320 days ago1738091939
0x2aAAb14F...f3457c78A
0.03831304 ETH
Transfer216041682025-01-11 22:16:47337 days ago1736633807
0x2aAAb14F...f3457c78A
0.11681797 ETH
Transfer216041682025-01-11 22:16:47337 days ago1736633807
0x2aAAb14F...f3457c78A
0.11681797 ETH
Transfer215926722025-01-10 7:45:35339 days ago1736495135
0x2aAAb14F...f3457c78A
0.01974637 ETH
Transfer215926722025-01-10 7:45:35339 days ago1736495135
0x2aAAb14F...f3457c78A
0.01974637 ETH
Transfer215925582025-01-10 7:22:47339 days ago1736493767
0x2aAAb14F...f3457c78A
0.13364581 ETH
Transfer215925582025-01-10 7:22:47339 days ago1736493767
0x2aAAb14F...f3457c78A
0.13364581 ETH
Transfer215833202025-01-09 0:23:35340 days ago1736382215
0x2aAAb14F...f3457c78A
0.68944028 ETH
Transfer215833202025-01-09 0:23:35340 days ago1736382215
0x2aAAb14F...f3457c78A
0.68944028 ETH
Transfer215781822025-01-08 7:12:23341 days ago1736320343
0x2aAAb14F...f3457c78A
0.03021656 ETH
Transfer215781822025-01-08 7:12:23341 days ago1736320343
0x2aAAb14F...f3457c78A
0.03021656 ETH
Transfer215781652025-01-08 7:08:59341 days ago1736320139
0x2aAAb14F...f3457c78A
0.03525149 ETH
Transfer215781652025-01-08 7:08:59341 days ago1736320139
0x2aAAb14F...f3457c78A
0.03525149 ETH
Transfer215781382025-01-08 7:03:35341 days ago1736319815
0x2aAAb14F...f3457c78A
0.02862447 ETH
Transfer215781382025-01-08 7:03:35341 days ago1736319815
0x2aAAb14F...f3457c78A
0.02862447 ETH
Transfer215781252025-01-08 7:00:59341 days ago1736319659
0x2aAAb14F...f3457c78A
0.03031484 ETH
Transfer215781252025-01-08 7:00:59341 days ago1736319659
0x2aAAb14F...f3457c78A
0.03031484 ETH
Transfer215779342025-01-08 6:22:35341 days ago1736317355
0x2aAAb14F...f3457c78A
0.0299504 ETH
View All Internal Transactions
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:
MetapoolRouter

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 10000000 runs

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

// Interfaces
import {IERC20, SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";
import {CurveTricryptoOptimizedWETH} from "@napier/v1-pool/src/interfaces/external/CurveTricryptoOptimizedWETH.sol";
import {IWETH9} from "@napier/v1-tranche/src/interfaces/IWETH9.sol";
import {INapierPool} from "@napier/v1-pool/src/interfaces/INapierPool.sol";
import {ITranche} from "@napier/v1-tranche/src/interfaces/ITranche.sol";
import {Twocrypto} from "./interfaces/external/Twocrypto.sol";
import {IVault} from "./interfaces/external/balancer/IVault.sol";
import {IFlashLoanRecipient} from "./interfaces/external/balancer/IFlashLoanRecipient.sol";
import {MetapoolFactory} from "./MetapoolFactory.sol";

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

// Libraries
import {TransientStorage} from "./TransientStorage.sol";
import {Math} from "@openzeppelin/[email protected]/utils/math/Math.sol";
import {TrancheMathHelper} from "@napier/v1-pool/src/libs/TrancheMathHelper.sol";
import {ApproxParams} from "@napier/v1-pool/src/interfaces/ApproxParams.sol";
import {Errors} from "./Errors.sol";

// Inherits
import {ReentrancyGuard} from "@openzeppelin/[email protected]/security/ReentrancyGuard.sol";

/// @title MetapoolRouter - A contract for swapping between PT, YT, and ETH on the 3LST NapierPool, 3LST-PT tricrypto, and Twocrypto metapools
contract MetapoolRouter is ReentrancyGuard, IFlashLoanRecipient, IMetapoolRouter {
    /// @dev Constants for the Twocrypto metapool indexes
    /// coins(0) is the pegged token (PT) and coins(1) is the base pool token (triLST-PT Tricrypto)
    uint128 constant PEGGED_PT_INDEX = 0;
    uint128 constant BASE_POOL_INDEX = 1;

    /// @dev Transient storage slots
    uint256 constant TSLOT_0 = 0; // Authorization flag for `receiveFlashLoan`
    uint256 constant TSLOT_1 = 1; // Temporary storage for `swapETHForYt` function return value
    uint256 constant TSLOT_CB_DATA_METAPOOL = 2; // `FlashLoanData.metapool` slot for `receiveFlashLoan` callback data
    uint256 constant TSLOT_CB_DATA_PT = 3; // `FlashLoanData.pt` slot for `receiveFlashLoan` callback data
    uint256 constant TSLOT_CB_DATA_SENDER = 4; // `FlashLoanData.sender` slot for `receiveFlashLoan` callback data
    uint256 constant TSLOT_CB_DATA_VALUE = 5; // `FlashLoanData.msgValue` slot for `receiveFlashLoan` callback data
    uint256 constant TSLOT_CB_DATA_MAX_ETH_SPENT = 6; // `FlashLoanData.maxEthSpent` slot for `receiveFlashLoan` callback data
    uint256 constant TSLOT_CB_DATA_RECEIPIENT = 7; // `FlashLoanData.recipient` slot for `receiveFlashLoan` callback data

    /// @notice The WETH9 contract
    IWETH9 public immutable WETH9;

    /// @notice The Factory contract for the Principal Token metapools
    MetapoolFactory public immutable metapoolFactory;

    /// @notice The rETH-PT<>stETH-PT<>sfrxETH-PT Curve TricryptoNG pool (triLST-PT Tricrypto)
    CurveTricryptoOptimizedWETH public immutable tricryptoLST;

    /// @notice The triLST-PT<>WETH NapierPool
    INapierPool public immutable triLSTPool;

    /// @notice The Balancer Vault contract for flash loans
    IVault public immutable vault;

    /// @dev The approval slot of (`token`, `spender`) is given by:
    /// ```
    ///     mstore(0x20, spender)
    ///     mstore(0x0c, _IS_APPROVED_SLOT_SEED)
    ///     mstore(0x00, token)
    ///     let allowanceSlot := keccak256(0x0c, 0x34)
    /// ```
    /// @dev Optimized storage slot for approval flags
    /// `mapping (address token => mapping (address spender => uint256 approved)) _isApproved;`
    uint256 private constant _IS_APPROVED_SLOT_SEED = 0xa8fe4407;

    /// @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.MetapoolRouterTransactionTooOld();
        _;
    }

    /// @notice If the metapool is not a TwoCrypto with Principal Token, revert.
    modifier checkMetapool(address metapool) {
        if (!metapoolFactory.isPtMetapool(metapool)) revert Errors.MetapoolRouterInvalidMetapool();
        _;
    }

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

    constructor(MetapoolFactory _metapoolFactory, INapierPool _triLSTPool, IVault _vault) {
        metapoolFactory = _metapoolFactory;
        triLSTPool = _triLSTPool;
        vault = _vault;
        WETH9 = IWETH9(_metapoolFactory.WETH9());
        tricryptoLST = _triLSTPool.tricrypto();

        (bool s, bytes memory data) = CurveTricryptoOptimizedWETH(tricryptoLST).factory().staticcall(
            abi.encodeWithSignature("get_coins(address)", tricryptoLST)
        );
        require(s);
        address[3] memory coins = abi.decode(data, (address[3]));
        // Approve rETH-PT<>stETH-PT<>sfrxETH-PT Curve TricryptoNG pool (triLST-PT Tricrypto) to spend meta tokens
        IERC20(coins[0]).approve(address(tricryptoLST), type(uint256).max);
        IERC20(coins[1]).approve(address(tricryptoLST), type(uint256).max);
        IERC20(coins[2]).approve(address(tricryptoLST), type(uint256).max);
        // Approve triLST-PT<>WETH NapierPool to spend WETH9
        SafeERC20.forceApprove(IWETH9(WETH9), address(triLSTPool), type(uint256).max);
        // Approve triLST-PT<>WETH NapierPool to spend tricryptoLST
        SafeERC20.forceApprove(tricryptoLST, address(triLSTPool), type(uint256).max);
    }

    /// @notice Swap ETH for PT
    /// @notice A caller must send ETH enough greater than the `maxEthSpent`. Remaining ETH will be sent back to the caller.
    /// @dev This function can't swap ETH for the exact amount of PT because of precision loss. So, `minPtOut` must be specified by the caller.
    /// @param metapool The address of the Twocrypto metapool
    /// @param ptAmount The amount of PT tokens to receive
    /// @param maxEthSpent The maximum amount of ETH to spend in the swap
    /// @param minPtOut The minimum amount of PT tokens to receive
    /// @param recipient The address to receive the swapped PT tokens
    /// @param deadline The timestamp after which the transaction will be reverted
    /// @return ethSpent The amount of ETH spent in the swap
    function swapETHForPt(
        address metapool,
        uint256 ptAmount,
        uint256 maxEthSpent,
        uint256 minPtOut, // TODO: really need this?
        address recipient,
        uint256 deadline
    ) external payable nonReentrant checkDeadline(deadline) checkMetapool(metapool) returns (uint256 ethSpent) {
        // Steps:
        // 1. Quote swap PT -> base pool token on triLST-PT Tricrypto (get_dx)
        // 2. Swap ETH -> base pool token on triLST-PT<>WETH NapierPool
        // 3. Swap base pool token -> PT on twocrypto metapool
        // 4. Send remaining ETH to the recipient

        // Calculate the amount of base pool token required for the specified PT amount
        uint256 basePoolTokenAmount = Twocrypto(metapool).get_dx({i: BASE_POOL_INDEX, j: PEGGED_PT_INDEX, dy: ptAmount});

        // Wrap the received ETH into WETH
        if (maxEthSpent > msg.value) revert Errors.MetapoolRouterInsufficientETHReceived();
        _wrapETH(msg.value);

        // Swap the received WETH for the required amount of base pool token on the NapierPool
        /// @dev Txn may revert if the triLSTPool tries to swap more than the received ETH.
        ethSpent = triLSTPool.swapUnderlyingForExactBaseLpToken({baseLpOut: basePoolTokenAmount, recipient: metapool});

        // Swap the received base pool token for PT on the Curve metapool
        Twocrypto(metapool).exchange_received({
            i: BASE_POOL_INDEX,
            j: PEGGED_PT_INDEX,
            dx: basePoolTokenAmount,
            // `get_dx` has a precision loss, so the actual amount of PT received may be less than `ptAmount`.
            min_dy: minPtOut,
            receiver: recipient
        });

        if (ethSpent > maxEthSpent) revert Errors.MetapoolRouterExceededLimitETHIn();

        // Send the remaining WETH back to the sender
        uint256 remainingWeth = msg.value - ethSpent;
        if (remainingWeth > 0) _unwrapWETH(msg.sender, remainingWeth);

        return ethSpent;
    }

    /// @notice Swap PT for ETH on the Curve metapool through the 3LST-PT<>ETH NapierPool
    /// @param metapool The address of the Twocrypto metapool
    /// @param ptAmount The amount of PT to swap
    /// @param minEthOut The minimum amount of ETH to receive
    /// @param recipient The address to receive the ETH
    /// @param deadline The timestamp after which the transaction will be reverted
    function swapPtForETH(address metapool, uint256 ptAmount, uint256 minEthOut, address recipient, uint256 deadline)
        external
        nonReentrant
        checkDeadline(deadline)
        checkMetapool(metapool)
        returns (uint256 ethOut)
    {
        // Steps:
        // 1. Exchange PT for the base pool token on twoCrypto metapool
        // 2. Swap the received base pool token -> ETH on triLST-PT<>WETH NapierPool
        // 3. Send remaining ETH to the recipient

        // Swap PT for the base pool token on the Curve metapool
        SafeERC20.safeTransferFrom(IERC20(Twocrypto(metapool).coins(PEGGED_PT_INDEX)), msg.sender, metapool, ptAmount);
        uint256 basePoolTokenAmount =
            Twocrypto(metapool).exchange_received(PEGGED_PT_INDEX, BASE_POOL_INDEX, ptAmount, 0, address(this));

        // Swap the received base pool token for ETH on the 3LST-PT<>ETH NapierPool
        ethOut =
            triLSTPool.swapExactBaseLpTokenForUnderlying({baseLptIn: basePoolTokenAmount, recipient: address(this)});

        // Check slippage
        if (minEthOut > ethOut) revert Errors.MetapoolRouterInsufficientETHOut();

        // Send native ETH to the recipient
        _unwrapWETH(recipient, ethOut);

        return ethOut;
    }

    /// @notice Swap Ethereum (ETH) for Yield Tokens (YT)
    /// @dev This function first issues PT and YT using ETH, then swaps the PT for the base pool token on the Curve metapool,
    ///      and finally swaps the received base pool token for ETH on the NapierPool.
    /// @notice Caller must send enough ETH equal to the `maxEthSpent` and the remaining ETH will be sent back to the caller.
    /// @dev `recipient` will receive at least `ytAmount` YTs and at most `ytAmount * (1 + approx.eps / 1e18)` YTs.
    /// @param metapool The address of the Curve metapool contract
    /// @param ytAmount The amount of YT tokens to receive
    /// @param maxEthSpent The maximum amount of ETH to spend in the swap
    /// @param recipient The address to receive the swapped YT tokens
    /// @param deadline The timestamp after which the transaction will be reverted
    /// @return ethSpent The amount of ETH spent in the swap
    function swapETHForYt(
        address metapool,
        uint256 ytAmount,
        uint256 maxEthSpent,
        address recipient,
        uint256 deadline,
        ApproxParams calldata approx
    ) external payable nonReentrant checkDeadline(deadline) checkMetapool(metapool) returns (uint256 ethSpent) {
        // Steps:
        // 1. Estimate the amount of WETH required to issue the PT and YT
        // 2. Issue PT and YT using the WETH
        // 3. Swap PT -> Base pool token on the Twocrypto metapool
        // 4. Swap Base pool token -> ETH on the NapierPool
        // 5. Refund the remaining WETH to the sender

        ITranche pt = ITranche(Twocrypto(metapool).coins(PEGGED_PT_INDEX));

        if (maxEthSpent > msg.value) revert Errors.MetapoolRouterInsufficientETHReceived();

        // Estimate the amount of WETH required to issue the PT and YT
        // Bisection method is used to find the approximate amount of WETH needed, which ensures the at least `ytAmount` YT tokens are issued.
        uint256 wethDeposit =
            TrancheMathHelper.getApproxUnderlyingNeededByYt({pt: pt, ytDesired: ytAmount, approx: approx});

        // Authorize access to `receiveFlashLoan` at the last step (flag=address(this))
        assembly {
            // Note: This slot should be used only for authorization purpose and should be cleared after use
            tstore(TSLOT_0, address())
        }

        IERC20[] memory tokens = new IERC20[](1);
        uint256[] memory amounts = new uint256[](1);
        tokens[0] = WETH9;
        amounts[0] = wethDeposit;
        // Record the flash loan data in the transient storage
        // Note: Based on try-and-error, passing userData directly is more expensive than using transient storage.
        assembly {
            tstore(TSLOT_CB_DATA_METAPOOL, metapool)
            tstore(TSLOT_CB_DATA_PT, shr(96, shl(96, pt)))
            tstore(TSLOT_CB_DATA_SENDER, caller())
            tstore(TSLOT_CB_DATA_VALUE, callvalue())
            tstore(TSLOT_CB_DATA_MAX_ETH_SPENT, maxEthSpent)
            tstore(TSLOT_CB_DATA_RECEIPIENT, recipient)
        }

        _wrapETH(msg.value);
        vault.flashLoan(this, tokens, amounts, ""); // call receiveFlashLoan

        assembly {
            ethSpent := tload(TSLOT_1)
            tstore(TSLOT_1, 0) // clear transitient storage
        }

        return ethSpent;
    }

    /// @notice Receive the flash loan and run operations to swap ETH for YT
    /// @dev Revert if the call is not initiated by the `swapETHForYt` function.
    /// @custom:param userData - Data structure for the flash loan callback data.
    /// @dev Those members are stored in the transient storage slots with prefix `TSLOT_CB_DATA_`.
    /// ```
    /// struct UserData {
    ///     address _metapool;
    ///     address _pt;
    ///     address _sender; // The address of the caller of `swapETHForYt`
    ///     uint256 _msgValue; // The amount of ETH sent with the call to `swapETHForYt`
    ///     uint256 _maxEthSpent;
    ///     address _recipient;
    /// }
    /// ```
    function receiveFlashLoan(
        IERC20[] calldata, /* tokens */
        uint256[] calldata amounts,
        uint256[] calldata feeAmounts,
        bytes calldata /* userData */
    ) external {
        // CHECK
        // Note: Call only through `swapETHForYt` && from the Vault should be allowed.
        // This ensures that the function call is invoked by `swapETHForYt` entry point.
        // Checking `msg.sender == address(vault)` may not be sufficient as the call may be initiated by other contracts and pass arbitrary data.
        assembly {
            let ctx := tload(TSLOT_0)
            tstore(TSLOT_0, 0) // Delete the authorization (flag=address(0))
            if iszero(eq(ctx, address())) {
                mstore(0x00, 0x5c501941) // `MetapoolRouterUnauthorized()`
                revert(0x1c, 0x04)
            }
        }

        address pt = TransientStorage.tloadAddress(TSLOT_CB_DATA_PT);
        address metapool = TransientStorage.tloadAddress(TSLOT_CB_DATA_METAPOOL);

        // Issue PT tokens using the WETH
        if (!_isApproved(address(WETH9), pt)) {
            _setApproval(address(WETH9), pt);
            WETH9.approve(pt, type(uint256).max);
        }
        uint256 wethDeposit = amounts[0];
        uint256 pyIssued = ITranche(pt).issue(address(this), wethDeposit);

        // Swap the PT for the base pool token on the Curve metapool
        ITranche(pt).transfer(metapool, pyIssued);
        uint256 basePoolTokenOut =
            Twocrypto(metapool).exchange_received(PEGGED_PT_INDEX, BASE_POOL_INDEX, pyIssued, 0, address(this));

        // Swap the received base pool token for ETH on the NapierPool
        uint256 wethReceived = triLSTPool.swapExactBaseLpTokenForUnderlying(basePoolTokenOut, address(this));

        // Unreasonable situation: Received more WETH than sold
        if (wethReceived > wethDeposit) revert Errors.MetapoolRouterNonSituationSwapETHForYt();

        // Calculate the amount of ETH spent in the swap
        uint256 repayAmount = wethDeposit + feeAmounts[0];
        uint256 spent = repayAmount - wethReceived; // wethDeposit + feeAmounts[0] - wethReceived

        // Revert if the ETH spent exceeds the specified maximum
        if (spent > TransientStorage.tloadU256(TSLOT_CB_DATA_MAX_ETH_SPENT)) {
            revert Errors.MetapoolRouterExceededLimitETHIn();
        }

        // Temporarily store a return value of `swapETHForYt` function across the call context
        assembly {
            tstore(TSLOT_1, spent)
        }

        // Transfer the YT tokens to the recipient
        IERC20(ITranche(pt).yieldToken()).transfer(TransientStorage.tloadAddress(TSLOT_CB_DATA_RECEIPIENT), pyIssued);

        // Repay the flash loan
        WETH9.transfer(msg.sender, repayAmount);

        // Unwrap and send the remaining WETH back to the sender
        // The next line will not underflow due to the `msg.value >= maxEthSpent` check in `swapETHForYt`
        uint256 refundAmount = TransientStorage.tloadU256(TSLOT_CB_DATA_VALUE) - spent;
        _unwrapWETH(TransientStorage.tloadAddress(TSLOT_CB_DATA_SENDER), refundAmount);
    }

    /// @notice Add liquidity to the Curve metapool using native ETH and receive LP tokens and YT
    /// @dev Revert if timestamp exceeds the maturity date.
    /// @param metapool The address of the Curve metapool contract
    /// @param minLiquidity The minimum amount of LP tokens to receive
    /// @param minYt The minimum amount of YT to receive
    /// @param recipient The address to receive the LP tokens and YT
    /// @param deadline The timestamp after which the transaction will be reverted
    function addLiquidityOneETHKeepYt(
        address metapool,
        uint256 minLiquidity,
        uint256 minYt,
        address recipient,
        uint256 deadline
    ) external payable nonReentrant checkDeadline(deadline) checkMetapool(metapool) returns (uint256 liquidity) {
        // Steps:
        // 1. Issue PT and YT using the received ETH
        // 2. Add liquidity to the Curve metapool
        // 3. Send the received LP token and YT to the recipient

        // Wrap the received ETH into WETH
        _wrapETH(msg.value);

        ITranche pt = ITranche(Twocrypto(metapool).coins(PEGGED_PT_INDEX));
        // Issue PT and YT using the received ETH
        if (!_isApproved(address(WETH9), address(pt))) {
            _setApproval(address(WETH9), address(pt));
            WETH9.approve(address(pt), type(uint256).max);
        }
        uint256 pyAmount = pt.issue({to: address(this), underlyingAmount: msg.value});
        if (pyAmount < minYt) revert Errors.MetapoolRouterInsufficientYtOut();

        // Add liquidity to the Curve metapool
        if (!_isApproved(address(pt), metapool)) {
            _setApproval(address(pt), metapool);
            pt.approve(metapool, type(uint256).max);
        }
        liquidity = Twocrypto(metapool).add_liquidity({
            amounts: [pyAmount, 0],
            min_mint_amount: minLiquidity,
            receiver: recipient
        });

        IERC20(pt.yieldToken()).transfer(recipient, pyAmount);
    }

    /// @notice Remove liquidity from the Curve twocrypto (metapool) and receive native ETH
    /// @notice Before the maturity date of PT, the PT is not redeemable, so the PT is swapped for the Base pool Token
    /// @param metapool The address of the Curve metapool contract
    /// @param liquidity The amount of LP tokens to remove from the Curve twocrypto (metapool)
    /// @param minEthOut The minimum amount of ETH to receive
    /// @param recipient The address to receive the ETH
    /// @param deadline The timestamp after which the transaction will be reverted
    function removeLiquidityOneETH(
        address metapool,
        uint256 liquidity,
        uint256 minEthOut,
        address recipient,
        uint256 deadline
    ) external nonReentrant checkDeadline(deadline) checkMetapool(metapool) returns (uint256 ethOut) {
        // Steps:
        // If PT is matured, redemption of PT is allowed:
        // 1. Remove liquidity from the Curve metapool and withdraw one PT
        // 2. Redeem the PT for ETH

        // If PT is not matured: redemption of PT is not allowed yet:
        // 1. Remove liquidity from the Curve metapool and withdraw one base pool token
        // 2. Swap the received base pool token for ETH on the NapierPool

        ITranche pt = ITranche(Twocrypto(metapool).coins(PEGGED_PT_INDEX));

        SafeERC20.safeTransferFrom(Twocrypto(metapool), msg.sender, address(this), liquidity);

        if (block.timestamp >= pt.maturity()) {
            // If PT is matured, we can directly redeem the PT for ETH
            uint256 ptAmount = Twocrypto(metapool).remove_liquidity_one_coin(liquidity, PEGGED_PT_INDEX, 0);
            ethOut = pt.redeem({principalAmount: ptAmount, to: address(this), from: address(this)});
        } else {
            // Otherwise, redemption of PT is not allowed, so we need to swap the base pool token for ETH
            uint256 basePoolTokenAmount = Twocrypto(metapool).remove_liquidity_one_coin(liquidity, BASE_POOL_INDEX, 0);
            ethOut = triLSTPool.swapExactBaseLpTokenForUnderlying(basePoolTokenAmount, address(this));
        }

        if (minEthOut > ethOut) revert Errors.MetapoolRouterInsufficientETHOut();

        _unwrapWETH(recipient, ethOut);
    }

    //// Helper functions ////

    /// @dev Get the approval status of the spender for the token. Return true if approved, 0 otherwise.
    function _isApproved(address token, address spender) internal view returns (bool approved) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, spender)
            mstore(0x0c, _IS_APPROVED_SLOT_SEED)
            mstore(0x00, token)
            approved := sload(keccak256(0x0c, 0x34))
        }
    }

    /// @dev Set the approval status to 1 for the spender for the token.
    function _setApproval(address token, address spender) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the approval slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, _IS_APPROVED_SLOT_SEED)
            mstore(0x00, token)
            sstore(keccak256(0x0c, 0x34), 1)
        }
    }

    function _wrapETH(uint256 value) internal {
        WETH9.deposit{value: value}();
    }

    function _unwrapWETH(address recipient, uint256 value) internal {
        WETH9.withdraw(value);
        _safeTransferETH(recipient, value);
    }

    /// @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();
    }
}

// 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: 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";

/// @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 {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;

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: UNLICENSED
pragma solidity ^0.8.0;

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

interface Twocrypto is IERC20 {
    function balances(uint256 i) external view returns (uint256);

    /// @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 receiver Address to send the output coin to, defaults to msg.sender
    /// @return Amount of tokens at index j received by the `receiver`
    function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy, address receiver) external returns (uint256);

    /// @notice Exchange: but user must transfer dx amount of coin[i] tokens to pool first.
    ///         Pool will not call transferFrom and will only check if a surplus of
    ///         coins[i] is greater than or equal to `dx`.
    /// @dev Use-case is to reduce the number of redundant ERC20 token
    ///      transfers in zaps. Primarily for dex-aggregators/arbitrageurs/searchers.
    ///      Note for users: please transfer + exchange_received in 1 tx.
    /// @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 receiver Address to send the output coin to
    /// @return Amount of tokens at index j received by the `receiver`
    function exchange_received(uint256 i, uint256 j, uint256 dx, uint256 min_dy, address receiver)
        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
    /// @param receiver Address to send the LP tokens to, defaults to msg.sender
    /// @return Amount of LP tokens received by the `receiver`
    function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount, address receiver)
        external
        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 receiver Address to send the withdrawn tokens to
    /// @return Amounts of pool tokens received by the `receiver`
    function remove_liquidity(uint256 _amount, uint256[2] calldata min_amounts, address receiver)
        external
        returns (uint256[2] 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 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, address receiver)
        external
        returns (uint256);

    function remove_liquidity_one_coin(uint256 token_amount, uint256 i, uint256 min_amount)
        external
        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 Amount of LP tokens deposited or withdrawn.
    function calc_token_amount(uint256[2] calldata amounts, bool deposit) external view returns (uint256);

    /// @notice Get amount of coin[j] tokens received for swapping in dx amount of coin[i]
    /// @dev Includes fee.
    /// @param i index of input token. Check pool.coins(i) to get coin address at ith index
    /// @param j index of output token
    /// @param dx amount of input coin[i] tokens
    /// @return Exact amount of output j tokens for dx amount of i input tokens.
    function get_dy(uint256 i, uint256 j, uint256 dx) external view returns (uint256);

    /// @notice Get amount of coin[i] tokens to input for swapping out dy amount of coin[j]
    /// @dev This is an approximate method, and returns estimates close to the input amount. Expensive to call on-chain.
    /// @param i index of input token. Check pool.coins(i) to get coin address at ith index
    /// @param j index of output token
    /// @param dy amount of input coin[j] tokens received
    /// @return Approximate amount of input i tokens to get dy amount of j tokens.
    function get_dx(uint256 i, uint256 j, uint256 dy) external view returns (uint256);

    /// @notice Calculates the current price of the LP token w.r.t coin at the 0th index
    /// @return LP price.
    function lp_price() external view returns (uint256);

    /// @notice Calculates the current virtual price of the pool LP token.
    /// @dev Not to be confused with `self.virtual_price` which is a cached virtual price.
    /// @return Virtual Price.
    function get_virtual_price() external view returns (uint256);

    /// @notice Returns the oracle price of the coin at index `k` w.r.t the coin at index 0.
    /// @dev The oracle is an exponential moving average, with a periodicity determined by `self.ma_time`.
    /// @return Price oracle value of kth coin.
    function price_oracle() external view returns (uint256);

    /// @notice Returns the oracle value for xcp.
    /// @dev The oracle is an exponential moving average, with a periodicity determined by `self.xcp_ma_time`.
    /// @return Oracle value of xcp.
    function xcp_oracle() external view returns (uint256);

    /// @notice Returns the price scale of the coin at index `k` w.r.t the coin at index 0.
    /// @dev Price scale determines the price band around which liquidity is concentrated.
    /// @return Price scale of coin.
    function price_scale() external view returns (uint256);

    /// @notice Returns the fee charged by the pool at current state.
    /// @dev Not to be confused with the fee charged at liquidity action.
    /// @return fee bps.
    function fee() external view returns (uint256);

    /// @notice Calculates output tokens with fee for withdrawing one coin
    /// @param token_amount LP Token amount to burn
    /// @param i token in which liquidity is withdrawn
    /// @return 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);

    /// @notice Returns the fee charged on the given amounts for add_liquidity.
    /// @param amounts The amounts of coins being added to the pool.
    /// @param xp The current balances of the pool multiplied by coin precisions.
    /// @return Fee charged.
    function calc_token_fee(uint256[] calldata amounts, uint256[] calldata xp) external view returns (uint256);

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

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

    /// @notice Returns the current mid fee
    /// @return mid_fee value.
    function mid_fee() external view returns (uint256);

    /// @notice Returns the current out fee
    /// @return out_fee value.
    function out_fee() external view returns (uint256);

    /// @notice Returns the current fee gamma
    /// @return fee_gamma value.
    function fee_gamma() external view returns (uint256);

    /// @notice Returns the current allowed extra profit
    /// @return allowed_extra_profit value.
    function allowed_extra_profit() external view returns (uint256);

    /// @notice Returns the current adjustment step
    /// @return adjustment_step value.
    function adjustment_step() external view returns (uint256);

    /// @notice Returns the current moving average time in seconds
    /// @dev To get time in seconds, the parameter is multiplied by ln(2).
    /// @return ma_time value.
    function ma_time() external view returns (uint256);

    function coins(uint256 i) external view returns (address);

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

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

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

interface IVault {
    /**
     * @dev Performs a 'flash loan', sending tokens to `recipient`, executing the `receiveFlashLoan` hook on it,
     * and then reverting unless the tokens plus a proportional protocol fee have been returned.
     *
     * The `tokens` and `amounts` arrays must have the same length, and each entry in these indicates the loan amount
     * for each token contract. `tokens` must be sorted in ascending order.
     *
     * The 'userData' field is ignored by the Vault, and forwarded as-is to `recipient` as part of the
     * `receiveFlashLoan` call.
     *
     * Emits `FlashLoan` events.
     */
    function flashLoan(
        IFlashLoanRecipient recipient,
        IERC20[] memory tokens,
        uint256[] memory amounts,
        bytes memory userData
    ) external;

    /**
     * @dev Returns the protocol fees collector contract.
     */
    function getProtocolFeesCollector() external view returns (IProtocolFeesCollector);
}

File 9 of 60 : IFlashLoanRecipient.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.10;

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

interface IFlashLoanRecipient {
    /**
     * @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient.
     *
     * At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this
     * call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the
     * Vault, or else the entire flash loan will revert.
     *
     * `userData` is the same value passed in the `IVault.flashLoan` call.
     */
    function receiveFlashLoan(
        IERC20[] calldata tokens,
        uint256[] calldata amounts,
        uint256[] calldata feeAmounts,
        bytes calldata userData
    ) external;
}

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

import {INapierPool} from "@napier/v1-pool/src/interfaces/INapierPool.sol";
import {ITranche} from "@napier/v1-tranche/src/interfaces/ITranche.sol";
import {TwocryptoFactory} from "./interfaces/external/TwocryptoFactory.sol";

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

import {Ownable2Step} from "@openzeppelin/[email protected]/access/Ownable2Step.sol";

/// @notice A factory contract for deploying TwoCrypto metapools (Technically it is just regular twocrypto pool with PT and tricrypto LP token)
/// @dev It's a wrapper around the TwocryptoFactory contract with additional checks
contract MetapoolFactory is Ownable2Step {
    /// @notice The TwocryptoFactory contract
    TwocryptoFactory public immutable twocryptoFactory;

    /// @notice The WETH9 address
    address public immutable WETH9;

    /// @notice Mapping (metapool -> deployed by this factory). True if the metapool was deployed by this factory
    mapping(address metapool => bool) public isPtMetapool;

    event MetapoolDeployed(address metapool, address pt, address napierPool);

    /// @notice A parameter struct for deploying a metapool
    struct TwocryptoParams {
        uint256 A;
        uint256 gamma;
        uint256 mid_fee;
        uint256 out_fee;
        uint256 fee_gamma;
        uint256 allowed_extra_profit;
        uint256 adjustment_step;
        uint256 ma_time;
        uint256 initial_price;
    }

    constructor(address owner, address _WETH9, TwocryptoFactory _twocryptoFactory) {
        WETH9 = _WETH9;
        twocryptoFactory = _twocryptoFactory;

        _transferOwnership(owner);
    }

    /// @notice Deploys a TwocryptoNG from Factory contract
    /// NOTE: This metapool is not typical metapool, Technically it is just a pool with 2 volatile coins,
    /// where one of the coins is PT, and the other is triLST-PT tricrypto LP token (like rETH-PT, sfrxETH-PT, stETH-PT)
    /// @dev Reverts if:
    /// - The NapierPool's underlying is not WETH
    /// - The PT's underlying is not WETH
    /// - Maturity of the PT is longer than the maturity of the NapierPool
    /// - Name or symbol are too long
    /// @param pt The Principal Token address
    /// @param pool The NapierPool address for the metapool to use (Underlying token of those PTs must be WETH)
    /// @custom:param implementation_idx The index of the implementation to use
    /// @param name The name of the metapool
    /// @param symbol The symbol of the metapool
    /// @param params The metapool parameters
    /// @return metapool The address of the deployed metapool (coin0 is PT, coin1 is tricrypto LP token)
    function deployMetapool(
        address pt,
        address pool,
        uint256, /* implementation_idx */
        string calldata name,
        string calldata symbol,
        TwocryptoParams calldata params
    ) external onlyOwner returns (address metapool) {
        (address underlying, address tricrypto) = INapierPool(pool).getAssets();

        /// CHECK
        if (underlying != WETH9) revert Errors.MetapoolFactoryWETHMismatch();
        if (ITranche(pt).underlying() != WETH9) revert Errors.MetapoolFactoryWETHMismatch();
        if (ITranche(pt).maturity() > INapierPool(pool).maturity()) revert Errors.MetapoolFactoryMaturityTooLong();

        // Deploy twocrypto
        address[2] memory coins = [pt, tricrypto];
        uint256 implementation_idx;
        // Workaround for stack too deep error
        assembly { implementation_idx := calldataload(0x44) }// forgefmt: disable-line
        bytes memory data = abi.encodeWithSelector(
            TwocryptoFactory.deploy_pool.selector,
            name, // name
            symbol, // symbol
            coins, // coins
            implementation_idx, // implementation_idx
            // Avoid stack too deep error by passing params as a struct instead of individual parameters
            // Note: the order of members in the struct must match the order of the parameters in the function
            params
        );
        (bool success, bytes memory ret) = address(twocryptoFactory).call(data);
        if (!success) revert Errors.MetapoolFactoryFailedToDeployMetapool();
        metapool = abi.decode(ret, (address));
        isPtMetapool[metapool] = true;
        // Workaround for stack too deep error
        emit MetapoolDeployed(metapool, calldataLoadAddress(0x04), calldataLoadAddress(0x24));
    }

    function calldataLoadAddress(uint256 offset) private pure returns (address value) {
        assembly {
            value := calldataload(offset)
        }
    }
}

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

import {ApproxParams} from "@napier/v1-pool/src/interfaces/ApproxParams.sol";

interface IMetapoolRouter {
    function swapETHForPt(
        address metapool,
        uint256 ptAmount,
        uint256 maxEthSpent,
        uint256 minPtOut,
        address recipient,
        uint256 deadline
    ) external payable returns (uint256 ethSpent);

    function swapPtForETH(address metapool, uint256 ptAmount, uint256 minEthOut, address recipient, uint256 deadline)
        external
        returns (uint256 ethOut);

    function swapETHForYt(
        address metapool,
        uint256 ytAmount,
        uint256 maxEthSpent,
        address recipient,
        uint256 deadline,
        ApproxParams calldata approx
    ) external payable returns (uint256 ethSpent);

    function addLiquidityOneETHKeepYt(
        address metapool,
        uint256 minLiquidity,
        uint256 minYt,
        address recipient,
        uint256 deadline
    ) external payable returns (uint256 liquidity);

    function removeLiquidityOneETH(
        address metapool,
        uint256 liquidity,
        uint256 minEthOut,
        address recipient,
        uint256 deadline
    ) external returns (uint256 ethOut);
}

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

/// @notice Transient storage utility functions
library TransientStorage {
    function tloadU256(uint256 slot) internal view returns (uint256 value) {
        assembly {
            value := tload(slot)
        }
    }

    /// @notice The return value may contain dirty upper bits
    function tloadAddress(uint256 slot) internal view returns (address value) {
        assembly {
            value := tload(slot)
        }
    }
}

// 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);
        }
    }
}

// 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 15 of 60 : 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;
}

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

library Errors {
    // Generic
    error FailedToSendEther();
    error NotWETH();

    // Factory
    error MetapoolFactoryFailedToDeployMetapool();
    error MetapoolFactoryMaturityTooLong();
    error MetapoolFactoryWETHMismatch();

    // Router
    error MetapoolRouterUnauthorized();
    error MetapoolRouterInvalidMetapool();
    error MetapoolRouterTransactionTooOld();
    error MetapoolRouterInsufficientETHOut();
    error MetapoolRouterInsufficientYtOut();
    error MetapoolRouterExceededLimitETHIn();
    error MetapoolRouterInsufficientETHReceived();
    error MetapoolRouterNonSituationSwapETHForYt();
}

// 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;
    }
}

// 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: 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: 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: 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.10;

/**
 * Taken from: https://github.com/balancer/balancer-v2-monorepo/blob/ac63d64018c6331248c7d77b9f317a06cced0243/pkg/vault/contracts/ProtocolFeesCollector.sol
 * @dev This an auxiliary contract to the Vault, deployed by it during construction. It offloads some of the tasks the
 * Vault performs to reduce its overall bytecode size.
 *
 * The current values for all protocol fee percentages are stored here, and any tokens charged as protocol fees are
 * sent to this contract, where they may be withdrawn by authorized entities. All authorization tasks are delegated
 * to the Vault's own authorizer.
 */
interface IProtocolFeesCollector {
    function getFlashLoanFeePercentage() external view returns (uint256);
}

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

pragma solidity ^0.8.0;

interface TwocryptoFactory {
    /// @notice Deploy a new pool
    /// @param _name Name of the new plain pool
    /// @param _symbol Symbol for the new plain pool - will be concatenated with factory symbol
    /// @param _coins Addresses of the coins in the pool
    /// @param implementation_id ID of the pool implementation
    /// @param A Amplification coefficient
    /// @param gamma Swap fee coefficient
    /// @param mid_fee Fee for stable swaps
    /// @param out_fee Fee for volatile swaps
    /// @param fee_gamma Adjustment coefficient for fees
    /// @param allowed_extra_profit Extra profit limit for adjusting swap fees
    /// @param adjustment_step Step size for fee adjustment
    /// @param ma_exp_time Moving average expiration time
    /// @param initial_price Initial price for the pool
    /// @return Address of the deployed pool
    function deploy_pool(
        string calldata _name,
        string calldata _symbol,
        address[2] calldata _coins,
        uint256 implementation_id,
        uint256 A,
        uint256 gamma,
        uint256 mid_fee,
        uint256 out_fee,
        uint256 fee_gamma,
        uint256 allowed_extra_profit,
        uint256 adjustment_step,
        uint256 ma_exp_time,
        uint256 initial_price
    ) external returns (address);

    /// @notice Set pool implementation
    /// @dev Set to address(0) to prevent deployment of new pools
    /// @param _pool_implementation Address of the new pool implementation
    /// @param _implementation_index Index of the pool implementation
    function set_pool_implementation(address _pool_implementation, uint256 _implementation_index) external;

    /// @notice Set gauge implementation
    /// @dev Set to address(0) to prevent deployment of new gauges
    /// @param _gauge_implementation Address of the new gauge implementation
    function set_gauge_implementation(address _gauge_implementation) external;

    /// @notice Set views contract implementation
    /// @param _views_implementation Address of the new views contract
    function set_views_implementation(address _views_implementation) external;

    /// @notice Set math implementation
    /// @param _math_implementation Address of the new math contract
    function set_math_implementation(address _math_implementation) external;

    function initialise_ownership(address fee_receiver, address admin) external;
}

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

pragma solidity ^0.8.0;

import "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }
}

// 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: 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.
    /// @dev no funds should be left in the contract after this call.
    ///      the caller must transfer Underlying to this contract before calling this function.
    /// @return underlyingUsed amount of Underlying used
    /// @return sharesMinted amount of Target minted
    function prefundedDeposit() external returns (uint256 underlyingUsed, uint256 sharesMinted);

    /// @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: 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))
        }
    }
}

File 29 of 60 : 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 30 of 60 : 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;

File 31 of 60 : 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 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: 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 60 : 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 60 : 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 (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

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

// 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 38 of 60 : 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 39 of 60 : 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 40 of 60 : 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 41 of 60 : 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 42 of 60 : 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 43 of 60 : 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 44 of 60 : 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 45 of 60 : 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 46 of 60 : 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 47 of 60 : 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 48 of 60 : 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 49 of 60 : 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 50 of 60 : 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 51 of 60 : 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 52 of 60 : 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 53 of 60 : 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 54 of 60 : 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 55 of 60 : 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 56 of 60 : 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 57 of 60 : 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 58 of 60 : 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 59 of 60 : 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 60 of 60 : 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": [
    "@napier/v1-pool/=lib/v1-pool/",
    "forge-std/=lib/forge-std/src/",
    "@napier/v1-tranche/=lib/v1-pool/lib/napier-v1/",
    "@openzeppelin/[email protected]/=lib/v1-pool/lib/openzeppelin-contracts/contracts/",
    "@prb/math/=lib/v1-pool/lib/prb-math/src/",
    "@prb/test/=lib/v1-pool/lib/prb-math/lib/prb-test/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/v1-pool/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "foundry-vyper/=lib/v1-pool/lib/foundry-vyper/src/",
    "hardhat-deployer/=lib/v1-pool/lib/hardhat-deployer/src/",
    "napier-v1/=lib/v1-pool/lib/napier-v1/",
    "openzeppelin-contracts/=lib/v1-pool/lib/openzeppelin-contracts/",
    "openzeppelin/=lib/v1-pool/lib/openzeppelin-contracts/contracts/",
    "prb-math/=lib/v1-pool/lib/prb-math/src/",
    "prb-test/=lib/v1-pool/lib/prb-math/lib/prb-test/src/",
    "tricrypto-ng/=lib/v1-pool/lib/tricrypto-ng/contracts/",
    "v1-pool/=lib/v1-pool/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract MetapoolFactory","name":"_metapoolFactory","type":"address"},{"internalType":"contract INapierPool","name":"_triLSTPool","type":"address"},{"internalType":"contract IVault","name":"_vault","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApproxBinarySearchInputInvalid","type":"error"},{"inputs":[],"name":"ApproxFail","type":"error"},{"inputs":[],"name":"FailedToSendEther","type":"error"},{"inputs":[],"name":"MetapoolRouterExceededLimitETHIn","type":"error"},{"inputs":[],"name":"MetapoolRouterInsufficientETHOut","type":"error"},{"inputs":[],"name":"MetapoolRouterInsufficientETHReceived","type":"error"},{"inputs":[],"name":"MetapoolRouterInsufficientYtOut","type":"error"},{"inputs":[],"name":"MetapoolRouterInvalidMetapool","type":"error"},{"inputs":[],"name":"MetapoolRouterNonSituationSwapETHForYt","type":"error"},{"inputs":[],"name":"MetapoolRouterTransactionTooOld","type":"error"},{"inputs":[],"name":"NotWETH","type":"error"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"contract IWETH9","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"metapool","type":"address"},{"internalType":"uint256","name":"minLiquidity","type":"uint256"},{"internalType":"uint256","name":"minYt","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityOneETHKeepYt","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"metapoolFactory","outputs":[{"internalType":"contract MetapoolFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"feeAmounts","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"receiveFlashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"metapool","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"minEthOut","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityOneETH","outputs":[{"internalType":"uint256","name":"ethOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"metapool","type":"address"},{"internalType":"uint256","name":"ptAmount","type":"uint256"},{"internalType":"uint256","name":"maxEthSpent","type":"uint256"},{"internalType":"uint256","name":"minPtOut","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapETHForPt","outputs":[{"internalType":"uint256","name":"ethSpent","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"metapool","type":"address"},{"internalType":"uint256","name":"ytAmount","type":"uint256"},{"internalType":"uint256","name":"maxEthSpent","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":"swapETHForYt","outputs":[{"internalType":"uint256","name":"ethSpent","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"metapool","type":"address"},{"internalType":"uint256","name":"ptAmount","type":"uint256"},{"internalType":"uint256","name":"minEthOut","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapPtForETH","outputs":[{"internalType":"uint256","name":"ethOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"triLSTPool","outputs":[{"internalType":"contract INapierPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tricryptoLST","outputs":[{"internalType":"contract CurveTricryptoOptimizedWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract IVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

61012060405234801562000011575f80fd5b5060405162003f7a38038062003f7a833981016040819052620000349162000828565b60015f556001600160a01b0380841660a081905283821660e05290821661010052604080516312a9293f60e21b81529051634aa4a4fc916004808201926020929091908290030181865afa1580156200008f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620000b591906200088b565b6001600160a01b03166080816001600160a01b031681525050816001600160a01b031663a0171ef56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200010b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200013191906200088b565b6001600160a01b031660c0816001600160a01b0316815250505f8060c0516001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200018b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620001b191906200088b565b60c0516040516001600160a01b03918216602482015291169060440160408051601f198184030181529181526020820180516001600160e01b0316639ac90d3d60e01b17905251620002049190620008d4565b5f60405180830381855afa9150503d805f81146200023e576040519150601f19603f3d011682016040523d82523d5f602084013e62000243565b606091505b50915091508162000252575f80fd5b5f81806020019051810190620002699190620008f1565b805160c05160405163095ea7b360e01b81526001600160a01b0391821660048201525f196024820152929350169063095ea7b3906044016020604051808303815f875af1158015620002bd573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620002e3919062000985565b50602081015160c05160405163095ea7b360e01b81526001600160a01b0391821660048201525f19602482015291169063095ea7b3906044016020604051808303815f875af115801562000339573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200035f919062000985565b5060408082015160c051915163095ea7b360e01b81526001600160a01b0392831660048201525f19602482015291169063095ea7b3906044016020604051808303815f875af1158015620003b5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620003db919062000985565b50620003f460805160e0515f196200041860201b60201c565b6200040c60c05160e0515f196200041860201b60201c565b505050505050620009da565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b17909152620004729085908390620004ed16565b620004e7576040516001600160a01b03841660248201525f6044820152620004db90859063095ea7b360e01b9060640160408051808303601f190181529190526020810180516001600160e01b0319939093166001600160e01b03938416179052906200059716565b620004e7848262000597565b50505050565b5f805f846001600160a01b0316846040516200050a9190620008d4565b5f604051808303815f865af19150503d805f811462000545576040519150601f19603f3d011682016040523d82523d5f602084013e6200054a565b606091505b5091509150818015620005785750805115806200057857508080602001905181019062000578919062000985565b80156200058e57506001600160a01b0385163b15155b95945050505050565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201525f90620005e5906001600160a01b03851690849062000672565b905080515f14806200060857508080602001905181019062000608919062000985565b6200066d5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084015b60405180910390fd5b505050565b60606200068284845f856200068a565b949350505050565b606082471015620006ed5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000664565b5f80866001600160a01b031685876040516200070a9190620008d4565b5f6040518083038185875af1925050503d805f811462000746576040519150601f19603f3d011682016040523d82523d5f602084013e6200074b565b606091505b5090925090506200075f878383876200076a565b979650505050505050565b60608315620007dd5782515f03620007d5576001600160a01b0385163b620007d55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000664565b508162000682565b620006828383815115620007f45781518083602001fd5b8060405162461bcd60e51b8152600401620006649190620009a6565b6001600160a01b038116811462000825575f80fd5b50565b5f805f606084860312156200083b575f80fd5b8351620008488162000810565b60208501519093506200085b8162000810565b60408501519092506200086e8162000810565b809150509250925092565b8051620008868162000810565b919050565b5f602082840312156200089c575f80fd5b8151620008a98162000810565b9392505050565b5f5b83811015620008cc578181015183820152602001620008b2565b50505f910152565b5f8251620008e7818460208701620008b0565b9190910192915050565b5f6060828403121562000902575f80fd5b82601f83011262000911575f80fd5b604051606081016001600160401b03811182821017156200094057634e487b7160e01b5f52604160045260245ffd5b60405280606084018581111562000955575f80fd5b845b818110156200097a576200096b8162000879565b83526020928301920162000957565b509195945050505050565b5f6020828403121562000996575f80fd5b81518015158114620008a9575f80fd5b602081525f8251806020840152620009c6816040850160208701620008b0565b601f01601f19169190910160400192915050565b60805160a05160c05160e051610100516134d062000aaa5f395f81816102e00152610b8d01525f81816102160152818161075801528181610ebc015281816118690152611da401525f61026801525f81816101e30152818161038d015281816108bf01528181610ca001528181611002015261169a01525f818160d80152818161018b01528181610a8f0152818161114c0152818161117e0152818161122401528181611a1401528181611a4701528181611aed015281816120120152818161220001526124d301526134d05ff3fe6080604052600436106100bb575f3560e01c806375cf294d11610071578063edde8bdd1161004c578063edde8bdd1461029d578063f04f2707146102b0578063fbfa77cf146102cf575f80fd5b806375cf294d1461023857806378150fb814610257578063b60e66041461028a575f80fd5b80634aa4a4fc116100a15780634aa4a4fc1461017a57806354ea3a86146101d25780636267168314610205575f80fd5b80633b404f561461013557806349318ac314610167575f80fd5b36610131573373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461012f576040517f5724f38500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b5f80fd5b348015610140575f80fd5b5061015461014f366004612c83565b610302565b6040519081526020015b60405180910390f35b610154610175366004612cd1565b610834565b348015610185575f80fd5b506101ad7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161015e565b3480156101dd575f80fd5b506101ad7f000000000000000000000000000000000000000000000000000000000000000081565b348015610210575f80fd5b506101ad7f000000000000000000000000000000000000000000000000000000000000000081565b348015610243575f80fd5b50610154610252366004612c83565b610c15565b348015610262575f80fd5b506101ad7f000000000000000000000000000000000000000000000000000000000000000081565b610154610298366004612c83565b610f77565b6101546102ab366004612d59565b61160f565b3480156102bb575f80fd5b5061012f6102ca366004612dfb565b6119e6565b3480156102da575f80fd5b506101ad7f000000000000000000000000000000000000000000000000000000000000000081565b5f61030b6120c0565b8180421115610346576040517f460229df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5128abae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808916600483015288917f000000000000000000000000000000000000000000000000000000000000000090911690635128abae90602401602060405180830381865afa1580156103d4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103f89190612ee0565b61042e576040517f8ebe7d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc66106570000000000000000000000000000000000000000000000000000000081525f600482018190529073ffffffffffffffffffffffffffffffffffffffff8a169063c661065790602401602060405180830381865afa158015610499573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104bd9190612f16565b90506104cb8933308b612136565b8073ffffffffffffffffffffffffffffffffffffffff1663204f83f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610514573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105389190612f31565b4210610682576040517ff1dc3cc9000000000000000000000000000000000000000000000000000000008152600481018990525f60248201819052604482018190529073ffffffffffffffffffffffffffffffffffffffff8b169063f1dc3cc9906064016020604051808303815f875af11580156105b8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105dc9190612f31565b6040517fba087652000000000000000000000000000000000000000000000000000000008152600481018290523060248201819052604482015290915073ffffffffffffffffffffffffffffffffffffffff83169063ba087652906064016020604051808303815f875af1158015610656573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061067a9190612f31565b9450506107db565b6040517ff1dc3cc900000000000000000000000000000000000000000000000000000000815260048101899052600160248201525f604482018190529073ffffffffffffffffffffffffffffffffffffffff8b169063f1dc3cc9906064016020604051808303815f875af11580156106fc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107209190612f31565b6040517f2390154f000000000000000000000000000000000000000000000000000000008152600481018290523060248201529091507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632390154f906044016020604051808303815f875af11580156107b3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d79190612f31565b9450505b83871115610815576040517feca4bc1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61081f86856121d1565b50505061082b60015f55565b95945050505050565b5f61083d6120c0565b8280421115610878576040517f460229df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5128abae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808a16600483015289917f000000000000000000000000000000000000000000000000000000000000000090911690635128abae90602401602060405180830381865afa158015610906573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061092a9190612ee0565b610960576040517f8ebe7d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc66106570000000000000000000000000000000000000000000000000000000081525f600482018190529073ffffffffffffffffffffffffffffffffffffffff8b169063c661065790602401602060405180830381865afa1580156109cb573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109ef9190612f16565b905034881115610a2b576040517fbf35eb4300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610a45828b610a40368a90038a018a612f48565b61227a565b9050305f5d6040805160018082528183019092525f916020808301908036833750506040805160018082528183019092529293505f929150602080830190803683370190505090507f0000000000000000000000000000000000000000000000000000000000000000825f81518110610ac057610ac0612fd1565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505082815f81518110610b0d57610b0d612fd1565b6020026020010181815250508c60025d73ffffffffffffffffffffffffffffffffffffffff841660035d3360045d3460055d8a60065d8960075d610b50346124d1565b6040517f5c38449e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690635c38449e90610bc690309086908690600401612ffe565b5f604051808303815f87803b158015610bdd575f80fd5b505af1158015610bef573d5f803e3d5ffd5b5050505060015c96505f60015d505050505050610c0b60015f55565b9695505050505050565b5f610c1e6120c0565b8180421115610c59576040517f460229df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5128abae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808916600483015288917f000000000000000000000000000000000000000000000000000000000000000090911690635128abae90602401602060405180830381865afa158015610ce7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d0b9190612ee0565b610d41576040517f8ebe7d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc66106570000000000000000000000000000000000000000000000000000000081525f6004820152610dd99073ffffffffffffffffffffffffffffffffffffffff8a169063c661065790602401602060405180830381865afa158015610dad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd19190612f16565b338a8a612136565b6040517f767691e70000000000000000000000000000000000000000000000000000000081525f600482018190526001602483015260448201899052606482018190523060848301529073ffffffffffffffffffffffffffffffffffffffff8a169063767691e79060a4016020604051808303815f875af1158015610e60573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e849190612f31565b6040517f2390154f000000000000000000000000000000000000000000000000000000008152600481018290523060248201529091507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632390154f906044016020604051808303815f875af1158015610f17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f3b9190612f31565b935083871115610815576040517feca4bc1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610f806120c0565b8180421115610fbb576040517f460229df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5128abae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808916600483015288917f000000000000000000000000000000000000000000000000000000000000000090911690635128abae90602401602060405180830381865afa158015611049573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061106d9190612ee0565b6110a3576040517f8ebe7d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ac346124d1565b6040517fc66106570000000000000000000000000000000000000000000000000000000081525f600482018190529073ffffffffffffffffffffffffffffffffffffffff8a169063c661065790602401602060405180830381865afa158015611117573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061113b9190612f16565b602081905263a8fe4407600c9081527f00000000000000000000000000000000000000000000000000000000000000005f526034902054909150611290576111b97f0000000000000000000000000000000000000000000000000000000000000000828060205263a8fe4407600c52815f5260016034600c20555050565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303815f875af115801561126a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061128e9190612ee0565b505b6040517f867904b40000000000000000000000000000000000000000000000000000000081523060048201523460248201525f9073ffffffffffffffffffffffffffffffffffffffff83169063867904b4906044016020604051808303815f875af1158015611301573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113259190612f31565b905087811015611361576040517f3dbb7e8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208a905263a8fe4407600c9081525f83905260349020546114545761139d828b8060205263a8fe4407600c52815f5260016034600c20555050565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b811660048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602483015283169063095ea7b3906044016020604051808303815f875af115801561142e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114529190612ee0565b505b6040805180820182528281525f602082015290517f0c3e4b5400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c1691630c3e4b54916114ba91908d908c906004016130a5565b6020604051808303815f875af11580156114d6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114fa9190612f31565b94508173ffffffffffffffffffffffffffffffffffffffff166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015611545573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115699190612f16565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff898116600483015260248201849052919091169063a9059cbb906044016020604051808303815f875af11580156115dd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116019190612ee0565b505050505061082b60015f55565b5f6116186120c0565b8180421115611653576040517f460229df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5128abae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808a16600483015289917f000000000000000000000000000000000000000000000000000000000000000090911690635128abae90602401602060405180830381865afa1580156116e1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117059190612ee0565b61173b576040517f8ebe7d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f37ed3a7a000000000000000000000000000000000000000000000000000000008152600160048201525f60248201819052604482018a90529073ffffffffffffffffffffffffffffffffffffffff8b16906337ed3a7a90606401602060405180830381865afa1580156117b4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117d89190612f31565b905034881115611814576040517fbf35eb4300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61181d346124d1565b6040517f3a19e2300000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff8b811660248301527f00000000000000000000000000000000000000000000000000000000000000001690633a19e230906044016020604051808303815f875af11580156118af573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118d39190612f31565b6040517f767691e7000000000000000000000000000000000000000000000000000000008152600160048201525f6024820152604481018390526064810189905273ffffffffffffffffffffffffffffffffffffffff8881166084830152919550908b169063767691e79060a4016020604051808303815f875af115801561195d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119819190612f31565b50878411156119bc576040517f96e9d4fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6119c78534613126565b905080156119d9576119d933826121d1565b50505050610c0b60015f55565b5f5c5f805d3081146119ff57635c5019415f526004601cfd5b5060035c602081905263a8fe4407600c9081527f00000000000000000000000000000000000000000000000000000000000000005f52603490205460025c90611b5957611a827f0000000000000000000000000000000000000000000000000000000000000000838060205263a8fe4407600c52815f5260016034600c20555050565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303815f875af1158015611b33573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b579190612ee0565b505b5f88885f818110611b6c57611b6c612fd1565b6040517f867904b4000000000000000000000000000000000000000000000000000000008152306004820152602090910292909201356024830181905292505f9173ffffffffffffffffffffffffffffffffffffffff8616915063867904b4906044016020604051808303815f875af1158015611beb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c0f9190612f31565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018390529192509085169063a9059cbb906044016020604051808303815f875af1158015611c85573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ca99190612ee0565b506040517f767691e70000000000000000000000000000000000000000000000000000000081525f600482018190526001602483015260448201839052606482018190523060848301529073ffffffffffffffffffffffffffffffffffffffff85169063767691e79060a4016020604051808303815f875af1158015611d31573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d559190612f31565b6040517f2390154f000000000000000000000000000000000000000000000000000000008152600481018290523060248201529091505f9073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690632390154f906044016020604051808303815f875af1158015611dea573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e0e9190612f31565b905083811115611e4a576040517f854911e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8a8a5f818110611e5d57611e5d612fd1565b9050602002013585611e6f9190613139565b90505f611e7c8383613126565b905060065c811115611eba576040517f96e9d4fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060015d8773ffffffffffffffffffffffffffffffffffffffff166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f07573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f2b9190612f16565b73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb60075c6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602481018890526044016020604051808303815f875af1158015611fb8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fdc9190612ee0565b506040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018390527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303815f875af115801561206d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120919190612ee0565b505f61209f8260055c613126565b90506120ad60045c826121d1565b5050505050505050505050505050505050565b60025f5403612130576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60025f55565b6040805173ffffffffffffffffffffffffffffffffffffffff85811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790526121cb908590612551565b50505050565b6040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632e1a7d4d906024015f604051808303815f87803b158015612256575f80fd5b505af1158015612268573d5f803e3d5ffd5b505050506122768282612663565b5050565b5f815f0151826020015110156122bc576040517f364de99200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81606001515f036122d55766b1a2bc2ec5000060608301525b81604001515f036122e857606460408301525b5f8473ffffffffffffffffffffffffffffffffffffffff1663e6432a346040518163ffffffff1660e01b815260040161010060405180830381865afa158015612333573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123579190613163565b90505f816060015173ffffffffffffffffffffffffffffffffffffffff1663f51e181a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123a7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123cb9190612f31565b90505f60405180608001604052808873ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612422573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124469190613242565b60ff1681526020018381526020016124628560a0015185612713565b815260c085015167ffffffffffffffff1660209091015290505f6124b761248a846001613139565b6124949089613262565b6127106124a36101f482613126565b85604001516124b29190613262565b61272c565b90506124c58288838961276e565b98975050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004015f604051808303818588803b158015612537575f80fd5b505af1158015612549573d5f803e3d5ffd5b505050505050565b5f6125b2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166128f79092919063ffffffff16565b905080515f14806125d25750808060200190518101906125d29190612ee0565b61265e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401612127565b505050565b604080515f8082526020820190925273ffffffffffffffffffffffffffffffffffffffff8416908390604051612699919061329b565b5f6040518083038185875af1925050503d805f81146126d3576040519150601f19603f3d011682016040523d82523d5f602084013e6126d8565b606091505b505090508061265e576040517fdcf35db000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8183116127215781612723565b825b90505b92915050565b5f827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048411830215820261275f575f80fd5b50910281810615159190040190565b83515f90819061277f90600a6133d4565b90505f612795868860200151896040015161272c565b90505f6127a28387613139565b8551909150156127bb5784516127b89083612713565b91505b6020850151156127d6576127d3856020015182612905565b90505b5f805b866040015181146128bc5760026127f08486613139565b6127fa91906133df565b91505f6128078b84612913565b90505f61282f8b61282084670de0b6b3a7640000613262565b61282a91906133df565b612950565b61284190670de0b6b3a7640000613417565b90508a821015801561286757508061285c8a60600151612950565b6128659061343d565b125b1561287b57839750505050505050506128ef565b84861480612892575084612890876001613139565b145b1561289e5750506128bc565b5f8113156128ae578395506128b2565b8394505b50506001016127d9565b506040517ffa711db200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b949350505050565b60606128ef84845f85612a05565b5f8183106127215781612723565b5f8061292683856060015161271061272c565b90505f6129406129368386613126565b8660200151612b1a565b905061082b818660400151612b2e565b5f7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115612a01576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e206160448201527f6e20696e743235360000000000000000000000000000000000000000000000006064820152608401612127565b5090565b606082471015612a97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401612127565b5f808673ffffffffffffffffffffffffffffffffffffffff168587604051612abf919061329b565b5f6040518083038185875af1925050503d805f8114612af9576040519150601f19603f3d011682016040523d82523d5f602084013e612afe565b606091505b5091509150612b0f87838387612b42565b979650505050505050565b5f61272383670de0b6b3a764000084612be1565b5f6127238383670de0b6b3a7640000612be1565b60608315612bd75782515f03612bd05773ffffffffffffffffffffffffffffffffffffffff85163b612bd0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401612127565b50816128ef565b6128ef8383612c1b565b5f827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0484118302158202612c14575f80fd5b5091020490565b815115612c2b5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121279190613473565b73ffffffffffffffffffffffffffffffffffffffff81168114612c80575f80fd5b50565b5f805f805f60a08688031215612c97575f80fd5b8535612ca281612c5f565b945060208601359350604086013592506060860135612cc081612c5f565b949793965091946080013592915050565b5f805f805f80868803610120811215612ce8575f80fd5b8735612cf381612c5f565b965060208801359550604088013594506060880135612d1181612c5f565b935060808881013593507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6082011215612d48575f80fd5b5060a0870190509295509295509295565b5f805f805f8060c08789031215612d6e575f80fd5b8635612d7981612c5f565b95506020870135945060408701359350606087013592506080870135612d9e81612c5f565b8092505060a087013590509295509295509295565b5f8083601f840112612dc3575f80fd5b50813567ffffffffffffffff811115612dda575f80fd5b6020830191508360208260051b8501011115612df4575f80fd5b9250929050565b5f805f805f805f806080898b031215612e12575f80fd5b883567ffffffffffffffff80821115612e29575f80fd5b612e358c838d01612db3565b909a50985060208b0135915080821115612e4d575f80fd5b612e598c838d01612db3565b909850965060408b0135915080821115612e71575f80fd5b612e7d8c838d01612db3565b909650945060608b0135915080821115612e95575f80fd5b818b0191508b601f830112612ea8575f80fd5b813581811115612eb6575f80fd5b8c6020828501011115612ec7575f80fd5b6020830194508093505050509295985092959890939650565b5f60208284031215612ef0575f80fd5b81518015158114612eff575f80fd5b9392505050565b8051612f1181612c5f565b919050565b5f60208284031215612f26575f80fd5b8151612eff81612c5f565b5f60208284031215612f41575f80fd5b5051919050565b5f60808284031215612f58575f80fd5b6040516080810181811067ffffffffffffffff82111715612fa0577f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f6080820173ffffffffffffffffffffffffffffffffffffffff8087168452602060808186015282875180855260a08701915082890194505f5b81811015613056578551851683529483019491830191600101613038565b505085810360408701528651808252908201935091508086015f5b8381101561308d57815185529382019390820190600101613071565b5050505082810360608401525f815260208101610c0b565b6080810181855f5b60028110156130cc5781518352602092830192909101906001016130ad565b50505083604083015273ffffffffffffffffffffffffffffffffffffffff83166060830152949350505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115612726576127266130f9565b80820180821115612726576127266130f9565b805167ffffffffffffffff81168114612f11575f80fd5b5f610100808385031215613175575f80fd5b6040519081019067ffffffffffffffff821181831017156131bd577f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b81604052835191506131ce82612c5f565b8181526131dd60208501612f06565b60208201526131ee60408501612f06565b60408201526131ff60608501612f06565b60608201526080840151608082015260a084015160a082015261322460c0850161314c565b60c082015261323560e0850161314c565b60e0820152949350505050565b5f60208284031215613252575f80fd5b815160ff81168114612eff575f80fd5b8082028115828204841417612726576127266130f9565b5f5b8381101561329357818101518382015260200161327b565b50505f910152565b5f82516132ac818460208701613279565b9190910192915050565b600181815b8085111561330f57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156132f5576132f56130f9565b8085161561330257918102915b93841c93908002906132bb565b509250929050565b5f8261332557506001612726565b8161333157505f612726565b816001811461334757600281146133515761336d565b6001915050612726565b60ff841115613362576133626130f9565b50506001821b612726565b5060208310610133831016604e8410600b8410161715613390575081810a612726565b61339a83836132b6565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156133cc576133cc6130f9565b029392505050565b5f6127238383613317565b5f82613412577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b8181035f831280158383131683831282161715613436576134366130f9565b5092915050565b5f7f8000000000000000000000000000000000000000000000000000000000000000820361346d5761346d6130f9565b505f0390565b602081525f8251806020840152613491816040850160208701613279565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fea164736f6c6343000818000a000000000000000000000000d5a7bf70bc6135d1a9df914ce364b1765460e6750000000000000000000000005a2d321ed5c525e52a3b8d295c6d097a3c78b821000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8

Deployed Bytecode

0x6080604052600436106100bb575f3560e01c806375cf294d11610071578063edde8bdd1161004c578063edde8bdd1461029d578063f04f2707146102b0578063fbfa77cf146102cf575f80fd5b806375cf294d1461023857806378150fb814610257578063b60e66041461028a575f80fd5b80634aa4a4fc116100a15780634aa4a4fc1461017a57806354ea3a86146101d25780636267168314610205575f80fd5b80633b404f561461013557806349318ac314610167575f80fd5b36610131573373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2161461012f576040517f5724f38500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b5f80fd5b348015610140575f80fd5b5061015461014f366004612c83565b610302565b6040519081526020015b60405180910390f35b610154610175366004612cd1565b610834565b348015610185575f80fd5b506101ad7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161015e565b3480156101dd575f80fd5b506101ad7f000000000000000000000000d5a7bf70bc6135d1a9df914ce364b1765460e67581565b348015610210575f80fd5b506101ad7f0000000000000000000000005a2d321ed5c525e52a3b8d295c6d097a3c78b82181565b348015610243575f80fd5b50610154610252366004612c83565b610c15565b348015610262575f80fd5b506101ad7f000000000000000000000000f7e009b8111c09a906d1d84938745b9edeedfdde81565b610154610298366004612c83565b610f77565b6101546102ab366004612d59565b61160f565b3480156102bb575f80fd5b5061012f6102ca366004612dfb565b6119e6565b3480156102da575f80fd5b506101ad7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c881565b5f61030b6120c0565b8180421115610346576040517f460229df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5128abae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808916600483015288917f000000000000000000000000d5a7bf70bc6135d1a9df914ce364b1765460e67590911690635128abae90602401602060405180830381865afa1580156103d4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103f89190612ee0565b61042e576040517f8ebe7d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc66106570000000000000000000000000000000000000000000000000000000081525f600482018190529073ffffffffffffffffffffffffffffffffffffffff8a169063c661065790602401602060405180830381865afa158015610499573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104bd9190612f16565b90506104cb8933308b612136565b8073ffffffffffffffffffffffffffffffffffffffff1663204f83f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610514573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105389190612f31565b4210610682576040517ff1dc3cc9000000000000000000000000000000000000000000000000000000008152600481018990525f60248201819052604482018190529073ffffffffffffffffffffffffffffffffffffffff8b169063f1dc3cc9906064016020604051808303815f875af11580156105b8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105dc9190612f31565b6040517fba087652000000000000000000000000000000000000000000000000000000008152600481018290523060248201819052604482015290915073ffffffffffffffffffffffffffffffffffffffff83169063ba087652906064016020604051808303815f875af1158015610656573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061067a9190612f31565b9450506107db565b6040517ff1dc3cc900000000000000000000000000000000000000000000000000000000815260048101899052600160248201525f604482018190529073ffffffffffffffffffffffffffffffffffffffff8b169063f1dc3cc9906064016020604051808303815f875af11580156106fc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107209190612f31565b6040517f2390154f000000000000000000000000000000000000000000000000000000008152600481018290523060248201529091507f0000000000000000000000005a2d321ed5c525e52a3b8d295c6d097a3c78b82173ffffffffffffffffffffffffffffffffffffffff1690632390154f906044016020604051808303815f875af11580156107b3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d79190612f31565b9450505b83871115610815576040517feca4bc1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61081f86856121d1565b50505061082b60015f55565b95945050505050565b5f61083d6120c0565b8280421115610878576040517f460229df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5128abae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808a16600483015289917f000000000000000000000000d5a7bf70bc6135d1a9df914ce364b1765460e67590911690635128abae90602401602060405180830381865afa158015610906573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061092a9190612ee0565b610960576040517f8ebe7d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc66106570000000000000000000000000000000000000000000000000000000081525f600482018190529073ffffffffffffffffffffffffffffffffffffffff8b169063c661065790602401602060405180830381865afa1580156109cb573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109ef9190612f16565b905034881115610a2b576040517fbf35eb4300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610a45828b610a40368a90038a018a612f48565b61227a565b9050305f5d6040805160018082528183019092525f916020808301908036833750506040805160018082528183019092529293505f929150602080830190803683370190505090507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2825f81518110610ac057610ac0612fd1565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505082815f81518110610b0d57610b0d612fd1565b6020026020010181815250508c60025d73ffffffffffffffffffffffffffffffffffffffff841660035d3360045d3460055d8a60065d8960075d610b50346124d1565b6040517f5c38449e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c81690635c38449e90610bc690309086908690600401612ffe565b5f604051808303815f87803b158015610bdd575f80fd5b505af1158015610bef573d5f803e3d5ffd5b5050505060015c96505f60015d505050505050610c0b60015f55565b9695505050505050565b5f610c1e6120c0565b8180421115610c59576040517f460229df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5128abae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808916600483015288917f000000000000000000000000d5a7bf70bc6135d1a9df914ce364b1765460e67590911690635128abae90602401602060405180830381865afa158015610ce7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d0b9190612ee0565b610d41576040517f8ebe7d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fc66106570000000000000000000000000000000000000000000000000000000081525f6004820152610dd99073ffffffffffffffffffffffffffffffffffffffff8a169063c661065790602401602060405180830381865afa158015610dad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd19190612f16565b338a8a612136565b6040517f767691e70000000000000000000000000000000000000000000000000000000081525f600482018190526001602483015260448201899052606482018190523060848301529073ffffffffffffffffffffffffffffffffffffffff8a169063767691e79060a4016020604051808303815f875af1158015610e60573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e849190612f31565b6040517f2390154f000000000000000000000000000000000000000000000000000000008152600481018290523060248201529091507f0000000000000000000000005a2d321ed5c525e52a3b8d295c6d097a3c78b82173ffffffffffffffffffffffffffffffffffffffff1690632390154f906044016020604051808303815f875af1158015610f17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f3b9190612f31565b935083871115610815576040517feca4bc1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610f806120c0565b8180421115610fbb576040517f460229df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5128abae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808916600483015288917f000000000000000000000000d5a7bf70bc6135d1a9df914ce364b1765460e67590911690635128abae90602401602060405180830381865afa158015611049573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061106d9190612ee0565b6110a3576040517f8ebe7d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ac346124d1565b6040517fc66106570000000000000000000000000000000000000000000000000000000081525f600482018190529073ffffffffffffffffffffffffffffffffffffffff8a169063c661065790602401602060405180830381865afa158015611117573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061113b9190612f16565b602081905263a8fe4407600c9081527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25f526034902054909150611290576111b97f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2828060205263a8fe4407600c52815f5260016034600c20555050565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169063095ea7b3906044016020604051808303815f875af115801561126a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061128e9190612ee0565b505b6040517f867904b40000000000000000000000000000000000000000000000000000000081523060048201523460248201525f9073ffffffffffffffffffffffffffffffffffffffff83169063867904b4906044016020604051808303815f875af1158015611301573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113259190612f31565b905087811015611361576040517f3dbb7e8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208a905263a8fe4407600c9081525f83905260349020546114545761139d828b8060205263a8fe4407600c52815f5260016034600c20555050565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b811660048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602483015283169063095ea7b3906044016020604051808303815f875af115801561142e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114529190612ee0565b505b6040805180820182528281525f602082015290517f0c3e4b5400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c1691630c3e4b54916114ba91908d908c906004016130a5565b6020604051808303815f875af11580156114d6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114fa9190612f31565b94508173ffffffffffffffffffffffffffffffffffffffff166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015611545573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115699190612f16565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff898116600483015260248201849052919091169063a9059cbb906044016020604051808303815f875af11580156115dd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116019190612ee0565b505050505061082b60015f55565b5f6116186120c0565b8180421115611653576040517f460229df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f5128abae00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808a16600483015289917f000000000000000000000000d5a7bf70bc6135d1a9df914ce364b1765460e67590911690635128abae90602401602060405180830381865afa1580156116e1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117059190612ee0565b61173b576040517f8ebe7d5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f37ed3a7a000000000000000000000000000000000000000000000000000000008152600160048201525f60248201819052604482018a90529073ffffffffffffffffffffffffffffffffffffffff8b16906337ed3a7a90606401602060405180830381865afa1580156117b4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117d89190612f31565b905034881115611814576040517fbf35eb4300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61181d346124d1565b6040517f3a19e2300000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff8b811660248301527f0000000000000000000000005a2d321ed5c525e52a3b8d295c6d097a3c78b8211690633a19e230906044016020604051808303815f875af11580156118af573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118d39190612f31565b6040517f767691e7000000000000000000000000000000000000000000000000000000008152600160048201525f6024820152604481018390526064810189905273ffffffffffffffffffffffffffffffffffffffff8881166084830152919550908b169063767691e79060a4016020604051808303815f875af115801561195d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119819190612f31565b50878411156119bc576040517f96e9d4fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6119c78534613126565b905080156119d9576119d933826121d1565b50505050610c0b60015f55565b5f5c5f805d3081146119ff57635c5019415f526004601cfd5b5060035c602081905263a8fe4407600c9081527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25f52603490205460025c90611b5957611a827f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2838060205263a8fe4407600c52815f5260016034600c20555050565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248301527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169063095ea7b3906044016020604051808303815f875af1158015611b33573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b579190612ee0565b505b5f88885f818110611b6c57611b6c612fd1565b6040517f867904b4000000000000000000000000000000000000000000000000000000008152306004820152602090910292909201356024830181905292505f9173ffffffffffffffffffffffffffffffffffffffff8616915063867904b4906044016020604051808303815f875af1158015611beb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c0f9190612f31565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018390529192509085169063a9059cbb906044016020604051808303815f875af1158015611c85573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ca99190612ee0565b506040517f767691e70000000000000000000000000000000000000000000000000000000081525f600482018190526001602483015260448201839052606482018190523060848301529073ffffffffffffffffffffffffffffffffffffffff85169063767691e79060a4016020604051808303815f875af1158015611d31573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d559190612f31565b6040517f2390154f000000000000000000000000000000000000000000000000000000008152600481018290523060248201529091505f9073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005a2d321ed5c525e52a3b8d295c6d097a3c78b8211690632390154f906044016020604051808303815f875af1158015611dea573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e0e9190612f31565b905083811115611e4a576040517f854911e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8a8a5f818110611e5d57611e5d612fd1565b9050602002013585611e6f9190613139565b90505f611e7c8383613126565b905060065c811115611eba576040517f96e9d4fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060015d8773ffffffffffffffffffffffffffffffffffffffff166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f07573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f2b9190612f16565b73ffffffffffffffffffffffffffffffffffffffff1663a9059cbb60075c6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602481018890526044016020604051808303815f875af1158015611fb8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fdc9190612ee0565b506040517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018390527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff169063a9059cbb906044016020604051808303815f875af115801561206d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120919190612ee0565b505f61209f8260055c613126565b90506120ad60045c826121d1565b5050505050505050505050505050505050565b60025f5403612130576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60025f55565b6040805173ffffffffffffffffffffffffffffffffffffffff85811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790526121cb908590612551565b50505050565b6040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1690632e1a7d4d906024015f604051808303815f87803b158015612256575f80fd5b505af1158015612268573d5f803e3d5ffd5b505050506122768282612663565b5050565b5f815f0151826020015110156122bc576040517f364de99200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81606001515f036122d55766b1a2bc2ec5000060608301525b81604001515f036122e857606460408301525b5f8473ffffffffffffffffffffffffffffffffffffffff1663e6432a346040518163ffffffff1660e01b815260040161010060405180830381865afa158015612333573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123579190613163565b90505f816060015173ffffffffffffffffffffffffffffffffffffffff1663f51e181a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123a7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123cb9190612f31565b90505f60405180608001604052808873ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612422573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124469190613242565b60ff1681526020018381526020016124628560a0015185612713565b815260c085015167ffffffffffffffff1660209091015290505f6124b761248a846001613139565b6124949089613262565b6127106124a36101f482613126565b85604001516124b29190613262565b61272c565b90506124c58288838961276e565b98975050505050505050565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004015f604051808303818588803b158015612537575f80fd5b505af1158015612549573d5f803e3d5ffd5b505050505050565b5f6125b2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166128f79092919063ffffffff16565b905080515f14806125d25750808060200190518101906125d29190612ee0565b61265e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401612127565b505050565b604080515f8082526020820190925273ffffffffffffffffffffffffffffffffffffffff8416908390604051612699919061329b565b5f6040518083038185875af1925050503d805f81146126d3576040519150601f19603f3d011682016040523d82523d5f602084013e6126d8565b606091505b505090508061265e576040517fdcf35db000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8183116127215781612723565b825b90505b92915050565b5f827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048411830215820261275f575f80fd5b50910281810615159190040190565b83515f90819061277f90600a6133d4565b90505f612795868860200151896040015161272c565b90505f6127a28387613139565b8551909150156127bb5784516127b89083612713565b91505b6020850151156127d6576127d3856020015182612905565b90505b5f805b866040015181146128bc5760026127f08486613139565b6127fa91906133df565b91505f6128078b84612913565b90505f61282f8b61282084670de0b6b3a7640000613262565b61282a91906133df565b612950565b61284190670de0b6b3a7640000613417565b90508a821015801561286757508061285c8a60600151612950565b6128659061343d565b125b1561287b57839750505050505050506128ef565b84861480612892575084612890876001613139565b145b1561289e5750506128bc565b5f8113156128ae578395506128b2565b8394505b50506001016127d9565b506040517ffa711db200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b949350505050565b60606128ef84845f85612a05565b5f8183106127215781612723565b5f8061292683856060015161271061272c565b90505f6129406129368386613126565b8660200151612b1a565b905061082b818660400151612b2e565b5f7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115612a01576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e206160448201527f6e20696e743235360000000000000000000000000000000000000000000000006064820152608401612127565b5090565b606082471015612a97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401612127565b5f808673ffffffffffffffffffffffffffffffffffffffff168587604051612abf919061329b565b5f6040518083038185875af1925050503d805f8114612af9576040519150601f19603f3d011682016040523d82523d5f602084013e612afe565b606091505b5091509150612b0f87838387612b42565b979650505050505050565b5f61272383670de0b6b3a764000084612be1565b5f6127238383670de0b6b3a7640000612be1565b60608315612bd75782515f03612bd05773ffffffffffffffffffffffffffffffffffffffff85163b612bd0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401612127565b50816128ef565b6128ef8383612c1b565b5f827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0484118302158202612c14575f80fd5b5091020490565b815115612c2b5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016121279190613473565b73ffffffffffffffffffffffffffffffffffffffff81168114612c80575f80fd5b50565b5f805f805f60a08688031215612c97575f80fd5b8535612ca281612c5f565b945060208601359350604086013592506060860135612cc081612c5f565b949793965091946080013592915050565b5f805f805f80868803610120811215612ce8575f80fd5b8735612cf381612c5f565b965060208801359550604088013594506060880135612d1181612c5f565b935060808881013593507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6082011215612d48575f80fd5b5060a0870190509295509295509295565b5f805f805f8060c08789031215612d6e575f80fd5b8635612d7981612c5f565b95506020870135945060408701359350606087013592506080870135612d9e81612c5f565b8092505060a087013590509295509295509295565b5f8083601f840112612dc3575f80fd5b50813567ffffffffffffffff811115612dda575f80fd5b6020830191508360208260051b8501011115612df4575f80fd5b9250929050565b5f805f805f805f806080898b031215612e12575f80fd5b883567ffffffffffffffff80821115612e29575f80fd5b612e358c838d01612db3565b909a50985060208b0135915080821115612e4d575f80fd5b612e598c838d01612db3565b909850965060408b0135915080821115612e71575f80fd5b612e7d8c838d01612db3565b909650945060608b0135915080821115612e95575f80fd5b818b0191508b601f830112612ea8575f80fd5b813581811115612eb6575f80fd5b8c6020828501011115612ec7575f80fd5b6020830194508093505050509295985092959890939650565b5f60208284031215612ef0575f80fd5b81518015158114612eff575f80fd5b9392505050565b8051612f1181612c5f565b919050565b5f60208284031215612f26575f80fd5b8151612eff81612c5f565b5f60208284031215612f41575f80fd5b5051919050565b5f60808284031215612f58575f80fd5b6040516080810181811067ffffffffffffffff82111715612fa0577f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f6080820173ffffffffffffffffffffffffffffffffffffffff8087168452602060808186015282875180855260a08701915082890194505f5b81811015613056578551851683529483019491830191600101613038565b505085810360408701528651808252908201935091508086015f5b8381101561308d57815185529382019390820190600101613071565b5050505082810360608401525f815260208101610c0b565b6080810181855f5b60028110156130cc5781518352602092830192909101906001016130ad565b50505083604083015273ffffffffffffffffffffffffffffffffffffffff83166060830152949350505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115612726576127266130f9565b80820180821115612726576127266130f9565b805167ffffffffffffffff81168114612f11575f80fd5b5f610100808385031215613175575f80fd5b6040519081019067ffffffffffffffff821181831017156131bd577f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b81604052835191506131ce82612c5f565b8181526131dd60208501612f06565b60208201526131ee60408501612f06565b60408201526131ff60608501612f06565b60608201526080840151608082015260a084015160a082015261322460c0850161314c565b60c082015261323560e0850161314c565b60e0820152949350505050565b5f60208284031215613252575f80fd5b815160ff81168114612eff575f80fd5b8082028115828204841417612726576127266130f9565b5f5b8381101561329357818101518382015260200161327b565b50505f910152565b5f82516132ac818460208701613279565b9190910192915050565b600181815b8085111561330f57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156132f5576132f56130f9565b8085161561330257918102915b93841c93908002906132bb565b509250929050565b5f8261332557506001612726565b8161333157505f612726565b816001811461334757600281146133515761336d565b6001915050612726565b60ff841115613362576133626130f9565b50506001821b612726565b5060208310610133831016604e8410600b8410161715613390575081810a612726565b61339a83836132b6565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156133cc576133cc6130f9565b029392505050565b5f6127238383613317565b5f82613412577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b8181035f831280158383131683831282161715613436576134366130f9565b5092915050565b5f7f8000000000000000000000000000000000000000000000000000000000000000820361346d5761346d6130f9565b505f0390565b602081525f8251806020840152613491816040850160208701613279565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fea164736f6c6343000818000a

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

000000000000000000000000d5a7bf70bc6135d1a9df914ce364b1765460e6750000000000000000000000005a2d321ed5c525e52a3b8d295c6d097a3c78b821000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8

-----Decoded View---------------
Arg [0] : _metapoolFactory (address): 0xd5a7bF70BC6135D1A9dF914ce364b1765460E675
Arg [1] : _triLSTPool (address): 0x5a2d321eD5C525e52A3b8d295c6D097a3c78B821
Arg [2] : _vault (address): 0xBA12222222228d8Ba445958a75a0704d566BF2C8

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000d5a7bf70bc6135d1a9df914ce364b1765460e675
Arg [1] : 0000000000000000000000005a2d321ed5c525e52a3b8d295c6d097a3c78b821
Arg [2] : 000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8


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.