Mux

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/Mux.sol": {
      "content": "// SPDX-License-Identifier: LicenseRef-CICADA-Proprietary
// SPDX-FileCopyrightText: (c) 2024 Cicada Software, CICADA DMCC. All rights reserved.
pragma solidity ^0.8.29;

import {IUniswapV2Router02} from "@uniswap-v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import {IQuoterV2} from "@uniswap-v3-periphery/contracts/interfaces/IQuoterV2.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {Enums, Exact} from "./lib/Common.sol";
import {UniV2} from "./lib/UniV2.sol";
import {UniV2Legacy} from "./mux/UniV2Legacy.sol";
import {UniV3QuoterV2} from "./mux/UniV3QuoterV2.sol";
import {BalancerV2Quoter, BalancerV2SwapParams} from "./mux/BalancerV2Quoter.sol";
import {CurveQuoter, SwapParams} from "./mux/CurveQuoter.sol";

/**
 * @title Mux Contract
 * @notice A multicall-like contract that multiplexes several DEX calls and provides aggregated data in a single response.
 * @dev Mux interacts with Uniswap V2 and V3 to fetch multiple price points.
 *      It performs basic filtering and returns the relevant information needed by DeGate in a single call.
 *      This helps optimize performance by reducing the need for multiple individual Uniswap calls.
 */
