Skip to content
Closed
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

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions v3/tasks/20250826-v3-unbalanced-add-router/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { UnbalancedLiquidityRouterViaSwapDeployment } from './input';
import { Task, TaskRunOptions } from '@src';

/* eslint-disable @typescript-eslint/no-non-null-assertion */
export default async (task: Task, { force, from }: TaskRunOptions = {}): Promise<void> => {
const input = task.input() as UnbalancedLiquidityRouterViaSwapDeployment;

const routerArgs = [input.Vault, input.Permit2, input.WETH, input.RouterVersion];
await task.deployAndVerify('AddUnbalancedLiquidityViaSwapRouter', routerArgs, from, force);
};
20 changes: 20 additions & 0 deletions v3/tasks/20250826-v3-unbalanced-add-router/input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Task, TaskMode } from '@src';

export type UnbalancedLiquidityRouterViaSwapDeployment = {
Vault: string;
Permit2: string;
WETH: string;
RouterVersion: string;
};

const Vault = new Task('20241204-v3-vault', TaskMode.READ_ONLY);
const Permit2 = new Task('00000000-permit2', TaskMode.READ_ONLY);
const WETH = new Task('00000000-tokens', TaskMode.READ_ONLY);
const BaseVersion = { version: 1, deployment: '20250826-v3-unbalanced-add-router' };

export default {
Vault,
Permit2,
WETH,
RouterVersion: JSON.stringify({ name: 'AddUnbalancedLiquidityViaSwapRouter', ...BaseVersion }),
};
22 changes: 22 additions & 0 deletions v3/tasks/20250826-v3-unbalanced-add-router/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# 2025-03-07 - V3 Router V2

Redeployment of the Base Router for V3.
Contains `Router` for basic, single step operations (e.g., pool initialization, add, remove, swap).

The original router can still be used. The difference is this version saves the sender (i.e., uses the `saveSender` modifier) on initialization as well as on add liquidity operations, enabling pools to verify the sender on liquidity operations (e.g., to enforce a single-LP, as in liquidity bootstrapping or treasury management pools).

There were also other changes to the Router contract and inheritance hierarchy since the original deployment (e.g., introduction of WethLib and `SenderGuard` in `RouterCommon`), but these are refactors designed to make router development easier (e.g., for the AggregatorRouter), and are not semantic or interface changes.

## Useful Files

