Skip to content

Commit 5211a7e

Browse files
committed
feat: add unit tests to check report simulation
1 parent c45288b commit 5211a7e

File tree

2 files changed

+174
-10
lines changed

2 files changed

+174
-10
lines changed

test/0.8.9/accounting.handleOracleReport.test.ts

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,161 @@ describe("Accounting.sol:report", () => {
121121
expect(simulated.postTotalShares).to.equal(preTotalShares);
122122
expect(simulated.postTotalPooledEther).to.equal(preTotalPooledEther);
123123
});
124+
125+
context("should match handleOracleReport results", () => {
126+
it("happy path", async () => {
127+
const simulated = await accounting.simulateOracleReport(report());
128+
129+
await accounting.handleOracleReport(report());
130+
131+
const postExternalShares = await lido.getExternalShares();
132+
133+
expect(simulated.withdrawalsVaultTransfer).to.equal(0n);
134+
expect(simulated.elRewardsVaultTransfer).to.equal(0n);
135+
expect(simulated.etherToFinalizeWQ).to.equal(0n);
136+
expect(simulated.sharesToFinalizeWQ).to.equal(0n);
137+
expect(simulated.sharesToBurnForWithdrawals).to.equal(0n);
138+
expect(simulated.totalSharesToBurn).to.equal(0n);
139+
expect(simulated.sharesToMintAsFees).to.equal(0n);
140+
141+
expect(simulated.principalClBalance).to.equal(0n);
142+
143+
expect(simulated.preTotalShares).to.equal(await lido.getTotalShares());
144+
expect(simulated.preTotalPooledEther).to.equal(await lido.getTotalPooledEther());
145+
146+
expect(simulated.postTotalShares).to.equal(simulated.preTotalShares + simulated.sharesToMintAsFees);
147+
expect(simulated.postInternalShares).to.equal(
148+
simulated.preTotalShares - postExternalShares + simulated.sharesToMintAsFees,
149+
);
150+
});
151+
152+
it("with CL validators increase", async () => {
153+
const depositedValidators = 100n;
154+
const clValidators = 50n;
155+
156+
await lido.mock__setDepositedValidators(depositedValidators);
157+
158+
const reportData = report({ clValidators, clBalance: 0n });
159+
const simulated = await accounting.simulateOracleReport(reportData);
160+
const tx = await accounting.handleOracleReport(reportData);
161+
const receipt = await tx.wait();
162+
163+
const collectEvent = receipt?.logs
164+
.map((log) => {
165+
try {
166+
return lido.interface.parseLog({ topics: [...log.topics], data: log.data });
167+
} catch {
168+
return null;
169+
}
170+
})
171+
.find((e) => e?.name === "Mock__CollectRewardsAndProcessWithdrawals");
172+
173+
if (collectEvent) {
174+
expect(simulated.principalClBalance).to.equal(collectEvent.args._principalCLBalance);
175+
expect(simulated.withdrawalsVaultTransfer).to.equal(collectEvent.args._withdrawalsToWithdraw);
176+
expect(simulated.elRewardsVaultTransfer).to.equal(collectEvent.args._elRewardsToWithdraw);
177+
}
178+
179+
const postExternalShares = await lido.getExternalShares();
180+
181+
expect(simulated.withdrawalsVaultTransfer).to.equal(0n);
182+
expect(simulated.elRewardsVaultTransfer).to.equal(0n);
183+
expect(simulated.etherToFinalizeWQ).to.equal(0n);
184+
expect(simulated.sharesToFinalizeWQ).to.equal(0n);
185+
expect(simulated.sharesToBurnForWithdrawals).to.equal(0n);
186+
expect(simulated.totalSharesToBurn).to.equal(0n);
187+
expect(simulated.sharesToMintAsFees).to.equal(0n);
188+
189+
const expectedPrincipalCl = 0n + clValidators * 32n * 10n ** 18n;
190+
expect(simulated.principalClBalance).to.equal(expectedPrincipalCl);
191+
192+
expect(simulated.preTotalShares).to.equal(await lido.getTotalShares());
193+
expect(simulated.preTotalPooledEther).to.equal(await lido.getTotalPooledEther());
194+
195+
expect(simulated.postTotalShares).to.equal(simulated.preTotalShares + simulated.sharesToMintAsFees);
196+
expect(simulated.postInternalShares).to.equal(
197+
simulated.preTotalShares - postExternalShares + simulated.sharesToMintAsFees,
198+
);
199+
});
200+
201+
it("with withdrawal finalization", async () => {
202+
const depositedValidators = 100n;
203+
const clValidators = 100n;
204+
const clBalance = ether("3200");
205+
const withdrawalFinalizationBatches = [1n, 2n, 3n];
206+
const simulatedShareRate = 10n ** 27n;
207+
208+
await lido.mock__setDepositedValidators(depositedValidators);
209+
await withdrawalQueue.mock__prefinalizeReturn(ether("10"), ether("0.01"));
210+
211+
const reportData = report({
212+
clValidators,
213+
clBalance,
214+
withdrawalFinalizationBatches,
215+
simulatedShareRate,
216+
});
217+
218+
const simulated = await accounting.simulateOracleReport(reportData);
219+
220+
await accounting.handleOracleReport(reportData);
221+
222+
const postExternalShares = await lido.getExternalShares();
223+
224+
expect(simulated.withdrawalsVaultTransfer).to.equal(0n);
225+
expect(simulated.elRewardsVaultTransfer).to.equal(0n);
226+
expect(simulated.etherToFinalizeWQ).to.equal(ether("10"));
227+
expect(simulated.sharesToFinalizeWQ).to.equal(ether("0.01"));
228+
expect(simulated.sharesToBurnForWithdrawals).to.equal(0n);
229+
expect(simulated.totalSharesToBurn).to.equal(0n);
230+
expect(simulated.sharesToMintAsFees).to.equal(0n);
231+
232+
const expectedPrincipalCl = 0n + clValidators * 32n * 10n ** 18n;
233+
expect(simulated.principalClBalance).to.equal(expectedPrincipalCl);
234+
235+
expect(simulated.preTotalShares).to.equal(await lido.getTotalShares());
236+
expect(simulated.preTotalPooledEther).to.equal(await lido.getTotalPooledEther());
237+
238+
expect(simulated.postTotalShares).to.equal(simulated.preTotalShares + simulated.sharesToMintAsFees);
239+
expect(simulated.postInternalShares).to.equal(
240+
simulated.preTotalShares - postExternalShares + simulated.sharesToMintAsFees,
241+
);
242+
});
243+
244+
it("with bad debt and external shares", async () => {
245+
const totalShares = ether("1000");
246+
const externalShares = ether("100");
247+
const badDebtToInternalize = ether("50");
248+
249+
await lido.mock__setTotalShares(totalShares);
250+
await lido.mock__setExternalShares(externalShares);
251+
await vaultHub.setBadDebtToInternalize(badDebtToInternalize);
252+
253+
const simulated = await accounting.simulateOracleReport(report());
254+
255+
await accounting.handleOracleReport(report());
256+
257+
const preExternalShares = externalShares;
258+
const preInternalShares = totalShares - preExternalShares;
259+
260+
expect(simulated.withdrawalsVaultTransfer).to.equal(0n);
261+
expect(simulated.elRewardsVaultTransfer).to.equal(0n);
262+
expect(simulated.etherToFinalizeWQ).to.equal(0n);
263+
expect(simulated.sharesToFinalizeWQ).to.equal(0n);
264+
expect(simulated.sharesToBurnForWithdrawals).to.equal(0n);
265+
expect(simulated.totalSharesToBurn).to.equal(0n);
266+
expect(simulated.sharesToMintAsFees).to.equal(0n);
267+
268+
expect(simulated.principalClBalance).to.equal(0n);
269+
270+
expect(simulated.preTotalShares).to.equal(await lido.getTotalShares());
271+
expect(simulated.preTotalPooledEther).to.equal(await lido.getTotalPooledEther());
272+
273+
expect(simulated.postTotalShares).to.equal(simulated.preTotalShares + simulated.sharesToMintAsFees);
274+
expect(simulated.postInternalShares).to.equal(
275+
preInternalShares + simulated.sharesToMintAsFees + badDebtToInternalize,
276+
);
277+
});
278+
});
124279
});
125280

