Skip to content

Commit bcbe228

Browse files
committed
test: updated with token whitelist coverage
1 parent 5a46536 commit bcbe228

File tree

2 files changed

+233
-9
lines changed

2 files changed

+233
-9
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.24;
4+
5+
import "../arbitration/dispute-kits/DisputeKitGated.sol";
6+
7+
/// @title KlerosCoreMock
8+
/// KlerosCore with view functions to use in Foundry tests.
9+
contract DisputeKitGatedMock is DisputeKitGated {
10+
function extraDataToTokenInfo(
11+
bytes memory _extraData
12+
) public pure returns (address tokenGate, bool isERC1155, uint256 tokenId) {
13+
(tokenGate, isERC1155, tokenId) = _extraDataToTokenInfo(_extraData);
14+
}
15+
}

contracts/test/arbitration/dispute-kit-gated.ts

Lines changed: 218 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,18 @@ import { deployUpgradable } from "../../deploy/utils/deployUpgradable";
1919
/* eslint-disable no-unused-vars */
2020
/* eslint-disable no-unused-expressions */ // https://github.com/standard/standard/issues/690#issuecomment-278533482
2121

22+
/**
23+
* Test suite for DisputeKitGated - a dispute kit that requires jurors to hold
24+
* specific tokens (ERC20, ERC721, or ERC1155) to participate in disputes.
25+
*
26+
* Tests cover:
27+
* - Token whitelist management and access control
28+
* - Error handling for unsupported tokens
29+
* - Token gating functionality for different token types
30+
* - Integration between whitelist and dispute creation
31+
*/
2232
describe("DisputeKitGated", async () => {
33+
// Constants for token amounts
2334
const ONE_THOUSAND_PNK = 10n ** 21n;
2435
const thousandPNK = (amount: BigNumberish) => toBigInt(amount) * ONE_THOUSAND_PNK;
2536
const PNK = (amount: BigNumberish) => toBigInt(amount) * 10n ** 18n;
@@ -35,8 +46,8 @@ describe("DisputeKitGated", async () => {
3546
let rng: IncrementalNG;
3647
let nft721: TestERC721;
3748
let nft1155: TestERC1155;
49+
let gatedDisputeKitID: number;
3850
const RANDOM = 424242n;
39-
const GATED_DK_ID = 3;
4051
const TOKEN_ID = 888;
4152
const minStake = PNK(200);
4253

@@ -62,8 +73,8 @@ describe("DisputeKitGated", async () => {
6273
log: true,
6374
});
6475
await core.addNewDisputeKit(deploymentResult.address);
65-
const disputeKitGatedID = (await core.getDisputeKitsLength()) - 1n;
66-
await core.enableDisputeKits(Courts.GENERAL, [disputeKitGatedID], true); // enable disputeKitGated on the General Court
76+
gatedDisputeKitID = Number((await core.getDisputeKitsLength()) - 1n);
77+
await core.enableDisputeKits(Courts.GENERAL, [gatedDisputeKitID], true); // enable disputeKitGated on the General Court
6778

6879
disputeKitGated = await ethers.getContract<DisputeKitGatedMock>("DisputeKitGatedMock");
6980

@@ -84,8 +95,14 @@ describe("DisputeKitGated", async () => {
8495
await deployERC1155(hre, deployer, "TestERC1155", "Nft1155");
8596
nft1155 = await ethers.getContract<TestERC1155>("Nft1155");
8697
await nft1155.mint(deployer, TOKEN_ID, 1, "0x00");
98+
99+
// Whitelist all tokens by default to make existing tests work
100+
await whitelistTokens([dai.target, nft721.target, nft1155.target], true);
87101
});
88102

103+
// Helper Functions
104+
105+
/** Encodes extra data for dispute creation with token gating parameters */
89106
const encodeExtraData = (
90107
courtId: number,
91108
minJurors: BigNumberish,
@@ -103,6 +120,29 @@ describe("DisputeKitGated", async () => {
103120
);
104121
};
105122

123+
/** Adds or removes tokens from the whitelist */
124+
const whitelistTokens = async (tokens: (string | Addressable)[], supported: boolean = true) => {
125+
const tokenAddresses = tokens.map((token) => (typeof token === "string" ? token : token.toString()));
126+
return disputeKitGated.changeSupportedTokens(tokenAddresses, supported);
127+
};
128+
129+
/** Creates a dispute with the specified token gate */
130+
const createDisputeWithToken = async (
131+
token: string | Addressable,
132+
isERC1155: boolean = false,
133+
tokenId: BigNumberish = 0
134+
) => {
135+
const extraData = encodeExtraData(Courts.GENERAL, 3, gatedDisputeKitID, token, isERC1155, tokenId);
136+
const arbitrationCost = await core["arbitrationCost(bytes)"](extraData);
137+
return core["createDispute(uint256,bytes)"](2, extraData, { value: arbitrationCost });
138+
};
139+
140+
/** Asserts whether a token is supported or not */
141+
const expectTokenSupported = async (token: string | Addressable, supported: boolean) => {
142+
const tokenAddress = typeof token === "string" ? token : token.toString();
143+
expect(await disputeKitGated.supportedTokens(tokenAddress)).to.equal(supported);
144+
};
145+
106146
const stakeAndDraw = async (
107147
courtId: number,
108148
minJurors: BigNumberish,
@@ -159,10 +199,122 @@ describe("DisputeKitGated", async () => {
159199
return core.draw(disputeId, 70, { gasLimit: 10000000 });
160200
};
161201

202+
describe("Token Whitelist Management", async () => {
203+
describe("changeSupportedTokens function", async () => {
204+
it("Should allow owner to whitelist single token", async () => {
205+
await whitelistTokens([dai.target], true);
206+
await expectTokenSupported(dai.target, true);
207+
});
208+
209+
it("Should allow owner to whitelist multiple tokens", async () => {
210+
await whitelistTokens([dai.target, nft721.target, nft1155.target], true);
211+
await expectTokenSupported(dai.target, true);
212+
await expectTokenSupported(nft721.target, true);
213+
await expectTokenSupported(nft1155.target, true);
214+
});
215+
216+
it("Should allow owner to remove single token from whitelist", async () => {
217+
await whitelistTokens([dai.target], true);
218+
await expectTokenSupported(dai.target, true);
219+
220+
await whitelistTokens([dai.target], false);
221+
await expectTokenSupported(dai.target, false);
222+
});
223+
224+
it("Should allow owner to remove multiple tokens from whitelist", async () => {
225+
await whitelistTokens([dai.target, nft721.target], true);
226+
await expectTokenSupported(dai.target, true);
227+
await expectTokenSupported(nft721.target, true);
228+
229+
await whitelistTokens([dai.target, nft721.target], false);
230+
await expectTokenSupported(dai.target, false);
231+
await expectTokenSupported(nft721.target, false);
232+
});
233+
234+
it("Should handle mixed operations (add some, remove some)", async () => {
235+
await whitelistTokens([dai.target, nft721.target], true);
236+
await expectTokenSupported(dai.target, true);
237+
await expectTokenSupported(nft721.target, true);
238+
239+
await whitelistTokens([dai.target], false);
240+
await whitelistTokens([nft1155.target], true);
241+
242+
await expectTokenSupported(dai.target, false);
243+
await expectTokenSupported(nft721.target, true);
244+
await expectTokenSupported(nft1155.target, true);
245+
});
246+
247+
it("Should handle duplicate operations correctly", async () => {
248+
// Whitelist token twice - should not revert
249+
await whitelistTokens([dai.target], true);
250+
await whitelistTokens([dai.target], true);
251+
await expectTokenSupported(dai.target, true);
252+
253+
// Remove token twice - should not revert
254+
await whitelistTokens([dai.target], false);
255+
await whitelistTokens([dai.target], false);
256+
await expectTokenSupported(dai.target, false);
257+
});
258+
});
259+
260+
describe("Access Control", async () => {
261+
it("Should revert when non-owner tries to change supported tokens", async () => {
262+
await expect(disputeKitGated.connect(juror1).changeSupportedTokens([dai.target], true)).to.be.reverted;
263+
});
264+
265+
it("Should revert when non-owner tries to remove supported tokens", async () => {
266+
// First whitelist as owner
267+
await whitelistTokens([dai.target], true);
268+
269+
// Then try to remove as non-owner
270+
await expect(disputeKitGated.connect(juror1).changeSupportedTokens([dai.target], false)).to.be.reverted;
271+
});
272+
});
273+
});
274+
275+
describe("Error Handling - Unsupported Tokens", async () => {
276+
it("Should revert with TokenNotSupported when creating dispute with unsupported ERC20", async () => {
277+
await whitelistTokens([dai.target], false);
278+
279+
await expect(createDisputeWithToken(dai.target))
280+
.to.be.revertedWithCustomError(disputeKitGated, "TokenNotSupported")
281+
.withArgs(dai.target);
282+
});
283+
284+
it("Should revert with TokenNotSupported when creating dispute with unsupported ERC721", async () => {
285+
await whitelistTokens([nft721.target], false);
286+
287+
await expect(createDisputeWithToken(nft721.target))
288+
.to.be.revertedWithCustomError(disputeKitGated, "TokenNotSupported")
289+
.withArgs(nft721.target);
290+
});
291+
292+
it("Should revert with TokenNotSupported when creating dispute with unsupported ERC1155", async () => {
293+
await whitelistTokens([nft1155.target], false);
294+
295+
await expect(createDisputeWithToken(nft1155.target, true, TOKEN_ID))
296+
.to.be.revertedWithCustomError(disputeKitGated, "TokenNotSupported")
297+
.withArgs(nft1155.target);
298+
});
299+
300+
it("Should allow dispute creation after token is whitelisted", async () => {
301+
await whitelistTokens([dai.target], false);
302+
303+
await expect(createDisputeWithToken(dai.target)).to.be.revertedWithCustomError(
304+
disputeKitGated,
305+
"TokenNotSupported"
306+
);
307+
308+
await whitelistTokens([dai.target], true);
309+
310+
await expect(createDisputeWithToken(dai.target)).to.not.be.reverted;
311+
});
312+
});
313+
162314
describe("When gating with DAI token", async () => {
163315
it("Should draw no juror if they don't have any DAI balance", async () => {
164316
const nbOfJurors = 15n;
165-
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, GATED_DK_ID, dai.target, false, 0).then((tx) =>
317+
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, gatedDisputeKitID, dai.target, false, 0).then((tx) =>
166318
tx.wait()
167319
);
168320

@@ -176,7 +328,7 @@ describe("DisputeKitGated", async () => {
176328
dai.transfer(juror1.address, 1);
177329

178330
const nbOfJurors = 15n;
179-
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, GATED_DK_ID, dai.target, false, 0).then((tx) =>
331+
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, gatedDisputeKitID, dai.target, false, 0).then((tx) =>
180332
tx.wait()
181333
);
182334

@@ -209,7 +361,7 @@ describe("DisputeKitGated", async () => {
209361
describe("When gating with ERC721 token", async () => {
210362
it("Should draw no juror if they don't own the ERC721 token", async () => {
211363
const nbOfJurors = 15n;
212-
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, GATED_DK_ID, nft721.target, false, 0).then((tx) =>
364+
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, gatedDisputeKitID, nft721.target, false, 0).then((tx) =>
213365
tx.wait()
214366
);
215367

@@ -223,7 +375,7 @@ describe("DisputeKitGated", async () => {
223375
await nft721.safeMint(juror2.address);
224376

225377
const nbOfJurors = 15n;
226-
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, GATED_DK_ID, nft721.target, false, 0).then((tx) =>
378+
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, gatedDisputeKitID, nft721.target, false, 0).then((tx) =>
227379
tx.wait()
228380
);
229381

@@ -256,7 +408,7 @@ describe("DisputeKitGated", async () => {
256408
describe("When gating with ERC1155 token", async () => {
257409
it("Should draw no juror if they don't own the ERC1155 token", async () => {
258410
const nbOfJurors = 15n;
259-
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, GATED_DK_ID, nft1155.target, true, TOKEN_ID).then(
411+
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, gatedDisputeKitID, nft1155.target, true, TOKEN_ID).then(
260412
(tx) => tx.wait()
261413
);
262414

@@ -270,7 +422,7 @@ describe("DisputeKitGated", async () => {
270422
await nft1155.mint(juror2.address, TOKEN_ID, 1, "0x00");
271423

272424
const nbOfJurors = 15n;
273-
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, GATED_DK_ID, nft1155.target, true, TOKEN_ID).then(
425+
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, gatedDisputeKitID, nft1155.target, true, TOKEN_ID).then(
274426
(tx) => tx.wait()
275427
);
276428

@@ -299,4 +451,61 @@ describe("DisputeKitGated", async () => {
299451
]);
300452
});
301453
});
454+
455+
describe("Whitelist Integration Tests", async () => {
456+
it("Should allow new disputes after whitelisting a token", async () => {
457+
// Whitelist DAI token
458+
await whitelistTokens([dai.target], true);
459+
460+
// Transfer DAI to juror1 for token gating
461+
await dai.transfer(juror1.address, 1);
462+
463+
const nbOfJurors = 3n;
464+
const tx = await stakeAndDraw(Courts.GENERAL, nbOfJurors, gatedDisputeKitID, dai.target, false, 0).then((tx) =>
465+
tx.wait()
466+
);
467+
468+
// Verify dispute was created and juror drawn
469+
const drawLogs =
470+
tx?.logs.filter((log: any) => log.fragment?.name === "Draw" && log.address === core.target) || [];
471+
expect(drawLogs).to.have.length(nbOfJurors);
472+
});
473+
474+
it("Should prevent new disputes after removing token from whitelist", async () => {
475+
await whitelistTokens([dai.target], true);
476+
await dai.transfer(juror1.address, 1);
477+
478+
// Create first dispute (should work)
479+
await expect(createDisputeWithToken(dai.target)).to.not.be.reverted;
480+
481+
// Remove token from whitelist
482+
await whitelistTokens([dai.target], false);
483+
484+
// Try to create another dispute (should fail)
485+
await expect(createDisputeWithToken(dai.target))
486+
.to.be.revertedWithCustomError(disputeKitGated, "TokenNotSupported")
487+
.withArgs(dai.target);
488+
});
489+
490+
it("Should maintain whitelist state correctly across multiple operations", async () => {
491+
const tokens = [dai.target, nft721.target, nft1155.target];
492+
493+
// All tokens should already be supported from the main setup
494+
for (const token of tokens) {
495+
await expectTokenSupported(token, true);
496+
}
497+
498+
// Remove middle token
499+
await whitelistTokens([nft721.target], false);
500+
await expectTokenSupported(dai.target, true);
501+
await expectTokenSupported(nft721.target, false);
502+
await expectTokenSupported(nft1155.target, true);
503+
504+
// Re-add middle token
505+
await whitelistTokens([nft721.target], true);
506+
for (const token of tokens) {
507+
await expectTokenSupported(token, true);
508+
}
509+
});
510+
});
302511
});

0 commit comments

Comments
 (0)