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

add aggregators router #727

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
62 changes: 62 additions & 0 deletions pkg/interfaces/contracts/vault/IAggRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IAggRouter {
/***************************************************************************
Swaps
***************************************************************************/

/**
* @dev 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
* @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 the swap
*/
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 with the amount of tokens sent directly to the Vault
* @param pool Address of the liquidity pool
* @param tokenIn Token to be swapped from
* @param tokenOut Token to be swapped to
* @param minAmountOut Minimum amount of tokens to be received
* @param deadline Deadline for the swap
* @param userData Additional (optional) data required for the swap
* @param wethIsEth If true, incoming ETH will be wrapped to WETH and outgoing WETH will be unwrapped to ETH
* @return amountOut Calculated amount of output tokens to be received in exchange for the given input tokens
*/
function swapSingleTokenDonated(
address pool,
IERC20 tokenIn,
IERC20 tokenOut,
uint256 exactAmountIn,
uint256 minAmountOut,
uint256 deadline,
bool wethIsEth,
bytes calldata userData
) external payable returns (uint256 amountOut);
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
179.1k
179.3k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
171.6k
171.8k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
214.4k
214.5k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
167.2k
167.3k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
166.4k
166.5k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
176.7k
176.8k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
178.5k
178.7k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
164.7k
164.8k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
234.2k
234.4k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
193.0k
193.2k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
230.5k
230.6k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
222.1k
222.3k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
187.7k
187.8k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
197.9k
198.0k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
211.6k
211.8k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
180.6k
180.8k
Original file line number Diff line number Diff line change
@@ -1 +1 @@
177.9k
178.0k
133 changes: 133 additions & 0 deletions pkg/vault/contracts/AggRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { IPermit2 } from "permit2/src/interfaces/IPermit2.sol";

import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";

import { IAggRouter } from "@balancer-labs/v3-interfaces/contracts/vault/IAggRouter.sol";
import { IWETH } from "@balancer-labs/v3-interfaces/contracts/solidity-utils/misc/IWETH.sol";
import "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol";

import {
ReentrancyGuardTransient
} from "@balancer-labs/v3-solidity-utils/contracts/openzeppelin/ReentrancyGuardTransient.sol";

import { RouterCommon } from "./RouterCommon.sol";

contract AggRouter is IAggRouter, RouterCommon, ReentrancyGuardTransient {
using SafeERC20 for IERC20;
using Address for address payable;

constructor(IVault vault, IWETH weth, IPermit2 permit2) RouterCommon(vault, weth, permit2) {
// solhint-disable-previous-line no-empty-blocks
}

/// @inheritdoc IAggRouter
function swapSingleTokenDonated(
address pool,
IERC20 tokenIn,
IERC20 tokenOut,
uint256 exactAmountIn,
uint256 minAmountOut,
uint256 deadline,
bool wethIsEth,
bytes calldata userData
) external payable saveSender(msg.sender) returns (uint256) {
return
abi.decode(
_vault.unlock(
_swapSingleTokenArgs(
pool,
tokenIn,
tokenOut,
exactAmountIn,
minAmountOut,
deadline,
wethIsEth,
userData
)
),
(uint256)
);
}

// Addresses stack-too-deep.
function _swapSingleTokenArgs(
address pool,
IERC20 tokenIn,
IERC20 tokenOut,
uint256 exactAmountIn,
uint256 minAmountOut,
uint256 deadline,
bool wethIsEth,
bytes calldata userData
) private view returns (bytes memory) {
return
abi.encodeWithSelector(
AggRouter.swapSingleTokenDonatedHook.selector,
SwapSingleTokenHookParams({
sender: msg.sender,
kind: SwapKind.EXACT_IN,
pool: pool,
tokenIn: tokenIn,
tokenOut: tokenOut,
amountGiven: exactAmountIn,
limit: minAmountOut,
deadline: deadline,
wethIsEth: wethIsEth,
userData: userData
})
);
}

/**
* @notice Hook for swaps.
* @dev Can only be called by the Vault. Also handles native ETH.
* @param params Swap parameters (see IRouter for struct definition)
* @return Token amount calculated by the pool math (e.g., amountOut for a exact in swap)
*/
function swapSingleTokenDonatedHook(
SwapSingleTokenHookParams calldata params
) external nonReentrant onlyVault returns (uint256) {
(uint256 amountCalculated, uint256 amountIn, uint256 amountOut) = _swapHook(params);

IERC20 tokenIn = params.tokenIn;

_vault.settle(tokenIn, amountIn);
_sendTokenOut(params.sender, params.tokenOut, amountOut, params.wethIsEth);

if (tokenIn == _weth) {
// Return the rest of ETH to sender
_returnEth(params.sender);
}

return amountCalculated;
}

function _swapHook(
SwapSingleTokenHookParams calldata params
) internal returns (uint256 amountCalculated, uint256 amountIn, uint256 amountOut) {
// The deadline is timestamp-based: it should not be relied upon for sub-minute accuracy.
// solhint-disable-next-line not-rely-on-time
if (block.timestamp > params.deadline) {
revert SwapDeadline();
}

(amountCalculated, amountIn, amountOut) = _vault.swap(
VaultSwapParams({
kind: params.kind,
pool: params.pool,
tokenIn: params.tokenIn,
tokenOut: params.tokenOut,
amountGivenRaw: params.amountGiven,
limitRaw: params.limit,
userData: params.userData
})
);
}
}
9 changes: 9 additions & 0 deletions pkg/vault/contracts/test/AggRouterMock.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 "../AggRouter.sol";

contract AggRouterMock is AggRouter {
constructor(IVault vault, IWETH weth, IPermit2 permit2) AggRouter(vault, weth, permit2) {}
}
2 changes: 2 additions & 0 deletions pkg/vault/test/.contract-sizes/AggRouter
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Bytecode 6.289
InitCode 6.948
1 change: 1 addition & 0 deletions pkg/vault/test/ContractSizes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('ContractSizes', function () {
'Router',
'BatchRouter',
'CompositeLiquidityRouter',
'AggRouter',
]) {
const artifact = getArtifact(`v3-vault/${contractName}`);

Expand Down
Loading
Loading