Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] Aggregators router - hooks version #1221

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 3 additions & 116 deletions pkg/interfaces/contracts/vault/IRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ 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";
import { AddLiquidityKind, RemoveLiquidityKind } from "./VaultTypes.sol";
import { IRouterSwap } from "./IRouterSwap.sol";

/// @notice User-friendly interface to basic Vault operations: swap, add/remove liquidity, and associated queries.
interface IRouter {
interface IRouter is IRouterSwap {
/***************************************************************************
Pool Initialization
***************************************************************************/
Expand Down Expand Up @@ -235,82 +236,6 @@ interface IRouter {
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
***************************************************************************/
Expand Down Expand Up @@ -459,42 +384,4 @@ interface IRouter {
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);
}
9 changes: 9 additions & 0 deletions pkg/interfaces/contracts/vault/IRouterPaymentHooks.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

interface IRouterPaymentHooks {
function onPay(IERC20 token, uint256 amount, bytes calldata userData) external;
}
128 changes: 128 additions & 0 deletions pkg/interfaces/contracts/vault/IRouterSwap.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

import { SwapKind } from "./VaultTypes.sol";

/// @notice User-friendly interface to basic Vault operations: swap, add/remove liquidity, and associated queries.
interface IRouterSwap {
/***************************************************************************
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 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);
}
11 changes: 6 additions & 5 deletions pkg/pool-hooks/test/foundry/LotteryHookExample.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "forge-std/Test.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { IRouter } from "@balancer-labs/v3-interfaces/contracts/vault/IRouter.sol";
import { IRouterSwap } from "@balancer-labs/v3-interfaces/contracts/vault/IRouterSwap.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";
import {
LiquidityManagement,
Expand Down Expand Up @@ -254,15 +255,15 @@ contract LotteryHookExampleTest is BaseVaultTest {
bytes4 routerMethod;
// If kind is BOTH, odd iterations are EXACT_IN and even iterations are EXACT_OUT.
if (kind == SwapKindLottery.EXACT_IN || (kind == SwapKindLottery.BOTH && iterations % 2 == 1)) {
routerMethod = IRouter.swapSingleTokenExactIn.selector;
routerMethod = IRouterSwap.swapSingleTokenExactIn.selector;
} else {
routerMethod = IRouter.swapSingleTokenExactOut.selector;
routerMethod = IRouterSwap.swapSingleTokenExactOut.selector;
}

uint8 randomNumber = LotteryHookExample(poolHooksContract).getRandomNumber();

uint256 amountGiven = swapAmount;
uint256 amountCalculated = routerMethod == IRouter.swapSingleTokenExactIn.selector
uint256 amountCalculated = routerMethod == IRouterSwap.swapSingleTokenExactIn.selector
? swapAmount - hookFee // If EXACT_IN, amount calculated is amount out; user receives less
: swapAmount + hookFee; // If EXACT_IN, amount calculated is amount in; user pays more

Expand All @@ -280,7 +281,7 @@ contract LotteryHookExampleTest is BaseVaultTest {
emit LotteryHookExample.LotteryWinningsPaid(poolHooksContract, alice, IERC20(usdc), usdcWinnings);
}
} else {
if (routerMethod == IRouter.swapSingleTokenExactIn.selector) {
if (routerMethod == IRouterSwap.swapSingleTokenExactIn.selector) {
vm.expectEmit();
emit LotteryHookExample.LotteryFeeCollected(poolHooksContract, IERC20(usdc), hookFee);
} else {
Expand Down Expand Up @@ -311,7 +312,7 @@ contract LotteryHookExampleTest is BaseVaultTest {
if (randomNumber == LotteryHookExample(poolHooksContract).LUCKY_NUMBER()) {
break;
} else {
if (routerMethod == IRouter.swapSingleTokenExactIn.selector) {
if (routerMethod == IRouterSwap.swapSingleTokenExactIn.selector) {
accruedFees[usdcIdx] += hookFee;
} else {
accruedFees[daiIdx] += hookFee;
Expand Down
Loading
Loading