Assistant

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/Assistant.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Interfaces
import {IQuoter} from "./interfaces/IQuoter.sol";
import {IVault} from "core/interfaces/IVault.sol";
import {IOracle} from "core/interfaces/IOracle.sol";
import {IUniswapV3Pool} from "v3-core/interfaces/IUniswapV3Pool.sol";

// Contracts and libraries
import {SirStructs} from "core/libraries/SirStructs.sol";
import {SystemConstants} from "core/libraries/SystemConstants.sol";
import {FullMath} from "core/libraries/FullMath.sol";
import {IWETH9, IERC20} from "core/interfaces/IWETH9.sol";
import {UniswapPoolAddress} from "core/libraries/UniswapPoolAddress.sol";
import {AddressClone} from "core/libraries/AddressClone.sol";
import {TickMath} from "v3-core/libraries/TickMath.sol";

import "forge-std/console.sol";

/**
 * @notice Helper functions for SIR protocol
 */
contract Assistant {
    IVault public immutable VAULT;
    IOracle private immutable SIR_ORACLE;
    address private immutable UNISWAPV3_FACTORY;
    IQuoter private immutable UNISWAPV3_QUOTER;

    error VaultDoesNotExist();
    error AmountTooLow();
    error TooMuchCollateral();
    error TEAMaxSupplyExceeded();

    enum VaultStatus {
        InvalidVault,
        NoUniswapPool,
        VaultCanBeCreated,
        VaultAlreadyExists
    }

    constructor(address vault, address oracle, address uniswapV3Factory) {
        VAULT = IVault(vault);
        SIR_ORACLE = IOracle(oracle);
        UNISWAPV3_FACTORY = uniswapV3Factory;

        if (block.chainid == 1) UNISWAPV3_QUOTER = IQuoter(0x5e55C9e631FAE526cd4B0526C4818D6e0a9eF0e3);
        else if (block.chainid == 11155111) UNISWAPV3_QUOTER = IQuoter(0xe3c07ebF66b9D070b589bCCa30903891F71A92Be);
        else revert("Network not supported");
    }

    /**
     *  @notice It returns the reserves of the vaults specified in vaultIds
     */
    function getReserves(uint48[] calldata vaultIds) external view returns (SirStructs.Reserves[] memory reserves) {
        reserves = new SirStructs.Reserves[](vaultIds.length);
        SirStructs.VaultParameters memory vaultParams;
        for (uint256 i = 0; i < vaultIds.length; i++) {
            vaultParams = VAULT.paramsById(vaultIds[i]);
            reserves[i] = VAULT.getReserves(vaultParams);
        }
    }

    /**
     *  @notice It returns the balances of the user in vaults [offset + 1, offset + numVaults].
     *  @param user The address of the user.
     *  @param offset The offset of the vaults.
     *  @param numVaults The number of vaults.
     */
    function getUserBalances(
        address user,
        uint offset,
        uint numVaults
    )
        external
        view
        returns (uint256[] memory apeBalances, uint256[] memory teaBalances, uint80[] memory unclaimedSirRewards)
    {
        IERC20 ape;
        apeBalances = new uint256[](numVaults);
        teaBalances = new uint256[](numVaults);
        unclaimedSirRewards = new uint80[](numVaults);
        for (uint256 vaultId = offset + 1; vaultId <= offset + numVaults; vaultId++) {
            ape = IERC20(AddressClone.getAddress(address(VAULT), vaultId));
            apeBalances[vaultId - offset - 1] = ape.balanceOf(user);
            teaBalances[vaultId - offset - 1] = VAULT.balanceOf(user, vaultId);
            unclaimedSirRewards[vaultId - offset - 1] = VAULT.unclaimedRewards(vaultId, user);
        }
    }

    /**
     * @notice It returns the ideal price of TEA.
     * To get the price as [units of Collateral][per unit of TEA], divide num by den.
     */
    function priceOfTEA(
        SirStructs.VaultParameters calldata vaultParams
    ) external view returns (uint256 num, uint256 den) {
        // Get current reserves
        SirStructs.Reserves memory reserves = VAULT.getReserves(vaultParams);
        num = reserves.reserveLPers;

        // Get supply of TEA
        SirStructs.VaultState memory vaultState = VAULT.vaultStates(vaultParams);
        den = VAULT.totalSupply(vaultState.vaultId);
    }

    /**
     * @notice It returns the price of the APE token.
     * To get the price as [units of Collateral][per unit of APE], divide num by den.
     */
    function priceOfAPE(
        SirStructs.VaultParameters calldata vaultParams
    ) external view returns (uint256 num, uint256 den) {
        // Get current reserves
        SirStructs.Reserves memory reserves = VAULT.getReserves(vaultParams);

        // Get system parameters
        SirStructs.SystemParameters memory systemParams = VAULT.systemParams();

        // Substract fees
        num = _feeAPE(reserves.reserveApes, systemParams.baseFee.fee, vaultParams.leverageTier);

        // Get supply of APE
        SirStructs.VaultState memory vaultState = VAULT.vaultStates(vaultParams);
        den = IERC20(getAddressAPE(vaultState.vaultId)).totalSupply();
    }

    /**
     * @notice It returns the status of the vault.
     * 0: InvalidVault - returned when the ERC20 tokens are not valid.
     * 1: NoUniswapPool - returned when no Uniswap pool of the two tokens does not exist.
     * 2: VaultCanBeCreated - vault does not exist and it can be created.
     * 3: VaultAlreadyExists - vault already exists.
     */
    function getVaultStatus(SirStructs.VaultParameters calldata vaultParams) external view returns (VaultStatus) {
        // Check if the token addresses are a smart contract
        if (vaultParams.collateralToken.code.length == 0) return VaultStatus.InvalidVault;
        if (vaultParams.debtToken.code.length == 0) return VaultStatus.InvalidVault;

        // Check if the token returns total supply
        (bool success, ) = vaultParams.collateralToken.staticcall(abi.encodeWithSelector(IERC20.totalSupply.selector));
        if (!success) return VaultStatus.InvalidVault;
        (success, ) = vaultParams.debtToken.staticcall(abi.encodeWithSelector(IERC20.totalSupply.selector));
        if (!success) return VaultStatus.InvalidVault;

        // Check if the leverage tier is valid
        if (
            vaultParams.leverageTier < SystemConstants.MIN_LEVERAGE_TIER ||
            vaultParams.leverageTier > SystemConstants.MAX_LEVERAGE_TIER
        ) return VaultStatus.InvalidVault;

        // Check if a Uniswap pool exists
        if (
            !_checkFeeTierExists(vaultParams, 100) &&
            !_checkFeeTierExists(vaultParams, 500) &&
            !_checkFeeTierExists(vaultParams, 3000) &&
            !_checkFeeTierExists(vaultParams, 10000)
        ) return VaultStatus.NoUniswapPool;

        // Check if vault already exists
        SirStructs.VaultState memory vaultState = VAULT.vaultStates(vaultParams);
        if (vaultState.vaultId == 0) return VaultStatus.VaultCanBeCreated;
        return VaultStatus.VaultAlreadyExists;
    }

    function getAddressAPE(uint48 vaultId) public view returns (address) {
        return AddressClone.getAddress(address(VAULT), vaultId);
    }

    /*////////////////////////////////////////////////////////////////
                            QUOTE FUNCTIONS
    ////////////////////////////////////////////////////////////////*/

    /**
     * @notice It returns the amount of TEA/APE tokens that would be obtained by depositing collateral token.
     * @dev If quoteMint reverts, mint will revert as well; vice versa is not necessarily true.
     * @return amountTokens that would be obtained by depositing amountCollateral.
     */
    function quoteMint(
        bool isAPE,
        SirStructs.VaultParameters calldata vaultParams,
        uint144 amountCollateral
    ) public view returns (uint256 amountTokens) {
        // Get vault state
        SirStructs.VaultState memory vaultState = VAULT.vaultStates(vaultParams);
        if (vaultState.vaultId == 0) revert VaultDoesNotExist();
        if (amountCollateral == 0) revert AmountTooLow();

        // Get current reserves
        SirStructs.Reserves memory reserves = VAULT.getReserves(vaultParams);

        SirStructs.SystemParameters memory systemParams = VAULT.systemParams();
        if (isAPE) {
            // Compute how much collateral actually gets deposited
            uint256 collateralIn = _feeAPE(amountCollateral, systemParams.baseFee.fee, vaultParams.leverageTier);

            // Get supply of APE
            address ape = getAddressAPE(vaultState.vaultId);
            uint256 supplyAPE = IERC20(ape).totalSupply();

            // Calculate tokens
            amountTokens = supplyAPE == 0
                ? collateralIn + reserves.reserveApes
                : FullMath.mulDiv(supplyAPE, collateralIn, reserves.reserveApes);
        } else {
            // Get collateralIn
            uint256 collateralIn = _feeMintTEA(amountCollateral, systemParams.lpFee.fee);

            // Get supply of TEA
            uint256 supplyTEA = VAULT.totalSupply(vaultState.vaultId);

            // Calculate tokens
            amountTokens = supplyTEA == 0
                ? _amountFirstMint(vaultParams.collateralToken, amountCollateral + reserves.reserveLPers)
                : FullMath.mulDiv(supplyTEA, amountCollateral, reserves.reserveLPers);

            // Check that total supply does not overflow
            if (amountTokens > SystemConstants.TEA_MAX_SUPPLY - supplyTEA) revert TEAMaxSupplyExceeded();

            // Minter's share of TEA
            amountTokens = FullMath.mulDiv(
                amountTokens,
                collateralIn,
                supplyTEA == 0
                    ? amountCollateral + reserves.reserveLPers // In the first mint, reserveLPers contains orphaned fees from apes
                    : amountCollateral
            );
        }

        if (amountTokens == 0) revert AmountTooLow();
    }

    /**
     * @notice It returns the amount of TEA/APE tokens that would be obtained by depositing debt token
     * @dev If quoteMint reverts, mint will revert as well; vice versa is not necessarily true.
     * @return amountTokens that would be obtained.
     */
    function quoteMintWithDebtToken(
        bool isAPE,
        SirStructs.VaultParameters calldata vaultParams,
        uint256 amountDebtToken
    ) external view returns (uint256 amountTokens, uint256 amountCollateral, uint256 amountCollateralIdeal) {
        if (amountDebtToken == 0) revert AmountTooLow();

        // Get fee tier
        uint24 feeTier = SIR_ORACLE.uniswapFeeTierOf(vaultParams.debtToken, vaultParams.collateralToken);

        // Quote Uniswap v3
        (amountCollateral, , , ) = UNISWAPV3_QUOTER.quoteExactInputSingle(
            IQuoter.QuoteExactInputSingleParams({
                tokenIn: vaultParams.debtToken,
                tokenOut: vaultParams.collateralToken,
                amountIn: amountDebtToken,
                fee: feeTier,
                sqrtPriceLimitX96: 0
            })
        );

        // Check that amountCollateral does not overflow
        if (amountCollateral > type(uint144).max) revert TooMuchCollateral();

        // Calculate ideal collateral amount using instant pool price (no slippage)
        // Get Uniswap pool
        address uniswapPool = SIR_ORACLE.uniswapFeeTierAddressOf(vaultParams.debtToken, vaultParams.collateralToken);

        // Get current price
        (uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(uniswapPool).slot0();

        // Calculate price fraction with better precision if it doesn't overflow when multiplied by itself
        bool inverse = vaultParams.collateralToken == IUniswapV3Pool(uniswapPool).token1();
        if (sqrtPriceX96 <= type(uint128).max) {
            uint256 priceX192 = uint256(sqrtPriceX96) * sqrtPriceX96;
            amountCollateralIdeal = inverse
                ? FullMath.mulDiv(priceX192, amountDebtToken, 1 << 192)
                : FullMath.mulDiv(1 << 192, amountDebtToken, priceX192);
        } else {
            uint256 priceX128 = FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, 1 << 64);
            amountCollateralIdeal = inverse
                ? FullMath.mulDiv(priceX128, amountDebtToken, 1 << 128)
                : FullMath.mulDiv(1 << 128, amountDebtToken, priceX128);
        }

        // Given that we know how much collateral we will get from Uniswap, we can now use the quoteMint function
        amountTokens = quoteMint(isAPE, vaultParams, uint144(amountCollateral));
    }

    function quoteCollateralToDebtToken(
        address debtToken,
        address collateralToken,
        uint256 amountCollateral
    ) external view returns (uint256 amountDebtToken) {
        // Get Uniswap pool
        address uniswapPool = SIR_ORACLE.uniswapFeeTierAddressOf(debtToken, collateralToken);

        // Get current price
        (uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(uniswapPool).slot0();

        // Calculate price fraction with better precision if it doesn't overflow when multiplied by itself
        bool inverse = collateralToken == IUniswapV3Pool(uniswapPool).token1();
        if (sqrtPriceX96 <= type(uint128).max) {
            uint256 priceX192 = uint256(sqrtPriceX96) * sqrtPriceX96;
            return
                !inverse
                    ? FullMath.mulDiv(priceX192, amountCollateral, 1 << 192)
                    : FullMath.mulDiv(1 << 192, amountCollateral, priceX192);
        } else {
            uint256 priceX128 = FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, 1 << 64);
            return
                !inverse
                    ? FullMath.mulDiv(priceX128, amountCollateral, 1 << 128)
                    : FullMath.mulDiv(1 << 128, amountCollateral, priceX128);
        }
    }

    /**
     * @notice If quoteBurn reverts, burn in Vault.sol will revert as well; vice versa is not necessarily true.
     * @return amountCollateral that would be obtained by burning amountTokens.
     * @return amountDebtToken the equivalent amount in debt token using Oracle TWAP price.
     */
    function quoteBurn(
        bool isAPE,
        SirStructs.VaultParameters calldata vaultParams,
        uint256 amountTokens
    ) external view returns (uint144 amountCollateral, uint256 amountDebtToken) {
        // Get vault state
        SirStructs.VaultState memory vaultState = VAULT.vaultStates(vaultParams);
        if (vaultState.vaultId == 0) revert VaultDoesNotExist();
        if (amountTokens == 0) revert AmountTooLow();

        // Get current reserves
        SirStructs.Reserves memory reserves = VAULT.getReserves(vaultParams);

        if (isAPE) {
            // Get supply of APE
            address ape = getAddressAPE(vaultState.vaultId);
            uint256 supplyAPE = IERC20(ape).totalSupply();

            // Get collateralOut
            uint144 collateralOut = uint144(FullMath.mulDiv(reserves.reserveApes, amountTokens, supplyAPE));

            // Get system parameters
            SirStructs.SystemParameters memory systemParams = VAULT.systemParams();

            // Get collateral withdrawn
            amountCollateral = _feeAPE(collateralOut, systemParams.baseFee.fee, vaultParams.leverageTier);
        } else {
            // Get supply of TEA
            uint256 supplyTEA = VAULT.totalSupply(vaultState.vaultId);

            // Get amount of collateral that would be withdrawn
            amountCollateral = uint144(FullMath.mulDiv(reserves.reserveLPers, amountTokens, supplyTEA));
        }

        // Convert collateral amount to debt token amount using Oracle TWAP
        amountDebtToken = _convertCollateralToDebtTokenUsingTWAP(
            vaultParams.collateralToken,
            vaultParams.debtToken,
            amountCollateral
        );
    }

    /**
     * @notice Converts collateral amount to debt token amount using the Oracle's TWAP price.
     * @dev This uses the same TWAP price calculation as the Oracle contract for consistency.
     * @param collateralToken The collateral token address.
     * @param debtToken The debt token address.
     * @param amountCollateral The amount of collateral to convert.
     * @return amountDebtToken The equivalent amount in debt tokens.
     */
    function _convertCollateralToDebtTokenUsingTWAP(
        address collateralToken,
        address debtToken,
        uint256 amountCollateral
    ) private view returns (uint256 amountDebtToken) {
        // Get the pool address from Oracle
        address poolAddress = SIR_ORACLE.uniswapFeeTierAddressOf(collateralToken, debtToken);

        IUniswapV3Pool pool = IUniswapV3Pool(poolAddress);

        // Get TWAP observation data similar to Oracle
        uint32[] memory secondsAgos = new uint32[](2);
        secondsAgos[0] = 1800; // 30 minutes (TWAP_DURATION from Oracle)
        secondsAgos[1] = 0;

        int56[] memory tickCumulatives;

        try pool.observe(secondsAgos) returns (int56[] memory tickCumulatives_, uint160[] memory) {
            tickCumulatives = tickCumulatives_;
        } catch {
            // If 30-minute TWAP not available, try to get the oldest available observation
            // This mimics Oracle's fallback behavior
            (, , uint16 observationIndex, uint16 observationCardinality, , , ) = pool.slot0();

            if (observationCardinality > 1) {
                // Get oldest observation
                uint32 oldestObservationSeconds;
                int56 oldestTickCumulative;
                bool initialized;

                // Try to get the oldest initialized observation
                uint16 oldestIndex = (observationIndex + 1) % observationCardinality;
                (oldestObservationSeconds, oldestTickCumulative, , initialized) = pool.observations(oldestIndex);

                if (!initialized) {
                    // Fallback to index 0 which is always initialized
                    (oldestObservationSeconds, oldestTickCumulative, , ) = pool.observations(0);
                }

                // Calculate time difference
                uint32 timeElapsed = uint32(block.timestamp) - oldestObservationSeconds;

                if (timeElapsed > 0) {
                    // Get current observation
                    secondsAgos[0] = timeElapsed;
                    tickCumulatives = new int56[](2);
                    (tickCumulatives, ) = pool.observe(secondsAgos);
                } else {
                    // Use spot price if no TWAP available
                    (, int24 currentTick, , , , , ) = pool.slot0();
                    tickCumulatives = new int56[](2);
                    tickCumulatives[0] = currentTick;
                    tickCumulatives[1] = currentTick;
                    secondsAgos[0] = 1; // Avoid division by zero
                }
            } else {
                // Use spot price if cardinality is 1
                (, int24 currentTick, , , , , ) = pool.slot0();
                tickCumulatives = new int56[](2);
                tickCumulatives[0] = currentTick;
                tickCumulatives[1] = currentTick;
                secondsAgos[0] = 1; // Avoid division by zero
            }
        }

        // Calculate average tick over the period
        int24 arithmeticMeanTick = int24((tickCumulatives[1] - tickCumulatives[0]) / int56(uint56(secondsAgos[0])));

        // Convert tick to price
        // The price is in terms of token1/token0 in the pool
        bool collateralIsToken0 = collateralToken < debtToken;

        // Calculate sqrt price from tick
        uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(arithmeticMeanTick);

        // Calculate the amount of debt tokens
        // Price calculation depends on token order in the pool
        if (collateralIsToken0) {
            // collateral is token0, debt is token1
            // Price is debt/collateral (token1/token0)
            // amountDebtToken = amountCollateral * price
            if (sqrtPriceX96 <= type(uint128).max) {
                uint256 priceX192 = uint256(sqrtPriceX96) * sqrtPriceX96;
                amountDebtToken = FullMath.mulDiv(amountCollateral, priceX192, 1 << 192);
            } else {
                uint256 priceX128 = FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, 1 << 64);
                amountDebtToken = FullMath.mulDiv(amountCollateral, priceX128, 1 << 128);
            }
        } else {
            // collateral is token1, debt is token0
            // Price is still token1/token0, but we need debt/collateral
            // So we need to invert: amountDebtToken = amountCollateral / price
            if (sqrtPriceX96 <= type(uint128).max) {
                uint256 priceX192 = uint256(sqrtPriceX96) * sqrtPriceX96;
                amountDebtToken = FullMath.mulDiv(amountCollateral, 1 << 192, priceX192);
            } else {
                uint256 priceX128 = FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, 1 << 64);
                amountDebtToken = FullMath.mulDiv(amountCollateral, 1 << 128, priceX128);
            }
        }
    }

    /*////////////////////////////////////////////////////////////////
                            PRIVATE FUNCTIONS
    ////////////////////////////////////////////////////////////////*/

    function _feeAPE(
        uint144 collateralDepositedOrOut,
        uint16 baseFee,
        int256 leverageTier
    ) private pure returns (uint144 collateralInOrWithdrawn) {
        unchecked {
            uint256 feeNum;
            uint256 feeDen;
            if (leverageTier >= 0) {
                feeNum = 10000; // baseFee is uint16, leverageTier is int8, so feeNum does not require more than 24 bits
                feeDen = 10000 + (uint256(baseFee) << uint256(leverageTier));
            } else {
                uint256 temp = 10000 << uint256(-leverageTier);
                feeNum = temp;
                feeDen = temp + uint256(baseFee);
            }

            collateralInOrWithdrawn = uint144((uint256(collateralDepositedOrOut) * feeNum) / feeDen);
        }
    }

    function _feeMintTEA(uint144 collateralDeposited, uint16 lpFee) private pure returns (uint144 collateralIn) {
        unchecked {
            uint256 feeNum = 10000;
            uint256 feeDen = 10000 + uint256(lpFee);

            collateralIn = uint144((uint256(collateralDeposited) * feeNum) / feeDen);
        }
    }

    function _checkFeeTierExists(
        SirStructs.VaultParameters calldata vaultParams,
        uint24 feeTier
    ) private view returns (bool) {
        return
            UniswapPoolAddress
                .computeAddress(
                    UNISWAPV3_FACTORY,
                    UniswapPoolAddress.getPoolKey(vaultParams.collateralToken, vaultParams.debtToken, feeTier)
                )
                .code
                .length != 0;
    }

    function _amountFirstMint(address collateral, uint144 collateralDeposited) private view returns (uint256 amount) {
        uint256 collateralTotalSupply = IERC20(collateral).totalSupply();
        amount = collateralTotalSupply > SystemConstants.TEA_MAX_SUPPLY / 1e6
            ? FullMath.mulDiv(SystemConstants.TEA_MAX_SUPPLY, collateralDeposited, collateralTotalSupply)
            : collateralDeposited * 1e6;
    }
}
"
    },
    "src/interfaces/IQuoter.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >0.7.6;
