PrvlFlashloanAaveBorrowV3

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/micro-managers/PrvlFlashloanAaveBorrowV3.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

import {UManager, FixedPointMathLib, ManagerWithMerkleVerification, ERC20} from "src/micro-managers/UManager.sol";
import {IUniswapV3Router} from "src/interfaces/IUniswapV3Router.sol";
import {DecoderCustomTypes} from "src/interfaces/DecoderCustomTypes.sol";
import {BalancerVault} from "src/interfaces/BalancerVault.sol";
import {BoringVault} from "src/base/BoringVault.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";

interface IQuoter {
    function quoteExactInputSingle(
        address tokenIn,
        address tokenOut,
        uint24 fee,
        uint256 amountIn,
        uint160 sqrtPriceLimitX96
    ) external returns (uint256 amountOut);

    function quoteExactOutputSingle(
        address tokenIn,
        address tokenOut,
        uint24 fee,
        uint256 amountOut,
        uint160 sqrtPriceLimitX96
    ) external returns (uint256 amountIn);
}

interface IAavePool {
    function getUserAccountData(address user) external view returns (
        uint256 totalCollateralBase,
        uint256 totalDebtBase,
        uint256 availableBorrowsBase,
        uint256 currentLiquidationThreshold,
        uint256 ltv,
        uint256 healthFactor
    );
}