contract Mux is UniV2Legacy, UniV3QuoterV2, BalancerV2Quoter, CurveQuoter {
    string public constant version = "v0.1.6";

    /**
     * @notice Struct that holds the result of Uniswap V2 prices for the given route prepared for DeGate.
     * @dev Contains buy/sell amounts per each level, reserves, and other metadata in a single response.
     * @param buyAmounts An array of valid buy amounts (base and quote tokens).
     * @param sellAmounts An array of valid sell amounts (base and quote tokens).
     * @param reservesInPair The current reserves of the token pair in the liquidity pool.
     * @param amountsHash A hash of the calculated buy/sell amounts and reserves.
     * @param baseTokenDecimals The number of decimals for the base token.
     * @param quoteTokenDecimals The number of decimals for the quote token.
     * @param timestamp The block timestamp when the data was calculated.
     */
    struct MuxResult {
        string version;
        SwapAmounts swapAmounts;
        Balances balances;
        Block blockData;
    }

    /**
     * @notice Packs base and quote token amounts together.
     * @dev Used to represent paired values of base and quote tokens, since they always appear together in operations
     * like reserves, buy/sell calculations, or price comparisons.
     */
    struct Amounts {
        uint256 baseTokens;
        uint256 quoteTokens;
    }

    struct Balances {
        Amounts vaultContractBtQtBalances;
        uint256[] weiBalances;
        bytes32 balancesHash;
    }

    struct Block {
        uint256 timestamp;
        uint256 height;
        uint256 chainId;
        uint256 baseFee;
        uint256 gasLimit;
    }

    struct SwapAmounts {
        Amounts[] buyAmounts;
        Amounts[] sellAmounts;
        bytes32 amountsHash;
    }

    // @notice Parameters for the OrderBook representation of AMM
    struct OrderBookParams {
        /// @notice Maximum number of steps for buy/sell calculations.
        uint256 maxLength;
        /// @notice Initial quote token amount for calculations.
        uint256 startQuoteSize;
        /// @notice Increment rate in basis points per each step
        uint256 quoteIncrementBps;
    }

    /**
     * @dev Initializes the UniV2Mux with the provided Uniswap V2 router.
     * @param _router Address of the Uniswap V2 router contract used for asset swaps.
     */
    constructor(
        IUniswapV2Router02 _router,
        IQuoterV2 _quoter,
        address _balancerV2Vault,
        address _balancerQueries,
        address _curveRouter
    )
        UniV2Legacy(_router)
        UniV3QuoterV2(_quoter)
        BalancerV2Quoter(_balancerV2Vault, _balancerQueries)
        CurveQuoter(_curveRouter)
    {}

    /**
     * @notice Multiplexes and returns various metrics used in DeGate: price and orderbook calculations, balances, etc.
     * @param vaults The array of vaults. First goes contract vault, and others are EOAs.
     * @param baseToken The address of the base token.
     * @param quoteToken The address of the quote token.
     * @param flavor The protocol and algorithm to use for calculations.
     * @param flavorParams The array of flavor parameters, depends on implementation.
     * @param orderBookParams The parameters for the OrderBook representation of AMM.
     * @return A `MuxResult` struct with all the information needed by DeGate.
     */
    function call(
        address[] calldata vaults,
        address baseToken,
        address quoteToken,
        Enums.Flavor flavor,
        bytes calldata flavorParams,
        OrderBookParams calldata orderBookParams
    ) public returns (MuxResult memory) {
        // vaults[0] is always the contract vault
        Amounts memory vaultContractBtQtBalances = Amounts({
            baseTokens: IERC20(baseToken).balanceOf(vaults[0]),
            quoteTokens: IERC20(quoteToken).balanceOf(vaults[0])
        });

        uint256[] memory weiBalances = new uint256[](vaults.length);
        for (uint256 i = 0; i < vaults.length; i++) {
            weiBalances[i] = vaults[i].balance;
        }

        Amounts[] memory buyAmounts = new Amounts[](orderBookParams.maxLength);
        uint256 quoteSize = orderBookParams.startQuoteSize;

        Amounts[] memory sellAmounts = new Amounts[](orderBookParams.maxLength);
        for (uint256 i = 0; i < orderBookParams.maxLength; i++) {
            if (flavor == Enums.Flavor.UniV2Legacy) {
                UniV2.UniV2PathVia memory params = abi.decode(flavorParams, (UniV2.UniV2PathVia));
                (sellAmounts[i].baseTokens, sellAmounts[i].quoteTokens) =
                    UniV2Legacy.getQuoteAmounts(Enums.Direction.BaseToQuote, baseToken, quoteToken, quoteSize, params);
                (buyAmounts[i].baseTokens, buyAmounts[i].quoteTokens) =
                    UniV2Legacy.getQuoteAmounts(Enums.Direction.QuoteToBase, baseToken, quoteToken, quoteSize, params);
            } else if (flavor == Enums.Flavor.UniV3QuoterV2) {
                UniV3QuoterV2Params memory params = abi.decode(flavorParams, (UniV3QuoterV2Params));
                (sellAmounts[i].baseTokens, sellAmounts[i].quoteTokens) =
                    UniV3QuoterV2.getQuoteAmounts(Enums.Direction.BaseToQuote, baseToken, quoteToken, quoteSize, params);
                (buyAmounts[i].baseTokens, buyAmounts[i].quoteTokens) =
                    UniV3QuoterV2.getQuoteAmounts(Enums.Direction.QuoteToBase, baseToken, quoteToken, quoteSize, params);
            } else if (flavor == Enums.Flavor.BalancerV2) {
                // Dropped because of STACK_TOO_DEEP error
                // BalancerV2SwapParams[] memory params = abi.decode(flavorParams, (BalancerV2SwapParams[]));

                // Everything is inverted here, because of the way BalancerV2Quoter.getQuoteAmounts is implemented (copy of strategy's quote code)
                (sellAmounts[i].quoteTokens, sellAmounts[i].baseTokens) = BalancerV2Quoter.getQuoteAmounts(
                    Enums.Direction.QuoteToBase,
                    quoteToken,
                    baseToken,
                    quoteSize,
                    abi.decode(flavorParams, (BalancerV2SwapParams[]))
                );

                (buyAmounts[i].quoteTokens, buyAmounts[i].baseTokens) = BalancerV2Quoter.getQuoteAmounts(
                    Enums.Direction.BaseToQuote,
                    quoteToken,
                    baseToken,
                    quoteSize,
                    abi.decode(flavorParams, (BalancerV2SwapParams[]))
                );
            } else if (flavor == Enums.Flavor.Curve) {
                (buyAmounts[i].quoteTokens, buyAmounts[i].baseTokens) =
                    quote(Exact.In, quoteSize, reverseCurveSwapParams(abi.decode(flavorParams, (SwapParams[]))));

                (sellAmounts[i].baseTokens, sellAmounts[i].quoteTokens) =
                    quote(Exact.Out, quoteSize, abi.decode(flavorParams, (SwapParams[])));
            }
            quoteSize = quoteSize * (10_000 + orderBookParams.quoteIncrementBps) / 10_000;
        }

        buyAmounts = extractValidAmounts(buyAmounts);
        sellAmounts = extractValidAmounts(sellAmounts);

        return MuxResult({
            version: version,
            swapAmounts: SwapAmounts({
                buyAmounts: buyAmounts,
                sellAmounts: sellAmounts,
                amountsHash: keccak256(abi.encode(buyAmounts, sellAmounts))
            }),
            balances: Balances({
                vaultContractBtQtBalances: vaultContractBtQtBalances,
                weiBalances: weiBalances,
                balancesHash: keccak256(abi.encode(vaultContractBtQtBalances, weiBalances))
            }),
            blockData: Block({
                timestamp: block.timestamp,
                height: block.number,
                chainId: block.chainid,
                baseFee: block.basefee,
                gasLimit: block.gaslimit
            })
        });
    }

    /**
     * @notice Calculates the price difference in basis points (BPS) between two `Amounts`.
     *         Used to filter out `Amounts` with a significant price deviation.
     * @param amountA The first `Amounts` struct (base and quote tokens).
     * @param amountB The second `Amounts` struct (base and quote tokens).
     * @return priceDifferenceBps The price difference in BPS. Returns max uint256 if either baseTokens is 0.
     */
    function calculatePriceDifferenceBps(Amounts memory amountA, Amounts memory amountB)
        public
        pure
        returns (uint256 priceDifferenceBps)
    {
        uint256 BPS = 10_000;
        uint256 PRECISION_MULTIPLIER = 1e18;
        if (amountA.baseTokens == 0 || amountB.baseTokens == 0) {
            priceDifferenceBps = type(uint256).max;
        } else {
            uint256 amountAPriceRatio = amountA.quoteTokens * PRECISION_MULTIPLIER / amountA.baseTokens;
            uint256 amountBPriceRatio = amountB.quoteTokens * PRECISION_MULTIPLIER / amountB.baseTokens;
            uint256 priceDifference;
            if (amountAPriceRatio > amountBPriceRatio) {
                priceDifference = amountAPriceRatio - amountBPriceRatio;
            } else {
                priceDifference = amountBPriceRatio - amountAPriceRatio;
            }
            priceDifferenceBps = priceDifference * BPS / amountAPriceRatio;
        }
    }

    /**
     * @notice Extracts and returns the valid `Amounts` from the input array, filtering out entries with zero base or quote tokens.
     * @param amounts An array of `Amounts` structs to be filtered.
     * @return filteredAmounts A new array containing only the valid `Amounts` with non-zero base and quote tokens.
     */
    function extractValidAmounts(Amounts[] memory amounts) internal pure returns (Amounts[] memory) {
        uint256 nonZeroCount = 0;
        for (uint256 srcIndex = 0; srcIndex < amounts.length; srcIndex++) {
            if (isAmountNonEmpty(amounts[srcIndex])) {
                nonZeroCount++;
            }
        }

        // Create a new array to hold the non-zero entries
        Amounts[] memory filteredAmounts = new Amounts[](nonZeroCount);
        uint256 dstIndex = 0;
        for (uint256 srcIndex = 0; srcIndex < amounts.length; srcIndex++) {
            if (isAmountNonEmpty(amounts[srcIndex])) {
                filteredAmounts[dstIndex] = amounts[srcIndex];
                dstIndex++;
            }
        }

        return filteredAmounts;
    }

    /**
     * @notice Checks if both baseTokens and quoteTokens in an `Amounts` struct are non-zero.
     * @param amount The `Amounts` struct containing base and quote tokens.
     * @return bool True if both baseTokens and quoteTokens are non-zero, false otherwise.
     */
    function isAmountNonEmpty(Amounts memory amount) internal pure returns (bool) {
        if (amount.baseTokens != 0 && amount.quoteTokens != 0) {
            return true;
        }
        return false;
    }

    /// @notice Checks if the direction is a buy
    /// @param direction the direction of the swap
    /// @return true if the direction is a buy, false otherwise
    function _isBuy(Enums.Direction direction)
        internal
        pure
        virtual
        override(BalancerV2Quoter, CurveQuoter)
        returns (bool)
    {
        return direction == Enums.Direction.QuoteToBase;
    }

    /**
     * @notice Implemented to make path always follow BT -> QT direction
     * @dev Such implementation would allow using same path for both BUY and SELL operations
     * @dev and both EXACT Base Token and Exact Quote Token operations
     * @dev Right now this implementation discontinued to implement same logic on client side which is easier
     */
    function reverseCurveSwapParams(SwapParams[] memory swapParams) internal pure returns (SwapParams[] memory) {
        SwapParams[] memory reversed = new SwapParams[](swapParams.length);

        for (uint256 i = 0; i < swapParams.length; i++) {
            reversed[i] = swapParams[swapParams.length - i - 1];
            (reversed[i].tokenInIndex, reversed[i].tokenOutIndex) =
                (reversed[i].tokenOutIndex, reversed[i].tokenInIndex);
        }

        return reversed;
    }
}
"
    },
    "dependencies/@uniswap-v2-periphery-1.1.0-beta.0/contracts/interfaces/IUniswapV2Router02.sol": {
      "content": "pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}
"
    },
    "dependencies/@uniswap-v3-periphery-1.4.4/contracts/interfaces/IQuoterV2.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
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 IQuoterV2 {
    /// @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 the initialized ticks that the swap crossed for each pool in the path
    /// @return gasEstimate The estimate of the gas that the swap consumes
    function quoteExactInput(bytes memory path, uint256 amountIn)
        external
        returns (
            uint256 amountOut,
            uint160[] memory sqrtPriceX96AfterList,
            uint32[] memory initializedTicksCrossedList,
            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
    /// tokenOut The token being swapped out
    /// fee The fee of the token pool to consider for the pair
    /// amountIn The desired input amount
    /// 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 that the swap crossed
    /// @return gasEstimate The estimate of the gas that the swap consumes
    function quoteExactInputSingle(QuoteExactInputSingleParams memory params)
        external
        returns (
            uint256 amountOut,
            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
    /// @return gasEstimate The estimate of the gas that the swap consumes
    function quoteExactOutput(bytes memory path, uint256 amountOut)
        external
        returns (
            uint256 amountIn,
            uint160[] memory sqrtPriceX96AfterList,
            uint32[] memory initializedTicksCrossedList,
            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
    /// fee The fee of the token pool to consider for the pair
    /// amountOut The desired output amount
    /// 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 that the swap crossed
    /// @return gasEstimate The estimate of the gas that the swap consumes
    function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params)
        external
        returns (
            uint256 amountIn,
            uint160 sqrtPriceX96After,
            uint32 initializedTicksCrossed,
            uint256 gasEstimate
        );
}
"
    },
    "dependencies/@openzeppelin-contracts-5.2.0/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
    },
    "src/lib/Common.sol": {
      "content": "// SPDX-License-Identifier: LicenseRef-CICADA-Proprietary
// SPDX-FileCopyrightText: (c) 2024 Cicada Software, CICADA DMCC. All rights reserved.
pragma solidity ^0.8.29;

enum Exact {
    In,
    Out
}

library Enums {
    enum Direction {
        QuoteToBase,
        BaseToQuote
    }

    enum Flavor {
        UniV2Legacy,
        UniV3QuoterV2,
        BalancerV2,
        Curve
    }
}
"
    },
    "src/lib/UniV2.sol": {
      "content": "// SPDX-License-Identifier: LicenseRef-CICADA-Proprietary
// SPDX-FileCopyrightText: (c) 2024 Cicada Software, CICADA DMCC. All rights reserved.
pragma solidity ^0.8.29;

import {Enums} from "./Common.sol";

/**
 * @title UniV2
 * @notice Library for handling Uniswap V2 specifics
 * @dev Provides a standard way to handle Uniswap V2 paths
 */
library UniV2 {
    /// @notice Represents a TRANSIT path (without in and out tokens) used internally in Cicada software
    struct UniV2PathVia {
        address[] tokens;
    }

    /**
     * @notice Converts a transit path (internal form of route used inside Cicada Perimeter)
     *   to a full path by adding in and out tokens
     * @param self Transit path struct containing intermediate tokens
     * @param direction The direction of the swap
     * @param baseToken Address of the base token
     * @param quoteToken Address of the quote token
     * @return Uniswap V2-compatible path
     */
    function toPathFull(UniV2PathVia memory self, Enums.Direction direction, address baseToken, address quoteToken)
        internal
        pure
        returns (address[] memory)
    {
        address[] memory path = new address[](self.tokens.length + 2);

        if (direction == Enums.Direction.BaseToQuote) {
            path[0] = baseToken;
            for (uint256 i = 0; i < self.tokens.length; i++) {
                path[i + 1] = self.tokens[i];
            }
            path[path.length - 1] = quoteToken;
        } else {
            path[0] = quoteToken;
            for (uint256 i = 0; i < self.tokens.length; i++) {
                path[i + 1] = self.tokens[self.tokens.length - 1 - i];
            }
            path[path.length - 1] = baseToken;
        }

        return path;
    }
}
"
    },
    "src/mux/UniV2Legacy.sol": {
      "content": "// SPDX-License-Identifier: LicenseRef-CICADA-Proprietary
// SPDX-FileCopyrightText: (c) 2024 Cicada Software, CICADA DMCC. All rights reserved.
pragma solidity ^0.8.29;

import {IUniswapV2Router02} from "@uniswap-v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import {Enums} from "../lib/Common.sol";
import {UniV2} from "../lib/UniV2.sol";

/**
 * @title UniV2Legacy Flavor Contract
 * @notice Abstract contract for interacting with Uniswap V2.
 * @dev Provides functions for getting quote amounts from UniswapV2-alike dexes used in Mux Orderbook algorithm.
 */
abstract contract UniV2Legacy {
    using UniV2 for UniV2.UniV2PathVia;

    IUniswapV2Router02 public immutable router;

    /**
     * @dev Initializes the UniV2Legacy with the provided Uniswap V2 router.
     * @param _router Address of the Uniswap V2 router contract used for asset swaps.
     */
    constructor(IUniswapV2Router02 _router) {
        require(_router.factory() != address(0) && _router.WETH() != address(0), "UniV2Legacy: Invalid router");
        router = _router;
    }

    /**
     * @notice Gets the quote amounts for a given swap direction, base token, quote token, and quote size.
     * @param direction The direction of the swap (Buy or Sell).
     * @param baseToken The address of the base token.
     * @param quoteToken The address of the quote token.
     * @param quoteSize The dial size nominated in guote tokens.
     * @param params The flavor parameters (contains the intermediate path)
     * @return baseTokens The amount of base tokens.
     * @return quoteTokens The amount of quote tokens.
     */
    function getQuoteAmounts(
        Enums.Direction direction,
        address baseToken,
        address quoteToken,
        uint256 quoteSize,
        UniV2.UniV2PathVia memory params
    ) public view returns (uint256 baseTokens, uint256 quoteTokens) {
        address[] memory path;
        uint256[] memory univ2CallResult;
        if (direction == Enums.Direction.QuoteToBase) {
            path = params.toPathFull(Enums.Direction.QuoteToBase, baseToken, quoteToken);
            univ2CallResult = router.getAmountsOut(quoteSize, path);
            quoteTokens = univ2CallResult[0];
            baseTokens = univ2CallResult[univ2CallResult.length - 1];
        } else if (direction == Enums.Direction.BaseToQuote) {
            path = params.toPathFull(Enums.Direction.BaseToQuote, baseToken, quoteToken);
            univ2CallResult = router.getAmountsIn(quoteSize, path);
            baseTokens = univ2CallResult[0];
            quoteTokens = univ2CallResult[univ2CallResult.length - 1];
        } else {
            revert("UniV2Legacy: WRONG_DIRECTION");
        }
    }
}
"
    },
    "src/mux/UniV3QuoterV2.sol": {
      "content": "// SPDX-License-Identifier: LicenseRef-CICADA-Proprietary
// SPDX-FileCopyrightText: (c) 2024 Cicada Software, CICADA DMCC. All rights reserved.
pragma solidity ^0.8.29;

import {IQuoterV2} from "@uniswap-v3-periphery/contracts/interfaces/IQuoterV2.sol";
import {Enums} from "../lib/Common.sol";
import {UniV3} from "../lib/UniV3.sol";

/**
 * @title UniV3QuoterV2 Flavor Contract
 * @notice Abstract contract for interacting with Uniswap V3.
 * @dev Provides functions for getting quote amounts from UniswapV3-alike dexes used in Mux Orderbook algorithm.
 */
abstract contract UniV3QuoterV2 {
    IQuoterV2 public immutable quoter;

    using UniV3 for UniV3.UniV3PathFull;
    using UniV3 for UniV3.UniV3PathVia;

    struct UniV3QuoterV2Params {
        UniV3.UniV3PathVia pathVia;
    }

    /**
     * @dev Initializes the UniV3QuoterV2 with the provided Uniswap V3 Quoter
     * @param _quoter Address of the Uniswap V2 router contract used for asset swaps.
     */
    constructor(IQuoterV2 _quoter) {
        bytes memory data = abi.encodeWithSelector(bytes4(keccak256("WETH9()")));
        (bool success, bytes memory returnData) = address(_quoter).call(data);
        require(success, "UniV3QuoterV2: Incorrect Quoter");
        address resultAddress = abi.decode(returnData, (address));
        require(resultAddress != address(0), "UniV3QuoterV2: Misconfigured Quoter");
        quoter = _quoter;
    }

    function getQuoteAmounts(
        Enums.Direction direction,
        address baseToken,
        address quoteToken,
        uint256 quoteSize,
        UniV3QuoterV2Params memory params
    ) public returns (uint256 baseTokens, uint256 quoteTokens) {
        UniV3.UniV3PathVia memory pathVia = params.pathVia;
        bytes memory pathFull = pathVia.toPathFull(Enums.Direction.QuoteToBase, baseToken, quoteToken).encode();
        quoteTokens = quoteSize;
        if (direction == Enums.Direction.QuoteToBase) {
            (baseTokens,,,) = quoter.quoteExactInput(pathFull, quoteTokens);
        } else if (direction == Enums.Direction.BaseToQuote) {
            (baseTokens,,,) = quoter.quoteExactOutput(pathFull, quoteTokens);
        } else {
            revert("UniV3QuoterV2: WRONG_DIRECTION");
        }
    }
}
"
    },
    "src/mux/BalancerV2Quoter.sol": {
      "content": "// SPDX-License-Identifier: LicenseRef-CICADA-Proprietary
// SPDX-FileCopyrightText: (c) 2024 Cicada Software, CICADA DMCC. All rights reserved.
pragma solidity ^0.8.29;

import {IBalancerQueries} from "@balancer-v2/interfaces/contracts/standalone-utils/IBalancerQueries.sol";
import {IVault, IAsset, IERC20 as IERC20Balancer} from "@balancer-v2/interfaces/contracts/vault/IVault.sol";
import {IBasePool} from "@balancer-v2/interfaces/contracts/vault/IBasePool.sol";

import {Enums} from "../lib/Common.sol";
import {SwapParams as BalancerV2SwapParams} from "../interfaces/IBalancerV2Strategy.sol";

/**
 * @title BalancerV2 Flavor Contract
 * @notice Abstract contract for interacting with Balancer V2.
 * @dev Provides functions for getting quote amounts from Balancer V2.
 */
abstract contract BalancerV2Quoter {
    address public immutable BALANCER_QUERIES;

    address public immutable BALANCER_VAULT;

    error PathIsEmpty();
    error InvalidBaseTokenAmount();
    error OutputTokenIsNotQuoteToken();

    error ZeroAddress();

    /**
     * @notice Initializes the Strategy with the provided Balancer V2 Vault.
     * @param _balancerV2Vault Address of the Balancer V2 Vault contract.
     * @param _balancerQueries Address of the Balancer Queries contract.
     */
    constructor(address _balancerV2Vault, address _balancerQueries) {
        if (_balancerV2Vault == address(0)) revert ZeroAddress();
        if (_balancerQueries == address(0)) revert ZeroAddress();

        BALANCER_VAULT = _balancerV2Vault;
        BALANCER_QUERIES = _balancerQueries;
    }

    /**
     * @notice Gets the quote amounts for the Balancer V2 swap using quote token as base
     * @notice Used to retrieve liquidity data in orderbook-like format
     * @dev Uses implementations from BalancerV2Strategy, but with some twists
     * @param direction the direction of the swap
     * @param exactToken the address of the token to be received/spent EXACTLY via EXACT_IN/OUT
     * @param otherToken the address of the other token to be received/spent without concreteness
     * @param baseTokenAmount the amount of the base token to swap
     * @param swapParams always ExactToken -> OtherToken path with BalancerV2 hop params (pool address, token out index)
     * @return exactTokens the amount of the exact token to spend/receive
     * @return otherTokens the amount of the other token to spend/receive
     */
    function getQuoteAmounts(
        Enums.Direction direction,
        address exactToken,
        address otherToken,
        uint256 baseTokenAmount,
        BalancerV2SwapParams[] memory swapParams
    ) public returns (uint256 exactTokens, uint256 otherTokens) {
        (
            IVault.BatchSwapStep[] memory swaps,
            IAsset[] memory assets,
            IVault.SwapKind kind,
            IVault.FundManagement memory fundManagement
        ) = _buildBatchSwapArgs(exactToken, otherToken, baseTokenAmount, direction, address(this), swapParams);

        int256[] memory deltas = IBalancerQueries(BALANCER_QUERIES).queryBatchSwap(kind, swaps, assets, fundManagement);

        // TODO: double check in tests before MR
        exactTokens = uint256(_isBuy(direction) ? -deltas[0] : deltas[0]);
        otherTokens = uint256(_isBuy(direction) ? deltas[deltas.length - 1] : -deltas[deltas.length - 1]);
    }

    /**
     * @notice Builds the batch swap arguments for the Balancer V2 vault
     * @param exactToken the address of the token to be received/spent EXACTLY via EXACT_IN/OUT
     * @param otherToken the address of the other token to be received/spent without concreteness
     * @param amount the amount of the base token to swap
     * @param direction the direction of the swap
     * @param swapParams the array of pool addresses
     * @return swaps the array of batch swap steps
     * @return assets the array of assets
     * @return kind the kind of the swap
     * @return fundManagement the fund management
     */
    function _buildBatchSwapArgs(
        address exactToken,
        address otherToken,
        uint256 amount,
        Enums.Direction direction,
        address recipient,
        BalancerV2SwapParams[] memory swapParams
    )
        internal
        view
        returns (
            IVault.BatchSwapStep[] memory swaps,
            IAsset[] memory assets,
            IVault.SwapKind kind,
            IVault.FundManagement memory fundManagement
        )
    {
        if (swapParams.length == 0) revert PathIsEmpty();
        if (amount == 0) revert InvalidBaseTokenAmount();

        swaps = new IVault.BatchSwapStep[](swapParams.length);
        // Constructed from swapParams using tokenInIndex and tokenOutIndex
        assets = new IAsset[](swapParams.length + 1);

        bool isBuy = _isBuy(direction);

        address currentToken = exactToken;

        for (uint256 i = 0; i < swapParams.length; i++) {
            IBasePool pool = IBasePool(swapParams[i].pool);
            bytes32 poolId = pool.getPoolId();

            (IERC20Balancer[] memory tokens,,) = IVault(BALANCER_VAULT).getPoolTokens(poolId);

            assets[i] = IAsset(currentToken);

            uint256 assetInIndex = isBuy ? i + 1 : i;
            uint256 assetOutIndex = isBuy ? i : i + 1;

            uint256 poolAssetOutIndex = swapParams[i].tokenOutIndex;

            currentToken = address(tokens[poolAssetOutIndex]);

            if (i == swapParams.length - 1 && currentToken != otherToken) {
                revert OutputTokenIsNotQuoteToken();
            }

            assets[i + 1] = IAsset(currentToken);

            swaps[i] = IVault.BatchSwapStep({
                poolId: poolId,
                assetInIndex: assetInIndex, // Index of asset in assets[]
                assetOutIndex: assetOutIndex, // Index of asset in assets[]
                // Amount is not known on execution, so it's taken from first amountIn
                amount: i == 0 ? amount : 0,
                userData: new bytes(0)
            });
        }

        kind = isBuy ? IVault.SwapKind.GIVEN_OUT : IVault.SwapKind.GIVEN_IN;

        fundManagement = IVault.FundManagement({
            sender: address(this),
            recipient: payable(recipient),
            fromInternalBalance: false,
            toInternalBalance: false
        });

        return (swaps, assets, kind, fundManagement);
    }

    /**
     * @notice Checks if the direction is a buy
     * @param direction the direction of the swap
     * @return true if the direction is a buy, false otherwise
     */
    function _isBuy(Enums.Direction direction) internal pure virtual returns (bool) {
        return direction == Enums.Direction.QuoteToBase;
    }
}
"
    },
    "src/mux/CurveQuoter.sol": {
      "content": "// SPDX-License-Identifier: LicenseRef-CICADA-Proprietary
// SPDX-FileCopyrightText: (c) 2024 Cicada Software, CICADA DMCC. All rights reserved.
pragma solidity ^0.8.28;

/* ====== INTERFACES IMPORTS ====== */

import {ICurveQuoter, SwapParams, SwapType, PoolType} from "../interfaces/ICurveQuoter.sol";
import {ICurveRouter} from "../interfaces/curve/IRouter.sol";
import {IPoolCommon} from "../interfaces/curve/pools/IPoolCommon.sol";
import {IStableSwapPool} from "../interfaces/curve/pools/IStableSwapPool.sol";

/* ====== CONTRACTS IMPORTS ====== */

import {Vault} from "../Vault.sol";
import {Enums, Exact} from "../lib/Common.sol";

/**
 * @title CurveStrategy
 * @notice This contract is a router for Curve, that creates unified interface
 * for Vault contracts to interact with Curve protocol
 */
abstract contract CurveQuoter is ICurveQuoter {
    /* ======== STATE ======== */

    /// @inheritdoc ICurveQuoter
    address public immutable ROUTER;

    /* ======== ERRORS ======== */

    /// @notice Revert for tokenIn is not quote token for BUY direction
    error TokenInIsNotQuoteTokenForBuyDirection();
    /// @notice Revert for tokenIn is not base token for SELL direction
    error TokenInIsNotBaseTokenForSellDirection();
    /// @notice Revert for tokenOut is not quote token for SELL direction
    error TokenOutIsNotQuoteTokenForSellDirection();
    /// @notice Revert for tokenOut is not base token for BUY direction
    error TokenOutIsNotBaseTokenForBuyDirection();
    /// @notice Revert for interrupted path, f.e. token out of some hop is not token in for next hop
    error InterruptedPath();

    /// @notice Revert for 0 amounts
    error ZeroAmount();

    /// @notice CurveRouterNG doesn't support more than 5 pools in the route
    error InvalidSwapParamsLength();

    /* ======== CONSTRUCTOR AND INIT ======== */

    /**
     * @notice Initializes the Strategy with the provided Curve Router.
     * @param _router Address of the Curve Router NG.
     */
    constructor(address _router) {
        require(_router != address(0), "CurveStrategy: ZERO_ROUTER");

        ROUTER = _router;
    }

    /* ======== EXTERNAL/PUBLIC ======== */

    /// @inheritdoc ICurveQuoter
    function quote(Exact exactType, uint256 amount, SwapParams[] memory swapParams)
        public
        view
        returns (uint256 amountIn, uint256 amountOut)
    {
        if (amount == 0) revert ZeroAmount();

        if (swapParams.length == 0 || swapParams.length > 5) revert InvalidSwapParamsLength();

        (address[11] memory route, address[5] memory pools) = _buildRoute(swapParams);

        uint256[5][5] memory _swapParams = _buildSwapParams(swapParams);

        ICurveRouter router = ICurveRouter(payable(ROUTER));

        if (exactType == Exact.Out) {
            uint256 amountToSwap = amount;

            // Iterate through pools in reverse order
            for (uint256 i = swapParams.length; i > 0; i--) {
                if (swapParams[i - 1].poolType == PoolType.Stable) {
                    int128 inIndex = int128(int8(swapParams[i - 1].tokenInIndex));
                    int128 outIndex = int128(int8(swapParams[i - 1].tokenOutIndex));

                    amountToSwap = IStableSwapPool(pools[i - 1]).get_dx(inIndex, outIndex, amountToSwap);
                } else {
                    uint256 inIndex = uint256(swapParams[i - 1].tokenInIndex);
                    uint256 outIndex = uint256(swapParams[i - 1].tokenOutIndex);

                    amountToSwap = IPoolCommon(pools[i - 1]).get_dx(inIndex, outIndex, amountToSwap);
                }
            }

            amountIn = amountToSwap;
            amountOut = amount;
        } else {
            amountOut = router.get_dy(route, _swapParams, amount, pools);
            amountIn = amount;
        }
    }

    /**
     * @notice Builds the route for the swap
     * @param swapParams the array of swap parameters
     * @return route array that consists of [token0, pool0, token1, pool1, token2, ...] sequences
     * @return pools array of pool addresses
     */
    function _buildRoute(SwapParams[] memory swapParams)
        internal
        view
        returns (address[11] memory route, address[5] memory pools)
    {
        if (swapParams.length == 0 || swapParams.length > 5) revert InvalidSwapParamsLength();

        for (uint256 i = 0; i < swapParams.length; i++) {
            address pool = swapParams[i].pool;

            address tokenIn = IPoolCommon(pool).coins(swapParams[i].tokenInIndex);

            route[i * 2] = tokenIn;
            route[i * 2 + 1] = pool;

            if (i == swapParams.length - 1) {
                address tokenOut = IPoolCommon(pool).coins(swapParams[i].tokenOutIndex);
                route[i * 2 + 2] = tokenOut;
            }
        }

        for (uint256 i = 0; i < swapParams.length; i++) {
            pools[i] = swapParams[i].pool;
        }

        return (route, pools);
    }

    /**
     * @notice Builds the swap params for exchange() function of CurveRouterNG
     * @dev adds swapType to parameters
     * @param swapParams the array of swap parameters
     * @return _swapParams array of swap params
     * @dev Line structure:
     * [i, j, swap_type, PoolType poolType, n_coins]
     * i - index of the input token
     * j - index of the output token
     * n_coins - number of coins in the pool
     */
    function _buildSwapParams(SwapParams[] memory swapParams)
        internal
        pure
        returns (uint256[5][5] memory _swapParams)
    {
        uint256 swapType = uint256(SwapType.Standard);

        for (uint256 i = 0; i < swapParams.length; i++) {
            _swapParams[i] = [
                swapParams[i].tokenInIndex,
                swapParams[i].tokenOutIndex,
                swapType,
                uint8(swapParams[i].poolType),
                swapParams[i].coinsInPool
            ];
        }
    }

    /**
     * @notice Validates the route for the swap
     * @param swapParams the array of swap parameters
     * @param direction the direction of the swap
     * @param vault the address of the vault
     */
    function _validateSwapParams(SwapParams[] memory swapParams, Enums.Direction direction, address vault)
        internal
        view
    {
        address baseToken = address(Vault(payable(vault)).baseToken());
        address quoteToken = address(Vault(payable(vault)).quoteToken());

        address tokenIn = IPoolCommon(swapParams[0].pool).coins(swapParams[0].tokenInIndex);

        SwapParams memory lastHop = swapParams[swapParams.length - 1];

        address tokenOut = IPoolCommon(lastHop.pool).coins(lastHop.tokenOutIndex);

        for (uint256 i = 0; i < swapParams.length - 1; i++) {
            address tokenOutPrevHop = IPoolCommon(swapParams[i].pool).coins(swapParams[i].tokenOutIndex);
            address tokenInNextHop = IPoolCommon(swapParams[i + 1].pool).coins(swapParams[i + 1].tokenInIndex);

            if (tokenOutPrevHop != tokenInNextHop) {
                revert InterruptedPath();
            }
        }

        if (_isBuy(direction)) {
            if (tokenIn != quoteToken) revert TokenInIsNotQuoteTokenForBuyDirection();
            if (tokenOut != baseToken) revert TokenOutIsNotBaseTokenForBuyDirection();
        } else {
            if (tokenIn != baseToken) revert TokenInIsNotBaseTokenForSellDirection();
            if (tokenOut != quoteToken) revert TokenOutIsNotQuoteTokenForSellDirection();
        }
    }

    /// @notice Checks if the direction is a buy
    /// @param direction the direction of the swap
    /// @return true if the direction is a buy, false otherwise
    function _isBuy(Enums.Direction direction) internal pure virtual returns (bool) {
        return direction == Enums.Direction.QuoteToBase;
    }
}
"
    },
    "dependencies/@uniswap-v2-periphery-1.1.0-beta.0/contracts/interfaces/IUniswapV2Router01.sol": {
      "content": "pragma solidity >=0.6.2;

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
"
    },
    "src/lib/UniV3.sol": {
      "content": "// SPDX-License-Identifier: LicenseRef-CICADA-Proprietary
// SPDX-FileCopyrightText: (c) 2024 Cicada Software, CICADA DMCC. All rights reserved.
pragma solidity ^0.8.29;

import {Enums} from "./Common.sol";

/**
 * @title UniV3
 * @notice Library for handling Uniswap V3 specifics
 * @dev Encodes token addresses and fee tiers into a bytes path for Uniswap V3 router
 */
library UniV3 {
    /// @notice Represents a FULL Uniswap V3 swap path with tokens and their corresponding pool fees
    struct UniV3PathFull {
        address[] tokens;
        uint24[] fees;
    }

    /// @notice Represents a TRANSIT path (without base and quote tokens) used internally in Cicada software
    struct UniV3PathVia {
        address[] tokens;
        uint24[] fees;
    }

    struct UniV3SwapParams {
        UniV3PathVia pathVia;
    }

    /**
     * @notice Encodes a path into bytes format required by Uniswap V3 router
     * @param self Path struct containing tokens and fees
     * @return Encoded path as bytes: token0 + fee0 + token1 + fee1 + ... + tokenN
     */
    function encode(UniV3PathFull memory self) internal pure returns (bytes memory) {
        require(validatePathFull(self), "UniV3Path: BAD_PFULL");

        bytes memory path = new bytes(0);
        for (uint256 i = 0; i < self.fees.length; i++) {
            path = bytes.concat(path, abi.encodePacked(self.tokens[i], self.fees[i]));
        }
        path = bytes.concat(path, abi.encodePacked(self.tokens[self.tokens.length - 1]));

        return path;
    }

    /**
     * @notice Converts a transit path (internal form of route used inside Cicada Perimeter)
     *   to a full path by adding base and quote tokens
     * @param self Transit path struct containing intermediate tokens and fees
     * @param baseToken Address of the base token
     * @param quoteToken Address of the quote token
     * @return UniV3-compatible Full path struct with tokens ordered based on swap direction
     */
    function toPathFull(UniV3PathVia memory self, Enums.Direction direction, address baseToken, address quoteToken)
        internal
        pure
        returns (UniV3PathFull memory)
    {
        require(validatePathVia(self), "UniV3Path: BAD_PVIA");
        address[] memory tokens = new address[](self.tokens.length + 2);
        if (direction == Enums.Direction.QuoteToBase) {
            self = reverse(self);
            tokens[0] = quoteToken;
            tokens[tokens.length - 1] = baseToken;
        } else if (direction == Enums.Direction.BaseToQuote) {
            tokens[0] = baseToken;
            tokens[tokens.length - 1] = quoteToken;
        } else {
            revert("UniV3Path: BAD_DIRECTION");
        }

        for (uint256 i = 0; i < self.tokens.length; i++) {
            tokens[i + 1] = self.tokens[i];
        }

        UniV3PathFull memory result = UniV3PathFull({tokens: tokens, fees: self.fees});
        require(validatePathFull(result), "UniV3Path: BAD_PFULL");
        return result;
    }

    /**
     * @notice Validates that the path has correct token and fee array lengths
     * @return true if tokens.length - 1 == fees.length and tokens not empty
     */
    function validatePathFull(UniV3PathFull memory self) internal pure returns (bool) {
        return self.fees.length > 0 && self.tokens.length == self.fees.length + 1;
    }

    /**
     * @notice Validates that the transit path has correct token and fee array lengths
     */
    function validatePathVia(UniV3PathVia memory self) internal pure returns (bool) {
        return self.fees.length > 0 && self.tokens.length == self.fees.length - 1;
    }

    /**
     * @notice Reverses the order of tokens and fees in a UniV3PathVia
     * @param self The UniV3PathVia struct to reverse
     * @return path UniV3PathVia with reversed tokens and fees arrays
     * @dev Used when converting Buy direction paths to maintain correct token ordering
     * @dev Requires fees.length == tokens.length + 1 to maintain valid path structure
     */
    function reverse(UniV3PathVia memory self) internal pure returns (UniV3PathVia memory) {
        uint256 tokensLength = self.tokens.length;
        uint256 feesLength = self.fees.length;
        require(feesLength == tokensLength + 1, "UniV3: BAD_PATH_LENGTHS");

        address[] memory reversedTokens = new address[](tokensLength);
        uint24[] memory reversedFees = new uint24[](feesLength);

        // Reverse tokens
        for (uint256 i = 0; i < tokensLength; i++) {
            reversedTokens[i] = self.tokens[tokensLength - 1 - i];
        }

        // Reverse fees
        for (uint256 i = 0; i < feesLength; i++) {
            reversedFees[i] = self.fees[feesLength - 1 - i];
        }

        return (UniV3PathVia({tokens: reversedTokens, fees: reversedFees}));
    }
}
"
    },
    "dependencies/@balancer-v2-1.0.0/pkg/interfaces/contracts/standalone-utils/IBalancerQueries.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity >=0.7.0 <0.9.0;