pragma abicoder v2;

/// @title QuoterV2 Interface
/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.
/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the pool after the swap.
/// @dev These functions are not marked view because they rely on calling non-view functions and reverting
/// to compute the result. They are also not gas efficient and should not be called on-chain.
interface IQuoter {
    /// @notice Returns the amount out received for a given exact input swap without executing the swap
    /// @param path The path of the swap, i.e. each token pair and the pool fee
    /// @param amountIn The amount of the first token to swap
    /// @return amountOut The amount of the last token that would be received
    /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
    /// @return initializedTicksCrossedList List of number of initialized ticks loaded
    function quoteExactInput(
        bytes memory path,
        uint256 amountIn
    )
        external
        view
        returns (
            uint256 amountOut,
            uint160[] memory sqrtPriceX96AfterList,
            uint32[] memory initializedTicksCrossedList,
            uint256 gasEstimate
        );

    struct QuoteExactInputSingleWithPoolParams {
        address tokenIn;
        address tokenOut;
        uint256 amountIn;
        address pool;
        uint24 fee;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Returns the amount out received for a given exact input but for a swap of a single pool
    /// @param params The params for the quote, encoded as `quoteExactInputSingleWithPool`
    /// tokenIn The token being swapped in
    /// amountIn The desired input amount
    /// tokenOut The token being swapped out
    /// fee The fee of the pool to consider for the pair
    /// pool The address of the pool to consider for the pair
    /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
    /// @return amountOut The amount of `tokenOut` that would be received
    /// @return sqrtPriceX96After The sqrt price of the pool after the swap
    /// @return initializedTicksCrossed The number of initialized ticks loaded
    function quoteExactInputSingleWithPool(
        QuoteExactInputSingleWithPoolParams memory params
    )
        external
        view
        returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);