- [Code](https://github.com/balancer/balancer-v3-monorepo/commit/577b86c7aec06c01e5f57bf20d4a0f728ce249b2).
- [Ethereum mainnet addresses](./output/mainnet.json)

Check failure on line 13 in v3/tasks/20250826-v3-unbalanced-add-router/readme.md

View workflow job for this annotation

GitHub Actions / Linkspector

[linkspector] v3/tasks/20250826-v3-unbalanced-add-router/readme.md#L13

Cannot reach ./output/mainnet.json Status: 404 Cannot find: ./output/mainnet.json
Raw output
message:"Cannot reach ./output/mainnet.json Status: 404 Cannot find: ./output/mainnet.json" location:{path:"v3/tasks/20250826-v3-unbalanced-add-router/readme.md" range:{start:{line:13 column:3} end:{line:13 column:54}}} severity:ERROR source:{name:"linkspector" url:"https://github.com/UmbrellaDocs/linkspector"}
- [Gnosis mainnet addresses](./output/gnosis.json)

Check failure on line 14 in v3/tasks/20250826-v3-unbalanced-add-router/readme.md

View workflow job for this annotation

GitHub Actions / Linkspector

[linkspector] v3/tasks/20250826-v3-unbalanced-add-router/readme.md#L14

Cannot reach ./output/gnosis.json Status: 404 Cannot find: ./output/gnosis.json
Raw output
message:"Cannot reach ./output/gnosis.json Status: 404 Cannot find: ./output/gnosis.json" location:{path:"v3/tasks/20250826-v3-unbalanced-add-router/readme.md" range:{start:{line:14 column:3} end:{line:14 column:51}}} severity:ERROR source:{name:"linkspector" url:"https://github.com/UmbrellaDocs/linkspector"}
- [Arbitrum mainnet addresses](./output/arbitrum.json)

Check failure on line 15 in v3/tasks/20250826-v3-unbalanced-add-router/readme.md

View workflow job for this annotation

GitHub Actions / Linkspector

[linkspector] v3/tasks/20250826-v3-unbalanced-add-router/readme.md#L15

Cannot reach ./output/arbitrum.json Status: 404 Cannot find: ./output/arbitrum.json
Raw output
message:"Cannot reach ./output/arbitrum.json Status: 404 Cannot find: ./output/arbitrum.json" location:{path:"v3/tasks/20250826-v3-unbalanced-add-router/readme.md" range:{start:{line:15 column:3} end:{line:15 column:55}}} severity:ERROR source:{name:"linkspector" url:"https://github.com/UmbrellaDocs/linkspector"}
- [Base mainnet addresses](./output/base.json)

Check failure on line 16 in v3/tasks/20250826-v3-unbalanced-add-router/readme.md

View workflow job for this annotation

GitHub Actions / Linkspector

[linkspector] v3/tasks/20250826-v3-unbalanced-add-router/readme.md#L16

Cannot reach ./output/base.json Status: 404 Cannot find: ./output/base.json
Raw output
message:"Cannot reach ./output/base.json Status: 404 Cannot find: ./output/base.json" location:{path:"v3/tasks/20250826-v3-unbalanced-add-router/readme.md" range:{start:{line:16 column:3} end:{line:16 column:47}}} severity:ERROR source:{name:"linkspector" url:"https://github.com/UmbrellaDocs/linkspector"}
- [Optimism mainnet addresses](./output/optimism.json)

Check failure on line 17 in v3/tasks/20250826-v3-unbalanced-add-router/readme.md

View workflow job for this annotation

GitHub Actions / Linkspector

[linkspector] v3/tasks/20250826-v3-unbalanced-add-router/readme.md#L17

Cannot reach ./output/optimism.json Status: 404 Cannot find: ./output/optimism.json
Raw output
message:"Cannot reach ./output/optimism.json Status: 404 Cannot find: ./output/optimism.json" location:{path:"v3/tasks/20250826-v3-unbalanced-add-router/readme.md" range:{start:{line:17 column:3} end:{line:17 column:55}}} severity:ERROR source:{name:"linkspector" url:"https://github.com/UmbrellaDocs/linkspector"}
- [Avalanche mainnet addresses](./output/avalanche.json)

Check failure on line 18 in v3/tasks/20250826-v3-unbalanced-add-router/readme.md

View workflow job for this annotation

GitHub Actions / Linkspector

[linkspector] v3/tasks/20250826-v3-unbalanced-add-router/readme.md#L18

Cannot reach ./output/avalanche.json Status: 404 Cannot find: ./output/avalanche.json
Raw output
message:"Cannot reach ./output/avalanche.json Status: 404 Cannot find: ./output/avalanche.json" location:{path:"v3/tasks/20250826-v3-unbalanced-add-router/readme.md" range:{start:{line:18 column:3} end:{line:18 column:57}}} severity:ERROR source:{name:"linkspector" url:"https://github.com/UmbrellaDocs/linkspector"}
- [Hyperevm mainnet addresses](./output/hyperevm.json)

Check failure on line 19 in v3/tasks/20250826-v3-unbalanced-add-router/readme.md

View workflow job for this annotation

GitHub Actions / Linkspector

[linkspector] v3/tasks/20250826-v3-unbalanced-add-router/readme.md#L19

Cannot reach ./output/hyperevm.json Status: 404 Cannot find: ./output/hyperevm.json
Raw output
message:"Cannot reach ./output/hyperevm.json Status: 404 Cannot find: ./output/hyperevm.json" location:{path:"v3/tasks/20250826-v3-unbalanced-add-router/readme.md" range:{start:{line:19 column:3} end:{line:19 column:55}}} severity:ERROR source:{name:"linkspector" url:"https://github.com/UmbrellaDocs/linkspector"}
- [Sepolia testnet addresses](./output/sepolia.json)

Check failure on line 20 in v3/tasks/20250826-v3-unbalanced-add-router/readme.md

View workflow job for this annotation

GitHub Actions / Linkspector

[linkspector] v3/tasks/20250826-v3-unbalanced-add-router/readme.md#L20

Cannot reach ./output/sepolia.json Status: 404 Cannot find: ./output/sepolia.json
Raw output
message:"Cannot reach ./output/sepolia.json Status: 404 Cannot find: ./output/sepolia.json" location:{path:"v3/tasks/20250826-v3-unbalanced-add-router/readme.md" range:{start:{line:20 column:3} end:{line:20 column:53}}} severity:ERROR source:{name:"linkspector" url:"https://github.com/UmbrellaDocs/linkspector"}
- [`Router` artifact](./artifact/Router.json)

Check failure on line 21 in v3/tasks/20250826-v3-unbalanced-add-router/readme.md

View workflow job for this annotation

GitHub Actions / Linkspector

[linkspector] v3/tasks/20250826-v3-unbalanced-add-router/readme.md#L21

Cannot reach ./artifact/Router.json Status: 404 Cannot find: ./artifact/Router.json
Raw output
message:"Cannot reach ./artifact/Router.json Status: 404 Cannot find: ./artifact/Router.json" location:{path:"v3/tasks/20250826-v3-unbalanced-add-router/readme.md" range:{start:{line:21 column:3} end:{line:21 column:46}}} severity:ERROR source:{name:"linkspector" url:"https://github.com/UmbrellaDocs/linkspector"}

119 changes: 119 additions & 0 deletions v3/tasks/20250826-v3-unbalanced-add-router/test/task.fork.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import hre, { ethers } from 'hardhat';
import { expect } from 'chai';
import { describeForkTest, getForkedNetwork, getSigner, impersonate, Task, TaskMode } from '@src';
import { Contract } from 'ethers';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { fp } from '@helpers/numbers';
import { ZERO_ADDRESS } from '@helpers/constants';
import { UnbalancedLiquidityRouterViaSwapDeployment } from '../input';
import { currentTimestamp, MINUTE } from '@helpers/time';

describeForkTest('V3-UnbalancedAddRouter', 'mainnet', 23227500, function () {
const versionNumber = 1;

const TASK_NAME = '20250826-v3-unbalanced-add-router';
const CONTRACT_NAME = 'AddUnbalancedLiquidityViaSwapRouter';
const POOL_CONTRACT_NAME = 'ReClammPool';

let task: Task;
let router: Contract, permit2: Contract;
let pool: Contract;
let wethSigner: SignerWithAddress, alice: SignerWithAddress;
let input: UnbalancedLiquidityRouterViaSwapDeployment;

const AAVE = '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9';
let WETH: string;
const AAVE_WETH_POOL = '0x9d1fcf346ea1b073de4d5834e25572cc6ad71f4d';

before('run task', async () => {
task = new Task(TASK_NAME, TaskMode.TEST, getForkedNetwork(hre));
await task.run({ force: true });

input = task.input() as UnbalancedLiquidityRouterViaSwapDeployment;

router = await task.deployedInstance(CONTRACT_NAME);
permit2 = await task.instanceAt('IPermit2', input.Permit2);

WETH = input.WETH;
wethSigner = await impersonate(input.WETH, fp(10e8));
alice = await getSigner();
});

before('setup contracts and parameters', async () => {
const task = new Task('20250702-v3-reclamm-pool-v2', TaskMode.READ_ONLY, getForkedNetwork(hre));
pool = await task.instanceAt(POOL_CONTRACT_NAME, AAVE_WETH_POOL);
});

it('checks router version', async () => {
const routerVersion = JSON.parse(await router.version());
expect(routerVersion.name).to.be.eq(CONTRACT_NAME);
expect(routerVersion.version).to.be.eq(versionNumber);
expect(routerVersion.deployment).to.be.eq(TASK_NAME);
});

it('checks getters', async () => {
expect(await router.getPermit2()).to.eq(permit2.address);
expect(await router.getWeth()).to.eq(input.WETH);
});

it('checks router WETH', async () => {
const wethTx = wethSigner.sendTransaction({
to: router.address,
value: ethers.utils.parseEther('1.0'),
});
await expect(wethTx).to.not.be.reverted;

const aliceTx = alice.sendTransaction({
to: router.address,
value: ethers.utils.parseEther('1.0'),
});
await expect(aliceTx).to.be.reverted;
});

it('adds liquidity unbalanced to a reclamm pool', async () => {
const totalSupply = await pool.totalSupply();
const bptAmountOut = totalSupply.div(10);

const amountsIn = await router
.connect(ZERO_ADDRESS)
.callStatic.queryAddLiquidityProportional(pool.address, bptAmountOut, alice.address, '0x');
const aaveAmountInAddProportional = amountsIn[0];
const wethAmountInAddProportional = amountsIn[1];

const wethSwapForAave = await router
.connect(ZERO_ADDRESS)
.callStatic.querySwapSingleTokenExactOut(
pool.address,
WETH,
AAVE,
aaveAmountInAddProportional,
alice.address,
'0x'
);

const addLiquidityParams = {
maxAmountsIn: amountsIn,
exactBptAmountOut: bptAmountOut,
userData: '0x',
};

const swapExactOutParams = {
tokenIn: WETH,
tokenOut: AAVE,
exactAmountOut: aaveAmountInAddProportional,
maxAmountIn: wethSwapForAave.mul(101).div(100), // 1% slippage
userData: '0x',
};

await router
.connect(alice)
.addUnbalancedLiquidityViaSwapExactOut(
pool.address,
(await currentTimestamp()).add(MINUTE),
true,
addLiquidityParams,
swapExactOutParams,
{ value: wethAmountInAddProportional.add(wethSwapForAave) }
);
});
});
Loading