126281
context("handleOracleReport", () => {

test/0.8.9/contracts/Lido__MockForAccounting.sol

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ contract Lido__MockForAccounting {
77
uint256 public depositedValidatorsValue;
88
uint256 public reportClValidators;
99
uint256 public reportClBalance;
10+
uint256 private externalSharesValue = 0;
11+
uint256 private totalSharesValue = 1000000000000000000;
1012

11-
// Emitted when validators number delivered by the oracle
1213
event CLValidatorsUpdated(uint256 indexed reportTimestamp, uint256 preCLValidators, uint256 postCLValidators);
1314
event Mock__CollectRewardsAndProcessWithdrawals(
1415
uint256 _reportTimestamp,
@@ -20,17 +21,20 @@ contract Lido__MockForAccounting {
2021
uint256 _withdrawalsShareRate,
2122
uint256 _etherToLockOnWithdrawalQueue
2223
);
23-
/**
24-
* @notice An executed shares transfer from `sender` to `recipient`.
25-
*
26-
* @dev emitted in pair with an ERC20-defined `Transfer` event.
27-
*/
2824
event TransferShares(address indexed from, address indexed to, uint256 sharesValue);
2925

3026
function mock__setDepositedValidators(uint256 _amount) external {
3127
depositedValidatorsValue = _amount;
3228
}
3329

30+
function mock__setExternalShares(uint256 _amount) external {
31+
externalSharesValue = _amount;
32+
}
33+
34+
function mock__setTotalShares(uint256 _amount) external {
35+
totalSharesValue = _amount;
36+
}
37+
3438
function getBeaconStat()
3539
external
3640
view
@@ -45,12 +49,12 @@ contract Lido__MockForAccounting {
4549
return 3201000000000000000000;
4650
}
4751

48-
function getTotalShares() external pure returns (uint256) {
49-
return 1000000000000000000;
52+
function getTotalShares() external view returns (uint256) {
53+
return totalSharesValue;
5054
}
5155

52-
function getExternalShares() external pure returns (uint256) {
53-
return 0;
56+
function getExternalShares() external view returns (uint256) {
57+
return externalSharesValue;
5458
}
5559

5660
function getExternalEther() external pure returns (uint256) {
@@ -114,4 +118,9 @@ contract Lido__MockForAccounting {
114118
function mintShares(address _recipient, uint256 _sharesAmount) external {
115119
emit TransferShares(address(0), _recipient, _sharesAmount);
116120
}
121+
122+
function internalizeExternalBadDebt(uint256 _badDebt) external {
123+
externalSharesValue -= _badDebt;
124+
totalSharesValue = totalSharesValue;
125+
}
117126
}

0 commit comments

Comments
 (0)