    struct QuoteExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint256 amountIn;
        uint24 fee;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Returns the amount out received for a given exact input but for a swap of a single pool
    /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`
    /// tokenIn The token being swapped in
    /// amountIn The desired input amount
    /// tokenOut The token being swapped out
    /// fee The fee of the token pool to consider for the pair
    /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
    /// @return amountOut The amount of `tokenOut` that would be received
    /// @return sqrtPriceX96After The sqrt price of the pool after the swap
    /// @return initializedTicksCrossed The number of initialized ticks loaded
    function quoteExactInputSingle(
        QuoteExactInputSingleParams memory params
    )
        external
        view
        returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);

    struct QuoteExactOutputSingleWithPoolParams {
        address tokenIn;
        address tokenOut;
        uint256 amount;
        uint24 fee;
        address pool;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool
    /// @param params The params for the quote, encoded as `QuoteExactOutputSingleWithPoolParams`
    /// tokenIn The token being swapped in
    /// tokenOut The token being swapped out
    /// amount The desired output amount
    /// fee The fee of the token pool to consider for the pair
    /// pool The address of the pool to consider for the pair
    /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
    /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`
    /// @return sqrtPriceX96After The sqrt price of the pool after the swap
    /// @return initializedTicksCrossed The number of initialized ticks loaded
    function quoteExactOutputSingleWithPool(
        QuoteExactOutputSingleWithPoolParams memory params
    )
        external
        view
        returns (uint256 amountIn, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);