contract PrvlFlashloanAaveBorrowV3 is UManager {
    using FixedPointMathLib for uint256;

    /*
     * @dev V3 removes hardcoded values
     */

    IUniswapV3Router public immutable uniswapV3Router;
    IQuoter public immutable quoter;
    BalancerVault public immutable balancerVault;
    address public immutable AAVE;
    address public immutable baseToken; // eg depositToken
    address public immutable depositToken; // eg wstdepositToken
    address public immutable aToken; // eg awstETH
    uint24 public immutable uniFeeTier;
    uint256 public immutable aaveVariableRate;

    bytes4 constant EXACT_INPUT_SINGLE_SELECTOR = 0x04e45aaf;
    bytes4 constant SUPPLY_SELECTOR = 0x617ba037;
    bytes4 constant BORROW_SELECTOR = 0xa415bcad;
    bytes4 constant FLASHLOAN_SELECTOR = 0x5c38449e;
    bytes4 constant REPAY_SELECTOR = 0x573ade81;
    bytes4 constant WITHDRAW_SELECTOR = 0x69328dec;

    bytes32[][] private borrowInnerManageProofs;
    address[] private borrowInnerDecodersAndSanitizers;
    bytes32[][] private repayInnerManageProofs;
    address[] private repayInnerDecodersAndSanitizers;
    address[] private outerDecodersAndSanitizers;
    bytes32[][] private outerManageProofs;

    constructor(
        address _owner,
        address _manager,
        address _boringVault,
        address _balancerVault,
        address _uniswapV3Router,
        address _quoter,
        address _aave,
        address _baseToken,
        address _depositToken,
        address _aToken,
        uint24 _uniFeeTier,
        uint256 _aaveVariableRate
    ) UManager(_owner, _manager, _boringVault) {
        uniswapV3Router = IUniswapV3Router(_uniswapV3Router);
        quoter = IQuoter(_quoter);
        balancerVault = BalancerVault(_balancerVault);
        AAVE = _aave;
        baseToken = _baseToken;
        depositToken = _depositToken;
        aToken = _aToken;
        uniFeeTier = _uniFeeTier;
        aaveVariableRate = _aaveVariableRate;
    }

    error InsufficientVaultBalance();

    function setBorrowInnerManageProofs(bytes32[][] calldata _borrowInnerManageProofs) external requiresAuth {
        // Manual deep copy to avoid calldata-to-storage issue
        delete borrowInnerManageProofs; // Clear existing
        borrowInnerManageProofs = new bytes32[][](_borrowInnerManageProofs.length);
        for (uint256 i = 0; i < _borrowInnerManageProofs.length; i++) {
            borrowInnerManageProofs[i] = new bytes32[](_borrowInnerManageProofs[i].length);
            for (uint256 j = 0; j < _borrowInnerManageProofs[i].length; j++) {
                borrowInnerManageProofs[i][j] = _borrowInnerManageProofs[i][j];
            }
        }
    }

    function setBorrowInnerDecodersAndSanitizers(address[] calldata _borrowInnerDecodersAndSanitizers)
        external
        requiresAuth
    {
        // Simple array: manual copy
        delete borrowInnerDecodersAndSanitizers;
        borrowInnerDecodersAndSanitizers = new address[](_borrowInnerDecodersAndSanitizers.length);
        for (uint256 i = 0; i < _borrowInnerDecodersAndSanitizers.length; i++) {
            borrowInnerDecodersAndSanitizers[i] = _borrowInnerDecodersAndSanitizers[i];
        }
    }

    function setRepayInnerManageProofs(bytes32[][] calldata _repayInnerManageProofs) external requiresAuth {
        // Manual deep copy to avoid calldata-to-storage issue
        delete repayInnerManageProofs; // Clear existing
        repayInnerManageProofs = new bytes32[][](_repayInnerManageProofs.length);
        for (uint256 i = 0; i < _repayInnerManageProofs.length; i++) {
            repayInnerManageProofs[i] = new bytes32[](_repayInnerManageProofs[i].length);
            for (uint256 j = 0; j < _repayInnerManageProofs[i].length; j++) {
                repayInnerManageProofs[i][j] = _repayInnerManageProofs[i][j];
            }
        }
    }

    function setRepayInnerDecodersAndSanitizers(address[] calldata _repayInnerDecodersAndSanitizers)
        external
        requiresAuth
    {
        // Simple array: manual copy
        delete repayInnerDecodersAndSanitizers;
        repayInnerDecodersAndSanitizers = new address[](_repayInnerDecodersAndSanitizers.length);
        for (uint256 i = 0; i < _repayInnerDecodersAndSanitizers.length; i++) {
            repayInnerDecodersAndSanitizers[i] = _repayInnerDecodersAndSanitizers[i];
        }
    }

    function setOuterDecodersAndSanitizers(address[] calldata _outerDecodersAndSanitizers) external requiresAuth {
        // Simple array: manual copy
        delete outerDecodersAndSanitizers;
        outerDecodersAndSanitizers = new address[](_outerDecodersAndSanitizers.length);
        for (uint256 i = 0; i < _outerDecodersAndSanitizers.length; i++) {
            outerDecodersAndSanitizers[i] = _outerDecodersAndSanitizers[i];
        }
    }

    function setOuterManageProofs(bytes32[][] calldata _outerManageProofs) external requiresAuth {
        // Manual deep copy to avoid calldata-to-storage issue
        delete outerManageProofs; // Clear existing
        outerManageProofs = new bytes32[][](_outerManageProofs.length);
        for (uint256 i = 0; i < _outerManageProofs.length; i++) {
            outerManageProofs[i] = new bytes32[](_outerManageProofs[i].length);
            for (uint256 j = 0; j < _outerManageProofs[i].length; j++) {
                outerManageProofs[i][j] = _outerManageProofs[i][j];
            }
        }
    }

    function borrow(uint256 collateralAmount, uint256 borrowAmount) external requiresAuth {
        uint256 vaultBalance = ERC20(baseToken).balanceOf(boringVault);
        if (vaultBalance < collateralAmount) {
            revert InsufficientVaultBalance();
        }

        bytes memory innerUserData = getBorrowUserData(collateralAmount, borrowAmount);

        _executeFlashloan(innerUserData, borrowAmount);
    }

    function repay(uint256 borrowAmount, uint256 supplyAmount) external requiresAuth {
        bytes memory innerUserData = getRepayUserData(borrowAmount, supplyAmount);
        _executeFlashloan(innerUserData, borrowAmount);
    }

    function settle() external requiresAuth {
        // Get total debt in USD (base currency)
        (, uint256 totalDebtBase, , , , ) = IAavePool(AAVE).getUserAccountData(boringVault);
        
        // Convert debt to baseToken amount (assuming 8 decimals for base currency, 6 for baseToken)
        uint256 debtAmount = totalDebtBase / 100; // Convert from 8 decimals to 6 decimals
        
        bytes memory innerUserData = getRepayUserData(type(uint256).max, type(uint256).max);
        
        // Use actual debt amount for flashloan
        _executeFlashloan(innerUserData, debtAmount);
    }

    function _executeFlashloan(bytes memory innerUserData, uint256 flashloanAmount) internal {
        bytes32[][] memory outerProofs = outerManageProofs;

        address[] memory outerTargets = new address[](1);
        outerTargets[0] = address(manager);

        uint256[] memory outerValues = new uint256[](1);

        bytes[] memory outerTargetData = new bytes[](1);
        address[] memory flashloanTokens = new address[](1);
        flashloanTokens[0] = baseToken;
        uint256[] memory flashloanAmounts = new uint256[](1);
        flashloanAmounts[0] = flashloanAmount;
        outerTargetData[0] = abi.encodeWithSelector(
            ManagerWithMerkleVerification.flashLoan.selector,
            address(manager),
            flashloanTokens,
            flashloanAmounts,
            innerUserData
        );

        manager.manageVaultWithMerkleVerification(
            outerProofs, outerDecodersAndSanitizers, outerTargets, outerTargetData, outerValues
        );
    }

    function getBorrowUserData(uint256 collateralAmount, uint256 borrowAmount)
        internal
        returns (bytes memory userData)
    {
        uint256 swapAmount = collateralAmount + borrowAmount;
        uint256 supplyAmount = quoter.quoteExactInputSingle(baseToken, depositToken, uniFeeTier, swapAmount, uint160(0));

        bytes memory swapData = abi.encodeWithSelector(
            EXACT_INPUT_SINGLE_SELECTOR, baseToken, depositToken, uniFeeTier, boringVault, swapAmount, 0, uint160(0)
        );
        bytes memory supplyData = abi.encodeWithSelector(SUPPLY_SELECTOR, depositToken, supplyAmount, boringVault, 0);
        bytes memory borrowData =
            abi.encodeWithSelector(BORROW_SELECTOR, baseToken, borrowAmount, aaveVariableRate, 0, boringVault);

        address[] memory innerTargets = new address[](3);
        innerTargets[0] = address(uniswapV3Router);
        innerTargets[1] = AAVE;
        innerTargets[2] = AAVE;

        bytes[] memory innerTargetData = new bytes[](3);
        innerTargetData[0] = swapData;
        innerTargetData[1] = supplyData;
        innerTargetData[2] = borrowData;

        uint256[] memory innerValues = new uint256[](3);

        userData = abi.encode(
            borrowInnerManageProofs, borrowInnerDecodersAndSanitizers, innerTargets, innerTargetData, innerValues
        );
    }

    function getRepayUserData(uint256 borrowAmount, uint256 supplyAmount) internal returns (bytes memory userData) {
        bytes memory repayData =
            abi.encodeWithSelector(REPAY_SELECTOR, baseToken, borrowAmount, aaveVariableRate, boringVault);
        bytes memory withdrawData = abi.encodeWithSelector(WITHDRAW_SELECTOR, depositToken, supplyAmount, boringVault);
        
        // For swap, use actual adepositToken balance if supplyAmount is max
        uint256 swapAmount = supplyAmount;
        if (supplyAmount == type(uint256).max) {
            swapAmount = ERC20(aToken).balanceOf(boringVault);
        }
        
        bytes memory swapData = abi.encodeWithSelector(
            EXACT_INPUT_SINGLE_SELECTOR, depositToken, baseToken, uniFeeTier, boringVault, swapAmount, 0, uint160(0)
        );

        address[] memory innerTargets = new address[](3);
        innerTargets[0] = AAVE;
        innerTargets[1] = AAVE;
        innerTargets[2] = address(uniswapV3Router);

        bytes[] memory innerTargetData = new bytes[](3);
        innerTargetData[0] = repayData;
        innerTargetData[1] = withdrawData;
        innerTargetData[2] = swapData;

        uint256[] memory innerValues = new uint256[](3);

        userData = abi.encode(
            repayInnerManageProofs, repayInnerDecodersAndSanitizers, innerTargets, innerTargetData, innerValues
        );
    }
}
"
    },
    "src/micro-managers/UManager.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {ManagerWithMerkleVerification} from "src/base/Roles/ManagerWithMerkleVerification.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";