pragma experimental ABIEncoderV2;

import "../vault/IVault.sol";

/**
 * @dev Provides a way to perform queries on swaps, joins and exits, simulating these operations and returning the exact
 * result they would have if called on the Vault given the current state. Note that the results will be affected by
 * other transactions interacting with the Pools involved.
 *
 * All query functions can be called both on-chain and off-chain.
 *
 * If calling them from a contract, note that all query functions are not `view`. Despite this, these functions produce
 * no net state change, and for all intents and purposes can be thought of as if they were indeed `view`. However,
 * calling them via STATICCALL will fail.
 *
 * If calling them from an off-chain client, make sure to use eth_call: most clients default to eth_sendTransaction for
 * non-view functions.
 *
 * In all cases, the `fromInternalBalance` and `toInternalBalance` fields are entirely ignored: we just use the same
 * structs for simplicity.
 */
interface IBalancerQueries {
    function querySwap(IVault.SingleSwap memory singleSwap, IVault.FundManagement memory funds)
        external
        returns (uint256);

    function queryBatchSwap(
        IVault.SwapKind kind,
        IVault.BatchSwapStep[] memory swaps,
        IAsset[] memory assets,
        IVault.FundManagement memory funds
    ) external returns (int256[] memory assetDeltas);

    function queryJoin(
        bytes32 poolId,
        address sender,
        address recipient,
        IVault.JoinPoolRequest memory request
    ) external returns (uint256 bptOut, uint256[] memory amountsIn);

