Contract Diff Checker

Contract Name:
CurvePoolUtil

Contract Source Code:

// SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.20;

import "../interfaces/ICurvePool.sol";
import "../interfaces/IPrincipalToken.sol";
import "openzeppelin-math/Math.sol";

/**
 * @title CurvePoolUtil library
 * @author Spectra Finance
 * @notice Provides miscellaneous utils for computations related to Curve protocol.
 */
library CurvePoolUtil {
    using Math for uint256;

    error SolutionNotFound();
    error FailedToFetchExpectedLPTokenAmount();
    error FailedToFetchExpectedCoinAmount();

    /// @notice Decimal precision used internally in the Curve AMM
    uint256 public constant CURVE_DECIMALS = 18;
    /// @notice Base unit for Curve AMM calculations
    uint256 public constant CURVE_UNIT = 1e18;
    /// @notice Make rounding errors favoring other LPs a tiny bit
    uint256 private constant APPROXIMATION_DECREMENT = 1;
    /// @notice Maximal number of iterations in the binary search algorithm
    uint256 private constant MAX_ITERATIONS_BINSEARCH = 255;

    /**
     * @notice Returns the expected LP token amount received for depositing given amounts of IBT and PT
     * @param _curvePool The address of the Curve Pool in which liquidity will be deposited
     * @param _amounts Array containing the amounts of IBT and PT to deposit in the Curve Pool
     * @return minMintAmount The amount of expected LP tokens received for depositing the liquidity in the pool
     */
    function previewAddLiquidity(
        address _curvePool,
        uint256[2] memory _amounts
    ) external view returns (uint256 minMintAmount) {
        (bool success, bytes memory responseData) = _curvePool.staticcall(
            abi.encodeCall(ICurvePool(address(0)).calc_token_amount, (_amounts))
        );
        if (!success) {
            revert FailedToFetchExpectedLPTokenAmount();
        }
        minMintAmount = abi.decode(responseData, (uint256));
    }

    /**
     * @notice Returns the IBT and PT amounts received for burning a given amount of LP tokens
     * @param _curvePool The address of the curve pool
     * @param _lpTokenAmount The amount of the lp token to burn
     * @return minAmounts The expected respective amounts of IBT and PT withdrawn from the curve pool
     */
    function previewRemoveLiquidity(
        address _curvePool,
        uint256 _lpTokenAmount
    ) external view returns (uint256[2] memory minAmounts) {
        address lpToken = ICurvePool(_curvePool).token();
        uint256 totalSupply = IERC20(lpToken).totalSupply();
        (uint256 ibtBalance, uint256 ptBalance) = _getCurvePoolBalances(_curvePool);
        // decrement following what Curve is doing
        if (_lpTokenAmount > APPROXIMATION_DECREMENT && totalSupply != 0) {
            _lpTokenAmount -= APPROXIMATION_DECREMENT;
            minAmounts = [
                (ibtBalance * _lpTokenAmount) / totalSupply,
                (ptBalance * _lpTokenAmount) / totalSupply
            ];
        } else {
            minAmounts = [uint256(0), uint256(0)];
        }
    }

    /**
     * @notice Returns the amount of coin i received for burning a given amount of LP tokens
     * @param _curvePool The address of the curve pool
     * @param _lpTokenAmount The amount of the LP tokens to burn
     * @param _i The index of the unique coin to withdraw
     * @return minAmount The expected amount of coin i withdrawn from the curve pool
     */
    function previewRemoveLiquidityOneCoin(
        address _curvePool,
        uint256 _lpTokenAmount,
        uint256 _i
    ) external view returns (uint256 minAmount) {
        (bool success, bytes memory responseData) = _curvePool.staticcall(
            abi.encodeCall(ICurvePool(address(0)).calc_withdraw_one_coin, (_lpTokenAmount, _i))
        );
        if (!success) {
            revert FailedToFetchExpectedCoinAmount();
        }
        minAmount = abi.decode(responseData, (uint256));
    }

    /**
     * @notice Return the amount of IBT to deposit in the curve pool, given the total amount of IBT available for deposit
     * @param _amount The total amount of IBT available for deposit
     * @param _curvePool The address of the pool to deposit the amounts
     * @param _pt The address of the PT
     * @return ibts The amount of IBT which will be deposited in the curve pool
     */
    function calcIBTsToTokenizeForCurvePool(
        uint256 _amount,
        address _curvePool,
        address _pt
    ) external view returns (uint256 ibts) {
        (uint256 ibtBalance, uint256 ptBalance) = _getCurvePoolBalances(_curvePool);
        uint256 ibtBalanceInPT = IPrincipalToken(_pt).previewDepositIBT(ibtBalance);
        // Liquidity added in a ratio that (closely) matches the existing pool's ratio
        ibts = _amount.mulDiv(ptBalance, ibtBalanceInPT + ptBalance);
    }

    /**
     * @param _curvePool : PT/IBT curve pool
     * @param _i token index
     * @param _j token index
     * @param _targetDy amount out desired
     * @return dx The amount of token to provide in order to obtain _targetDy after swap
     */
    function getDx(
        address _curvePool,
        uint256 _i,
        uint256 _j,
        uint256 _targetDy
    ) external view returns (uint256 dx) {
        // Initial guesses
        uint256 _minGuess = type(uint256).max;
        uint256 _maxGuess = type(uint256).max;
        uint256 _factor100;
        uint256 _guess = ICurvePool(_curvePool).get_dy(_i, _j, _targetDy);

        if (_guess > _targetDy) {
            _maxGuess = _targetDy;
            _factor100 = 10;
        } else {
            _minGuess = _targetDy;
            _factor100 = 1000;
        }
        uint256 loops;
        _guess = _targetDy;
        while (!_dxSolved(_curvePool, _i, _j, _guess, _targetDy, _minGuess, _maxGuess)) {
            loops++;

            (_minGuess, _maxGuess, _guess) = _runLoop(
                _minGuess,
                _maxGuess,
                _factor100,
                _guess,
                _targetDy,
                _curvePool,
                _i,
                _j
            );

            if (loops >= MAX_ITERATIONS_BINSEARCH) {
                revert SolutionNotFound();
            }
        }
        dx = _guess;
    }

    /**
     * @dev Runs bisection search
     * @param _minGuess lower bound on searched value
     * @param _maxGuess upper bound on searched value
     * @param _factor100 search interval scaling factor
     * @param _guess The previous guess for the `dx` value that is being refined through the search process
     * @param _targetDy The target output of the `get_dy` function, which the search aims to achieve by adjusting `dx`.
     * @param _curvePool PT/IBT curve pool
     * @param _i token index, either 0 or 1
     * @param _j token index, either 0 or 1, must be different than _i
     * @return The lower bound on _guess, upper bound on _guess and next _guess
     */
    function _runLoop(
        uint256 _minGuess,
        uint256 _maxGuess,
        uint256 _factor100,
        uint256 _guess,
        uint256 _targetDy,
        address _curvePool,
        uint256 _i,
        uint256 _j
    ) internal view returns (uint256, uint256, uint256) {
        if (_minGuess == type(uint256).max || _maxGuess == type(uint256).max) {
            _guess = (_guess * _factor100) / 100;
        } else {
            _guess = (_maxGuess + _minGuess) >> 1;
        }
        uint256 dy = ICurvePool(_curvePool).get_dy(_i, _j, _guess);
        if (dy < _targetDy) {
            _minGuess = _guess;
        } else if (dy > _targetDy) {
            _maxGuess = _guess;
        }
        return (_minGuess, _maxGuess, _guess);
    }

    /**
     * @dev Returns true if algorithm converged
     * @param _curvePool PT/IBT curve pool
     * @param _i token index, either 0 or 1
     * @param _j token index, either 0 or 1, must be different than _i
     * @param _dx The current guess for the `dx` value that is being refined through the search process.
     * @param _targetDy The target output of the `get_dy` function, which the search aims to achieve by adjusting `dx`.
     * @param _minGuess lower bound on searched value
     * @param _maxGuess upper bound on searched value
     * @return true if the solution to the search problem was found, false otherwise
     */
    function _dxSolved(
        address _curvePool,
        uint256 _i,
        uint256 _j,
        uint256 _dx,
        uint256 _targetDy,
        uint256 _minGuess,
        uint256 _maxGuess
    ) internal view returns (bool) {
        if (_minGuess == type(uint256).max || _maxGuess == type(uint256).max) {
            return false;
        }
        uint256 dy = ICurvePool(_curvePool).get_dy(_i, _j, _dx);
        if (dy == _targetDy) {
            return true;
        }
        uint256 dy1 = ICurvePool(_curvePool).get_dy(_i, _j, _dx + 1);
        if (dy < _targetDy && _targetDy < dy1) {
            return true;
        }
        return false;
    }

    /**
     * @notice Returns the balances of the two tokens in provided curve pool
     * @param _curvePool address of the curve pool
     * @return The IBT and PT balances of the curve pool
     */
    function _getCurvePoolBalances(address _curvePool) internal view returns (uint256, uint256) {
        return (ICurvePool(_curvePool).balances(0), ICurvePool(_curvePool).balances(1));
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.20;

interface ICurvePool {
    function coins(uint256 index) external view returns (address);

    function balances(uint256 index) external view returns (uint256);

    function A() external view returns (uint256);

    function gamma() external view returns (uint256);

    function D() external view returns (uint256);

    function token() external view returns (address);

    function price_scale() external view returns (uint256);

    function future_A_gamma_time() external view returns (uint256);

    function future_A_gamma() external view returns (uint256);

    function initial_A_gamma_time() external view returns (uint256);

    function initial_A_gamma() external view returns (uint256);

    function fee_gamma() external view returns (uint256);

    function mid_fee() external view returns (uint256);

    function out_fee() external view returns (uint256);

    function allowed_extra_profit() external view returns (uint256);

    function adjustment_step() external view returns (uint256);

    function admin_fee() external view returns (uint256);

    function ma_half_time() external view returns (uint256);

    function get_virtual_price() external view returns (uint256);

    function fee() external view returns (uint256);

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

    function last_prices() external view returns (uint256);

    function calc_token_amount(uint256[2] calldata amounts) external view returns (uint256);

    function calc_withdraw_one_coin(
        uint256 _token_amount,
        uint256 i
    ) external view returns (uint256);

    function exchange(
        uint256 i,
        uint256 j,
        uint256 dx,
        uint256 min_dy,
        bool use_eth,
        address receiver
    ) external returns (uint256);

    function add_liquidity(
        uint256[2] calldata amounts,
        uint256 min_mint_amount
    ) external returns (uint256);

    function add_liquidity(
        uint256[2] calldata amounts,
        uint256 min_mint_amount,
        bool use_eth,
        address receiver
    ) external returns (uint256);

    function remove_liquidity(uint256 amount, uint256[2] calldata min_amounts) external;

    function remove_liquidity(
        uint256 amount,
        uint256[2] calldata min_amounts,
        bool use_eth,
        address receiver
    ) external;

    function remove_liquidity_one_coin(
        uint256 token_amount,
        uint256 i,
        uint256 min_amount
    ) external;

    function remove_liquidity_one_coin(
        uint256 token_amount,
        uint256 i,
        uint256 min_amount,
        bool use_eth,
        address receiver
    ) external;
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.20;

import "openzeppelin-contracts/interfaces/IERC20.sol";
import "openzeppelin-contracts/interfaces/IERC20Metadata.sol";
import "openzeppelin-contracts/interfaces/IERC3156FlashLender.sol";

interface IPrincipalToken is IERC20, IERC20Metadata, IERC3156FlashLender {
    /* ERRORS
     *****************************************************************************************************************/

    error InvalidDecimals();
    error BeaconNotSet();
    error PTExpired();
    error PTNotExpired();
    error RateError();
    error AddressError();
    error UnauthorizedCaller();
    error RatesAtExpiryAlreadyStored();
    error ERC5143SlippageProtectionFailed();
    error InsufficientBalance();
    error FlashLoanExceedsMaxAmount();
    error FlashLoanCallbackFailed();
    error NoRewardsProxy();
    error ClaimRewardsFailed();

    /* Functions
     *****************************************************************************************************************/

    function initialize(address _ibt, uint256 _duration, address initialAuthority) external;

    /**
     * @notice Toggle Pause
     * @dev Should only be called in extraordinary situations by the admin of the contract
     */
    function pause() external;

    /**
     * @notice Toggle UnPause
     * @dev Should only be called in extraordinary situations by the admin of the contract
     */
    function unPause() external;

    /**
     * @notice Deposits amount of assets in the PT vault
     * @param assets The amount of assets being deposited
     * @param receiver The receiver address of the shares
     * @return shares The amount of shares minted (same amount for PT & yt)
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @notice Deposits amount of assets in the PT vault
     * @param assets The amount of assets being deposited
     * @param ptReceiver The receiver address of the PTs
     * @param ytReceiver the receiver address of the YTs
     * @return shares The amount of shares minted (same amount for PT & yt)
     */
    function deposit(
        uint256 assets,
        address ptReceiver,
        address ytReceiver
    ) external returns (uint256 shares);

    /**
     * @notice Deposits amount of assets with a lower bound on shares received
     * @param assets The amount of assets being deposited
     * @param ptReceiver The receiver address of the PTs
     * @param ytReceiver The receiver address of the YTs
     * @param minShares The minimum allowed shares from this deposit
     * @return shares The amount of shares actually minted to the receiver
     */
    function deposit(
        uint256 assets,
        address ptReceiver,
        address ytReceiver,
        uint256 minShares
    ) external returns (uint256 shares);

    /**
     * @notice Same as normal deposit but with IBTs
     * @param ibts The amount of IBT being deposited
     * @param receiver The receiver address of the shares
     * @return shares The amount of shares minted to the receiver
     */
    function depositIBT(uint256 ibts, address receiver) external returns (uint256 shares);

    /**
     * @notice Same as normal deposit but with IBTs
     * @param ibts The amount of IBT being deposited
     * @param ptReceiver The receiver address of the PTs
     * @param ytReceiver the receiver address of the YTs
     * @return shares The amount of shares minted to the receiver
     */
    function depositIBT(
        uint256 ibts,
        address ptReceiver,
        address ytReceiver
    ) external returns (uint256 shares);

    /**
     * @notice Same as normal deposit but with IBTs
     * @param ibts The amount of IBT being deposited
     * @param ptReceiver The receiver address of the PTs
     * @param ytReceiver The receiver address of the YTs
     * @param minShares The minimum allowed shares from this deposit
     * @return shares The amount of shares minted to the receiver
     */
    function depositIBT(
        uint256 ibts,
        address ptReceiver,
        address ytReceiver,
        uint256 minShares
    ) external returns (uint256 shares);

    /**
     * @notice Burns owner's shares (PTs and YTs before expiry, PTs after expiry)
     * and sends assets to receiver
     * @param shares The amount of shares to burn
     * @param receiver The address that will receive the assets
     * @param owner The owner of the shares
     * @return assets The actual amount of assets received for burning the shares
     */
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    /**
     * @notice Burns owner's shares (PTs and YTs before expiry, PTs after expiry)
     * and sends assets to receiver
     * @param shares The amount of shares to burn
     * @param receiver The address that will receive the assets
     * @param owner The owner of the shares
     * @param minAssets The minimum assets that should be returned to user
     * @return assets The actual amount of assets received for burning the shares
     */
    function redeem(
        uint256 shares,
        address receiver,
        address owner,
        uint256 minAssets
    ) external returns (uint256 assets);

    /**
     * @notice Burns owner's shares (PTs and YTs before expiry, PTs after expiry)
     * and sends IBTs to receiver
     * @param shares The amount of shares to burn
     * @param receiver The address that will receive the IBTs
     * @param owner The owner of the shares
     * @return ibts The actual amount of IBT received for burning the shares
     */
    function redeemForIBT(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 ibts);

    /**
     * @notice Burns owner's shares (PTs and YTs before expiry, PTs after expiry)
     * and sends IBTs to receiver
     * @param shares The amount of shares to burn
     * @param receiver The address that will receive the IBTs
     * @param owner The owner of the shares
     * @param minIbts The minimum IBTs that should be returned to user
     * @return ibts The actual amount of IBT received for burning the shares
     */
    function redeemForIBT(
        uint256 shares,
        address receiver,
        address owner,
        uint256 minIbts
    ) external returns (uint256 ibts);

    /**
     * @notice Burns owner's shares (before expiry : PTs and YTs) and sends assets to receiver
     * @param assets The amount of assets to be received
     * @param receiver The address that will receive the assets
     * @param owner The owner of the shares (PTs and YTs)
     * @return shares The actual amount of shares burnt for receiving the assets
     */
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    /**
     * @notice Burns owner's shares (before expiry : PTs and YTs) and sends assets to receiver
     * @param assets The amount of assets to be received
     * @param receiver The address that will receive the assets
     * @param owner The owner of the shares (PTs and YTs)
     * @param maxShares The maximum shares allowed to be burnt
     * @return shares The actual amount of shares burnt for receiving the assets
     */
    function withdraw(
        uint256 assets,
        address receiver,
        address owner,
        uint256 maxShares
    ) external returns (uint256 shares);

    /**
     * @notice Burns owner's shares (before expiry : PTs and YTs) and sends IBTs to receiver
     * @param ibts The amount of IBT to be received
     * @param receiver The address that will receive the IBTs
     * @param owner The owner of the shares (PTs and YTs)
     * @return shares The actual amount of shares burnt for receiving the IBTs
     */
    function withdrawIBT(
        uint256 ibts,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    /**
     * @notice Burns owner's shares (before expiry : PTs and YTs) and sends IBTs to receiver
     * @param ibts The amount of IBT to be received
     * @param receiver The address that will receive the IBTs
     * @param owner The owner of the shares (PTs and YTs)
     * @param maxShares The maximum shares allowed to be burnt
     * @return shares The actual amount of shares burnt for receiving the IBTs
     */
    function withdrawIBT(
        uint256 ibts,
        address receiver,
        address owner,
        uint256 maxShares
    ) external returns (uint256 shares);

    /**
     * @notice Updates _user's yield since last update
     * @param _user The user whose yield will be updated
     * @return updatedUserYieldInIBT The unclaimed yield of the user in IBT (not just the updated yield)
     */
    function updateYield(address _user) external returns (uint256 updatedUserYieldInIBT);

    /**
     * @notice Claims caller's unclaimed yield in asset
     * @param _receiver The receiver of yield
     * @param _minAssets The minimum amount of assets that should be received
     * @return yieldInAsset The amount of yield claimed in asset
     */
    function claimYield(
        address _receiver,
        uint256 _minAssets
    ) external returns (uint256 yieldInAsset);

    /**
     * @notice Claims caller's unclaimed yield in IBT
     * @param _receiver The receiver of yield
     * @param _minIBT The minimum amount of IBT that should be received
     * @return yieldInIBT The amount of yield claimed in IBT
     */
    function claimYieldInIBT(
        address _receiver,
        uint256 _minIBT
    ) external returns (uint256 yieldInIBT);

    /**
     * @notice Claims the collected ibt fees and redeems them to the fee collector
     * @param _minAssets The minimum amount of assets that should be received
     * @return assets The amount of assets sent to the fee collector
     */
    function claimFees(uint256 _minAssets) external returns (uint256 assets);

    /**
     * @notice Updates yield of both sender and receiver of YTs
     * @param _from the sender of YTs
     * @param _to the receiver of YTs
     */
    function beforeYtTransfer(address _from, address _to) external;

    /**
     * Call the claimRewards function of the rewards contract
     * @param data The optional data to be passed to the rewards contract
     */
    function claimRewards(bytes memory data) external;

    /* SETTERS
     *****************************************************************************************************************/

    /**
     * @notice Stores PT and IBT rates at expiry. Ideally, it should be called the day of expiry
     */
    function storeRatesAtExpiry() external;

    /** Set a new Rewards Proxy
     * @param _rewardsProxy The address of the new reward proxy
     */
    function setRewardsProxy(address _rewardsProxy) external;

    /* GETTERS
     *****************************************************************************************************************/

    /**
     * @notice Returns the amount of shares minted for the theorical deposited amount of assets
     * @param assets The amount of assets deposited
     * @return The amount of shares minted
     */
    function previewDeposit(uint256 assets) external view returns (uint256);

    /**
     * @notice Returns the amount of shares minted for the theorical deposited amount of IBT
     * @param ibts The amount of IBT deposited
     * @return The amount of shares minted
     */
    function previewDepositIBT(uint256 ibts) external view returns (uint256);

    /**
     * @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     * @param receiver The receiver of the shares
     * @return The maximum amount of assets that can be deposited
     */
    function maxDeposit(address receiver) external view returns (uint256);

    /**
     * @notice Returns the theorical amount of shares that need to be burnt to receive assets of underlying
     * @param assets The amount of assets to receive
     * @return The amount of shares burnt
     */
    function previewWithdraw(uint256 assets) external view returns (uint256);

    /**
     * @notice Returns the theorical amount of shares that need to be burnt to receive amount of IBT
     * @param ibts The amount of IBT to receive
     * @return The amount of shares burnt
     */
    function previewWithdrawIBT(uint256 ibts) external view returns (uint256);

    /**
     * @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     * @param owner The owner of the Vault shares
     * @return The maximum amount of assets that can be withdrawn
     */
    function maxWithdraw(address owner) external view returns (uint256);

    /**
     * @notice Returns the maximum amount of the IBT that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     * @param owner The owner of the Vault shares
     * @return The maximum amount of IBT that can be withdrawn
     */
    function maxWithdrawIBT(address owner) external view returns (uint256);

    /**
     * @notice Returns the amount of assets received for the theorical amount of burnt shares
     * @param shares The amount of shares to burn
     * @return The amount of assets received
     */
    function previewRedeem(uint256 shares) external view returns (uint256);

    /**
     * @notice Returns the amount of IBT received for the theorical amount of burnt shares
     * @param shares The amount of shares to burn
     * @return The amount of IBT received
     */
    function previewRedeemForIBT(uint256 shares) external view returns (uint256);

    /**
     * @notice Returns the maximum amount of Vault shares that can be redeemed by the owner
     * @notice This function behaves differently before and after expiry. Before expiry an equal amount of PT and YT
     * needs to be burnt, while after expiry only PTs are burnt.
     * @param owner The owner of the shares
     * @return The maximum amount of shares that can be redeemed
     */
    function maxRedeem(address owner) external view returns (uint256);

    /**
     * Returns the total amount of the underlying asset that is owned by the Vault in the form of IBT.
     */
    function totalAssets() external view returns (uint256);

    /**
     * @notice Converts an underlying amount in principal. Equivalent to ERC-4626's convertToShares method.
     * @param underlyingAmount The amount of underlying (or assets) to convert
     * @return The resulting amount of principal (or shares)
     */
    function convertToPrincipal(uint256 underlyingAmount) external view returns (uint256);

    /**
     * @notice Converts a principal amount in underlying. Equivalent to ERC-4626's convertToAssets method.
     * @param principalAmount The amount of principal (or shares) to convert
     * @return The resulting amount of underlying (or assets)
     */
    function convertToUnderlying(uint256 principalAmount) external view returns (uint256);

    /**
     * @notice Returns whether or not the contract is paused.
     * @return true if the contract is paused, and false otherwise
     */
    function paused() external view returns (bool);

    /**
     * @notice Returns the unix timestamp (uint256) at which the PT contract expires
     * @return The unix timestamp (uint256) when PTs become redeemable
     */
    function maturity() external view returns (uint256);

    /**
     * @notice Returns the duration of the PT contract
     * @return The duration (in s) to expiry/maturity of the PT contract
     */
    function getDuration() external view returns (uint256);

    /**
     * @notice Returns the address of the underlying token (or asset). Equivalent to ERC-4626's asset method.
     * @return The address of the underlying token (or asset)
     */
    function underlying() external view returns (address);

    /**
     * @notice Returns the IBT address of the PT contract
     * @return ibt The address of the IBT
     */
    function getIBT() external view returns (address ibt);

    /**
     * @notice Returns the yt address of the PT contract
     * @return yt The address of the yt
     */
    function getYT() external view returns (address yt);

    /**
     * @notice Returns the current ibtRate
     * @return The current ibtRate
     */
    function getIBTRate() external view returns (uint256);

    /**
     * @notice Returns the current ptRate
     * @return The current ptRate
     */
    function getPTRate() external view returns (uint256);

    /**
     * @notice Returns 1 unit of IBT
     * @return The IBT unit
     */
    function getIBTUnit() external view returns (uint256);

    /**
     * @notice Get the unclaimed fees in IBT
     * @return The unclaimed fees in IBT
     */
    function getUnclaimedFeesInIBT() external view returns (uint256);

    /**
     * @notice Get the total collected fees in IBT (claimed and unclaimed)
     * @return The total fees in IBT
     */
    function getTotalFeesInIBT() external view returns (uint256);

    /**
     * @notice Get the tokenization fee of the PT
     * @return The tokenization fee
     */
    function getTokenizationFee() external view returns (uint256);

    /**
     * @notice Get the current IBT yield of the user
     * @param _user The address of the user to get the current yield from
     * @return The yield of the user in IBT
     */
    function getCurrentYieldOfUserInIBT(address _user) external view returns (uint256);
}

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

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @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 towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (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 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                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.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 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.

            uint256 twos = denominator & (0 - denominator);
            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 (unsignedRoundsUp(rounding) && 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
     * towards zero.
     *
     * 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 + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * 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 + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC3156FlashLender.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface of the ERC3156 FlashLender, as defined in
 * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
 */
interface IERC3156FlashLender {
    /**
     * @dev The amount of currency available to be lended.
     * @param token The loan currency.
     * @return The amount of `token` that can be borrowed.
     */
    function maxFlashLoan(address token) external view returns (uint256);

    /**
     * @dev The fee to be charged for a given loan.
     * @param token The loan currency.
     * @param amount The amount of tokens lent.
     * @return The amount of `token` to be charged for the loan, on top of the returned principal.
     */
    function flashFee(address token, uint256 amount) external view returns (uint256);

    /**
     * @dev Initiate a flash loan.
     * @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
     * @param token The loan currency.
     * @param amount The amount of tokens lent.
     * @param data Arbitrary data structure, intended to contain user-defined parameters.
     */
    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    ) external returns (bool);
}

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

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
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 (last updated v5.0.0) (interfaces/IERC3156FlashBorrower.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC3156 FlashBorrower, as defined in
 * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
 */
interface IERC3156FlashBorrower {
    /**
     * @dev Receive a flash loan.
     * @param initiator The initiator of the loan.
     * @param token The loan currency.
     * @param amount The amount of tokens lent.
     * @param fee The additional amount of tokens to repay.
     * @param data Arbitrary data structure, intended to contain user-defined parameters.
     * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
     */
    function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external returns (bytes32);
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):