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
Submitted on: 2025-09-24 20:50:46
Comments
Log in to comment.
No comments yet.