-
Notifications
You must be signed in to change notification settings - Fork 213
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
test: Second opinion oracle test #857
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
dc3c289
test: second opinion test
vp4242 5f3ce0b
feat: remove unused stub
vp4242 87a29e0
test: improve test
vp4242 ecf9fd0
fix: modify accounting to enable negative rebase checker
vp4242 4ec12a8
test: refine second opinion test
vp4242 81eef77
feat: refine comments
vp4242 47e43bd
test: second opinion test
vp4242 3ff99fc
feat: remove unused stub
vp4242 66f5e7c
test: improve test
vp4242 3249449
fix: modify accounting to enable negative rebase checker
vp4242 c0f4b98
test: refine second opinion test
vp4242 f45f506
feat: refine comments
vp4242 5257193
Merge branch 'feat/second-opinion-test' of git-ldo:lidofinance/core i…
vp4242 d2e4479
Merge branch 'develop' into feat/second-opinion-test
tamtamchik 2ec00cc
fix: tests
tamtamchik a862c7a
fix: fix mainnet fork update for sanity checker
tamtamchik 071b0df
feat: fix clBalanceOraclesErrorUpperBPLimit value
vp4242 261f90f
fix: clBalanceOraclesErrorUpperBPLimit value
vp4242 5897662
Merge branch 'feat/second-opinion-test' of git-ldo:lidofinance/lido-d…
vp4242 ba3c50e
test: improve second opinion tests
vp4242 7dea205
fix: remove PostTotalShares event checks
tamtamchik 5888b1c
chore: fix SecondOpinionOracleMock naming
tamtamchik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
import { expect } from "chai"; | ||
import { ethers } from "hardhat"; | ||
|
||
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; | ||
|
||
import { SecondOpinionOracle__Mock } from "typechain-types"; | ||
|
||
import { ether, impersonate, log, ONE_GWEI } from "lib"; | ||
import { getProtocolContext, ProtocolContext } from "lib/protocol"; | ||
import { finalizeWithdrawalQueue, norEnsureOperators, report, sdvtEnsureOperators } from "lib/protocol/helpers"; | ||
|
||
import { bailOnFailure, Snapshot } from "test/suite"; | ||
|
||
const AMOUNT = ether("100"); | ||
const MAX_DEPOSIT = 150n; | ||
const CURATED_MODULE_ID = 1n; | ||
const INITIAL_REPORTED_BALANCE = ether("32") * 3n; // 32 ETH * 3 validators | ||
|
||
const ZERO_HASH = new Uint8Array(32).fill(0); | ||
|
||
// Diff amount is 10% of total supply | ||
function getDiffAmount(totalSupply: bigint): bigint { | ||
return (totalSupply / 10n / ONE_GWEI) * ONE_GWEI; | ||
} | ||
|
||
describe("Second opinion", () => { | ||
let ctx: ProtocolContext; | ||
|
||
let ethHolder: HardhatEthersSigner; | ||
let stEthHolder: HardhatEthersSigner; | ||
|
||
let snapshot: string; | ||
let originalState: string; | ||
|
||
let secondOpinion: SecondOpinionOracle__Mock; | ||
let totalSupply: bigint; | ||
|
||
before(async () => { | ||
ctx = await getProtocolContext(); | ||
|
||
[stEthHolder, ethHolder] = await ethers.getSigners(); | ||
|
||
snapshot = await Snapshot.take(); | ||
|
||
const { lido, depositSecurityModule, oracleReportSanityChecker } = ctx.contracts; | ||
|
||
await finalizeWithdrawalQueue(ctx, stEthHolder, ethHolder); | ||
|
||
await norEnsureOperators(ctx, 3n, 5n); | ||
await sdvtEnsureOperators(ctx, 3n, 5n); | ||
|
||
const { chainId } = await ethers.provider.getNetwork(); | ||
// Sepolia-specific initialization | ||
if (chainId === 11155111n) { | ||
// Sepolia deposit contract address https://sepolia.etherscan.io/token/0x7f02c3e3c98b133055b8b348b2ac625669ed295d | ||
const sepoliaDepositContractAddress = "0x7f02C3E3c98b133055B8B348B2Ac625669Ed295D"; | ||
const bepoliaWhaleHolder = "0xf97e180c050e5Ab072211Ad2C213Eb5AEE4DF134"; | ||
const BEPOLIA_TO_TRANSFER = 20; | ||
|
||
const bepoliaToken = await ethers.getContractAt("ISepoliaDepositContract", sepoliaDepositContractAddress); | ||
const bepiloaSigner = await ethers.getImpersonatedSigner(bepoliaWhaleHolder); | ||
|
||
const adapterAddr = await ctx.contracts.stakingRouter.DEPOSIT_CONTRACT(); | ||
await bepoliaToken.connect(bepiloaSigner).transfer(adapterAddr, BEPOLIA_TO_TRANSFER); | ||
} | ||
const dsmSigner = await impersonate(depositSecurityModule.address, AMOUNT); | ||
await lido.connect(dsmSigner).deposit(MAX_DEPOSIT, CURATED_MODULE_ID, ZERO_HASH); | ||
|
||
secondOpinion = await ethers.deployContract("SecondOpinionOracle__Mock", []); | ||
const soAddress = await secondOpinion.getAddress(); | ||
|
||
const agentSigner = await ctx.getSigner("agent", AMOUNT); | ||
await oracleReportSanityChecker | ||
.connect(agentSigner) | ||
.grantRole(await oracleReportSanityChecker.SECOND_OPINION_MANAGER_ROLE(), agentSigner.address); | ||
|
||
let { beaconBalance } = await lido.getBeaconStat(); | ||
// Report initial balances if TVL is zero | ||
if (beaconBalance === 0n) { | ||
await report(ctx, { | ||
clDiff: INITIAL_REPORTED_BALANCE, | ||
clAppearedValidators: 3n, | ||
excludeVaultsBalances: true, | ||
}); | ||
beaconBalance = (await lido.getBeaconStat()).beaconBalance; | ||
} | ||
totalSupply = beaconBalance; | ||
|
||
await oracleReportSanityChecker.connect(agentSigner).setSecondOpinionOracleAndCLBalanceUpperMargin(soAddress, 74n); | ||
}); | ||
|
||
beforeEach(bailOnFailure); | ||
|
||
beforeEach(async () => (originalState = await Snapshot.take())); | ||
|
||
afterEach(async () => await Snapshot.restore(originalState)); | ||
|
||
after(async () => await Snapshot.restore(snapshot)); // Rollback to the initial state pre deployment | ||
|
||
it("Should fail report without second opinion ready", async () => { | ||
const { oracleReportSanityChecker } = ctx.contracts; | ||
|
||
const reportedDiff = getDiffAmount(totalSupply); | ||
|
||
await expect(report(ctx, { clDiff: -reportedDiff, excludeVaultsBalances: true })).to.be.revertedWithCustomError( | ||
oracleReportSanityChecker, | ||
"NegativeRebaseFailedSecondOpinionReportIsNotReady", | ||
); | ||
}); | ||
|
||
it("Should correctly report negative rebase with second opinion", async () => { | ||
const { hashConsensus, accountingOracle } = ctx.contracts; | ||
|
||
const reportedDiff = getDiffAmount(totalSupply); | ||
|
||
// Provide a second opinion | ||
const curFrame = await hashConsensus.getCurrentFrame(); | ||
const expectedBalance = (totalSupply - reportedDiff) / ONE_GWEI; | ||
await secondOpinion.addPlainReport(curFrame.reportProcessingDeadlineSlot, expectedBalance, 0n); | ||
|
||
const lastProcessingRefSlotBefore = await accountingOracle.getLastProcessingRefSlot(); | ||
await report(ctx, { clDiff: -reportedDiff, excludeVaultsBalances: true }); | ||
const lastProcessingRefSlotAfter = await accountingOracle.getLastProcessingRefSlot(); | ||
expect(lastProcessingRefSlotBefore).to.be.lessThan( | ||
lastProcessingRefSlotAfter, | ||
"LastProcessingRefSlot should be updated", | ||
); | ||
}); | ||
|
||
it("Should fail report with smaller second opinion cl balance", async () => { | ||
const { hashConsensus, oracleReportSanityChecker } = ctx.contracts; | ||
|
||
const reportedDiff = getDiffAmount(totalSupply); | ||
|
||
const curFrame = await hashConsensus.getCurrentFrame(); | ||
const expectedBalance = (totalSupply - reportedDiff) / ONE_GWEI - 1n; | ||
await secondOpinion.addPlainReport(curFrame.reportProcessingDeadlineSlot, expectedBalance, 0n); | ||
|
||
await expect(report(ctx, { clDiff: -reportedDiff, excludeVaultsBalances: true })).to.be.revertedWithCustomError( | ||
oracleReportSanityChecker, | ||
"NegativeRebaseFailedCLBalanceMismatch", | ||
); | ||
}); | ||
|
||
it("Should tolerate report with slightly bigger second opinion cl balance", async () => { | ||
const { hashConsensus, accountingOracle } = ctx.contracts; | ||
|
||
const reportedDiff = getDiffAmount(totalSupply); | ||
|
||
const curFrame = await hashConsensus.getCurrentFrame(); | ||
const expectedBalance = (totalSupply - reportedDiff) / ONE_GWEI; | ||
// Less than 0.5% diff in balances | ||
const correction = (expectedBalance * 4n) / 1000n; | ||
await secondOpinion.addPlainReport(curFrame.reportProcessingDeadlineSlot, expectedBalance + correction, 0n); | ||
log.debug("Reporting parameters", { | ||
totalSupply, | ||
reportedDiff, | ||
expectedBalance, | ||
correction, | ||
reportedBalance: totalSupply - reportedDiff, | ||
}); | ||
|
||
const lastProcessingRefSlotBefore = await accountingOracle.getLastProcessingRefSlot(); | ||
await report(ctx, { clDiff: -reportedDiff, excludeVaultsBalances: true }); | ||
const lastProcessingRefSlotAfter = await accountingOracle.getLastProcessingRefSlot(); | ||
expect(lastProcessingRefSlotBefore).to.be.lessThan( | ||
lastProcessingRefSlotAfter, | ||
"LastProcessingRefSlot should be updated", | ||
); | ||
}); | ||
|
||
it("Should fail report with significantly bigger second opinion cl balance", async () => { | ||
const { hashConsensus, oracleReportSanityChecker } = ctx.contracts; | ||
|
||
const reportedDiff = getDiffAmount(totalSupply); | ||
|
||
const curFrame = await hashConsensus.getCurrentFrame(); | ||
const expectedBalance = (totalSupply - reportedDiff) / ONE_GWEI; | ||
// More than 0.5% diff in balances | ||
const correction = (expectedBalance * 9n) / 1000n; | ||
await secondOpinion.addPlainReport(curFrame.reportProcessingDeadlineSlot, expectedBalance + correction, 0n); | ||
log.debug("Reporting parameters", { | ||
totalSupply, | ||
reportedDiff, | ||
expectedBalance, | ||
correction, | ||
"expected + correction": expectedBalance + correction, | ||
}); | ||
|
||
await expect(report(ctx, { clDiff: -reportedDiff, excludeVaultsBalances: true })).to.be.revertedWithCustomError( | ||
oracleReportSanityChecker, | ||
"NegativeRebaseFailedCLBalanceMismatch", | ||
); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd move this to the protocol discovery since this may be used somewhere else, and here, take data from the context or move it under the protocol context flag to explicitly set tests state for Sepolia network.