    function queryExit(
        bytes32 poolId,
        address sender,
        address recipient,
        IVault.ExitPoolRequest memory request
    ) external returns (uint256 bptIn, uint256[] memory amountsOut);
}
"
    },
    "dependencies/@balancer-v2-1.0.0/pkg/interfaces/contracts/vault/IVault.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma experimental ABIEncoderV2;

import "../solidity-utils/openzeppelin/IERC20.sol";
import "../solidity-utils/helpers/IAuthentication.sol";
import "../solidity-utils/helpers/ISignaturesValidator.sol";
import "../solidity-utils/helpers/ITemporarilyPausable.sol";
import "../solidity-utils/misc/IWETH.sol";

import "./IAsset.sol";
import "./IAuthorizer.sol";
import "./IFlashLoanRecipient.sol";
import "./IProtocolFeesCollector.sol";

pragma solidity >=0.7.0 <0.9.0;

/**
 * @dev Full external interface for the Vault core contract - no external or public methods exist in the contract that
 * don't override one of these declarations.
 */
interface IVault is ISignaturesValidator, ITemporarilyPausable, IAuthentication {
    // Generalities about the Vault:
    //
    // - Whenever documentation refers to 'tokens', it strictly refers to ERC20-compliant token contracts. Tokens are
    // transferred out of the Vault by calling the `IERC20.transfer` function, and transferred in by calling
    // `IERC20.transferFrom`. In these cases, the sender must have previously allowed the Vault to use their tokens by
    // calling `IERC20.approve`. The only deviation from the ERC20 standard that is supported is functions not returning
    // a boolean value: in these scenarios, a non-reverting call is assumed to be successful.
    //
    // - All non-view functions in the Vault are non-reentrant: calling them while another one is mid-execution (e.g.
    // while execution control is transferred to a token contract during a swap) will result in a revert. View
    // functions can be called in a re-reentrant way, but doing so might cause them to return inconsistent results.
    // Contracts calling view functions in the Vault must make sure the Vault has not already been entered.
    //
    // - View functions revert if referring to either unregistered Pools, or unregistered tokens for registered Pools.