abstract contract UManager is Auth {
    using FixedPointMathLib for uint256;

    // ========================================= STATE =========================================

    /**
     * @notice The period in seconds for the rate limit.
     */
    uint16 public period;

    /**
     * @notice The number of calls allowed per period.
     */
    uint16 public allowedCallsPerPeriod;

    /**
     * @notice The number of calls made in the current period.
     */
    mapping(uint256 => uint256) public callCountPerPeriod;

    //============================== ERRORS ===============================

    error UManager__CallCountExceeded();

    //============================== EVENTS ===============================

    event PeriodUpdated(uint16 oldPeriod, uint16 newPeriod);
    event AllowedCallsPeriodUpdated(uint16 oldAllowance, uint16 newAllowance);

    //============================== MODIFIERS ===============================

    modifier enforceRateLimit() {
        // Use parenthesis to avoid stack too deep error.
        {
            // We include this call in the current call count for period.
            uint256 currentCallCountForPeriod = callCountPerPeriod[block.timestamp % period] + 1;
            if (currentCallCountForPeriod > allowedCallsPerPeriod) {
                revert UManager__CallCountExceeded();
            }
            callCountPerPeriod[block.timestamp % period] = currentCallCountForPeriod;
        }
        _;
    }

    //============================== IMMUTABLES ===============================

    /**
     * @notice The ManagerWithMerkleVerification this uManager works with.
     */
    ManagerWithMerkleVerification internal immutable manager;

    /**
     * @notice The BoringVault this uManager works with.
     */
    address internal immutable boringVault;

    constructor(address _owner, address _manager, address _boringVault) Auth(_owner, Authority(address(0))) {
        manager = ManagerWithMerkleVerification(_manager);
        boringVault = _boringVault;
    }

    // ========================================= ADMIN FUNCTIONS =========================================

    /**
     * @notice Sets the duration of the period.
     * @dev Callable by MULTISIG_ROLE.
     */
    function setPeriod(uint16 _period) external requiresAuth {
        emit PeriodUpdated(period, _period);
        period = _period;
    }

    /**
     * @notice Sets the number of calls allowed per period.
     * @dev Callable by MULTISIG_ROLE.
     */
    function setAllowedCallsPerPeriod(uint16 _allowedCallsPerPeriod) external requiresAuth {
        emit AllowedCallsPeriodUpdated(allowedCallsPerPeriod, _allowedCallsPerPeriod);
        allowedCallsPerPeriod = _allowedCallsPerPeriod;
    }

    /**
     * @notice Allows auth to set token approvals to zero.
     * @dev Callable by STRATEGIST_ROLE.
     */
    function revokeTokenApproval(
        bytes32[][] calldata manageProofs,
        address[] calldata decodersAndSanitizers,
        ERC20[] calldata tokens,
        address[] calldata spenders
    ) external requiresAuth {
        uint256 tokensLength = tokens.length;
        address[] memory targets = new address[](tokensLength);
        bytes[] memory targetData = new bytes[](tokensLength);
        uint256[] memory values = new uint256[](tokensLength);

        for (uint256 i; i < tokensLength; ++i) {
            targets[i] = address(tokens[i]);
            targetData[i] = abi.encodeWithSelector(ERC20.approve.selector, spenders[i], 0);
            // values[i] = 0;
        }

        // Make the manage call.
        manager.manageVaultWithMerkleVerification(manageProofs, decodersAndSanitizers, targets, targetData, values);
    }
}
"
    },
    "src/interfaces/IUniswapV3Router.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;