    struct QuoteExactOutputSingleParams {
        address tokenIn;
        address tokenOut;
        uint256 amount;
        uint24 fee;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool
    /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`
    /// tokenIn The token being swapped in
    /// tokenOut The token being swapped out
    /// amountOut The desired output amount
    /// fee The fee of the token pool to consider for the pair
    /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
    /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`
    /// @return sqrtPriceX96After The sqrt price of the pool after the swap
    /// @return initializedTicksCrossed The number of initialized ticks loaded
    function quoteExactOutputSingle(
        QuoteExactOutputSingleParams memory params
    )
        external
        view
        returns (uint256 amountIn, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);

    /// @notice Returns the amount in required for a given exact output swap without executing the swap
    /// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
    /// @param amountOut The amount of the last token to receive
    /// @return amountIn The amount of first token required to be paid
    /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
    /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
    function quoteExactOutput(
        bytes memory path,
        uint256 amountOut
    )
        external
        view
        returns (
            uint256 amountIn,
            uint160[] memory sqrtPriceX96AfterList,
            uint32[] memory initializedTicksCrossedList,
            uint256 gasEstimate
        );
}
"
    },
    "lib/Core/src/interfaces/IVault.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {SirStructs} from "../libraries/SirStructs.sol";

interface IVault {
    error AmountTooLow();
    error DeadlineExceeded();
    error ExcessiveDeposit();
    error InsufficientCollateralReceivedFromUniswap();
    error InsufficientDeposit();
    error LengthMismatch();
    error LeverageTierOutOfRange();
    error Locked();
    error NotAWETHVault();
    error NotAuthorized();
    error StringsInsufficientHexLength(uint256 value, uint256 length);
    error TEAMaxSupplyExceeded();
    error TransferToZeroAddress();
    error UnsafeRecipient();
    error VaultAlreadyInitialized();
    error VaultDoesNotExist();