    // Authorizer
    //
    // Some system actions are permissioned, like setting and collecting protocol fees. This permissioning system exists
    // outside of the Vault in the Authorizer contract: the Vault simply calls the Authorizer to check if the caller
    // can perform a given action.

    /**
     * @dev Returns the Vault's Authorizer.
     */
    function getAuthorizer() external view returns (IAuthorizer);

    /**
     * @dev Sets a new Authorizer for the Vault. The caller must be allowed by the current Authorizer to do this.
     *
     * Emits an `AuthorizerChanged` event.
     */
    function setAuthorizer(IAuthorizer newAuthorizer) external;

    /**
     * @dev Emitted when a new authorizer is set by `setAuthorizer`.
     */
    event AuthorizerChanged(IAuthorizer indexed newAuthorizer);

    // Relayers
    //
    // Additionally, it is possible for an account to perform certain actions on behalf of another one, using their
    // Vault ERC20 allowance and Internal Balance. These accounts are said to be 'relayers' for these Vault functions,
    // and are expected to be smart contracts with sound authentication mechanisms. For an account to be able to wield
    // this power, two things must occur:
    //  - The Authorizer must grant the account the permission to be a relayer for the relevant Vault function. This
    //    means that Balancer governance must approve each individual contract to act as a relayer for the intended
    //    functions.
    //  - Each user must approve the relayer to act on their behalf.
    // This double protection means users cannot be tricked into approving malicious relayers (because they will not
    // have been allowed by the Authorizer via governance), nor can malicious relayers approved by a compromised
    // Authorizer or governance drain user funds, since they would also need to be approved by each individual user.

