LeverageManager

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/LeverageManager.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;

// Dependency imports
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {ReentrancyGuardTransientUpgradeable} from
    "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardTransientUpgradeable.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

// Internal imports
import {IRebalanceAdapterBase} from "src/interfaces/IRebalanceAdapterBase.sol";
import {IBeaconProxyFactory} from "src/interfaces/IBeaconProxyFactory.sol";
import {ILendingAdapter} from "src/interfaces/ILendingAdapter.sol";
import {ILeverageManager} from "src/interfaces/ILeverageManager.sol";
import {ILeverageToken} from "src/interfaces/ILeverageToken.sol";
import {FeeManager} from "src/FeeManager.sol";
import {LeverageTokenState} from "src/types/DataTypes.sol";
import {LeverageToken} from "src/LeverageToken.sol";
import {
    ActionData,
    ActionType,
    ExternalAction,
    LeverageTokenConfig,
    BaseLeverageTokenConfig,
    RebalanceAction
} from "src/types/DataTypes.sol";

/**
 * @dev The LeverageManager contract is an upgradeable core contract that is responsible for managing the creation of LeverageTokens.
 * It also acts as an entry point for users to mint and redeem LeverageTokens (shares), and for
 * rebalancers to rebalance LeverageTokens.
 *
 * LeverageTokens are ERC20 tokens that are akin to shares in an ERC-4626 vault - they represent a claim on the equity held by
 * the LeverageToken. They can be created on this contract by calling `createNewLeverageToken`, and their configuration on the
 * LeverageManager is immutable.
 * Note: Although the LeverageToken configuration saved on the LeverageManager is immutable, the configured LendingAdapter and
 *       RebalanceAdapter for the LeverageToken may be upgradeable contracts.
 *
 * The LeverageManager also inherits the `FeeManager` contract, which is used to manage LeverageToken fees (which accrue to
 * the share value of the LeverageToken) and the treasury fees.
 *
 * For mints of LeverageTokens (shares), the collateral and debt required is calculated by using the LeverageToken's
 * current collateral ratio. As such, the collateral ratio after a mint must be equal to the collateral ratio before a
 * mint, within some rounding error.
 *
 * [CAUTION]
 * ====
 * - LeverageTokens are susceptible to inflation attacks like ERC-4626 vaults:
 *   "In empty (or nearly empty) ERC-4626 vaults, mints are at high risk of being stolen through frontrunning
 *   with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
 *   attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
 *   mint of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Redeems may
 *   similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by
 *   verifying the amount received is as expected, using a wrapper that performs these checks such as
 *   https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router]."
 *
 *   As such it is highly recommended that LeverageToken creators make an initial mint of a non-trivial amount of equity.
 *   It is also recommended to use a router that performs slippage checks when minting and redeeming.
 *
 * - LeverageToken creation is permissionless and can be configured with arbitrary lending adapters, rebalance adapters, and
 *   underlying collateral and debt assets. As such, the adapters and tokens used by a LeverageToken are part of the risk
 *   profile of the LeverageToken, and should be carefully considered by users before using a LeverageToken.
 *
 * - LeverageTokens can be configured with arbitrary lending adapters, thus LeverageTokens are directly affected by the
 *   specific mechanisms of the underlying lending market that their lending adapter integrates with. As mentioned above,
 *   it is highly recommended that users research and understand the lending adapter used by the LeverageToken they are
 *   considering using. Some examples:
 *   - Morpho: Users should be aware that Morpho market creation is permissionless, and that the price oracle used by
 *     by the market may be manipulatable.
 *   - Aave v3: Allows rehypothecation of collateral, which may lead to reverts when trying to remove collateral from the
 *     market during redeems and rebalances.
 *
 * @custom:contact security@seamlessprotocol.com
 */