    event ApprovalForAll(address indexed account, address indexed operator, bool approved);
    event ReservesChanged(uint48 indexed vaultId, bool isAPE, bool isMint, uint144 reserveLPers, uint144 reserveApes);
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] vaultIds,
        uint256[] amounts
    );
    event TransferSingle(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 id,
        uint256 amount
    );
    event URI(string value, uint256 indexed id);
    event VaultInitialized(
        address indexed debtToken,
        address indexed collateralToken,
        int8 indexed leverageTier,
        uint256 vaultId,
        address ape
    );
    event VaultNewTax(uint48 indexed vault, uint8 tax, uint16 cumulativeTax);

    function APE_IMPLEMENTATION() external view returns (address);
    function ORACLE() external view returns (address);
    function SIR() external view returns (address);
    function SYSTEM_CONTROL() external view returns (address);
    function TIMESTAMP_ISSUANCE_START() external view returns (uint40);
    function balanceOf(address account, uint256 vaultId) external view returns (uint256);
    function balanceOfBatch(
        address[] memory owners,
        uint256[] memory vaultIds
    ) external view returns (uint256[] memory balances_);
    function burn(
        bool isAPE,
        SirStructs.VaultParameters memory vaultParams,
        uint256 amount,
        uint40 deadline
    ) external returns (uint144);
    function claimSIR(uint256 vaultId, address lper) external returns (uint80);
    function cumulativeSIRPerTEA(uint256 vaultId) external view returns (uint176 cumulativeSIRPerTEAx96);
    function getReserves(
        SirStructs.VaultParameters memory vaultParams
    ) external view returns (SirStructs.Reserves memory);
    function initialize(SirStructs.VaultParameters memory vaultParams) external;
    function isApprovedForAll(address, address) external view returns (bool);
    function mint(
        bool isAPE,
        SirStructs.VaultParameters memory vaultParams,
        uint256 amountToDeposit,
        uint144 collateralToDepositMin,
        uint40 deadline
    ) external payable returns (uint256 amount);
    function numberOfVaults() external view returns (uint48);
    function paramsById(uint48 vaultId) external view returns (SirStructs.VaultParameters memory);
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory vaultIds,
        uint256[] memory amounts,
        bytes memory data
    ) external;
    function safeTransferFrom(address from, address to, uint256 vaultId, uint256 amount, bytes memory data) external;
    function setApprovalForAll(address operator, bool approved) external;
    function supportsInterface(bytes4 interfaceId) external pure returns (bool);
    function systemParams() external view returns (SirStructs.SystemParameters memory systemParams_);
    function totalReserves(address collateral) external view returns (uint256);
    function totalSupply(uint256 vaultId) external view returns (uint256);
    function unclaimedRewards(uint256 vaultId, address lper) external view returns (uint80);
    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes memory data) external;
    function updateSystemState(uint16 baseFee, uint16 lpFee, bool mintingStopped) external;
    function updateVaults(
        uint48[] memory oldVaults,
        uint48[] memory newVaults,
        uint8[] memory newTaxes,
        uint16 cumulativeTax
    ) external;
    function uri(uint256 vaultId) external view returns (string memory);
    function vaultStates(
        SirStructs.VaultParameters memory vaultParams
    ) external view returns (SirStructs.VaultState memory);
    function vaultTax(uint48 vaultId) external view returns (uint8);
    function withdrawFees(address token) external returns (uint256 totalFeesToStakers);
    function withdrawToSaveSystem(address[] memory tokens, address to) external returns (uint256[] memory amounts);
}
"
    },
    "lib/Core/src/interfaces/IOracle.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {SirStructs} from "../libraries/SirStructs.sol";

interface IOracle {
    error NoUniswapPool();
    error OracleNotInitialized();
    error UniswapFeeTierIndexOutOfBounds();

    event OracleFeeTierChanged(uint24 feeTierPrevious, uint24 feeTierSelected);
    event OracleInitialized(
        address indexed token0,
        address indexed token1,
        uint24 feeTierSelected,
        uint136 avLiquidity,
        uint40 period
    );
    event PriceUpdated(address indexed token0, address indexed token1, bool priceTruncated, int64 priceTickX42);
    event UniswapFeeTierAdded(uint24 fee);
    event UniswapOracleProbed(uint24 fee, uint136 avLiquidity, uint40 period, uint16 cardinalityToIncrease);