    /**
     * @dev Returns true if `user` has approved `relayer` to act as a relayer for them.
     */
    function hasApprovedRelayer(address user, address relayer) external view returns (bool);

    /**
     * @dev Allows `relayer` to act as a relayer for `sender` if `approved` is true, and disallows it otherwise.
     *
     * Emits a `RelayerApprovalChanged` event.
     */
    function setRelayerApproval(
        address sender,
        address relayer,
        bool approved
    ) external;

    /**
     * @dev Emitted every time a relayer is approved or disapproved by `setRelayerApproval`.
     */
    event RelayerApprovalChanged(address indexed relayer, address indexed sender, bool approved);

    // Internal Balance
    //
    // Users can deposit tokens into the Vault, where they are allocated to their Internal Balance, and later
    // transferred or withdrawn. It can also be used as a source of tokens when joining Pools, as a destination
    // when exiting them, and as either when performing swaps. This usage of Internal Balance results in greatly reduced
    // gas costs when compared to relying on plain ERC20 transfers, leading to large savings for frequent users.
    //
    // Internal Balance management features batching, which means a single contract call can be used to perform multiple
    // operations of different kinds, with different senders and recipients, at once.

    /**
     * @dev Returns `user`'s Internal Balance for a set of tokens.
     */
    function getInternalBalance(address user, IERC20[] memory tokens) external view returns (uint256[] memory);