/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
    /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
    /// @dev In the implementation you must pay the pool tokens owed for the swap.
    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
    /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
    /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
    /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
}

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface IUniswapV3Router is IUniswapV3SwapCallback {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);

    struct ExactOutputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);

    struct ExactOutputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
"
    },
    "src/interfaces/DecoderCustomTypes.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

contract DecoderCustomTypes {
    // ========================================= BALANCER =========================================
    struct JoinPoolRequest {
        address[] assets;
        uint256[] maxAmountsIn;
        bytes userData;
        bool fromInternalBalance;
    }

    struct ExitPoolRequest {
        address[] assets;
        uint256[] minAmountsOut;
        bytes userData;
        bool toInternalBalance;
    }

    enum SwapKind {
        GIVEN_IN,
        GIVEN_OUT
    }

    struct SingleSwap {
        bytes32 poolId;
        SwapKind kind;
        address assetIn;
        address assetOut;
        uint256 amount;
        bytes userData;
    }

    struct FundManagement {
        address sender;
        bool fromInternalBalance;
        address recipient;
        bool toInternalBalance;
    }

    // ========================================= UNISWAP V3 =========================================

    struct MintParams {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    struct ExactInputParamsRouter02 {
        bytes path;
        address recipient;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    struct PancakeSwapExactInputParams {
        bytes path;
        address recipient;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    // ========================================= UNISWAP V4 =========================================
    
    struct SwapParams {
        /// Whether to swap token0 for token1 or vice versa
        bool zeroForOne;
        /// The desired input amount if negative (exactIn), or the desired output amount if positive (exactOut)
        int256 amountSpecified;
        /// The sqrt price at which, if reached, the swap will stop executing
        uint160 sqrtPriceLimitX96;
    }

    struct PoolKey {
        /// @notice The lower currency of the pool, sorted numerically
        address currency0;
        /// @notice The higher currency of the pool, sorted numerically
        address currency1;
        /// @notice The pool LP fee, capped at 1_000_000. If the highest bit is 1, the pool has a dynamic fee and must be exactly equal to 0x800000
        uint24 fee;
        /// @notice Ticks that involve positions must be a multiple of tick spacing
        int24 tickSpacing;
        /// @notice The hooks of the pool
        address hooks;
    }

    /// @dev comes from IV4 Router
    struct ExactInputSingleParams {
        PoolKey poolKey;
        bool zeroForOne;
        uint128 amountIn;
        uint128 amountOutMinimum;
        bytes hookData;
    }

     /// @notice Parameters for a single-hop exact-output swap
    struct ExactOutputSingleParams {
        PoolKey poolKey;
        bool zeroForOne;
        uint128 amountOut;
        uint128 amountInMaximum;
        bytes hookData;
    }


    // ========================================= MORPHO BLUE =========================================

    struct MarketParams {
        address loanToken;
        address collateralToken;
        address oracle;
        address irm;
        uint256 lltv;
    }

    // ========================================= 1INCH =========================================

    struct SwapDescription {
        address srcToken;
        address dstToken;
        address payable srcReceiver;
        address payable dstReceiver;
        uint256 amount;
        uint256 minReturnAmount;
        uint256 flags;
    }

    // ========================================= PENDLE =========================================
    struct TokenInput {
        // TOKEN DATA
        address tokenIn;
        uint256 netTokenIn;
        address tokenMintSy;
        // AGGREGATOR DATA
        address pendleSwap;
        SwapData swapData;
    }

    struct TokenOutput {
        // TOKEN DATA
        address tokenOut;
        uint256 minTokenOut;
        address tokenRedeemSy;
        // AGGREGATOR DATA
        address pendleSwap;
        SwapData swapData;
    }

    struct ApproxParams {
        uint256 guessMin;
        uint256 guessMax;
        uint256 guessOffchain; // pass 0 in to skip this variable
        uint256 maxIteration; // every iteration, the diff between guessMin and guessMax will be divided by 2
        uint256 eps; // the max eps between the returned result & the correct result, base 1e18. Normally this number will be set
            // to 1e15 (1e18/1000 = 0.1%)
    }

    struct SwapData {
        SwapType swapType;
        address extRouter;
        bytes extCalldata;
        bool needScale;
    }

    enum SwapType {
        NONE,
        KYBERSWAP,
        ONE_INCH,
        // ETH_WETH not used in Aggregator
        ETH_WETH
    }

    struct LimitOrderData {
        address limitRouter;
        uint256 epsSkipMarket; // only used for swap operations, will be ignored otherwise
        FillOrderParams[] normalFills;
        FillOrderParams[] flashFills;
        bytes optData;
    }

    struct FillOrderParams {
        Order order;
        bytes signature;
        uint256 makingAmount;
    }

    struct Order {
        uint256 salt;
        uint256 expiry;
        uint256 nonce;
        OrderType orderType;
        address token;
        address YT;
        address maker;
        address receiver;
        uint256 makingAmount;
        uint256 lnImpliedRate;
        uint256 failSafeRate;
        bytes permit;
    }

    enum OrderType {
        SY_FOR_PT,
        PT_FOR_SY,
        SY_FOR_YT,
        YT_FOR_SY
    }

    // ========================================= EIGEN LAYER =========================================

    struct QueuedWithdrawalParams {
        // Array of strategies that the QueuedWithdrawal contains
        address[] strategies;
        // Array containing the amount of shares in each Strategy in the `strategies` array
        uint256[] shares;
        // The address of the withdrawer
        address withdrawer;
    }

    struct Withdrawal {
        // The address that originated the Withdrawal
        address staker;
        // The address that the staker was delegated to at the time that the Withdrawal was created
        address delegatedTo;
        // The address that can complete the Withdrawal + will receive funds when completing the withdrawal
        address withdrawer;
        // Nonce used to guarantee that otherwise identical withdrawals have unique hashes
        uint256 nonce;
        // Block number when the Withdrawal was created
        uint32 startBlock;
        // Array of strategies that the Withdrawal contains
        address[] strategies;
        // Array containing the amount of shares in each Strategy in the `strategies` array
        uint256[] shares;
    }

    struct SignatureWithExpiry {
        // the signature itself, formatted as a single bytes object
        bytes signature;
        // the expiration timestamp (UTC) of the signature
        uint256 expiry;
    }

    struct EarnerTreeMerkleLeaf {
        address earner;
        bytes32 earnerTokenRoot;
    }

    struct TokenTreeMerkleLeaf {
        address token;
        uint256 cumulativeEarnings;
    }

    struct RewardsMerkleClaim {
        uint32 rootIndex;
        uint32 earnerIndex;
        bytes earnerTreeProof;
        EarnerTreeMerkleLeaf earnerLeaf;
        uint32[] tokenIndices;
        bytes[] tokenTreeProofs;
        TokenTreeMerkleLeaf[] tokenLeaves;
    }

    // ========================================= CCIP =========================================

    // If extraArgs is empty bytes, the default is 200k gas limit.
    struct EVM2AnyMessage {
        bytes receiver; // abi.encode(receiver address) for dest EVM chains
        bytes data; // Data payload
        EVMTokenAmount[] tokenAmounts; // Token transfers
        address feeToken; // Address of feeToken. address(0) means you will send msg.value.
        bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV2)
    }

    /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
    struct EVMTokenAmount {
        address token; // token address on the local chain.
        uint256 amount; // Amount of tokens.
    }

    struct EVMExtraArgsV1 {
        uint256 gasLimit;
    }

    // ========================================= OFT =========================================

    struct SendParam {
        uint32 dstEid; // Destination endpoint ID.
        bytes32 to; // Recipient address.
        uint256 amountLD; // Amount to send in local decimals.
        uint256 minAmountLD; // Minimum amount to send in local decimals.
        bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
        bytes composeMsg; // The composed message for the send() operation.
        bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
    }

    struct MessagingFee {
        uint256 nativeFee;
        uint256 lzTokenFee;
    }
    // ========================================= L1StandardBridge =========================================

    struct WithdrawalTransaction {
        uint256 nonce;
        address sender;
        address target;
        uint256 value;
        uint256 gasLimit;
        bytes data;
    }

    struct OutputRootProof {
        bytes32 version;
        bytes32 stateRoot;
        bytes32 messagePasserStorageRoot;
        bytes32 latestBlockhash;
    }

    // ========================================= Mantle L1StandardBridge =========================================

    struct MantleWithdrawalTransaction {
        uint256 nonce;
        address sender;
        address target;
        uint256 mntValue;
        uint256 value;
        uint256 gasLimit;
        bytes data;
    }

    // ========================================= Linea Bridge =========================================

    struct ClaimMessageWithProofParams {
        bytes32[] proof;
        uint256 messageNumber;
        uint32 leafIndex;
        address from;
        address to;
        uint256 fee;
        uint256 value;
        address payable feeRecipient;
        bytes32 merkleRoot;
        bytes data;
    }

    // ========================================= Scroll Bridge =========================================

    struct L2MessageProof {
        uint256 batchIndex;
        bytes merkleProof;
    }

    // ========================================= Camelot V3 / Algebra V3  =========================================

    struct CamelotMintParams {
        address token0;
        address token1;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    // ========================================= Algebra V4 =========================================
    
    struct AlgebraMintParams {
        address token0;
        address token1;
        address deployer;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    // ========================================= Velodrome V3 =========================================

    struct VelodromeMintParams {
        address token0;
        address token1;
        int24 tickSpacing;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
        uint160 sqrtPriceX96;
    }

    // ========================================= Karak =========================================

    struct QueuedWithdrawal {
        address staker;
        address delegatedTo;
        uint256 nonce;
        uint256 start;
        WithdrawRequest request;
    }

    struct WithdrawRequest {
        address[] vaults;
        uint256[] shares;
        address withdrawer;
    }

    // ========================================= Term Finance ==================================

    /// @dev TermAuctionOfferSubmission represents an offer submission to offeror an amount of money for a specific interest rate
    struct TermAuctionOfferSubmission {
        /// @dev For an existing offer this is the unique onchain identifier for this offer. For a new offer this is a randomized input that will be used to generate the unique onchain identifier.
        bytes32 id;
        /// @dev The address of the offeror
        address offeror;
        /// @dev Hash of the offered price as a percentage of the initial loaned amount vs amount returned at maturity. This stores 9 decimal places
        bytes32 offerPriceHash;
        /// @dev The maximum amount of purchase tokens that can be lent
        uint256 amount;
        /// @dev The address of the ERC20 purchase token
        address purchaseToken;
    }

    // ========================================= Dolomite Finance ==================================

    enum BalanceCheckFlag {
        Both,
        From,
        To,
        None
    }

    // ========================================= Silo Finance ==================================
    /// @dev There are 2 types of accounting in the system: for non-borrowable collateral deposit called "protected" and
    ///      for borrowable collateral deposit called "collateral". System does
    ///      identical calculations for each type of accounting but it uses different data. To avoid code duplication
    ///      this enum is used to decide which data should be read.
    enum CollateralType {
        Protected, // default
        Collateral
    }

    enum ActionType {
        Deposit,
        Mint,
        Repay,
        RepayShares
    }

    struct Action {
        // what do you want to do?
        uint8 actionType;
        // which Silo are you interacting with?
        address silo;
        // what asset do you want to use?
        address asset;
        // options specific for actions
        bytes options;
    }

    struct AnyAction {
        // how much assets or shares do you want to use?
        uint256 amount;
        // are you using Protected, Collateral
        uint8 assetType;
    }

    // ========================================= LBTC Bridge ==================================
    struct DepositBridgeAction {
        uint256 fromChain;
        bytes32 fromContract;
        uint256 toChain;
        address toContract;
        address recipient;
        uint64 amount;
        uint256 nonce;
    }

    // ========================================= Odos ==================================
    
    struct swapTokenInfo {
        address inputToken;
        uint256 inputAmount;
        address inputReceiver;
        address outputToken;
        uint256 outputQuote;
        uint256 outputMin;
        address outputReceiver;
    }

    struct swapTokenInfoOogaBooga {
        address inputToken;
        uint256 inputAmount;
        address outputToken;
        uint256 outputQuote;
        uint256 outputMin;
        address outputReceiver;
    }
    // ========================================= Level ==================================
    
    /// @dev for reference 
    //enum OrderType {
    //    MINT,
    //    REDEEM
    //}
    
    struct LevelOrder {
        uint8 order_type;
        address benefactor;
        address beneficiary;
        address collateral_asset;
        uint256 collateral_amount;
        uint256 lvlusd_amount;
    }    

    struct LevelOrderV2 {
        address beneficiary;
        address collateral_asset;
        uint256 collateral_amount;
        uint256 min_lvlusd_amount;
    }

    struct Route {
        address[] addresses;
        uint256[] ratios;
    }


    // ========================================= Royco ==================================
    struct APOffer { // RecipeMarketHub
        uint256 offerID;
        bytes32 targetMarketHash;
        address ap;
        address fundingVault;
        uint256 quantity;
        uint256 expiry;
        address[] incentivesRequested;
        uint256[] incentiveAmountsRequested;
    }
    struct APOfferVault { // VaultMarketHub (renamed to avoid collision)
        uint256 offerID;
        address targetVault;
        address ap;
        address fundingVault;
        uint256 expiry;
        address[] incentivesRequested;
        uint256[] incentivesRatesRequested;
    }

    struct Reward {
        uint48 startEpoch;
        uint48 endEpoch;
        address token;
        uint256 rewardRate;
    }

    // ========================================= Permit2 ==================================
    
    struct TokenSpenderPair {
        address token; 
        address spender;
    }

    // ========================================= OnChainQueue ==================================
    
    struct OnChainWithdraw {
        uint96 nonce; // read from state, used to make it impossible for request Ids to be repeated.
        address user; // msg.sender
        address assetOut; // input sanitized
        uint128 amountOfShares; // input transfered in
        uint128 amountOfAssets; // derived from amountOfShares and price
        uint40 creationTime; // time withdraw was made
        uint24 secondsToMaturity; // in contract, from withdrawAsset?
        uint24 secondsToDeadline; // in contract, from withdrawAsset? To get the deadline you take the creationTime add seconds to maturity, add the secondsToDeadline
    }

    // ========================================= Beraborrow ==================================
    
    struct OpenDenVaultParams {
        address denManager;
        address collVault;
        uint256 _maxFeePercentage;
        uint256 _debtAmount;
        uint256 _collAssetToDeposit;
        address _upperHint;
        address _lowerHint;
        uint256 _minSharesMinted;
        uint256 _collIndex;
        bytes _preDeposit;
    }

    struct AdjustDenVaultParams {
        address denManager;
        address collVault;
        uint256 _maxFeePercentage;
        uint256 _collAssetToDeposit;
        uint256 _collWithdrawal;
        uint256 _debtChange;
        bool _isDebtIncrease;
        address _upperHint;
        address _lowerHint;
        bool unwrap;
        uint256 _minSharesMinted;
        uint256 _minAssetsWithdrawn;
        uint256 _collIndex;
        bytes _preDeposit;
    }

     struct RedeemCollateralVaultParams {
        address denManager;
        address collVault;
        uint256 _debtAmount;
        address _firstRedemptionHint;
        address _upperPartialRedemptionHint;
        address _lowerPartialRedemptionHint;
        uint256 _partialRedemptionHintNICR;
        uint256 _maxIterations;
        uint256 _maxFeePercentage;
        uint256 _minSharesWithdrawn;
        uint256 minAssetsWithdrawn;
        uint256 collIndex;
        bool unwrap;
    }

    struct AddCollParams {
        address upperHint;
        address lowerHint;
        uint256 minSharesOut;
        uint256 minCollVaultShares;
    }

    struct ExternalRebalanceParams {
        address swapper;
        bytes payload;
        uint256 minRebalanceOut;
    }
}

"
    },
    "src/interfaces/BalancerVault.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

import {DecoderCustomTypes} from "src/interfaces/DecoderCustomTypes.sol";

interface BalancerVault {
    function flashLoan(address, address[] memory tokens, uint256[] memory amounts, bytes calldata userData) external;
    function swap(
        DecoderCustomTypes.SingleSwap memory singleSwap,
        DecoderCustomTypes.FundManagement memory funds,
        uint256 limit,
        uint256 deadline
    ) external returns (uint256 amountCalculated);
}
"
    },
    "src/base/BoringVault.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";

contract BoringVault is ERC20, Auth, ERC721Holder, ERC1155Holder {
    using Address for address;
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    // ========================================= STATE =========================================

    /**
     * @notice Contract responsbile for implementing `beforeTransfer`.
     */
    BeforeTransferHook public hook;

    //============================== EVENTS ===============================

    event Enter(address indexed from, address indexed asset, uint256 amount, address indexed to, uint256 shares);
    event Exit(address indexed to, address indexed asset, uint256 amount, address indexed from, uint256 shares);

    //============================== CONSTRUCTOR ===============================

    constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
        ERC20(_name, _symbol, _decimals)
        Auth(_owner, Authority(address(0)))
    {}

    //============================== MANAGE ===============================

    /**
     * @notice Allows manager to make an arbitrary function call from this contract.
     * @dev Callable by MANAGER_ROLE.
     */
    function manage(address target, bytes calldata data, uint256 value)
        external
        requiresAuth
        returns (bytes memory result)
    {
        result = target.functionCallWithValue(data, value);
    }

    /**
     * @notice Allows manager to make arbitrary function calls from this contract.
     * @dev Callable by MANAGER_ROLE.
     */
    function manage(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
        external
        requiresAuth
        returns (bytes[] memory results)
    {
        uint256 targetsLength = targets.length;
        results = new bytes[](targetsLength);
        for (uint256 i; i < targetsLength; ++i) {
            results[i] = targets[i].functionCallWithValue(data[i], values[i]);
        }
    }

    //============================== ENTER ===============================

    /**
     * @notice Allows minter to mint shares, in exchange for assets.
     * @dev If assetAmount is zero, no assets are transferred in.
     * @dev Callable by MINTER_ROLE.
     */
    function enter(address from, ERC20 asset, uint256 assetAmount, address to, uint256 shareAmount)
        external
        requiresAuth
    {
        // Transfer assets in
        if (assetAmount > 0) asset.safeTransferFrom(from, address(this), assetAmount);

        // Mint shares.
        _mint(to, shareAmount);

        emit Enter(from, address(asset), assetAmount, to, shareAmount);
    }

    //============================== EXIT ===============================

    /**
     * @notice Allows burner to burn shares, in exchange for assets.
     * @dev If assetAmount is zero, no assets are transferred out.
     * @dev Callable by BURNER_ROLE.
     */
    function exit(address to, ERC20 asset, uint256 assetAmount, address from, uint256 shareAmount)
        external
        requiresAuth
    {
        // Burn shares.
        _burn(from, shareAmount);

        // Transfer assets out.
        if (assetAmount > 0) asset.safeTransfer(to, assetAmount);

        emit Exit(to, address(asset), assetAmount, from, shareAmount);
    }

    //============================== BEFORE TRANSFER HOOK ===============================
    /**
     * @notice Sets the share locker.
     * @notice If set to zero address, the share locker logic is disabled.
     * @dev Callable by OWNER_ROLE.
     */
    function setBeforeTransferHook(address _hook) external requiresAuth {
        hook = BeforeTransferHook(_hook);
    }

    /**
     * @notice Call `beforeTransferHook` passing in `from` `to`, and `msg.sender`.
     */
    function _callBeforeTransfer(address from, address to) internal view {
        if (address(hook) != address(0)) hook.beforeTransfer(from, to, msg.sender);
    }

    function transfer(address to, uint256 amount) public override returns (bool) {
        _callBeforeTransfer(msg.sender, to);
        return super.transfer(to, amount);
    }

    function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
        _callBeforeTransfer(from, to);
        return super.transferFrom(from, to, amount);
    }

    //============================== RECEIVE ===============================

    receive() external payable {}
}
"
    },
    "lib/solmate/src/auth/Auth.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
    event OwnershipTransferred(address indexed user, address indexed newOwner);

    event AuthorityUpdated(address indexed user, Authority indexed newAuthority);

    address public owner;

    Authority public authority;

    constructor(address _owner, Authority _authority) {
        owner = _owner;
        authority = _authority;

        emit OwnershipTransferred(msg.sender, _owner);
        emit AuthorityUpdated(msg.sender, _authority);
    }

    modifier requiresAuth() virtual {
        require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");

        _;
    }

    function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
        Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.

        // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
        // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
        return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
    }

    function setAuthority(Authority newAuthority) public virtual {
        // We check if the caller is the owner first because we want to ensure they can
        // always swap out the authority even if it's reverting or using up a lot of gas.
        require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));

        authority = newAuthority;

        emit AuthorityUpdated(msg.sender, newAuthority);
    }

    function transferOwnership(address newOwner) public virtual requiresAuth {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
    function canCall(
        address user,
        address target,
        bytes4 functionSig
    ) external view returns (bool);
}
"
    },
    "lib/solmate/src/utils/FixedPointMathLib.sol": {
      "content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}
"
    },
    "src/base/Roles/ManagerWithMerkleVerification.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {BoringVault} from "src/base/BoringVault.sol";
