@@ -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+ */
2232describe ( "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