Skip to content

Commit a8ea627

Browse files
committed
feat: add vault accounting integration test for withdrawals
1 parent 37eb353 commit a8ea627

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { expect } from "chai";
2+
import { ethers } from "hardhat";
3+
4+
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
5+
import { setBalance } from "@nomicfoundation/hardhat-network-helpers";
6+
7+
import { Dashboard, StakingVault, VaultHub } from "typechain-types";
8+
9+
import { findEventsWithInterfaces } from "lib";
10+
import {
11+
createVaultWithDashboard,
12+
getProtocolContext,
13+
ProtocolContext,
14+
report,
15+
reportVaultDataWithProof,
16+
setupLidoForVaults,
17+
} from "lib/protocol";
18+
import { finalizeWQViaElVault } from "lib/protocol";
19+
import { ether } from "lib/units";
20+
21+
import { Snapshot } from "test/suite";
22+
23+
describe("Integration: accounting", () => {
24+
let ctx: ProtocolContext;
25+
let snapshot: string;
26+
let originalSnapshot: string;
27+
28+
let owner: HardhatEthersSigner;
29+
let stranger: HardhatEthersSigner;
30+
let agentSigner: HardhatEthersSigner;
31+
let nodeOperator: HardhatEthersSigner;
32+
let stakingVault: StakingVault;
33+
let dashboard: Dashboard;
34+
let vaultHub: VaultHub;
35+
36+
before(async () => {
37+
ctx = await getProtocolContext();
38+
const { stakingVaultFactory } = ctx.contracts;
39+
vaultHub = ctx.contracts.vaultHub;
40+
originalSnapshot = await Snapshot.take();
41+
42+
[, owner, nodeOperator, stranger] = await ethers.getSigners();
43+
await setupLidoForVaults(ctx);
44+
45+
agentSigner = await ctx.getSigner("agent");
46+
47+
({ stakingVault, dashboard } = await createVaultWithDashboard(
48+
ctx,
49+
stakingVaultFactory,
50+
owner,
51+
nodeOperator,
52+
nodeOperator,
53+
));
54+
55+
dashboard = dashboard.connect(owner);
56+
57+
await dashboard.fund({ value: ether("100") });
58+
await dashboard.mintShares(owner, await dashboard.remainingMintingCapacityShares(0n));
59+
60+
await ctx.contracts.lido.connect(stranger).submit(owner.address, { value: ether("100") });
61+
62+
await finalizeWQViaElVault(ctx);
63+
await reportVaultDataWithProof(ctx, stakingVault);
64+
65+
await setBalance(ctx.contracts.elRewardsVault.address, 0);
66+
await setBalance(ctx.contracts.withdrawalVault.address, 0);
67+
});
68+
69+
beforeEach(async () => (snapshot = await Snapshot.take()));
70+
afterEach(async () => await Snapshot.restore(snapshot));
71+
after(async () => await Snapshot.restore(originalSnapshot));
72+
73+
context("Withdrawals: finalization with external shares", () => {
74+
it("Should finalize requests from withdrawal vault using force rebalance", async () => {
75+
const withdrawalRequestAmount = ether("10");
76+
const { withdrawalQueue, lido } = ctx.contracts;
77+
const stakingVaultAddress = await stakingVault.getAddress();
78+
79+
await lido.connect(owner).approve(withdrawalQueue.address, withdrawalRequestAmount);
80+
await lido.connect(stranger).approve(withdrawalQueue.address, withdrawalRequestAmount);
81+
82+
const firstRequestTx = await withdrawalQueue
83+
.connect(owner)
84+
.requestWithdrawals([withdrawalRequestAmount], owner.address);
85+
const secondRequestTx = await withdrawalQueue
86+
.connect(stranger)
87+
.requestWithdrawals([withdrawalRequestAmount], stranger.address);
88+
89+
const firstRequestReceipt = await firstRequestTx.wait();
90+
const secondRequestReceipt = await secondRequestTx.wait();
91+
92+
const [firstRequestEvent] = findEventsWithInterfaces(firstRequestReceipt!, "WithdrawalRequested", [
93+
withdrawalQueue.interface,
94+
]);
95+
const [secondRequestEvent] = findEventsWithInterfaces(secondRequestReceipt!, "WithdrawalRequested", [
96+
withdrawalQueue.interface,
97+
]);
98+
99+
const firstRequest = firstRequestEvent!.args.requestId;
100+
const secondRequest = secondRequestEvent!.args.requestId;
101+
102+
let [firstStatus, secondStatus] = await withdrawalQueue.getWithdrawalStatus([firstRequest, secondRequest]);
103+
104+
expect(firstStatus.isFinalized).to.be.false;
105+
expect(secondStatus.isFinalized).to.be.false;
106+
107+
// Set balance to cover only first request
108+
await setBalance(ctx.contracts.lido.address, withdrawalRequestAmount);
109+
110+
await expect(report(ctx, { clDiff: 0n })).to.be.reverted;
111+
112+
const balanceBefore = await ethers.provider.getBalance(stakingVaultAddress);
113+
114+
await vaultHub.connect(agentSigner).setLiabilitySharesTarget(stakingVaultAddress, 0n);
115+
const forceRebalanceTx = await vaultHub.connect(agentSigner).forceRebalance(stakingVaultAddress);
116+
117+
const forceRebalanceReceipt = await forceRebalanceTx.wait();
118+
const [rebalanceEvent] = findEventsWithInterfaces(forceRebalanceReceipt!, "VaultRebalanced", [
119+
vaultHub.interface,
120+
]);
121+
const rebalancedValue = rebalanceEvent!.args.etherWithdrawn;
122+
123+
await report(ctx, { clDiff: 0n });
124+
125+
const balanceAfter = await ethers.provider.getBalance(stakingVault);
126+
127+
[firstStatus, secondStatus] = await withdrawalQueue.getWithdrawalStatus([firstRequest, secondRequest]);
128+
129+
expect(firstStatus.isFinalized).to.be.true;
130+
expect(secondStatus.isFinalized).to.be.true;
131+
132+
const balanceWithdrawn = balanceBefore - balanceAfter;
133+
134+
expect(balanceWithdrawn).to.equal(rebalancedValue);
135+
expect(rebalancedValue).to.be.gte(withdrawalRequestAmount);
136+
});
137+
});
138+
});

0 commit comments

Comments
 (0)