    function TWAP_DURATION() external view returns (uint40);
    function getPrice(address collateralToken, address debtToken) external view returns (int64);
    function getUniswapFeeTiers() external view returns (SirStructs.UniswapFeeTier[] memory uniswapFeeTiers);
    function initialize(address tokenA, address tokenB) external;
    function newUniswapFeeTier(uint24 fee) external;
    function state(address token0, address token1) external view returns (SirStructs.OracleState memory);
    function uniswapFeeTierAddressOf(address tokenA, address tokenB) external view returns (address);
    function uniswapFeeTierOf(address tokenA, address tokenB) external view returns (uint24);
    function updateOracleState(
        address collateralToken,
        address debtToken
    ) external returns (int64 tickPriceX42, address uniswapPoolAddress);
}
"
    },
    "lib/Core/lib/v3-core/contracts/interfaces/IUniswapV3Pool.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {IUniswapV3PoolImmutables} from './pool/IUniswapV3PoolImmutables.sol';
import {IUniswapV3PoolState} from './pool/IUniswapV3PoolState.sol';
import {IUniswapV3PoolDerivedState} from './pool/IUniswapV3PoolDerivedState.sol';
import {IUniswapV3PoolActions} from './pool/IUniswapV3PoolActions.sol';
import {IUniswapV3PoolOwnerActions} from './pool/IUniswapV3PoolOwnerActions.sol';
import {IUniswapV3PoolErrors} from './pool/IUniswapV3PoolErrors.sol';
import {IUniswapV3PoolEvents} from './pool/IUniswapV3PoolEvents.sol';

/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
    IUniswapV3PoolImmutables,
    IUniswapV3PoolState,
    IUniswapV3PoolDerivedState,
    IUniswapV3PoolActions,
    IUniswapV3PoolOwnerActions,
    IUniswapV3PoolErrors,
    IUniswapV3PoolEvents
{

}
"
    },
    "lib/Core/src/libraries/SirStructs.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library SirStructs {
    struct VaultIssuanceParams {
        uint8 tax; // (tax / type(uint8).max * 10%) of its fee revenue is directed to the Treasury.
        uint40 timestampLastUpdate; // timestamp of the last time cumulativeSIRPerTEAx96 was updated. 0 => use systemParams.timestampIssuanceStart instead
        uint176 cumulativeSIRPerTEAx96; // Q104.96, cumulative SIR minted by the vaultId per unit of TEA.
    }

    struct VaultParameters {
        address debtToken;
        address collateralToken;
        int8 leverageTier;
    }

    struct FeeStructure {
        uint16 fee; // Fee in basis points.
        uint16 feeNew; // New fee to replace fee if current time exceeds FEE_CHANGE_DELAY since timestampUpdate
        uint40 timestampUpdate; // Timestamp fee change was made. If 0, feeNew is not used.
    }

    struct SystemParameters {
        FeeStructure baseFee;
        FeeStructure lpFee;
        bool mintingStopped; // If true, no minting of TEA/APE
        /** Aggregated taxes for all vaults. Choice of uint16 type.
            For vault i, (tax_i / type(uint8).max)*10% is charged, where tax_i is of type uint8.
            They must satisfy the condition
                Σ_i (tax_i / type(uint8).max)^2 ≤ 0.1^2
            Under this constraint, cumulativeTax = Σ_i tax_i is maximized when all taxes are equal (tax_i = tax for all i) and
                tax = type(uint8).max / sqrt(Nvaults)
            Since the lowest non-zero value is tax=1, the maximum number of vaults with non-zero tax is
                Nvaults = type(uint8).max^2 < type(uint16).max
         */
        uint16 cumulativeTax;
    }

    /** Collateral owned by the apes and LPers in a vault
     */
    struct Reserves {
        uint144 reserveApes;
        uint144 reserveLPers;
        int64 tickPriceX42;
    }

    /** Data needed for recoverying the amount of collateral owned by the apes and LPers in a vault
     */
    struct VaultState {
        uint144 reserve; // reserve =  reserveApes + reserveLPers
        /** Price at the border of the power and saturation zone.
            Q21.42 - Fixed point number with 42 bits of precision after the comma.
            type(int64).max and type(int64).min are used to represent +∞ and -∞ respectively.
         */
        int64 tickPriceSatX42; // Saturation price in Q21.42 fixed point
        uint48 vaultId; // Allows the creation of approximately 281 trillion vaults
    }

    /** The sum of all amounts in Fees are equal to the amounts deposited by the user (in the case of a mint)
        or taken out by the user (in the case of a burn).
        collateralInOrWithdrawn: Amount of collateral deposited by the user (in the case of a mint) or taken out by the user (in the case of a burn).
        collateralFeeToStakers: Amount of collateral paid to the stakers.
        collateralFeeToLPers: Amount of collateral paid to the gentlemen.
        collateralFeeToProtocol: Amount of collateral paid to the protocol.
     */
    struct Fees {
        uint144 collateralInOrWithdrawn;
        uint144 collateralFeeToStakers;
        uint144 collateralFeeToLPers; // Sometimes all LPers and sometimes only protocol owned liquidity
    }

    struct StakingParams {
        uint80 stake; // Amount of staked SIR
        uint176 cumulativeETHPerSIRx80; // Cumulative ETH per SIR * 2^80
    }

    struct StakerParams {
        uint80 stake; // Total amount of staked SIR by the staker
        uint176 cumulativeETHPerSIRx80; // Cumulative ETH per SIR * 2^80 last time the user updated his balance of ETH dividends
        uint80 lockedStake; // Amount of stake that was locked at time 'tsLastUpdate'
        uint40 tsLastUpdate; // Timestamp of the last time the user staked or unstaked
    }

    struct Auction {
        address bidder; // Address of the bidder
        uint96 bid; // Amount of the bid
        uint40 startTime; // Auction start time
    }

    struct OracleState {
        int64 tickPriceX42; // Last stored price. Q21.42
        uint40 timeStampPrice; // Timestamp of the last stored price
        uint8 indexFeeTier; // Uniswap v3 fee tier currently being used as oracle
        uint8 indexFeeTierProbeNext; // Uniswap v3 fee tier to probe next
        uint40 timeStampFeeTier; // Timestamp of the last probed fee tier
        bool initialized; // Whether the oracle has been initialized
        UniswapFeeTier uniswapFeeTier; // Uniswap v3 fee tier currently being used as oracle
    }

    /**
     * Parameters of a Uniswap v3 tier.
     */
    struct UniswapFeeTier {
        uint24 fee;
        int24 tickSpacing;
    }
}
"
    },
    "lib/Core/src/libraries/SystemConstants.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library SystemConstants {
    uint8 internal constant SIR_DECIMALS = 12;

    /** SIR Token Issuance Rate
        If we want to issue 2,015,000,000 SIR per year, this implies an issuance rate of 63.9 SIR/s.
     */
    uint72 internal constant ISSUANCE = uint72(2015e6 * 10 ** SIR_DECIMALS - 1) / 365 days + 1; // [sir/s]

    /** During the first 3 years, 30%-to-33% of the emissions are diverged to contributors.
        - 10% to pre-mainnet contributors
        - 10%-13% to fundraising contributors
        - 10% to a treasury for post-mainnet stuff
     */
    uint72 internal constant LP_ISSUANCE_FIRST_3_YEARS = uint72((uint256(56048532400000000) * ISSUANCE) / 1e17);

    uint128 internal constant TEA_MAX_SUPPLY = (uint128(LP_ISSUANCE_FIRST_3_YEARS) << 96) / type(uint16).max; // Must fit in uint128

    uint40 internal constant THREE_YEARS = 3 * 365 days;

    int64 internal constant MAX_TICK_X42 = 1951133415219145403; // log_1.0001(x)*2^42 where x is the max possible Q64.64 value, i.e., 2^64 - 2^-64

    // Approximately 10 days. We did not choose 10 days precisely to avoid auctions always ending on the same day and time of the week.
    uint40 internal constant AUCTION_COOLDOWN = 247 hours; // 247h & 240h have no common factors

    // Duration of an auction
    uint40 internal constant AUCTION_DURATION = 24 hours;

    // Time it takes for a change of LP or base fee to take effect
    uint256 internal constant FEE_CHANGE_DELAY = 10 days;

    uint40 internal constant SHUTDOWN_WITHDRAWAL_DELAY = 20 days;

    int8 internal constant MAX_LEVERAGE_TIER = 2;

    int8 internal constant MIN_LEVERAGE_TIER = -4;

    uint256 internal constant HALVING_PERIOD = 30 days; // Every 30 days, half of the locked stake is unlocked
}
"
    },
    "lib/Core/src/libraries/FullMath.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
