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/Vault.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
// --- Balancer and OpenZeppelin imports for DeFi operations and security ---
import "../lib/balancer-v3-monorepo/pkg/interfaces/contracts/vault/ICompositeLiquidityRouter.sol";
import "../lib/balancer-v3-monorepo/pkg/interfaces/contracts/vault/IRouter.sol";
import "../lib/balancer-v3-monorepo/pkg/interfaces/contracts/vault/IBatchRouter.sol";
import "./interfaces/IVault.sol";
import "./interfaces/IGauge.sol";
import "./interfaces/INative.sol";
import "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "../lib/balancer-v3-monorepo/pkg/interfaces/contracts/pool-utils/IPoolInfo.sol";
import "../lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import "../lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
import "../lib/openzeppelin-contracts/contracts/access/Ownable.sol";
import "../lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/IWRAPPED.sol";
import "./interfaces/IPermit2.sol";
import "./interfaces/IpesudoMinter.sol";
/**
* @title Vault
* @author Cashflowapp Team
* @notice Automated liquidity management vault for Balancer pools that supports:
* - Proportional and unbalanced token deposits/withdrawals
* - Gauge staking for reward earning
* - Automated harvesting and compound rewards via multicall
* - Platform fee collection on rewards
* - ETH/WETH handling for seamless user experience
* @dev This contract manages liquidity in Balancer pools, allowing users to deposit tokens,
* participate in gauge farming, and benefit from automated reward compounding. It issues ERC20 vault tokens
* representing shares in the vault. The vault uses multicall functionality for advanced reward compounding
* strategies executed by whitelisted addresses. Public addLiquidity functions are intended ONLY for
* internal use via multicall for reward compounding - users should use deposit functions instead.
*
* Key Features:
* - ERC4626-like vault mechanics with share-based accounting
* - Balancer V3 integration with composite liquidity router
* - Gauge integration for earning BAL and other rewards
* - Permit2 support for gasless approvals
* - Reentrancy protection on all external functions
* - SafeERC20 usage for secure token operations
* - Platform fee mechanism for sustainable operations
*/
contract Vault is IVault, ERC20, ReentrancyGuard, Ownable {
using SafeERC20 for IERC20;
// --- Constants and Immutables ---
/// @notice Platform fee in basis points (e.g., 500 = 5%)
uint256 public platformFeePercentage = 500;
/// @notice Balancer router for liquidity operations
ICompositeLiquidityRouter public immutable router;
/// @notice Gauge for staking pool tokens and earning rewards
IGauge public immutable gauge;
/// @notice Minter for additional rewards
IpesudoMinter public immutable pesudoMinter;
/// @notice Balancer pool address
address public immutable pool;
/// @notice Underlying tokens in the pool
IERC20[] public poolTokens;
/// @notice Native token (e.g., WETH)
address public immutable nativeToken;
/// @notice Router for batch swaps
IBatchRouter public immutable batchRouter;
/// @notice protocol fee receiver address
address public feeReceiver;
/// @notice Access control for multicall
mapping(address => bool) public multicallWhitelist;
// --- Events ---
/// @notice Emitted when a token approval is set
event Approve(
address indexed token,
address indexed spender,
uint256 amount
);
/// @notice Emitted when a Permit2 approval is set
event Permit2Approve(
address indexed permit2,
address indexed token,
address indexed spender,
uint160 amount,
uint48 expiration
);
/// @notice Emitted when the multicall whitelist is updated
event MulticallWhitelistUpdated(address indexed target, bool allowed);
/// @notice Emitted when the platform fee percentage is updated
event FeePercentageUpdated(uint256 newFeePercentage);
/// @notice Emitted when a deposit is made
event Deposit(
address indexed sender,
address indexed recipient,
uint256 bptAmount,
uint256 shareAmount,
bool proportional,
uint256[] inputAmounts
);
/// @notice Emitted when a withdrawal is made
event Withdraw(
address indexed sender,
uint256 sharesBurned,
address[] tokensOut,
uint256[] amountsOut
);
/// @notice Emitted when a multicall is executed
event Multicall(address[] targets, bytes[] data);
/// @notice Emitted when fees are deducted
event FeeDeducted(address indexed token, uint256 amount);
/// @notice Emitted when protocol fee receiver address updated
event UpdatedFeeReceiver(address indexed feeReceiver);
// --- Modifiers ---
/// @dev Modifier to restrict access to only whitelisted multicall addresses
modifier onlyWhitelisted() {
if (!multicallWhitelist[msg.sender]) revert NotWhitelisted(msg.sender);
_;
}
// --- Receive Ether ---
/// @notice Receive function to accept ETH
receive() external payable {}
// --- Constructor ---
/// @notice Vault constructor sets up all required contract references and approvals
/// @param _pool Balancer pool address
/// @param _poolTokens Array of pool token addresses
/// @param name ERC20 name
/// @param symbol ERC20 symbol
/// @param _router Balancer router address
/// @param _batchRouter Batch router address
/// @param _nativeToken Native token address (e.g., WETH)
/// @param _gauge Gauge address for staking
/// @param _pesudoMinter Pseudo minter address for rewards
/// @param _permit2 Permit2 contract address
/// @param _owner Owner address
constructor(
address _pool,
IERC20[] memory _poolTokens,
string memory name,
string memory symbol,
address _router,
address _batchRouter,
address _nativeToken,
address _gauge,
address _pesudoMinter,
address _permit2,
address _owner,
address _feeReceiver
) ERC20(name, symbol) Ownable(_owner) {
if (
_pool == address(0) ||
_router == address(0) ||
_batchRouter == address(0) ||
_nativeToken == address(0) ||
_pesudoMinter == address(0) ||
_permit2 == address(0) ||
_owner == address(0) ||
_feeReceiver == address(0)
) revert ZeroAddress();
if (_poolTokens.length == 0) revert ArrayLengthMismatch();
pool = _pool;
router = ICompositeLiquidityRouter(_router);
gauge = IGauge(_gauge);
poolTokens = _poolTokens;
pesudoMinter = IpesudoMinter(_pesudoMinter);
batchRouter = IBatchRouter(_batchRouter);
nativeToken = _nativeToken;
feeReceiver = _feeReceiver;
// Approve pool tokens and routers for maximum allowance
if (address(gauge) != address(0)) {
IERC20(_pool).approve(_gauge, type(uint256).max);
}
IERC20(_pool).approve(address(router), type(uint256).max);
for (uint256 i = 0; i < _poolTokens.length; ++i) {
address token = address(_poolTokens[i]);
if (token == address(0)) revert PoolTokenIsZero();
IERC20(token).approve(_permit2, type(uint256).max);
IPermit2(_permit2).approve(
token,
address(router),
type(uint160).max,
type(uint48).max
);
IPermit2(_permit2).approve(
token,
address(batchRouter),
type(uint160).max,
type(uint48).max
);
IERC20(token).approve(address(batchRouter), type(uint256).max);
}
}
// --- External Functions ---
/// @notice Approve a token for a spender (owner only)
/// @param token The ERC20 token address
/// @param spender The address allowed to spend the tokens
/// @param amount The amount to approve
function approve(
address token,
address spender,
uint256 amount
) external override onlyOwner {
IERC20(token).approve(spender, amount);
emit Approve(token, spender, amount);
}
/// @notice Approve a token for a spender using Permit2 (owner only)
/// @param permit2 The Permit2 contract address
/// @param token The ERC20 token address
/// @param spender The address allowed to spend the tokens
/// @param amount The amount to approve
/// @param expiration The expiration timestamp for the approval
function permit2Approve(
address permit2,
address token,
address spender,
uint160 amount,
uint48 expiration
) external override onlyOwner {
IPermit2(permit2).approve(token, spender, amount, expiration);
emit Permit2Approve(permit2, token, spender, amount, expiration);
}
/// @notice Set or unset an address as whitelisted for multicall
/// @param target The address to whitelist or remove
/// @param allowed True to whitelist, false to remove
function setMulticallWhitelist(
address target,
bool allowed
) external override onlyOwner {
if (target == address(0)) revert ZeroAddress();
if (target == address(gauge)) revert GaugeTargetForbidden();
multicallWhitelist[target] = allowed;
emit MulticallWhitelistUpdated(target, allowed);
}
/// @notice Update the platform fee percentage (owner only)
/// @param _platformFeePercentage New fee percentage in basis points (max 2000 = 20%)
function updateFeePercentage(
uint256 _platformFeePercentage
) external override onlyOwner {
if (_platformFeePercentage > 2000) revert("Max 20% (2000 bps)");
platformFeePercentage = _platformFeePercentage;
emit FeePercentageUpdated(_platformFeePercentage);
}
/**
* @notice update protocol fee receiver address
* @param _feeReceiver new fee receiver address
*/
function updateFeeReceiverAddress(
address _feeReceiver
) external override onlyOwner {
feeReceiver = _feeReceiver;
emit UpdatedFeeReceiver(_feeReceiver);
}
/// @notice Deposit tokens in an unbalanced way (custom ratios)
/// @param recipient The address to receive vault shares
/// @param wrapUnderlying Array indicating if each token should be wrapped
/// @param inputAmounts The amounts of each token to deposit
/// @param minBptAmountOut Minimum BPT to receive from the pool
/// @param wethIsEth Whether WETH should be treated as ETH
/// @param userData Additional user data for the router
/// @return shareAmount Amount of vault shares minted
function depositUnbalanced(
address recipient,
bool[] memory wrapUnderlying,
uint256[] memory inputAmounts,
uint256 minBptAmountOut,
bool wethIsEth,
bytes memory userData
) external payable override nonReentrant returns (uint256 shareAmount) {
_validateDeposit(
recipient,
wrapUnderlying,
inputAmounts,
minBptAmountOut
);
uint256[] memory balancesBefore = _collectAndSnapshot(
inputAmounts,
_collectPayment
);
// Capture backing before adding liquidity for correct share calculation
uint256 backingBefore = totalSupply() == 0 ? 0 : (
address(gauge) != address(0)
? IERC20(address(gauge)).balanceOf(address(this))
: IERC20(pool).balanceOf(address(this))
);
uint256 bptAmountOut = addLiquidityUnbalanced(
wrapUnderlying,
inputAmounts,
minBptAmountOut,
wethIsEth,
userData,
msg.value
);
shareAmount = calculateDepositShares(bptAmountOut, backingBefore);
_mint(recipient, shareAmount);
_refundExcessAll(balancesBefore);
emit Deposit(
msg.sender,
recipient,
bptAmountOut,
shareAmount,
false,
inputAmounts
);
}
/// @notice Deposit tokens in a proportional way (fixed pool ratio)
/// @param recipient The address to receive vault shares
/// @param wrapUnderlying Array indicating if each token should be wrapped
/// @param maxInputAmounts The max amounts of each token to deposit
/// @param exactBptAmountOut Exact BPT to receive from the pool
/// @param wethIsEth Whether WETH should be treated as ETH
/// @param userData Additional user data for the router
/// @return shareAmount Amount of vault shares minted
function depositProportional(
address recipient,
bool[] memory wrapUnderlying,
uint256[] memory maxInputAmounts,
uint256 exactBptAmountOut,
bool wethIsEth,
bytes memory userData
) external payable override nonReentrant returns (uint256 shareAmount) {
_validateDeposit(
recipient,
wrapUnderlying,
maxInputAmounts,
exactBptAmountOut
);
uint256[] memory balancesBefore = _collectAndSnapshot(
maxInputAmounts,
_collectPayment
);
// Capture backing before adding liquidity for correct share calculation
uint256 backingBefore = totalSupply() == 0 ? 0 : (
address(gauge) != address(0)
? IERC20(address(gauge)).balanceOf(address(this))
: IERC20(pool).balanceOf(address(this))
);
uint256[] memory actualInputAmounts = addLiquidityProportional(
wrapUnderlying,
maxInputAmounts,
exactBptAmountOut,
wethIsEth,
userData,
msg.value
);
shareAmount = calculateDepositShares(exactBptAmountOut, backingBefore);
_mint(recipient, shareAmount);
_refundExcessAll(balancesBefore);
emit Deposit(
msg.sender,
recipient,
exactBptAmountOut,
shareAmount,
true,
actualInputAmounts
);
}
/// @notice Withdraw tokens from the vault, burning shares
/// @param sharesToBurn Amount of vault shares to burn
/// @param minAmountsOut Minimum amounts of each token to withdraw
/// @param unwrapWrapped Array indicating if each token should be unwrapped
/// @param wethIsEth Whether WETH should be treated as ETH
/// @param userData Additional user data for the router
/// @return tokensOut The addresses of tokens withdrawn
/// @return amountsOut The amounts of tokens withdrawn
function withdraw(
uint256 sharesToBurn,
uint256[] memory minAmountsOut,
bool[] memory unwrapWrapped,
bool wethIsEth,
bytes memory userData
)
external
override
nonReentrant
returns (address[] memory tokensOut, uint256[] memory amountsOut)
{
if (sharesToBurn == 0) revert InvalidAmount();
if (
minAmountsOut.length != poolTokens.length ||
unwrapWrapped.length != poolTokens.length
) {
revert ArrayLengthMismatch();
}
(tokensOut, amountsOut) = _withdrawAndRemoveLiquidity(
sharesToBurn,
minAmountsOut,
unwrapWrapped,
wethIsEth,
userData
);
for (uint256 i = 0; i < tokensOut.length; ) {
if (wethIsEth && tokensOut[i] == nativeToken) {
(bool success, ) = msg.sender.call{value: amountsOut[i]}("");
if (!success) revert ETHRefundFailed();
} else {
IERC20(tokensOut[i]).safeTransfer(msg.sender, amountsOut[i]);
}
unchecked {
++i;
}
}
emit Withdraw(msg.sender, sharesToBurn, tokensOut, amountsOut);
}
/// @notice Claim rewards from the gauge and minter
function claimRewards() external override nonReentrant {
if (address(gauge) != address(0)) gauge.claim_rewards();
if (address(pesudoMinter) != address(0)) {
pesudoMinter.mint(address(gauge));
}
}
/// @notice Multicall for batch execution of whitelisted contract calls (owner only)
/// @dev Only whitelisted addresses can call this function. Each target must also be whitelisted.
/// @param targets The contract addresses to call
/// @param data The calldata for each call
/// @return results The return data from each call
function multicall(
address[] calldata targets,
bytes[] calldata data
)
external
override
nonReentrant
onlyWhitelisted
returns (bytes[] memory results)
{
if (targets.length != data.length) revert ArrayLengthMismatch();
results = new bytes[](targets.length);
for (uint256 i = 0; i < targets.length; ++i) {
if (!multicallWhitelist[targets[i]]) {
revert NotWhitelisted(targets[i]); //bal, gho, weth-oseth //bal-weth gh
}
(bool success, bytes memory result) = targets[i].call(data[i]);
if (!success) revert MulticallSubcallFailed(targets[i], data[i]);
results[i] = result;
}
emit Multicall(targets, data);
}
/// @notice Deduct platform fees from a token balance
/// @dev Only callable by the contract itself (e.g., via multicall)
/// @param token The token address to deduct fees from
function deductFees(address token) external override {
if (msg.sender != address(this)) revert("NA");
if (token == address(gauge) || token == pool) revert("NA");
if (token == address(0)) revert ZeroAddress();
//if token guage then reward
uint256 feesAmount = (IERC20(token).balanceOf(address(this)) *
platformFeePercentage) / 10000;
IERC20(token).safeTransfer(feeReceiver, feesAmount);
emit FeeDeducted(token, feesAmount);
}
// --- Public Functions for Direct Pool Interaction ---
// NOTE: These functions are intended for internal use (e.g., reward compounding via multicall)
// and should NOT be called directly by users.
/// @notice Add liquidity in a proportional way
/// @dev For internal use (e.g., reward compounding via multicall). Users should not call directly.
/// @param wrapUnderlying Array indicating if each token should be wrapped
/// @param maxInputAmounts The max amounts of each token to deposit
/// @param exactBptAmountOut Exact BPT to receive from the pool
/// @param wethIsEth Whether WETH should be treated as ETH
/// @param userData Additional user data
/// @param ethAmount Amount of ETH sent (if any)
/// @return actualInputAmounts The actual amounts of tokens deposited
function addLiquidityProportional(
bool[] memory wrapUnderlying,
uint256[] memory maxInputAmounts,
uint256 exactBptAmountOut,
bool wethIsEth,
bytes memory userData,
uint256 ethAmount
) public returns (uint256[] memory actualInputAmounts) {
// This function is intended for internal use (e.g., reward compounding via multicall).
// Users should not call this directly.
(, actualInputAmounts) = router.addLiquidityProportionalToERC4626Pool{
value: ethAmount
}(
pool,
wrapUnderlying,
maxInputAmounts,
exactBptAmountOut,
wethIsEth,
userData
);
// Only deposit into gauge if a gauge is configured
if (address(gauge) != address(0)) {
gauge.deposit(exactBptAmountOut);
}
}
/// @notice Add liquidity in an unbalanced way
/// @dev For internal use (e.g., reward compounding via multicall). Users should not call directly.
/// @param wrapUnderlying Array indicating if each token should be wrapped
/// @param inputAmounts The amounts of each token to deposit
/// @param minBptAmountOut Minimum BPT to receive from the pool
/// @param wethIsEth Whether WETH should be treated as ETH
/// @param userData Additional user data
/// @param ethAmount Amount of ETH sent (if any)
/// @return bptAmountOut The amount of BPT received
function addLiquidityUnbalanced(
bool[] memory wrapUnderlying,
uint256[] memory inputAmounts,
uint256 minBptAmountOut,
bool wethIsEth,
bytes memory userData,
uint256 ethAmount
) public returns (uint256 bptAmountOut) {
// This function is intended for internal use (e.g., reward compounding via multicall).
// Users should not call this directly.
bptAmountOut = router.addLiquidityUnbalancedToERC4626Pool{
value: ethAmount
}(
pool,
wrapUnderlying,
inputAmounts,
minBptAmountOut,
wethIsEth,
userData
);
// Only deposit into gauge if a gauge is configured
if (address(gauge) != address(0)) {
gauge.deposit(bptAmountOut);
}
}
/// @notice Internal function to calculate shares with a pre-captured backing amount
/// @param bptAmountOut The amount of BPT received
/// @param backingBefore The backing amount before adding liquidity
/// @return shares The amount of shares to mint
function calculateDepositShares(
uint256 bptAmountOut,
uint256 backingBefore
) public view returns (uint256 shares) {
if (totalSupply() == 0) {
shares = bptAmountOut;
} else {
shares = (bptAmountOut * totalSupply()) / backingBefore;
}
}
/// @notice Calculate BPT amount to withdraw for a given share amount
/// @param sharesToBurn The amount of shares to burn
/// @return bptAmount The amount of BPT to withdraw
function calculateWithdrawShares(
uint256 sharesToBurn
) public view returns (uint256 bptAmount) {
uint256 backing = address(gauge) != address(0)
? IERC20(address(gauge)).balanceOf(address(this))
: IERC20(pool).balanceOf(address(this));
bptAmount = (backing * sharesToBurn) / totalSupply();
}
// --- Internal Helper Functions ---
/// @dev Collect payment from user and snapshot balances
/// @param token Token address to collect
/// @param amount Amount to collect
function _collectPayment(address token, uint256 amount) internal {
if (token == address(0)) revert ZeroAddress();
if (amount == 0) revert InvalidAmount();
if (token == nativeToken && msg.value > 0) {
if (msg.value != amount) revert IncorrectETHSent();
} else {
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
}
}
/// @dev Refund any excess tokens to the user
/// @param token Token address to refund
/// @param amount Amount to refund
function _refundExcess(address token, uint256 amount) internal {
if (token == address(0)) revert ZeroAddress();
if (amount == 0) return;
if (token == nativeToken && msg.value > 0) {
(bool success, ) = msg.sender.call{value: amount}("");
if (!success) revert ETHRefundFailed();
} else {
IERC20(token).safeTransfer(msg.sender, amount);
}
}
/// @dev Collect tokens and snapshot balances before deposit
/// @param inputAmounts Amounts to collect for each token
/// @param collectFn Function to call for collecting tokens
/// @return balancesBefore Array of balances before collection
function _collectAndSnapshot(
uint256[] memory inputAmounts,
function(address, uint256) internal collectFn
) internal returns (uint256[] memory) {
uint256[] memory balancesBefore = new uint256[](poolTokens.length);
for (uint256 i = 0; i < poolTokens.length; ) {
address token = address(poolTokens[i]);
if (token == nativeToken && msg.value > 0) {
balancesBefore[i] = address(this).balance - msg.value;
} else {
balancesBefore[i] = IERC20(token).balanceOf(address(this));
}
if (inputAmounts[i] > 0) {
collectFn(token, inputAmounts[i]);
}
unchecked {
++i;
}
}
return balancesBefore;
}
/// @dev Refund all excess tokens after deposit
/// @param balancesBefore Array of balances before deposit
function _refundExcessAll(uint256[] memory balancesBefore) internal {
for (uint256 i = 0; i < poolTokens.length; ) {
address token = address(poolTokens[i]);
uint256 balanceAfter;
if (token == nativeToken && msg.value > 0) {
balanceAfter = address(this).balance;
} else {
balanceAfter = IERC20(token).balanceOf(address(this));
}
uint256 excess = balanceAfter > balancesBefore[i]
? balanceAfter - balancesBefore[i]
: 0;
if (excess > 0) {
_refundExcess(token, excess);
}
unchecked {
++i;
}
}
}
/// @dev Withdraw and remove liquidity from the pool, burning shares
/// @param sharesToBurn Amount of shares to burn
/// @param minAmountsOut Minimum amounts to withdraw
/// @param unwrapWrapped Array indicating if each token should be unwrapped
/// @param wethIsEth Whether WETH should be treated as ETH
/// @param userData Additional user data
/// @return tokensOut Array of token addresses withdrawn
/// @return amountsOut Array of amounts withdrawn
function _withdrawAndRemoveLiquidity(
uint256 sharesToBurn,
uint256[] memory minAmountsOut,
bool[] memory unwrapWrapped,
bool wethIsEth,
bytes memory userData
)
internal
returns (address[] memory tokensOut, uint256[] memory amountsOut)
{
uint256 bptAmount = calculateWithdrawShares(sharesToBurn);
// If gauge exists, withdraw staked BPT from gauge and remove the delta
if (address(gauge) != address(0)) {
uint256 lpAmountBefore = IERC20(pool).balanceOf(address(this));
gauge.withdraw(bptAmount);
uint256 lpAmountAfter = IERC20(pool).balanceOf(address(this));
uint256 lpDiff = lpAmountAfter > lpAmountBefore
? lpAmountAfter - lpAmountBefore
: 0;
(tokensOut, amountsOut) = router.removeLiquidityProportionalFromERC4626Pool(
pool,
unwrapWrapped,
lpDiff,
minAmountsOut,
wethIsEth,
userData
);
} else {
// No gauge configured: remove directly from pool using bptAmount
(tokensOut, amountsOut) = router.removeLiquidityProportionalFromERC4626Pool(
pool,
unwrapWrapped,
bptAmount,
minAmountsOut,
wethIsEth,
userData
);
}
_burn(msg.sender, sharesToBurn);
}
/// @dev Validate deposit input arrays and amounts
/// @param recipient Recipient address
/// @param wrapUnderlying Array indicating if each token should be wrapped
/// @param maxInputAmounts Array of max input amounts
/// @param exactBptAmountOut Exact BPT amount out
function _validateDeposit(
address recipient,
bool[] memory wrapUnderlying,
uint256[] memory maxInputAmounts,
uint256 exactBptAmountOut
) internal view {
if (recipient == address(0)) revert ZeroAddress();
if (
wrapUnderlying.length != poolTokens.length ||
maxInputAmounts.length != poolTokens.length
) {
revert ArrayLengthMismatch();
}
if (exactBptAmountOut == 0) revert InvalidAmount();
}
}
"
},
"lib/balancer-v3-monorepo/pkg/interfaces/contracts/vault/ICompositeLiquidityRouter.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
/**
* @notice The composite liquidity router supports add/remove liquidity operations on ERC4626 pools.
* @dev This contract allow interacting with ERC4626 Pools (which contain wrapped ERC4626 tokens) using only underlying
* standard tokens. For instance, with `addLiquidityUnbalancedToERC4626Pool` it is possible to add liquidity to an
* ERC4626 Pool with [waDAI, waUSDC], using only DAI, only USDC, or an arbitrary amount of both. If the ERC4626 buffers
* in the Vault have liquidity, these will be used to avoid wrapping/unwrapping through the wrapped token interface,
* saving gas.
*
* For instance, adding only DAI to the pool above (and assuming an aDAI buffer with enough liquidity), would pull in
* the DAI from the user, swap it for waDAI in the internal Vault buffer, and deposit the waDAI into the ERC4626 pool:
* 1) without having to do any expensive ERC4626 wrapping operations; and
* 2) without requiring the user to construct a batch operation containing the buffer swap.
*/
interface ICompositeLiquidityRouter {
/// @notice `tokensOut` array does not have all the tokens from `expectedTokensOut`.
error WrongTokensOut(address[] expectedTokensOut, address[] tokensOut);
/***************************************************************************
ERC4626 Pools
***************************************************************************/
/**
* @notice Add arbitrary amounts of tokens to an ERC4626 pool through the buffer.
* @dev An "ERC4626 pool" contains IERC4626 yield-bearing tokens (e.g., waDAI). Ensure that any buffers associated
* with the wrapped tokens in the ERC4626 pool have been initialized before initializing or adding liquidity to
* the "parent" pool, and also make sure limits are set properly.
*
* @param pool Address of the liquidity pool
* @param wrapUnderlying Flags indicating whether the corresponding token should be wrapped or
* used as a standard ERC20
* @param exactAmountsIn Exact amounts of underlying/wrapped tokens in, sorted in token registration order
* @param minBptAmountOut Minimum amount of pool tokens to be received
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data required for adding liquidity
* @return bptAmountOut Actual amount of pool tokens received
*/
function addLiquidityUnbalancedToERC4626Pool(
address pool,
bool[] memory wrapUnderlying,
uint256[] memory exactAmountsIn,
uint256 minBptAmountOut,
bool wethIsEth,
bytes memory userData
) external payable returns (uint256 bptAmountOut);
/**
* @notice Add proportional amounts of tokens to an ERC4626 pool through the buffer.
* @dev An "ERC4626 pool" contains IERC4626 yield-bearing tokens (e.g., waDAI). Ensure that any buffers associated
* with the wrapped tokens in the ERC4626 pool have been initialized before initializing or adding liquidity to
* the "parent" pool, and also make sure limits are set properly.
*
* @param pool Address of the liquidity pool
* @param wrapUnderlying Flags indicating whether the corresponding token should be wrapped or
* used as a standard ERC20
* @param maxAmountsIn Maximum amounts of underlying/wrapped tokens in, sorted in token registration order
* wrapped tokens in the pool
* @param exactBptAmountOut Exact amount of pool tokens to be received
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data required for adding liquidity
* @return tokensIn Actual tokens added to the pool
* @return amountsIn Actual amounts of tokens added to the pool
*/
function addLiquidityProportionalToERC4626Pool(
address pool,
bool[] memory wrapUnderlying,
uint256[] memory maxAmountsIn,
uint256 exactBptAmountOut,
bool wethIsEth,
bytes memory userData
) external payable returns (address[] memory tokensIn, uint256[] memory amountsIn);
/**
* @notice Remove proportional amounts of tokens from an ERC4626 pool, burning an exact pool token amount.
* @dev An "ERC4626 pool" contains IERC4626 yield-bearing tokens (e.g., waDAI).
* @param pool Address of the liquidity pool
* @param unwrapWrapped Flags indicating whether the corresponding token should be unwrapped or
* used as a standard ERC20
* @param exactBptAmountIn Exact amount of pool tokens provided
* @param minAmountsOut Minimum amounts of each token, corresponding to `tokensOut`
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data required for removing liquidity
* @return tokensOut Actual tokens received
* @return amountsOut Actual amounts of tokens received
*/
function removeLiquidityProportionalFromERC4626Pool(
address pool,
bool[] memory unwrapWrapped,
uint256 exactBptAmountIn,
uint256[] memory minAmountsOut,
bool wethIsEth,
bytes memory userData
) external payable returns (address[] memory tokensOut, uint256[] memory amountsOut);
/**
* @notice Queries an `addLiquidityUnbalancedToERC4626Pool` operation without actually executing it.
* @dev An "ERC4626 pool" contains IERC4626 yield-bearing tokens (e.g., waDAI).
* @param pool Address of the liquidity pool
* @param wrapUnderlying Flags indicating whether the corresponding token should be wrapped or
* used as a standard ERC20
* @param exactAmountsIn Exact amounts of underlying/wrapped tokens in, sorted in token registration order
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data required for the query
* @return bptAmountOut Expected amount of pool tokens to receive
*/
function queryAddLiquidityUnbalancedToERC4626Pool(
address pool,
bool[] memory wrapUnderlying,
uint256[] memory exactAmountsIn,
address sender,
bytes memory userData
) external returns (uint256 bptAmountOut);
/**
* @notice Queries an `addLiquidityProportionalToERC4626Pool` operation without actually executing it.
* @dev An "ERC4626 pool" contains IERC4626 yield-bearing tokens (e.g., waDAI).
* @param pool Address of the liquidity pool
* @param wrapUnderlying Flags indicating whether the corresponding token should be wrapped or
* used as a standard ERC20
* @param exactBptAmountOut Exact amount of pool tokens to be received
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data required for the query
* @return tokensIn Expected tokens added to the pool
* @return amountsIn Expected amounts of tokens added to the pool
*/
function queryAddLiquidityProportionalToERC4626Pool(
address pool,
bool[] memory wrapUnderlying,
uint256 exactBptAmountOut,
address sender,
bytes memory userData
) external returns (address[] memory tokensIn, uint256[] memory amountsIn);
/**
* @notice Queries a `removeLiquidityProportionalFromERC4626Pool` operation without actually executing it.
* @dev An "ERC4626 pool" contains IERC4626 yield-bearing tokens (e.g., waDAI).
* @param pool Address of the liquidity pool
* @param unwrapWrapped Flags indicating whether the corresponding token should be unwrapped or
* used as a standard ERC20
* @param exactBptAmountIn Exact amount of pool tokens provided for the query
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data required for the query
* @return tokensOut Expected tokens to receive
* @return amountsOut Expected amounts of tokens to receive
*/
function queryRemoveLiquidityProportionalFromERC4626Pool(
address pool,
bool[] memory unwrapWrapped,
uint256 exactBptAmountIn,
address sender,
bytes memory userData
) external returns (address[] memory tokensOut, uint256[] memory amountsOut);
}
"
},
"lib/balancer-v3-monorepo/pkg/interfaces/contracts/vault/IRouter.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { AddLiquidityKind, RemoveLiquidityKind, SwapKind } from "./VaultTypes.sol";
/// @notice User-friendly interface to basic Vault operations: swap, add/remove liquidity, and associated queries.
interface IRouter {
/***************************************************************************
Pool Initialization
***************************************************************************/
/**
* @notice Data for the pool initialization hook.
* @param sender Account originating the pool initialization operation
* @param pool Address of the liquidity pool
* @param tokens Pool tokens, in token registration order
* @param exactAmountsIn Exact amounts of tokens to be added, sorted in token registration order
* @param minBptAmountOut Minimum amount of pool tokens to be received
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the request to add initial liquidity
*/
struct InitializeHookParams {
address sender;
address pool;
IERC20[] tokens;
uint256[] exactAmountsIn;
uint256 minBptAmountOut;
bool wethIsEth;
bytes userData;
}
/**
* @notice Initialize a liquidity pool.
* @param pool Address of the liquidity pool
* @param tokens Pool tokens, in token registration order
* @param exactAmountsIn Exact amounts of tokens to be added, sorted in token registration order
* @param minBptAmountOut Minimum amount of pool tokens to be received
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the request to add initial liquidity
* @return bptAmountOut Actual amount of pool tokens minted in exchange for initial liquidity
*/
function initialize(
address pool,
IERC20[] memory tokens,
uint256[] memory exactAmountsIn,
uint256 minBptAmountOut,
bool wethIsEth,
bytes memory userData
) external payable returns (uint256 bptAmountOut);
/***************************************************************************
Add Liquidity
***************************************************************************/
/**
* @notice Adds liquidity to a pool with proportional token amounts, receiving an exact amount of pool tokens.
* @param pool Address of the liquidity pool
* @param maxAmountsIn Maximum amounts of tokens to be added, sorted in token registration order
* @param exactBptAmountOut Exact amount of pool tokens to be received
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the request to add liquidity
* @return amountsIn Actual amounts of tokens added, sorted in token registration order
*/
function addLiquidityProportional(
address pool,
uint256[] memory maxAmountsIn,
uint256 exactBptAmountOut,
bool wethIsEth,
bytes memory userData
) external payable returns (uint256[] memory amountsIn);
/**
* @notice Adds liquidity to a pool with arbitrary token amounts.
* @param pool Address of the liquidity pool
* @param exactAmountsIn Exact amounts of tokens to be added, sorted in token registration order
* @param minBptAmountOut Minimum amount of pool tokens to be received
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the request to add liquidity
* @return bptAmountOut Actual amount of pool tokens received
*/
function addLiquidityUnbalanced(
address pool,
uint256[] memory exactAmountsIn,
uint256 minBptAmountOut,
bool wethIsEth,
bytes memory userData
) external payable returns (uint256 bptAmountOut);
/**
* @notice Adds liquidity to a pool in a single token, receiving an exact amount of pool tokens.
* @param pool Address of the liquidity pool
* @param tokenIn Token used to add liquidity
* @param maxAmountIn Maximum amount of tokens to be added
* @param exactBptAmountOut Exact amount of pool tokens to be received
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the request to add liquidity
* @return amountIn Actual amount of tokens added
*/
function addLiquiditySingleTokenExactOut(
address pool,
IERC20 tokenIn,
uint256 maxAmountIn,
uint256 exactBptAmountOut,
bool wethIsEth,
bytes memory userData
) external payable returns (uint256 amountIn);
/**
* @notice Adds liquidity to a pool by donating the amounts in (no BPT out).
* @dev To support donation, the pool config `enableDonation` flag must be set to true.
* @param pool Address of the liquidity pool
* @param amountsIn Amounts of tokens to be donated, sorted in token registration order
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the request to donate liquidity
*/
function donate(address pool, uint256[] memory amountsIn, bool wethIsEth, bytes memory userData) external payable;
/**
* @notice Adds liquidity to a pool with a custom request.
* @dev The given maximum and minimum amounts given may be interpreted as exact depending on the pool type.
* In any case the caller can expect them to be hard boundaries for the request.
*
* @param pool Address of the liquidity pool
* @param maxAmountsIn Maximum amounts of tokens to be added, sorted in token registration order
* @param minBptAmountOut Minimum amount of pool tokens to be received
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the request to add liquidity
* @return amountsIn Actual amounts of tokens added, sorted in token registration order
* @return bptAmountOut Actual amount of pool tokens received
* @return returnData Arbitrary (optional) data with an encoded response from the pool
*/
function addLiquidityCustom(
address pool,
uint256[] memory maxAmountsIn,
uint256 minBptAmountOut,
bool wethIsEth,
bytes memory userData
) external payable returns (uint256[] memory amountsIn, uint256 bptAmountOut, bytes memory returnData);
/***************************************************************************
Remove Liquidity
***************************************************************************/
/**
* @notice Removes liquidity with proportional token amounts from a pool, burning an exact pool token amount.
* @param pool Address of the liquidity pool
* @param exactBptAmountIn Exact amount of pool tokens provided
* @param minAmountsOut Minimum amounts of tokens to be received, sorted in token registration order
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the request to remove liquidity
* @return amountsOut Actual amounts of tokens received, sorted in token registration order
*/
function removeLiquidityProportional(
address pool,
uint256 exactBptAmountIn,
uint256[] memory minAmountsOut,
bool wethIsEth,
bytes memory userData
) external payable returns (uint256[] memory amountsOut);
/**
* @notice Removes liquidity from a pool via a single token, burning an exact pool token amount.
* @param pool Address of the liquidity pool
* @param exactBptAmountIn Exact amount of pool tokens provided
* @param tokenOut Token used to remove liquidity
* @param minAmountOut Minimum amount of tokens to be received
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the request to remove liquidity
* @return amountOut Actual amount of tokens received
*/
function removeLiquiditySingleTokenExactIn(
address pool,
uint256 exactBptAmountIn,
IERC20 tokenOut,
uint256 minAmountOut,
bool wethIsEth,
bytes memory userData
) external payable returns (uint256 amountOut);
/**
* @notice Removes liquidity from a pool via a single token, specifying the exact amount of tokens to receive.
* @param pool Address of the liquidity pool
* @param maxBptAmountIn Maximum amount of pool tokens provided
* @param tokenOut Token used to remove liquidity
* @param exactAmountOut Exact amount of tokens to be received
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the request to remove liquidity
* @return bptAmountIn Actual amount of pool tokens burned
*/
function removeLiquiditySingleTokenExactOut(
address pool,
uint256 maxBptAmountIn,
IERC20 tokenOut,
uint256 exactAmountOut,
bool wethIsEth,
bytes memory userData
) external payable returns (uint256 bptAmountIn);
/**
* @notice Removes liquidity from a pool with a custom request.
* @dev The given maximum and minimum amounts given may be interpreted as exact depending on the pool type.
* In any case the caller can expect them to be hard boundaries for the request.
*
* @param pool Address of the liquidity pool
* @param maxBptAmountIn Maximum amount of pool tokens provided
* @param minAmountsOut Minimum amounts of tokens to be received, sorted in token registration order
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the request to remove liquidity
* @return bptAmountIn Actual amount of pool tokens burned
* @return amountsOut Actual amounts of tokens received, sorted in token registration order
* @return returnData Arbitrary (optional) data with an encoded response from the pool
*/
function removeLiquidityCustom(
address pool,
uint256 maxBptAmountIn,
uint256[] memory minAmountsOut,
bool wethIsEth,
bytes memory userData
) external payable returns (uint256 bptAmountIn, uint256[] memory amountsOut, bytes memory returnData);
/**
* @notice Removes liquidity proportionally, burning an exact pool token amount. Only available in Recovery Mode.
* @param pool Address of the liquidity pool
* @param exactBptAmountIn Exact amount of pool tokens provided
* @param minAmountsOut Minimum amounts of tokens to be received, sorted in token registration order
* @return amountsOut Actual amounts of tokens received, sorted in token registration order
*/
function removeLiquidityRecovery(
address pool,
uint256 exactBptAmountIn,
uint256[] memory minAmountsOut
) external payable returns (uint256[] memory amountsOut);
/***************************************************************************
Swaps
***************************************************************************/
/**
* @notice Data for the swap hook.
* @param sender Account initiating the swap operation
* @param kind Type of swap (exact in or exact out)
* @param pool Address of the liquidity pool
* @param tokenIn Token to be swapped from
* @param tokenOut Token to be swapped to
* @param amountGiven Amount given based on kind of the swap (e.g., tokenIn for exact in)
* @param limit Maximum or minimum amount based on the kind of swap (e.g., maxAmountIn for exact out)
* @param deadline Deadline for the swap, after which it will revert
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the swap request
*/
struct SwapSingleTokenHookParams {
address sender;
SwapKind kind;
address pool;
IERC20 tokenIn;
IERC20 tokenOut;
uint256 amountGiven;
uint256 limit;
uint256 deadline;
bool wethIsEth;
bytes userData;
}
/**
* @notice Executes a swap operation specifying an exact input token amount.
* @param pool Address of the liquidity pool
* @param tokenIn Token to be swapped from
* @param tokenOut Token to be swapped to
* @param exactAmountIn Exact amounts of input tokens to send
* @param minAmountOut Minimum amount of tokens to be received
* @param deadline Deadline for the swap, after which it will revert
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @param userData Additional (optional) data sent with the swap request
* @return amountOut Calculated amount of output tokens to be received in exchange for the given input tokens
*/
function swapSingleTokenExactIn(
address pool,
IERC20 tokenIn,
IERC20 tokenOut,
uint256 exactAmountIn,
uint256 minAmountOut,
uint256 deadline,
bool wethIsEth,
bytes calldata userData
) external payable returns (uint256 amountOut);
/**
* @notice Executes a swap operation specifying an exact output token amount.
* @param pool Address of the liquidity pool
* @param tokenIn Token to be swapped from
* @param tokenOut Token to be swapped to
* @param exactAmountOut Exact amounts of input tokens to receive
* @param maxAmountIn Maximum amount of tokens to be sent
* @param deadline Deadline for the swap, after which it will revert
* @param userData Additional (optional) data sent with the swap request
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @return amountIn Calculated amount of input tokens to be sent in exchange for the requested output tokens
*/
function swapSingleTokenExactOut(
address pool,
IERC20 tokenIn,
IERC20 tokenOut,
uint256 exactAmountOut,
uint256 maxAmountIn,
uint256 deadline,
bool wethIsEth,
bytes calldata userData
) external payable returns (uint256 amountIn);
/***************************************************************************
Queries
***************************************************************************/
/**
* @notice Queries an `addLiquidityProportional` operation without actually executing it.
* @param pool Address of the liquidity pool
* @param exactBptAmountOut Exact amount of pool tokens to be received
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data sent with the query request
* @return amountsIn Expected amounts of tokens to add, sorted in token registration order
*/
function queryAddLiquidityProportional(
address pool,
uint256 exactBptAmountOut,
address sender,
bytes memory userData
) external returns (uint256[] memory amountsIn);
/**
* @notice Queries an `addLiquidityUnbalanced` operation without actually executing it.
* @param pool Address of the liquidity pool
* @param exactAmountsIn Exact amounts of tokens to be added, sorted in token registration order
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data sent with the query request
* @return bptAmountOut Expected amount of pool tokens to receive
*/
function queryAddLiquidityUnbalanced(
address pool,
uint256[] memory exactAmountsIn,
address sender,
bytes memory userData
) external returns (uint256 bptAmountOut);
/**
* @notice Queries an `addLiquiditySingleTokenExactOut` operation without actually executing it.
* @param pool Address of the liquidity pool
* @param tokenIn Token used to add liquidity
* @param exactBptAmountOut Expected exact amount of pool tokens to receive
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data sent with the query request
* @return amountIn Expected amount of tokens to add
*/
function queryAddLiquiditySingleTokenExactOut(
address pool,
IERC20 tokenIn,
uint256 exactBptAmountOut,
address sender,
bytes memory userData
) external returns (uint256 amountIn);
/**
* @notice Queries an `addLiquidityCustom` operation without actually executing it.
* @param pool Address of the liquidity pool
* @param maxAmountsIn Maximum amounts of tokens to be added, sorted in token registration order
* @param minBptAmountOut Expected minimum amount of pool tokens to receive
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data sent with the query request
* @return amountsIn Expected amounts of tokens to add, sorted in token registration order
* @return bptAmountOut Expected amount of pool tokens to receive
* @return returnData Arbitrary (optional) data with an encoded response from the pool
*/
function queryAddLiquidityCustom(
address pool,
uint256[] memory maxAmountsIn,
uint256 minBptAmountOut,
address sender,
bytes memory userData
) external returns (uint256[] memory amountsIn, uint256 bptAmountOut, bytes memory returnData);
/**
* @notice Queries a `removeLiquidityProportional` operation without actually executing it.
* @param pool Address of the liquidity pool
* @param exactBptAmountIn Exact amount of pool tokens provided for the query
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data sent with the query request
* @return amountsOut Expected amounts of tokens to receive, sorted in token registration order
*/
function queryRemoveLiquidityProportional(
address pool,
uint256 exactBptAmountIn,
address sender,
bytes memory userData
) external returns (uint256[] memory amountsOut);
/**
* @notice Queries a `removeLiquiditySingleTokenExactIn` operation without actually executing it.
* @param pool Address of the liquidity pool
* @param exactBptAmountIn Exact amount of pool tokens provided for the query
* @param tokenOut Token used to remove liquidity
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data sent with the query request
* @return amountOut Expected amount of tokens to receive
*/
function queryRemoveLiquiditySingleTokenExactIn(
address pool,
uint256 exactBptAmountIn,
IERC20 tokenOut,
address sender,
bytes memory userData
) external returns (uint256 amountOut);
/**
* @notice Queries a `removeLiquiditySingleTokenExactOut` operation without actually executing it.
* @param pool Address of the liquidity pool
* @param tokenOut Token used to remove liquidity
* @param exactAmountOut Expected exact amount of tokens to receive
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data sent with the query request
* @return bptAmountIn Expected amount of pool tokens to burn
*/
function queryRemoveLiquiditySingleTokenExactOut(
address pool,
IERC20 tokenOut,
uint256 exactAmountOut,
address sender,
bytes memory userData
) external returns (uint256 bptAmountIn);
/**
* @notice Queries a `removeLiquidityCustom` operation without actually executing it.
* @param pool Address of the liquidity pool
* @param maxBptAmountIn Maximum amount of pool tokens provided
* @param minAmountsOut Expected minimum amounts of tokens to receive, sorted in token registration order
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data sent with the query request
* @return bptAmountIn Expected amount of pool tokens to burn
* @return amountsOut Expected amounts of tokens to receive, sorted in token registration order
* @return returnData Arbitrary (optional) data with an encoded response from the pool
*/
function queryRemoveLiquidityCustom(
address pool,
uint256 maxBptAmountIn,
uint256[] memory minAmountsOut,
address sender,
bytes memory userData
) external returns (uint256 bptAmountIn, uint256[] memory amountsOut, bytes memory returnData);
/**
* @notice Queries a `removeLiquidityRecovery` operation without actually executing it.
* @param pool Address of the liquidity pool
* @param exactBptAmountIn Exact amount of pool tokens provided for the query
* @return amountsOut Expected amounts of tokens to receive, sorted in token registration order
*/
function queryRemoveLiquidityRecovery(
address pool,
uint256 exactBptAmountIn
) external returns (uint256[] memory amountsOut);
/**
* @notice Queries a swap operation specifying an exact input token amount without actually executing it.
* @param pool Address of the liquidity pool
* @param tokenIn Token to be swapped from
* @param tokenOut Token to be swapped to
* @param exactAmountIn Exact amounts of input tokens to send
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data sent with the query request
* @return amountOut Calculated amount of output tokens to be received in exchange for the given input tokens
*/
function querySwapSingleTokenExactIn(
address pool,
IERC20 tokenIn,
IERC20 tokenOut,
uint256 exactAmountIn,
address sender,
bytes calldata userData
) external returns (uint256 amountOut);
/**
* @notice Queries a swap operation specifying an exact output token amount without actually executing it.
* @param pool Address of the liquidity pool
* @param tokenIn Token to be swapped from
* @param tokenOut Token to be swapped to
* @param exactAmountOut Exact amounts of input tokens to receive
* @param sender The sender passed to the operation. It can influence results (e.g., with user-dependent hooks)
* @param userData Additional (optional) data sent with the query request
* @return amountIn Calculated amount of input tokens to be sent in exchange for the requested output tokens
*/
function querySwapSingleTokenExactOut(
address pool,
IERC20 tokenIn,
IERC20 tokenOut,
uint256 exactAmountOut,
address sender,
bytes calldata userData
) external returns (uint256 amountIn);
}
"
},
"lib/balancer-v3-monorepo/pkg/interfaces/contracts/vault/IBatchRouter.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { AddLiquidityKind, RemoveLiquidityKind, SwapKind } from "./VaultTypes.sol";
/// @notice Interface for the `BatchRouter`, supporting multi-hop swaps.
interface IBatchRouter {
/***************************************************************************
Swaps
***************************************************************************/
struct SwapPathStep {
address pool;
IERC20 tokenOut;
// If true, the "pool" is an ERC4626 Buffer. Used to wrap/unwrap tokens if pool doesn't have enough liquidity.
bool isBuffer;
}
struct SwapPathExactAmountIn {
IERC20 tokenIn;
// For each step:
// If tokenIn == pool, use removeLiquidity SINGLE_TOKEN_EXACT_IN.
// If tokenOut == pool, use addLiquidity UNBALANCED.
SwapPathStep[] steps;
uint256 exactAmountIn;
uint256 minAmountOut;
}
struct SwapPathExactAmountOut {
IERC20 tokenIn;
// for each step:
// If tokenIn == pool, use removeLiquidity SINGLE_TOKEN_EXACT_OUT.
// If tokenOut == pool, use addLiquidity SINGLE_TOKEN_EXACT_OUT.
SwapPathStep[] steps;
uint256 maxAmountIn;
uint256 exactAmountOut;
}
struct SwapExactInHookParams {
address sender;
SwapPathExactAmountIn[] paths;
uint256 deadline;
bool wethIsEth;
bytes userData;
}
struct SwapExactOutHookParams {
address sender;
SwapPathExactAmountOut[] paths;
uint256 deadline;
bool wethIsEth;
bytes userData;
}
/**
* @notice Executes a swap operation involving multiple paths (steps), specifying exact input token amounts.
* @param paths Swap paths from token
Submitted on: 2025-10-07 11:45:29
Comments
Log in to comment.
No comments yet.