import {MerkleProofLib} from "@solmate/utils/MerkleProofLib.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {BalancerVault} from "src/interfaces/BalancerVault.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
import {IPausable} from "src/interfaces/IPausable.sol";
import {DroneLib} from "src/base/Drones/DroneLib.sol";

contract ManagerWithMerkleVerification is Auth, IPausable {
    using FixedPointMathLib for uint256;
    using SafeTransferLib for ERC20;
    using Address for address;

    // ========================================= STATE =========================================

    /**
     * @notice A merkle tree root that restricts what data can be passed to the BoringVault.
     * @dev Maps a strategist address to their specific merkle root.
     * @dev Each leaf is composed of the keccak256 hash of abi.encodePacked {decodersAndSanitizer, target, valueIsNonZero, selector, argumentAddress_0, ...., argumentAddress_N}
     *      Where:
     *             - decodersAndSanitizer is the addres to call to extract packed address arguments from the calldata
     *             - target is the address to make the call to
     *             - valueIsNonZero is a bool indicating whether or not the value is non-zero
     *             - selector is the function selector on target
     *             - argumentAddress is each allowed address argument in that call
     */
    mapping(address => bytes32) public manageRoot;

    /**
     * @notice Bool indicating whether or not this contract is actively performing a flash loan.
     * @dev Used to block flash loans that are initiated outside a manage call.
     */
    bool internal performingFlashLoan;

    /**
     * @notice keccak256 hash of flash loan data.
     */
    bytes32 internal flashLoanIntentHash = bytes32(0);

    /**
     * @notice Used to pause calls to `manageVaultWithMerkleVerification`.
     */
    bool public isPaused;

    //============================== ERRORS ===============================

    error ManagerWithMerkleVerification__InvalidManageProofLength();
    error ManagerWithMerkleVerification__InvalidTargetDataLength();
    error ManagerWithMerkleVerification__InvalidValuesLength();
    error ManagerWithMerkleVerification__InvalidDecodersAndSanitizersLength();
    error ManagerWithMerkleVerification__FlashLoanNotExecuted();
    error ManagerWithMerkleVerification__FlashLoanNotInProgress();
    error ManagerWithMerkleVerification__BadFlashLoanIntentHash();
    error ManagerWithMerkleVerification__FailedToVerifyManageProof(address target, bytes targetData, uint256 value);
    error ManagerWithMerkleVerification__Paused();
    error ManagerWithMerkleVerification__OnlyCallableByBoringVault();
    error ManagerWithMerkleVerification__OnlyCallableByBalancerVault();
    error ManagerWithMerkleVerification__TotalSupplyMustRemainConstantDuringPlatform();

    //============================== EVENTS ===============================

    event ManageRootUpdated(address indexed strategist, bytes32 oldRoot, bytes32 newRoot);
    event BoringVaultManaged(uint256 callsMade);
    event Paused();
    event Unpaused();

    //============================== IMMUTABLES ===============================

    /**
     * @notice The BoringVault this contract can manage.
     */
    BoringVault public immutable vault;

    /**
     * @notice The balancer vault this contract can use for flash loans.
     */
    BalancerVault public immutable balancerVault;

    constructor(address _owner, address _vault, address _balancerVault) Auth(_owner, Authority(address(0))) {
        vault = BoringVault(payable(_vault));
        balancerVault = BalancerVault(_balancerVault);
    }

    // ========================================= ADMIN FUNCTIONS =========================================

    /**
     * @notice Sets the manageRoot.
     * @dev Callable by OWNER_ROLE.
     */
    function setManageRoot(address strategist, bytes32 _manageRoot) external requiresAuth {
        bytes32 oldRoot = manageRoot[strategist];
        manageRoot[strategist] = _manageRoot;
        emit ManageRootUpdated(strategist, oldRoot, _manageRoot);
    }

    /**
     * @notice Pause this contract, which prevents future calls to `manageVaultWithMerkleVerification`.
     * @dev Callable by MULTISIG_ROLE.
     */
    function pause() external requiresAuth {
        isPaused = true;
        emit Paused();
    }

    /**
     * @notice Unpause this contract, which allows future calls to `manageVaultWithMerkleVerification`.
     * @dev Callable by MULTISIG_ROLE.
     */
    function unpause() external requiresAuth {
        isPaused = false;
        emit Unpaused();
    }

    // ========================================= STRATEGIST F

Tags:
ERC165, Multisig, Swap, Liquidity, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xac78c8fd0cf09b0f39d98dc3f0fb4f35360e6ba7|verified:true|block:23631310|tx:0xf290f3fe4907b4602e3a83a4c3507c1e7265719553041d97a6ea45b5960aa5c3|first_check:1761237275

Submitted on: 2025-10-23 18:34:38

Comments

Log in to comment.

No comments yet.