contract LeverageManager is
    ILeverageManager,
    AccessControlUpgradeable,
    ReentrancyGuardTransientUpgradeable,
    FeeManager,
    UUPSUpgradeable
{
    // Base collateral ratio constant, 1e18 means that collateral / debt ratio is 1:1
    uint256 public constant BASE_RATIO = 1e18;
    bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

    /// @dev Struct containing all state for the LeverageManager contract
    /// @custom:storage-location erc7201:seamless.contracts.storage.LeverageManager
    struct LeverageManagerStorage {
        /// @dev Factory for deploying new LeverageTokens
        IBeaconProxyFactory tokenFactory;
        /// @dev LeverageToken address => Base config for LeverageToken
        mapping(ILeverageToken token => BaseLeverageTokenConfig) config;
    }

    function _getLeverageManagerStorage() internal pure returns (LeverageManagerStorage storage $) {
        // slither-disable-next-line assembly
        assembly {
            // keccak256(abi.encode(uint256(keccak256("seamless.contracts.storage.LeverageManager")) - 1)) & ~bytes32(uint256(0xff));
            $.slot := 0x326e20d598a681eb69bc11b5176604d340fccf9864170f09484f3c317edf3600
        }
    }

    function initialize(address initialAdmin, address treasury, IBeaconProxyFactory leverageTokenFactory)
        external
        initializer
    {
        __FeeManager_init(initialAdmin, treasury);
        __ReentrancyGuardTransient_init();
        _grantRole(DEFAULT_ADMIN_ROLE, initialAdmin);
        _getLeverageManagerStorage().tokenFactory = leverageTokenFactory;
        emit LeverageManagerInitialized(leverageTokenFactory);
    }

    function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) {}

    /// @inheritdoc ILeverageManager
    function convertCollateralToDebt(ILeverageToken token, uint256 collateral, Math.Rounding rounding)
        external
        view
        returns (uint256 debt)
    {
        ILendingAdapter lendingAdapter = getLeverageTokenLendingAdapter(token);
        uint256 totalCollateral = lendingAdapter.getCollateral();
        uint256 totalDebt = lendingAdapter.getDebt();

        return _convertCollateralToDebt(token, lendingAdapter, collateral, totalCollateral, totalDebt, rounding);
    }

    /// @inheritdoc ILeverageManager
    function convertCollateralToShares(ILeverageToken token, uint256 collateral, Math.Rounding rounding)
        external
        view
        returns (uint256 shares)
    {
        ILendingAdapter lendingAdapter = getLeverageTokenLendingAdapter(token);
        uint256 totalSupply = getFeeAdjustedTotalSupply(token);
        return _convertCollateralToShares(token, lendingAdapter, collateral, totalSupply, rounding);
    }

    /// @inheritdoc ILeverageManager
    function convertDebtToCollateral(ILeverageToken token, uint256 debt, Math.Rounding rounding)
        external
        view
        returns (uint256 collateral)
    {
        ILendingAdapter lendingAdapter = getLeverageTokenLendingAdapter(token);
        uint256 totalCollateral = lendingAdapter.getCollateral();
        uint256 totalDebt = lendingAdapter.getDebt();

        if (totalDebt == 0) {
            if (totalCollateral == 0) {
                // Initial state: no collateral or debt, use initial collateral ratio
                uint256 initialCollateralRatio = getLeverageTokenInitialCollateralRatio(token);
                return lendingAdapter.convertDebtToCollateralAsset(
                    Math.mulDiv(debt, initialCollateralRatio, BASE_RATIO, rounding)
                );
            }
            // Liquidated state: no debt but collateral exists, cannot convert
            return 0;
        }

        return Math.mulDiv(debt, totalCollateral, totalDebt, rounding);
    }

    /// @inheritdoc ILeverageManager
    function convertSharesToCollateral(ILeverageToken token, uint256 shares, Math.Rounding rounding)
        external
        view
        returns (uint256 collateral)
    {
        ILendingAdapter lendingAdapter = getLeverageTokenLendingAdapter(token);
        uint256 totalCollateral = lendingAdapter.getCollateral();
        uint256 totalSupply = getFeeAdjustedTotalSupply(token);
        return _convertSharesToCollateral(token, lendingAdapter, shares, totalCollateral, totalSupply, rounding);
    }

    /// @inheritdoc ILeverageManager
    function convertSharesToDebt(ILeverageToken token, uint256 shares, Math.Rounding rounding)
        external
        view
        returns (uint256 debt)
    {
        ILendingAdapter lendingAdapter = getLeverageTokenLendingAdapter(token);
        uint256 totalDebt = lendingAdapter.getDebt();
        uint256 totalSupply = getFeeAdjustedTotalSupply(token);
        return _convertSharesToDebt(token, lendingAdapter, shares, totalDebt, totalSupply, rounding);
    }

    /// @inheritdoc ILeverageManager
    function convertToAssets(ILeverageToken token, uint256 shares)
        external
        view
        returns (uint256 equityInCollateralAsset)
    {
        uint256 totalEquityInCollateralAsset = getLeverageTokenLendingAdapter(token).getEquityInCollateralAsset();
        uint256 totalSupply = getFeeAdjustedTotalSupply(token);

        // slither-disable-next-line incorrect-equality,timestamp
        if (totalSupply == 0) {
            return 0;
        }

        return Math.mulDiv(shares, totalEquityInCollateralAsset, totalSupply, Math.Rounding.Floor);
    }

    /// @inheritdoc ILeverageManager
    function convertToShares(ILeverageToken token, uint256 equityInCollateralAsset)
        external
        view
        returns (uint256 shares)
    {
        uint256 totalEquityInCollateralAsset = getLeverageTokenLendingAdapter(token).getEquityInCollateralAsset();
        uint256 totalSupply = getFeeAdjustedTotalSupply(token);

        if (totalEquityInCollateralAsset == 0) {
            return 0;
        }

        return Math.mulDiv(equityInCollateralAsset, totalSupply, totalEquityInCollateralAsset, Math.Rounding.Floor);
    }

    /// @inheritdoc ILeverageManager
    function getLeverageTokenFactory() public view returns (IBeaconProxyFactory factory) {
        return _getLeverageManagerStorage().tokenFactory;
    }

    /// @inheritdoc ILeverageManager
    function getLeverageTokenCollateralAsset(ILeverageToken token) public view returns (IERC20 collateralAsset) {
        return getLeverageTokenLendingAdapter(token).getCollateralAsset();
    }

    /// @inheritdoc ILeverageManager
    function getLeverageTokenDebtAsset(ILeverageToken token) public view returns (IERC20 debtAsset) {
        return getLeverageTokenLendingAdapter(token).getDebtAsset();
    }

    /// @inheritdoc ILeverageManager
    function getLeverageTokenRebalanceAdapter(ILeverageToken token)
        public
        view
        returns (IRebalanceAdapterBase module)
    {
        return _getLeverageManagerStorage().config[token].rebalanceAdapter;
    }

    /// @inheritdoc ILeverageManager
    function getLeverageTokenConfig(ILeverageToken token) external view returns (LeverageTokenConfig memory config) {
        BaseLeverageTokenConfig memory baseConfig = _getLeverageManagerStorage().config[token];
        uint256 mintTokenFee = getLeverageTokenActionFee(token, ExternalAction.Mint);
        uint256 redeemTokenFee = getLeverageTokenActionFee(token, ExternalAction.Redeem);

        return LeverageTokenConfig({
            lendingAdapter: baseConfig.lendingAdapter,
            rebalanceAdapter: baseConfig.rebalanceAdapter,
            mintTokenFee: mintTokenFee,
            redeemTokenFee: redeemTokenFee
        });
    }

    /// @inheritdoc ILeverageManager
    function getLeverageTokenLendingAdapter(ILeverageToken token) public view returns (ILendingAdapter adapter) {
        return _getLeverageManagerStorage().config[token].lendingAdapter;
    }

    /// @inheritdoc ILeverageManager
    function getLeverageTokenInitialCollateralRatio(ILeverageToken token) public view returns (uint256) {
        uint256 initialCollateralRatio =
            getLeverageTokenRebalanceAdapter(token).getLeverageTokenInitialCollateralRatio(token);

        if (initialCollateralRatio <= BASE_RATIO) {
            revert InvalidLeverageTokenInitialCollateralRatio(initialCollateralRatio);
        }

        return initialCollateralRatio;
    }

    /// @inheritdoc ILeverageManager
    function getLeverageTokenState(ILeverageToken token) external view returns (LeverageTokenState memory state) {
        return _getLeverageTokenState(getLeverageTokenLendingAdapter(token));
    }

    /// @inheritdoc ILeverageManager
    function createNewLeverageToken(LeverageTokenConfig calldata tokenConfig, string memory name, string memory symbol)
        external
        nonReentrant
        returns (ILeverageToken token)
    {
        IBeaconProxyFactory tokenFactory = getLeverageTokenFactory();

        // slither-disable-next-line reentrancy-events
        token = ILeverageToken(
            tokenFactory.createProxy(
                abi.encodeWithSelector(LeverageToken.initialize.selector, address(this), name, symbol),
                bytes32(tokenFactory.numProxies())
            )
        );

        _getLeverageManagerStorage().config[token] = BaseLeverageTokenConfig({
            lendingAdapter: tokenConfig.lendingAdapter,
            rebalanceAdapter: tokenConfig.rebalanceAdapter
        });
        _setLeverageTokenActionFee(token, ExternalAction.Mint, tokenConfig.mintTokenFee);
        _setLeverageTokenActionFee(token, ExternalAction.Redeem, tokenConfig.redeemTokenFee);
        _setNewLeverageTokenManagementFee(token);

        tokenConfig.lendingAdapter.postLeverageTokenCreation(msg.sender, address(token));
        tokenConfig.rebalanceAdapter.postLeverageTokenCreation(msg.sender, address(token));

        emit LeverageTokenCreated(
            token,
            tokenConfig.lendingAdapter.getCollateralAsset(),
            tokenConfig.lendingAdapter.getDebtAsset(),
            tokenConfig
        );
        return token;
    }

    /// @inheritdoc ILeverageManager
    function previewDeposit(ILeverageToken token, uint256 collateral) public view returns (ActionData memory) {
        ILendingAdapter lendingAdapter = getLeverageTokenLendingAdapter(token);
        uint256 feeAdjustedTotalSupply = getFeeAdjustedTotalSupply(token);

        uint256 debt = _convertCollateralToDebt(
            token,
            lendingAdapter,
            collateral,
            lendingAdapter.getCollateral(),
            lendingAdapter.getDebt(),
            Math.Rounding.Floor
        );

        uint256 shares =
            _convertCollateralToShares(token, lendingAdapter, collateral, feeAdjustedTotalSupply, Math.Rounding.Floor);
        (uint256 sharesAfterFee, uint256 sharesFee, uint256 treasuryFee) =
            _computeFeesForGrossShares(token, shares, ExternalAction.Mint);

        return ActionData({
            collateral: collateral,
            debt: debt,
            shares: sharesAfterFee,
            tokenFee: sharesFee,
            treasuryFee: treasuryFee
        });
    }

    /// @inheritdoc ILeverageManager
    function previewMint(ILeverageToken token, uint256 shares) public view returns (ActionData memory) {
        (uint256 grossShares, uint256 sharesFee, uint256 treasuryFee) =
            _computeFeesForNetShares(token, shares, ExternalAction.Mint);

        ILendingAdapter lendingAdapter = getLeverageTokenLendingAdapter(token);
        uint256 feeAdjustedTotalSupply = getFeeAdjustedTotalSupply(token);
        uint256 collateral = _convertSharesToCollateral(
            token,
            lendingAdapter,
            grossShares,
            lendingAdapter.getCollateral(),
            feeAdjustedTotalSupply,
            Math.Rounding.Ceil
        );
        uint256 debt = _convertCollateralToDebt(
            token,
            lendingAdapter,
            collateral,
            lendingAdapter.getCollateral(),
            lendingAdapter.getDebt(),
            Math.Rounding.Floor
        );

        return ActionData({
            collateral: collateral,
            debt: debt,
            shares: shares,
            tokenFee: sharesFee,
            treasuryFee: treasuryFee
        });
    }

    /// @inheritdoc ILeverageManager
    function previewRedeem(ILeverageToken token, uint256 shares) public view returns (ActionData memory) {
        (uint256 sharesAfterFees, uint256 sharesFee, uint256 treasuryFee) =
            _computeFeesForGrossShares(token, shares, ExternalAction.Redeem);

        ILendingAdapter lendingAdapter = getLeverageTokenLendingAdapter(token);
        uint256 feeAdjustedTotalSupply = getFeeAdjustedTotalSupply(token);

        // The redeemer receives collateral and repays debt for the net shares after fees are subtracted. The amount of
        // shares their balance is decreased by is that net share amount (which is burned) plus the fees.
        // - the treasury fee shares are given to the treasury
        // - the token fee shares are burned to increase share value
        uint256 collateral = _convertSharesToCollateral(
            token,
            lendingAdapter,
            sharesAfterFees,
            lendingAdapter.getCollateral(),
            feeAdjustedTotalSupply,
            Math.Rounding.Floor
        );
        uint256 debt = _convertSharesToDebt(
            token, lendingAdapter, sharesAfterFees, lendingAdapter.getDebt(), feeAdjustedTotalSupply, Math.Rounding.Ceil
        );

        return ActionData({
            collateral: collateral,
            debt: debt,
            shares: shares,
            tokenFee: sharesFee,
            treasuryFee: treasuryFee
        });
    }

    /// @inheritdoc ILeverageManager
    function previewWithdraw(ILeverageToken token, uint256 collateral) public view returns (ActionData memory) {
        ILendingAdapter lendingAdapter = getLeverageTokenLendingAdapter(token);
        uint256 feeAdjustedTotalSupply = getFeeAdjustedTotalSupply(token);

        // The withdrawer receives their specified collateral amount and pays debt for the shares that can be exchanged
        // for the collateral amount. The amount of shares their balance is decreased by is that share amount (which is
        // burned) plus the fees.
        // - the treasury fee shares are given to the treasury
        // - the token fee shares are burned to increase share value
        uint256 shares =
            _convertCollateralToShares(token, lendingAdapter, collateral, feeAdjustedTotalSupply, Math.Rounding.Ceil);
        uint256 debt = _convertCollateralToDebt(
            token,
            lendingAdapter,
            collateral,
            lendingAdapter.getCollateral(),
            lendingAdapter.getDebt(),
            Math.Rounding.Ceil
        );

        (uint256 sharesAfterFees, uint256 sharesFee, uint256 treasuryFee) =
            _computeFeesForNetShares(token, shares, ExternalAction.Redeem);

        return ActionData({
            collateral: collateral,
            debt: debt,
            shares: sharesAfterFees,
            tokenFee: sharesFee,
            treasuryFee: treasuryFee
        });
    }

    /// @inheritdoc ILeverageManager
    function deposit(ILeverageToken token, uint256 collateral, uint256 minShares)
        external
        nonReentrant
        returns (ActionData memory actionData)
    {
        // Management fee is calculated from the total supply of the LeverageToken, so we need to charge it first
        // before total supply is updated due to the mint
        chargeManagementFee(token);

        ActionData memory depositData = previewDeposit(token, collateral);

        // slither-disable-next-line timestamp
        if (depositData.shares < minShares) {
            revert SlippageTooHigh(depositData.shares, minShares);
        }

        _mint(token, depositData);

        return depositData;
    }

    /// @inheritdoc ILeverageManager
    function mint(ILeverageToken token, uint256 shares, uint256 maxCollateral)
        external
        nonReentrant
        returns (ActionData memory actionData)
    {
        // Management fee is calculated from the total supply of the LeverageToken, so we need to charge it first
        // before total supply is updated due to the mint
        chargeManagementFee(token);

        ActionData memory mintData = previewMint(token, shares);

        // slither-disable-next-line timestamp
        if (mintData.collateral > maxCollateral) {
            revert SlippageTooHigh(mintData.collateral, maxCollateral);
        }

        _mint(token, mintData);

        return mintData;
    }

    /// @inheritdoc ILeverageManager
    function rebalance(
        ILeverageToken leverageToken,
        RebalanceAction[] calldata actions,
        IERC20 tokenIn,
        IERC20 tokenOut,
        uint256 amountIn,
        uint256 amountOut
    ) external nonReentrant {
        _transferTokens(tokenIn, msg.sender, address(this), amountIn);

        ILendingAdapter lendingAdapter = getLeverageTokenLendingAdapter(leverageToken);

        // Check if the LeverageToken is eligible for rebalance
        LeverageTokenState memory stateBefore = _getLeverageTokenState(lendingAdapter);

        IRebalanceAdapterBase rebalanceAdapter = getLeverageTokenRebalanceAdapter(leverageToken);
        if (!rebalanceAdapter.isEligibleForRebalance(leverageToken, stateBefore, msg.sender)) {
            revert LeverageTokenNotEligibleForRebalance();
        }

        for (uint256 i = 0; i < actions.length; i++) {
            _executeLendingAdapterAction(leverageToken, actions[i].actionType, actions[i].amount);
        }

        // Validate the LeverageToken state after rebalancing
        if (!rebalanceAdapter.isStateAfterRebalanceValid(leverageToken, stateBefore)) {
            revert InvalidLeverageTokenStateAfterRebalance(leverageToken);
        }

        _transferTokens(tokenOut, address(this), msg.sender, amountOut);

        LeverageTokenState memory stateAfter = _getLeverageTokenState(lendingAdapter);

        emit Rebalance(leverageToken, msg.sender, stateBefore, stateAfter, actions);
    }

    /// @inheritdoc ILeverageManager
    function redeem(ILeverageToken token, uint256 shares, uint256 minCollateral)
        external
        nonReentrant
        returns (ActionData memory actionData)
    {
        // Management fee is calculated from the total supply of the LeverageToken, so we need to claim it first
        // before total supply is updated due to the redeem
        chargeManagementFee(token);

        ActionData memory redeemData = previewRedeem(token, shares);

        // slither-disable-next-line timestamp
        if (redeemData.collateral < minCollateral) {
            revert SlippageTooHigh(redeemData.collateral, minCollateral);
        }

        _redeem(token, redeemData);

        return redeemData;
    }

    /// @inheritdoc ILeverageManager
    function withdraw(ILeverageToken token, uint256 collateral, uint256 maxShares)
        external
        nonReentrant
        returns (ActionData memory actionData)
    {
        // Management fee is calculated from the total supply of the LeverageToken, so we need to claim it first
        // before total supply is updated due to the redeem
        chargeManagementFee(token);

        ActionData memory withdrawData = previewWithdraw(token, collateral);

        // slither-disable-next-line timestamp
        if (withdrawData.shares > maxShares) {
            revert SlippageTooHigh(withdrawData.shares, maxShares);
        }

        _redeem(token, withdrawData);

        return withdrawData;
    }

    /// @notice Converts collateral to debt given the state of the LeverageToken
    /// @param token LeverageToken to convert collateral for
    /// @param lendingAdapter Lending adapter of the LeverageToken
    /// @param collateral Collateral to convert to debt
    /// @param totalCollateral Total collateral of the LeverageToken
    /// @param totalDebt Total debt of the LeverageToken
    /// @param rounding Rounding mode
    /// @return debt Debt
    function _convertCollateralToDebt(
        ILeverageToken token,
        ILendingAdapter lendingAdapter,
        uint256 collateral,
        uint256 totalCollateral,
        uint256 totalDebt,
        Math.Rounding rounding
    ) internal view returns (uint256 debt) {
        if (totalCollateral == 0) {
            if (totalDebt == 0) {
                // Initial state: no collateral or debt, use initial collateral ratio
                uint256 initialCollateralRatio = getLeverageTokenInitialCollateralRatio(token);
                return lendingAdapter.convertCollateralToDebtAsset(
                    Math.mulDiv(collateral, BASE_RATIO, initialCollateralRatio, rounding)
                );
            }
            // Liquidated state: no collateral but debt exists, cannot convert
            return 0;
        }

        return Math.mulDiv(collateral, totalDebt, totalCollateral, rounding);
    }

    /// @notice Converts collateral to shares given the state of the LeverageToken
    /// @param token LeverageToken to convert collateral for
    /// @param lendingAdapter Lending adapter of the LeverageToken
    /// @param collateral Collateral to convert to shares
    /// @param totalSupply Total supply of shares of the LeverageToken
    /// @param rounding Rounding mode
    /// @return shares Shares
    function _convertCollateralToShares(
        ILeverageToken token,
        ILendingAdapter lendingAdapter,
        uint256 collateral,
        uint256 totalSupply,
        Math.Rounding rounding
    ) internal view returns (uint256 shares) {
        uint256 totalCollateral = lendingAdapter.getCollateral();

        // slither-disable-next-line incorrect-equality,timestamp
        if (totalSupply == 0) {
            uint256 initialCollateralRatio = getLeverageTokenInitialCollateralRatio(token);

            uint256 equityInCollateralAsset =
                Math.mulDiv(collateral, initialCollateralRatio - BASE_RATIO, initialCollateralRatio, rounding);

            uint256 leverageTokenDecimals = IERC20Metadata(address(token)).decimals();
            uint256 collateralDecimals = IERC20Metadata(address(lendingAdapter.getCollateralAsset())).decimals();

            // If collateral asset has more decimals than leverage token, we scale down the equity in collateral asset
            // Otherwise we scale up the equity in collateral asset
            if (collateralDecimals > leverageTokenDecimals) {
                uint256 scalingFactor = 10 ** (collateralDecimals - leverageTokenDecimals);
                return equityInCollateralAsset / scalingFactor;
            } else {
                uint256 scalingFactor = 10 ** (leverageTokenDecimals - collateralDecimals);
                return equityInCollateralAsset * scalingFactor;
            }
        }

        // If total supply != 0 and total collateral is zero, the LeverageToken was fully liquidated. In this case,
        // no amount of collateral can be converted to shares. An implication of this is that new mints of shares
        // will not be possible for the LeverageToken, unless a deposit of collateral occurs directly on the
        // lending adapter of the LeverageToken resulting in totalCollateral no longer being zero.
        if (totalCollateral == 0) {
            return 0;
        }

        return Math.mulDiv(collateral, totalSupply, totalCollateral, rounding);
    }

    /// @notice Converts shares to collateral given the state of the LeverageToken
    /// @param token LeverageToken to convert shares for
    /// @param lendingAdapter Lending adapter of the LeverageToken
    /// @param shares Shares to convert to collateral
    /// @param totalCollateral Total collateral of the LeverageToken
    /// @param totalSupply Total supply of shares of the LeverageToken
    /// @param rounding Rounding mode
    function _convertSharesToCollateral(
        ILeverageToken token,
        ILendingAdapter lendingAdapter,
        uint256 shares,
        uint256 totalCollateral,
        uint256 totalSupply,
        Math.Rounding rounding
    ) internal view returns (uint256 collateral) {
        // slither-disable-next-line incorrect-equality,timestamp
        if (totalSupply == 0) {
            uint256 leverageTokenDecimals = IERC20Metadata(address(token)).decimals();
            uint256 collateralDecimals = IERC20Metadata(address(lendingAdapter.getCollateralAsset())).decimals();

            uint256 initialCollateralRatio = getLeverageTokenInitialCollateralRatio(token);

            // If collateral asset has more decimals than leverage token, we scale down the equity in collateral asset
            // Otherwise we scale up the equity in collateral asset
            if (collateralDecimals > leverageTokenDecimals) {
                uint256 scalingFactor = 10 ** (collateralDecimals - leverageTokenDecimals);
                return Math.mulDiv(
                    shares * scalingFactor, initialCollateralRatio, initialCollateralRatio - BASE_RATIO, rounding
                );
            } else {
                uint256 scalingFactor = 10 ** (leverageTokenDecimals - collateralDecimals);
                return Math.mulDiv(
                    shares, initialCollateralRatio, (initialCollateralRatio - BASE_RATIO) * scalingFactor, rounding
                );
            }
        }

        return Math.mulDiv(shares, totalCollateral, totalSupply, rounding);
    }

    /// @notice Converts shares to debt given the state of the LeverageToken
    /// @param token LeverageToken to convert shares for
    /// @param lendingAdapter Lending adapter of the LeverageToken
    /// @param shares Shares to convert to debt
    /// @param totalDebt Total debt of the LeverageToken
    /// @param totalSupply Total supply of shares of the LeverageToken
    /// @param rounding Rounding mode
    function _convertSharesToDebt(
        ILeverageToken token,
        ILendingAdapter lendingAdapter,
        uint256 shares,
        uint256 totalDebt,
        uint256 totalSupply,
        Math.Rounding rounding
    ) internal view returns (uint256 debt) {
        // slither-disable-next-line incorrect-equality,timestamp
        if (totalSupply == 0) {
            uint256 leverageTokenDecimals = IERC20Metadata(address(token)).decimals();
            uint256 collateralDecimals = IERC20Metadata(address(lendingAdapter.getCollateralAsset())).decimals();

            uint256 initialCollateralRatio = getLeverageTokenInitialCollateralRatio(token);

            // If collateral asset has more decimals than leverage token, we scale down the equity in collateral asset
            // Otherwise we scale up the equity in collateral asset
            if (collateralDecimals > leverageTokenDecimals) {
                uint256 scalingFactor = 10 ** (collateralDecimals - leverageTokenDecimals);
                return lendingAdapter.convertCollateralToDebtAsset(
                    Math.mulDiv(shares * scalingFactor, BASE_RATIO, initialCollateralRatio - BASE_RATIO, rounding)
                );
            } else {
                uint256 scalingFactor = 10 ** (leverageTokenDecimals - collateralDecimals);
                return lendingAdapter.convertCollateralToDebtAsset(
                    Math.mulDiv(shares, BASE_RATIO, (initialCollateralRatio - BASE_RATIO) * scalingFactor, rounding)
                );
            }
        }

        return Math.mulDiv(shares, totalDebt, totalSupply, rounding);
    }

    /// @notice Executes actions on the LendingAdapter for a specific LeverageToken
    /// @param token LeverageToken to execute action for
    /// @param actionType Type of the action to execute
    /// @param amount Amount to execute action with
    function _executeLendingAdapterAction(ILeverageToken token, ActionType actionType, uint256 amount) internal {
        ILendingAdapter lendingAdapter = getLeverageTokenLendingAdapter(token);

        if (actionType == ActionType.AddCollateral) {
            IERC20 collateralAsset = lendingAdapter.getCollateralAsset();
            // slither-disable-next-line reentrancy-events
            SafeERC20.forceApprove(collateralAsset, address(lendingAdapter), amount);
            // slither-disable-next-line reentrancy-events
            lendingAdapter.addCollateral(amount);
        } else if (actionType == ActionType.RemoveCollateral) {
            // slither-disable-next-line reentrancy-events
            lendingAdapter.removeCollateral(amount);
        } else if (actionType == ActionType.Borrow) {
            // slither-disable-next-line reentrancy-events
            lendingAdapter.borrow(amount);
        } else if (actionType == ActionType.Repay) {
            IERC20 debtAsset = lendingAdapter.getDebtAsset();
            // slither-disable-next-line reentrancy-events
            SafeERC20.forceApprove(debtAsset, address(lendingAdapter), amount);
            // slither-disable-next-line reentrancy-events
            lendingAdapter.repay(amount);
        }
    }

    //// @notice Returns all data required to describe current LeverageToken state - collateral, debt, equity and collateral ratio
    /// @param lendingAdapter LendingAdapter of the LeverageToken
    /// @return state LeverageToken state
    function _getLeverageTokenState(ILendingAdapter lendingAdapter)
        internal
        view
        returns (LeverageTokenState memory state)
    {
        uint256 collateral = lendingAdapter.getCollateralInDebtAsset();
        uint256 debt = lendingAdapter.getDebt();
        uint256 equity = lendingAdapter.getEquityInDebtAsset();

        uint256 collateralRatio =
            debt > 0 ? Math.mulDiv(collateral, BASE_RATIO, debt, Math.Rounding.Floor) : type(uint256).max;

        return LeverageTokenState({
            collateralInDebtAsset: collateral,
            debt: debt,
            equity: equity,
            collateralRatio: collateralRatio
        });
    }

    /// @notice Helper function for executing a mint action on a LeverageToken
    /// @param token LeverageToken to mint shares for
    /// @param mintData Action data for the mint
    function _mint(ILeverageToken token, ActionData memory mintData) internal {
        // Take collateral asset from sender
        IERC20 collateralAsset = getLeverageTokenCollateralAsset(token);
        SafeERC20.safeTransferFrom(collateralAsset, msg.sender, address(this), mintData.collateral);

        // Add collateral to LeverageToken
        _executeLendingAdapterAction(token, ActionType.AddCollateral, mintData.collateral);

        // Borrow and send debt assets to caller
        _executeLendingAdapterAction(token, ActionType.Borrow, mintData.debt);
        SafeERC20.safeTransfer(getLeverageTokenDebtAsset(token), msg.sender, mintData.debt);

        // Charge treasury fee
        _chargeTreasuryFee(token, mintData.treasuryFee);

        // Mint shares to user
        // slither-disable-next-line reentrancy-events
        token.mint(msg.sender, mintData.shares);

        // Emit event and explicit return statement
        emit Mint(token, msg.sender, mintData);
    }

    /// @notice Helper function for executing a redeem action on a LeverageToken
    /// @param token LeverageToken to redeem shares for
    /// @param redeemData Action data for the redeem
    function _redeem(ILeverageToken token, ActionData memory redeemData) internal {
        // Burn shares from user and total supply
        token.burn(msg.sender, redeemData.shares);

        // Mint shares to treasury for the treasury action fee
        _chargeTreasuryFee(token, redeemData.treasuryFee);

        // Take assets from sender and repay the debt
        SafeERC20.safeTransferFrom(getLeverageTokenDebtAsset(token), msg.sender, address(this), redeemData.debt);
        _executeLendingAdapterAction(token, ActionType.Repay, redeemData.debt);

        // Remove collateral from lending pool
        _executeLendingAdapterAction(token, ActionType.RemoveCollateral, redeemData.collateral);

        // Send collateral assets to sender
        SafeERC20.safeTransfer(getLeverageTokenCollateralAsset(token), msg.sender, redeemData.collateral);

        // Emit event and explicit return statement
        emit Redeem(token, msg.sender, redeemData);
    }

    /// @notice Helper function for transferring tokens, or no-op if token is 0 address
    /// @param token Token to transfer
    /// @param from Address to transfer tokens from
    /// @param to Address to transfer tokens to
    /// @dev If from address is this smart contract it will use the regular transfer function otherwise it will use transferFrom
    function _transferTokens(IERC20 token, address from, address to, uint256 amount) internal {
        if (address(token) == address(0)) {
            return;
        }

        if (from == address(this)) {
            SafeERC20.safeTransfer(token, to, amount);
        } else {
            SafeERC20.safeTransferFrom(token, from, to, amount);
        }
    }
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.20;