/// @dev Modified from https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol so that it can compile on Solidity 8
library FullMath {
    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
    function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
        // 512-bit multiply [prod1 prod0] = a * b
        // Compute the product mod 2**256 and mod 2**256 - 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**256 + prod0
        uint256 prod0; // Least significant 256 bits of the product
        uint256 prod1; // Most significant 256 bits of the product
        assembly {
            let mm := mulmod(a, b, not(0))
            prod0 := mul(a, b)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        // Handle non-overflow cases, 256 by 256 division
        if (prod1 == 0) {
            require(denominator > 0);
            assembly {
                result := div(prod0, denominator)
            }
            return result;
        }

        // Make sure the result is less than 2**256.
        // Also prevents denominator == 0
        require(denominator > prod1);

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

        // Make division exact by subtracting the remainder from [prod1 prod0]
        // Compute remainder using mulmod
        uint256 remainder;
        assembly {
            remainder := mulmod(a, b, denominator)
        }
        // Subtract 256 bit number from 512 bit number
        assembly {
            prod1 := sub(prod1, gt(remainder, prod0))
            prod0 := sub(prod0, remainder)
        }

        // Factor powers of two out of denominator
        // Compute largest power of two divisor of denominator.
        // Always >= 1.
        unchecked {
            uint256 twos = (type(uint256).max - denominator + 1) & denominator;
            // Divide denominator by power of two
            assembly {
                denominator := div(denominator, twos)
            }

            // Divide [prod1 prod0] by the factors of two
            assembly {
                prod0 := div(prod0, twos)
            }
            // Shift in bits from prod1 into prod0. For this we need
            // to flip `twos` such that it is 2**256 / twos.
            // If twos is zero, then it becomes one
            assembly {
                twos := add(div(sub(0, twos), twos), 1)
            }
            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
            // correct for four bits. That is, denominator * inv = 1 mod 2**4
            uint256 inv = (3 * denominator) ^ 2;
            // Now use 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.
            inv *= 2 - denominator * inv; // inverse mod 2**8
            inv *= 2 - denominator * inv; // inverse mod 2**16
            inv *= 2 - denominator * inv; // inverse mod 2**32
            inv *= 2 - denominator * inv; // inverse mod 2**64
            inv *= 2 - denominator * inv; // inverse mod 2**128
            inv *= 2 - denominator * inv; // 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 precoditions 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 * inv;
            return result;
        }
    }

    function tryMulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (bool success, uint256 result) {
        // 512-bit multiply [prod1 prod0] = a * b
        // Compute the product mod 2**256 and mod 2**256 - 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**256 + prod0
        uint256 prod0; // Least significant 256 bits of the product
        uint256 prod1; // Most significant 256 bits of the product
        assembly {
            let mm := mulmod(a, b, not(0))
            prod0 := mul(a, b)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        // Handle non-overflow cases, 256 by 256 division
        if (prod1 == 0) {
            if (denominator == 0) return (false, 0);
            assembly {
                result := div(prod0, denominator)
            }
            return (true, result);
        }

        // Make sure the result is less than 2**256.
        // Also prevents denominator == 0
        if (denominator <= prod1) return (false, 0);

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

        // Make division exact by subtracting the remainder from [prod1 prod0]
        // Compute remainder using mulmod
        uint256 remainder;
        assembly {
            remainder := mulmod(a, b, denominator)
        }
        // Subtract 256 bit number from 512 bit number
        assembly {
            prod1 := sub(prod1, gt(remainder, prod0))
            prod0 := sub(prod0, remainder)
        }

        // Factor powers of two out of denominator
        // Compute largest power of two divisor of denominator.
        // Always >= 1.
        unchecked {
            uint256 twos = (type(uint256).max - denominator + 1) & denominator;
            // Divide denominator by power of two
            assembly {
                denominator := div(denominator, twos)
            }

            // Divide [prod1 prod0] by the factors of two
            assembly {
                prod0 := div(prod0, twos)
            }
            // Shift in bits from prod1 into prod0. For this we need
            // to flip `twos` such that it is 2**256 / twos.
            // If twos is zero, then it becomes one
            assembly {
                twos := add(div(sub(0, twos), twos), 1)
            }
            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
            // correct for four bits. That is, denominator * inv = 1 mod 2**4
            uint256 inv = (3 * denominator) ^ 2;
            // Now use 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.
            inv *= 2 - denominator * inv; // inverse mod 2**8
            inv *= 2 - denominator * inv; // inverse mod 2**16
            inv *= 2 - denominator * inv; // inverse mod 2**32
            inv *= 2 - denominator * inv; // inverse mod 2**64
            inv *= 2 - denominator * inv; // inverse mod 2**128
            inv *= 2 - denominator * inv; // 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 precoditions 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 * inv;
            return (true, result);
        }
    }

    /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
        result = mulDiv(a, b, denominator);
        if (mulmod(a, b, denominator) > 0) {
            require(result < type(uint256).max);
            result++;
        }
    }

    function tryMulDivRoundingUp(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (bool success, uint256 result) {
        (success, result) = tryMulDiv(a, b, denominator);
        if (success && mulmod(a, b, denominator) > 0) {
            if (result == type(uint256).max) return (false, 0);
            result++;
        }
    }
}
"
    },
    "lib/Core/src/interfaces/IWETH9.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title Interface for WETH9
