Skip to content

Commit 2c4b322

Browse files
committed
refactor: refactor integration tests for CollateralManagement and FlyoverDiscovery
1 parent 155c223 commit 2c4b322

File tree

5 files changed

+984
-341
lines changed

5 files changed

+984
-341
lines changed
Lines changed: 391 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,391 @@
1+
import {
2+
loadFixture,
3+
mine,
4+
} from "@nomicfoundation/hardhat-toolbox/network-helpers";
5+
import { expect } from "chai";
6+
import { ethers } from "hardhat";
7+
import { deployDiscoveryFixture } from "../test/discovery/fixtures";
8+
import { ProviderType, COLLATERAL_CONSTANTS } from "../test/utils/constants";
9+
10+
describe("CollateralManagement Integration Tests", () => {
11+
describe("Cross-contract: Adding collateral affects Discovery", () => {
12+
it("should make provider operational in Discovery after adding sufficient collateral", async () => {
13+
const {
14+
discovery,
15+
collateralManagement,
16+
signers,
17+
MIN_COLLATERAL,
18+
owner,
19+
} = await loadFixture(deployDiscoveryFixture);
20+
const lp = signers.at(-1)!;
21+
22+
// Register with minimum collateral
23+
await discovery
24+
.connect(lp)
25+
.register("LP", "url", true, ProviderType.PegIn, {
26+
value: MIN_COLLATERAL,
27+
});
28+
29+
// Slash to below minimum
30+
await collateralManagement
31+
.connect(owner)
32+
.grantRole(
33+
await collateralManagement.COLLATERAL_SLASHER(),
34+
owner.address
35+
);
36+
const { getEmptyPegInQuote } = await import("../test/utils/quotes");
37+
const quote = getEmptyPegInQuote();
38+
quote.liquidityProviderRskAddress = lp.address;
39+
quote.penaltyFee = MIN_COLLATERAL;
40+
await collateralManagement
41+
.connect(owner)
42+
.slashPegInCollateral(ethers.ZeroAddress, quote, ethers.ZeroHash);
43+
44+
// Verify not operational in Discovery
45+
expect(
46+
await discovery.isOperational(ProviderType.PegIn, lp.address)
47+
).to.equal(false);
48+
49+
// Add collateral in CollateralManagement
50+
await collateralManagement
51+
.connect(lp)
52+
.addPegInCollateral({ value: MIN_COLLATERAL });
53+
54+
// Verify operational again in Discovery
55+
expect(
56+
await discovery.isOperational(ProviderType.PegIn, lp.address)
57+
).to.equal(true);
58+
expect(
59+
await collateralManagement.getPegInCollateral(lp.address)
60+
).to.equal(MIN_COLLATERAL);
61+
});
62+
});
63+
64+
describe("Cross-contract: Slashing affects Discovery", () => {
65+
it("should make provider non-operational in Discovery after slashing below minimum", async () => {
66+
const {
67+
discovery,
68+
collateralManagement,
69+
signers,
70+
MIN_COLLATERAL,
71+
owner,
72+
} = await loadFixture(deployDiscoveryFixture);
73+
const lp = signers.at(-1)!;
74+
75+
// Register with 2x minimum collateral
76+
await discovery
77+
.connect(lp)
78+
.register("LP", "url", true, ProviderType.PegIn, {
79+
value: MIN_COLLATERAL * 2n,
80+
});
81+
82+
// Verify operational
83+
expect(
84+
await discovery.isOperational(ProviderType.PegIn, lp.address)
85+
).to.equal(true);
86+
87+
// Slash in CollateralManagement to below minimum
88+
await collateralManagement
89+
.connect(owner)
90+
.grantRole(
91+
await collateralManagement.COLLATERAL_SLASHER(),
92+
owner.address
93+
);
94+
const { getEmptyPegInQuote } = await import("../test/utils/quotes");
95+
const quote = getEmptyPegInQuote();
96+
quote.liquidityProviderRskAddress = lp.address;
97+
quote.penaltyFee = MIN_COLLATERAL * 2n;
98+
99+
await collateralManagement
100+
.connect(owner)
101+
.slashPegInCollateral(ethers.ZeroAddress, quote, ethers.ZeroHash);
102+
103+
// Verify not operational in Discovery
104+
expect(
105+
await discovery.isOperational(ProviderType.PegIn, lp.address)
106+
).to.equal(false);
107+
108+
// Provider should also disappear from Discovery listing
109+
const providers = await discovery.getProviders();
110+
expect(providers.length).to.equal(0);
111+
});
112+
113+
it("should keep provider in Discovery listing if still above minimum after slashing", async () => {
114+
const {
115+
discovery,
116+
collateralManagement,
117+
signers,
118+
MIN_COLLATERAL,
119+
owner,
120+
} = await loadFixture(deployDiscoveryFixture);
121+
const lp = signers.at(-1)!;
122+
123+
// Register with 3x minimum collateral
124+
await discovery
125+
.connect(lp)
126+
.register("LP", "url", true, ProviderType.PegIn, {
127+
value: MIN_COLLATERAL * 3n,
128+
});
129+
130+
// Slash but keep above minimum
131+
await collateralManagement
132+
.connect(owner)
133+
.grantRole(
134+
await collateralManagement.COLLATERAL_SLASHER(),
135+
owner.address
136+
);
137+
const { getEmptyPegInQuote } = await import("../test/utils/quotes");
138+
const quote = getEmptyPegInQuote();
139+
quote.liquidityProviderRskAddress = lp.address;
140+
quote.penaltyFee = MIN_COLLATERAL;
141+
142+
await collateralManagement
143+
.connect(owner)
144+
.slashPegInCollateral(ethers.ZeroAddress, quote, ethers.ZeroHash);
145+
146+
// Still operational in Discovery
147+
expect(
148+
await discovery.isOperational(ProviderType.PegIn, lp.address)
149+
).to.equal(true);
150+
151+
// Still in Discovery listing
152+
const providers = await discovery.getProviders();
153+
expect(providers.length).to.equal(1);
154+
expect(providers[0].providerAddress).to.equal(lp.address);
155+
});
156+
});
157+
158+
describe("Cross-contract: Resignation affects Discovery", () => {
159+
it("should immediately hide provider from Discovery listing upon resignation", async () => {
160+
const { discovery, collateralManagement, signers, MIN_COLLATERAL } =
161+
await loadFixture(deployDiscoveryFixture);
162+
const [lp1, lp2] = signers.slice(-2);
163+
164+
// Register two providers
165+
await discovery
166+
.connect(lp1)
167+
.register("LP1", "url1", true, ProviderType.PegIn, {
168+
value: MIN_COLLATERAL,
169+
});
170+
await discovery
171+
.connect(lp2)
172+
.register("LP2", "url2", true, ProviderType.PegIn, {
173+
value: MIN_COLLATERAL,
174+
});
175+
176+
// Both listed in Discovery
177+
let providers = await discovery.getProviders();
178+
expect(providers.length).to.equal(2);
179+
180+
// Resign LP1 in CollateralManagement
181+
await collateralManagement.connect(lp1).resign();
182+
183+
// LP1 should disappear from Discovery listing immediately
184+
providers = await discovery.getProviders();
185+
expect(providers.length).to.equal(1);
186+
expect(providers[0].providerAddress).to.equal(lp2.address);
187+
188+
// But LP1 can still be queried in Discovery
189+
const lp1Provider = await discovery.getProvider(lp1.address);
190+
expect(lp1Provider.id).to.equal(1n);
191+
192+
// LP1 is not operational in Discovery
193+
expect(
194+
await discovery.isOperational(ProviderType.PegIn, lp1.address)
195+
).to.equal(false);
196+
});
197+
198+
it("should keep provider hidden in Discovery even after withdrawal", async () => {
199+
const { discovery, collateralManagement, signers, MIN_COLLATERAL } =
200+
await loadFixture(deployDiscoveryFixture);
201+
const lp = signers.at(-1)!;
202+
203+
// Register provider
204+
await discovery
205+
.connect(lp)
206+
.register("LP", "url", true, ProviderType.PegIn, {
207+
value: MIN_COLLATERAL,
208+
});
209+
210+
// Verify listed
211+
let providers = await discovery.getProviders();
212+
expect(providers.length).to.equal(1);
213+
214+
// Resign in CollateralManagement
215+
await collateralManagement.connect(lp).resign();
216+
217+
// Hidden from Discovery listing
218+
providers = await discovery.getProviders();
219+
expect(providers.length).to.equal(0);
220+
221+
// Withdraw collateral
222+
await mine(COLLATERAL_CONSTANTS.TEST_RESIGN_DELAY_BLOCKS);
223+
await collateralManagement.connect(lp).withdrawCollateral();
224+
225+
// Still hidden from Discovery listing
226+
providers = await discovery.getProviders();
227+
expect(providers.length).to.equal(0);
228+
229+
// Still not operational
230+
expect(
231+
await discovery.isOperational(ProviderType.PegIn, lp.address)
232+
).to.equal(false);
233+
234+
// But can still be queried
235+
const provider = await discovery.getProvider(lp.address);
236+
expect(provider.id).to.equal(1n);
237+
});
238+
239+
it("should allow provider to appear in Discovery again after re-registration", async () => {
240+
const { discovery, collateralManagement, signers, MIN_COLLATERAL } =
241+
await loadFixture(deployDiscoveryFixture);
242+
const lp = signers.at(-1)!;
243+
244+
// Initial registration
245+
await discovery
246+
.connect(lp)
247+
.register("LP First", "url1", true, ProviderType.PegIn, {
248+
value: MIN_COLLATERAL,
249+
});
250+
251+
let providers = await discovery.getProviders();
252+
expect(providers.length).to.equal(1);
253+
expect(providers[0].id).to.equal(1n);
254+
255+
// Resign and withdraw
256+
await collateralManagement.connect(lp).resign();
257+
await mine(COLLATERAL_CONSTANTS.TEST_RESIGN_DELAY_BLOCKS);
258+
await collateralManagement.connect(lp).withdrawCollateral();
259+
260+
// Hidden from listing
261+
providers = await discovery.getProviders();
262+
expect(providers.length).to.equal(0);
263+
264+
// Re-register
265+
await discovery
266+
.connect(lp)
267+
.register("LP Second", "url2", true, ProviderType.PegOut, {
268+
value: MIN_COLLATERAL,
269+
});
270+
271+
// Appears in listing again with new ID
272+
providers = await discovery.getProviders();
273+
expect(providers.length).to.equal(1);
274+
expect(providers[0].id).to.equal(2n);
275+
expect(providers[0].name).to.equal("LP Second");
276+
expect(providers[0].providerType).to.equal(ProviderType.PegOut);
277+
278+
// Operational for new type
279+
expect(
280+
await discovery.isOperational(ProviderType.PegOut, lp.address)
281+
).to.equal(true);
282+
expect(
283+
await discovery.isOperational(ProviderType.PegIn, lp.address)
284+
).to.equal(false);
285+
});
286+
});
287+
288+
describe("Cross-contract: Complex collateral scenarios", () => {
289+
it("should handle multiple providers with varying collateral levels affecting Discovery", async () => {
290+
const {
291+
discovery,
292+
collateralManagement,
293+
signers,
294+
MIN_COLLATERAL,
295+
owner,
296+
} = await loadFixture(deployDiscoveryFixture);
297+
const [lp1, lp2, lp3, lp4] = signers.slice(-4);
298+
299+
// Register 4 providers with different collateral amounts
300+
await discovery
301+
.connect(lp1)
302+
.register("LP1", "url1", true, ProviderType.PegIn, {
303+
value: MIN_COLLATERAL, // Exactly minimum
304+
});
305+
await discovery
306+
.connect(lp2)
307+
.register("LP2", "url2", true, ProviderType.PegIn, {
308+
value: MIN_COLLATERAL * 2n, // 2x minimum
309+
});
310+
await discovery
311+
.connect(lp3)
312+
.register("LP3", "url3", true, ProviderType.PegIn, {
313+
value: MIN_COLLATERAL * 5n, // 5x minimum
314+
});
315+
await discovery
316+
.connect(lp4)
317+
.register("LP4", "url4", true, ProviderType.PegIn, {
318+
value: MIN_COLLATERAL * 10n, // 10x minimum
319+
});
320+
321+
// All should be operational and listed
322+
let providers = await discovery.getProviders();
323+
expect(providers.length).to.equal(4);
324+
325+
// Slash LP1 by small amount (goes below minimum)
326+
await collateralManagement
327+
.connect(owner)
328+
.grantRole(
329+
await collateralManagement.COLLATERAL_SLASHER(),
330+
owner.address
331+
);
332+
const { getEmptyPegInQuote } = await import("../test/utils/quotes");
333+
const quote1 = getEmptyPegInQuote();
334+
quote1.liquidityProviderRskAddress = lp1.address;
335+
quote1.penaltyFee = MIN_COLLATERAL / 10n;
336+
await collateralManagement
337+
.connect(owner)
338+
.slashPegInCollateral(ethers.ZeroAddress, quote1, ethers.ZeroHash);
339+
340+
// LP1 should disappear from Discovery
341+
providers = await discovery.getProviders();
342+
expect(providers.length).to.equal(3);
343+
expect(providers.map((p) => p.providerAddress)).to.not.include(
344+
lp1.address
345+
);
346+
347+
// Slash LP2 significantly but still above minimum
348+
const quote2 = getEmptyPegInQuote();
349+
quote2.liquidityProviderRskAddress = lp2.address;
350+
quote2.penaltyFee = MIN_COLLATERAL;
351+
await collateralManagement
352+
.connect(owner)
353+
.slashPegInCollateral(ethers.ZeroAddress, quote2, ethers.ZeroHash);
354+
355+
// LP2 should still be listed
356+
providers = await discovery.getProviders();
357+
expect(providers.length).to.equal(3);
358+
expect(providers.map((p) => p.providerAddress)).to.include(lp2.address);
359+
360+
// Resign LP3
361+
await collateralManagement.connect(lp3).resign();
362+
363+
// LP3 should disappear
364+
providers = await discovery.getProviders();
365+
expect(providers.length).to.equal(2);
366+
expect(providers.map((p) => p.providerAddress)).to.not.include(
367+
lp3.address
368+
);
369+
370+
// Only LP2 and LP4 should be listed
371+
expect(providers.map((p) => p.providerAddress)).to.deep.equal([
372+
lp2.address,
373+
lp4.address,
374+
]);
375+
376+
// Verify operational status
377+
expect(
378+
await discovery.isOperational(ProviderType.PegIn, lp1.address)
379+
).to.equal(false);
380+
expect(
381+
await discovery.isOperational(ProviderType.PegIn, lp2.address)
382+
).to.equal(true);
383+
expect(
384+
await discovery.isOperational(ProviderType.PegIn, lp3.address)
385+
).to.equal(false);
386+
expect(
387+
await discovery.isOperational(ProviderType.PegIn, lp4.address)
388+
).to.equal(true);
389+
});
390+
});
391+
});

0 commit comments

Comments
 (0)