    /**
     * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer)
     * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as
     * it lets integrators reuse a user's Vault allowance.
     *
     * For each operation, if the caller is not `sender`, it must be an authorized relayer for them.
     */
    function manageUserBalance(UserBalanceOp[] memory ops) external payable;

    /**
     * @dev Data for `manageUserBalance` operations, which include the possibility for ETH to be sent and received
     without manual WETH wrapping or unwrapping.
     */
    struct UserBalanceOp {
        UserBalanceOpKind kind;
        IAsset asset;
        uint256 amount;
        address sender;
        address payable recipient;
    }

    // There are four possible operations in `manageUserBalance`:
    //
    // - DEPOSIT_INTERNAL
    // Increases the Internal Balance of the `recipient` account by transferring tokens from the corresponding
    // `sender`. The sender must have allowed the Vault to use their tokens via `IERC20.approve()`.
    //
    // ETH can be used by passing the ETH sentinel value as the asset and forwarding ETH in the call: it will be wrapped
    // and deposited as WETH. Any ETH amount remaining will be sent back to the caller (not the sender, which is
    // relevant for relayers).
    //
    // Emits an `InternalBalanceChanged` event.
    //
    //
    // - WITHDRAW_INTERNAL
    // Decreases the Internal Balance of the `sender` account by transferring tokens to the `reci

Tags:
ERC20, ERC165, Multisig, Swap, Liquidity, Yield, Voting, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x4ea13a9b223cef0ea6450948dda508b1e39a7501|verified:true|block:23434792|tx:0xc1aaff404fbff601d5ac54071ef6eb79ee2b5921bb5ff07d3351a30144137414|first_check:1758739845

Submitted on: 2025-09-24 20:50:46

Comments

Log in to comment.

No comments yet.