interface IWETH9 is IERC20 {
    /// @notice Deposit ether to get wrapped ether
    function deposit() external payable;

    /// @notice Withdraw wrapped ether to get ether
    function withdraw(uint256) external;
}
"
    },
    "lib/Core/src/libraries/UniswapPoolAddress.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee
/// @notice Modified from https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/PoolAddress.sol
library UniswapPoolAddress {
    bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;

    /// @notice The identifying key of the pool
    struct PoolKey {
        address token0;
        address token1;
        uint24 fee;
    }

    /// @notice Returns PoolKey: the ordered tokens with the matched fee levels
    /// @param tokenA The first token of a pool, unsorted
    /// @param tokenB The second token of a pool, unsorted
    /// @param fee The fee level of the pool
    /// @return Poolkey The pool details with ordered token0 and token1 assignments
    function getPoolKey(address tokenA, address tokenB, uint24 fee) internal pure returns (PoolKey memory) {
        if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
        return PoolKey({token0: tokenA, token1: tokenB, fee: fee});
    }

    /// @notice Deterministically computes the pool address given the factory and PoolKey
    /// @param factory The Uniswap V3 factory contract address
    /// @param key The PoolKey
    /// @return pool The contract address of the V3 pool
    function computeAddress(address factory, PoolKey memory key) internal pure returns (address pool) {
        require(key.token0 < key.token1);
        pool = address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            hex"ff",
                            factory,
                            keccak256(abi.encode(key.token0, key.token1, key.fee)),
                            POOL_INIT_CODE_HASH
                        )
                    )
                )
            )
        );
    }
}
"
    },
    "lib/Core/src/libraries/AddressClone.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library AddressClone {
    /// @dev Hash of the `_CREATE3_PROXY_BYTECODE`.
    /// Equivalent to `keccak256(abi.encodePacked(hex"67363d3d37363d34f03d5260086018f3"))`.
    bytes32 private constant _CREATE3_PROXY_BYTECODE_HASH =
        0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;

    function getAddress(address deployer, uint256 vaultId) internal pure returns (address clone) {
        /// @solidity memory-safe-assembly
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Cache the free memory pointer.
            let m := mload(0x40)
            // Store `address(this)`.
            mstore(0x00, deployer)
            // Store the prefix.
            mstore8(0x0b, 0xff)
            // Store the salt.
            mstore(0x20, vaultId)
            // Store the bytecode hash.
            mstore(0x40, _CREATE3_PROXY_BYTECODE_HASH)

            // Store the proxy's address.
            mstore(0x14, keccak256(0x0b, 0x55))
            // Restore the free memory pointer.
            mstore(0x40, m)
            // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
            // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
            mstore(0x00, 0xd694)
            // Nonce of the proxy contract (1).
            mstore8(0x34, 0x01)

            clone := and(keccak256(0x1e, 0x17), 0xffffffffffffffffffffffffffffffffffffffff)
        }
    }
}
"
    },
    "lib/Core/lib/v3-core/contracts/libraries/TickMath.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
    error T();
    error R();

    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
    int24 internal constant MIN_TICK = -887272;
    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
    int24 internal constant MAX_TICK = -MIN_TICK;

    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
    uint160 internal constant MIN_SQRT_RATIO = 4295128739;
    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

    /// @notice Calculates sqrt(1.0001^tick) * 2^96
    /// @dev Throws if |tick| > max tick
    /// @param tick The input tick for the above formula
    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
    /// at the given tick
    function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
        unchecked {
            uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
            if (absTick > uint256(int256(MAX_TICK))) revert T();

            uint256 ratio = absTick & 0x1 != 0
                ? 0xfffcb933bd6fad37aa2d162d1a594001
                : 0x100000000000000000000000000000000;
            if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
            if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
            if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
            if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
            if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
            if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
            if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
            if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
            if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
            if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
            if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
            if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
            if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
            if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
            if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
            if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
            if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
            if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
            if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;

            if (tick > 0) ratio = type(uint256).max / ratio;

            // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
            // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
            // we round up in the division so getTickAtSqrtRatio of the output price is always consistent
            sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
        }
    }

    /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
    /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
    /// ever return.
    /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
    /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
    function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
        unchecked {
            // second inequality must be < because the price can never reach the price at the max tick
            if (!(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO)) revert R();
            uint256 ratio = uint256(sqrtPriceX96) << 32;

            uint256 r = ratio;
            uint256 msb = 0;

            assembly {
                let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
 

Tags:
ERC20, ERC1155, ERC165, Multisig, Mintable, Burnable, Non-Fungible, Swap, Liquidity, Staking, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xff14f91285580aed3733c0b1f3c8b6d04804c5ec|verified:true|block:23524975|tx:0x4bb646262c207a09084dedcfb6ca11208a86e9f1761c7110c6738934ca96fc74|first_check:1759830629

Submitted on: 2025-10-07 11:50:29

Comments

Log in to comment.

No comments yet.