import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable __self = address(this);

    /**
     * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
     * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
     * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
     * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
     * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
     * during an upgrade.
     */
    string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

    /**
     * @dev The call is from an unauthorized context.
     */
    error UUPSUnauthorizedCallContext();

    /**
     * @dev The storage `slot` is unsupported as a UUID.
     */
    error UUPSUnsupportedProxiableUUID(bytes32 slot);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        _checkProxy();
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        _checkNotDelegated();
        _;
    }

    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual notDelegated returns (bytes32) {
        return ERC1967Utils.IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data);
    }

    /**
     * @dev Reverts if the execution is not performed via delegatecall or the execution
     * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
     * See {_onlyProxy}.
     */
    function _checkProxy() internal view virtual {
        if (
            address(this) == __self || // Must be called through delegatecall
            ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
        ) {
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Reverts if the execution is performed via delegatecall.
     * See {notDelegated}.
     */
    function _checkNotDelegated() internal view virtual {
        if (address(this) != __self) {
            // Must not be called through delegatecall
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
     *
     * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
     * is expected to be the implementation slot in ERC-1967.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
            if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                revert UUPSUnsupportedProxiableUUID(slot);
            }
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        } catch {
            // The implementation is not UUPS
            revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
        }
    }
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/math/Math.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    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 success flag (no overflow).
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

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

    /**
     * @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        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 success flag (no division by zero).
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

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

    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * SafeCast.toUint(condition));
        }
    }

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

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return ternary(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.
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }

        // The following calculation ensures accurate ceiling division without overflow.
        // Since a is non-zero, (a - 1) / b will not overflow.
        // The largest possible result occurs when (a - 1) / b is type(uint256).max,
        // but the largest value we can obtain is type(uint256).max - 1, which happens
        // when a = type(uint256).max and b = 1.
        unchecked {
            return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
        }
    }

    /**
     * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     *
     * 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²⁵⁶ and mod 2²⁵⁶ - 1, then use
            // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2²⁵⁶ + 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²⁵⁶. Also prevents denominator == 0.
            if (denominator <= prod1) {
                Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_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.

            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²⁵⁶ / 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²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
            // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv ≡ 1 mod 2⁴.
            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⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
            inverse *= 2 - denominator * inverse; // inverse mod 2³²
            inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
            inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶

            // 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²⁵⁶. Since the preconditions guarantee that the outcome is
            // less than 2²⁵⁶, 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;
        }
    }

    /**
     * @dev 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) {
        return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
    }

    /**
     * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
     *
     * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
     * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
     *
     * If the input value is not inversible, 0 is returned.
     *
     * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
     * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
     */
    function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
        unchecked {
            if (n == 0) return 0;

            // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
            // Used to compute integers x and y such that: ax + ny = gcd(a, n).
            // When the gcd is 1, then the inverse of a modulo n exists and it's x.
            // ax + ny = 1
            // ax = 1 + (-y)n
            // ax ≡ 1 (mod n) # x is the inverse of a modulo n

            // If the remainder is 0 the gcd is n right away.
            uint256 remainder = a % n;
            uint256 gcd = n;

            // Therefore the initial coefficients are:
            // ax + ny = gcd(a, n) = n
            // 0a + 1n = n
            int256 x = 0;
            int256 y = 1;

            while (remainder != 0) {
                uint256 quotient = gcd / remainder;

                (gcd, remainder) = (
                    // The old remainder is the next gcd to try.
                    remainder,
                    // Compute the next remainder.
                    // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
                    // where gcd is at most n (capped to type(uint256).max)
                    gcd - remainder * quotient
                );

                (x, y) = (
                    // Increment the coefficient of a.
                    y,
                    // Decrement the coefficient of n.
                    // Can overflow, but the result is casted to uint256 so that the
                    // next value of y is "wrapped around" to a value between 0 and n - 1.
                    x - y * int256(quotient)
                );
            }

            if (gcd != 1) return 0; // No inverse exists.
            return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
        }
    }

    /**
     * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
     *
     * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
     * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
     * `a**(p-2)` is the modular multiplicative inverse of a in Fp.
     *
     * NOTE: this function does NOT check that `p` is a prime greater than `2`.
     */
    function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
        unchecked {
            return Math.modExp(a, p - 2, p);
        }
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
     *
     * Requirements:
     * - modulus can't be zero
     * - underlying staticcall to precompile must succeed
     *
     * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
     * sure the chain you're using it on supports the precompiled contract for modular exponentiation
     * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
     * the underlying function will succeed given the lack of a revert, but the result may be incorrectly
     * interpreted as 0.
     */
    function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
        (bool success, uint256 result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
     * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
     * to operate modulo 0 or if the underlying precompile reverted.
     *
     * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
     * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
     * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
     * of a revert, but the result may be incorrectly interpreted as 0.
     */
    function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
        if (m == 0) return (false, 0);
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            // | Offset    | Content    | Content (Hex)                                                      |
            // |-----------|------------|--------------------------------------------------------------------|
            // | 0x00:0x1f | size of b  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x20:0x3f | size of e  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x40:0x5f | size of m  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x60:0x7f | value of b | 0x<.............................................................b> |
            // | 0x80:0x9f | value of e | 0x<.............................................................e> |
            // | 0xa0:0xbf | value of m | 0x<.............................................................m> |
            mstore(ptr, 0x20)
            mstore(add(ptr, 0x20), 0x20)
            mstore(add(ptr, 0x40), 0x20)
            mstore(add(ptr, 0x60), b)
            mstore(add(ptr, 0x80), e)
            mstore(add(ptr, 0xa0), m)

            // Given the result < m, it's guaranteed to fit in 32 bytes,
            // so we can use the memory scratch space located at offset 0.
            success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
            result := mload(0x00)
        }
    }

    /**
     * @dev Variant of {modExp} that supports inputs of arbitrary length.
     */
    function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
        (bool success, bytes memory result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Variant of {tryModExp} that supports inputs of arbitrary length.
     */
    function tryModExp(
        bytes memory b,
        bytes memory e,
        bytes memory m
    ) internal view returns (bool success, bytes memory result) {
        if (_zeroBytes(m)) return (false, new bytes(0));

        uint256 mLen = m.length;

        // Encode call args in result and move the free memory pointer
        result = abi.encodePacked(b.length, e.length, mLen, b, e, m);

        assembly ("memory-safe") {
            let dataPtr := add(result, 0x20)
            // Write result on top of args to avoid allocating extra memory.
            success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
            // Overwrite the length.
            // result.length > returndatasize() is guaranteed because returndatasize() == m.length
            mstore(result, mLen)
            // Set the memory pointer after the returned data.
            mstore(0x40, add(dataPtr, mLen))
        }
    }

    /**
     * @dev Returns whether the provided byte array is zero.
     */
    function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
        for (uint256 i = 0; i < byteArray.length; ++i) {
            if (byteArray[i] != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * This method is based on Newton's method for computing square roots; the algorithm is restricted to only
     * using integer operations.
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        unchecked {
            // Take care of easy edge cases when a == 0 or a == 1
            if (a <= 1) {
                return a;
            }

            // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
            // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
            // the current value as `ε_n = | x_n - sqrt(a) |`.
            //
            // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
            // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
            // bigger than any uint256.
            //
            // By noticing that
            // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
            // we can deduce that `e - 1` is `log2(a) / 2`. We

Tags:
ERC20, ERC165, Multisig, Mintable, Burnable, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x9d04f65b58ced1fddef50aec8b0b3d64fe64220e|verified:true|block:23471225|tx:0xf99f83f882ef9ec513c03a7a48785c5385bcc0f0f0ede14a93609f377b1ea95e|first_check:1759219975

Submitted on: 2025-09-30 10:12:55

Comments

Log in to comment.

No comments yet.