Skip to content

Commit 57319d4

Browse files
committed
Merge branch 'dev' into fix/dk-shutter-commit
2 parents 613b8c3 + 13f3708 commit 57319d4

File tree

10 files changed

+1798
-1009
lines changed

10 files changed

+1798
-1009
lines changed

contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
204204
uint256 _numberOfChoices,
205205
bytes calldata _extraData,
206206
uint256 /*_nbVotes*/
207-
) external override onlyByCore {
207+
) public virtual override onlyByCore {
208208
uint256 localDisputeID;
209209
Dispute storage dispute;
210210
Active storage active = coreDisputeIDToActive[_coreDisputeID];

contracts/src/arbitration/dispute-kits/DisputeKitGated.sol

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ interface IBalanceHolderERC1155 {
2929
contract DisputeKitGated is DisputeKitClassicBase {
3030
string public constant override version = "2.0.0";
3131

32+
address private constant NO_TOKEN_GATE = address(0);
33+
34+
// ************************************* //
35+
// * Storage * //
36+
// ************************************* //
37+
38+
mapping(address token => bool supported) public supportedTokens; // Whether the token is supported or not.
39+
3240
// ************************************* //
3341
// * Constructor * //
3442
// ************************************* //
@@ -50,6 +58,7 @@ contract DisputeKitGated is DisputeKitClassicBase {
5058
uint256 _jumpDisputeKitID
5159
) external initializer {
5260
__DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID);
61+
supportedTokens[NO_TOKEN_GATE] = true; // Allows disputes without token gating
5362
}
5463

5564
// ************************ //
@@ -62,6 +71,34 @@ contract DisputeKitGated is DisputeKitClassicBase {
6271
// NOP
6372
}
6473

74+
/// @notice Changes the supported tokens.
75+
/// @param _tokens The tokens to support.
76+
/// @param _supported Whether the tokens are supported or not.
77+
function changeSupportedTokens(address[] memory _tokens, bool _supported) external onlyByOwner {
78+
for (uint256 i = 0; i < _tokens.length; i++) {
79+
supportedTokens[_tokens[i]] = _supported;
80+
}
81+
}
82+
83+
// ************************************* //
84+
// * State Modifiers * //
85+
// ************************************* //
86+
87+
/// @inheritdoc DisputeKitClassicBase
88+
function createDispute(
89+
uint256 _coreDisputeID,
90+
uint256 _coreRoundID,
91+
uint256 _numberOfChoices,
92+
bytes calldata _extraData,
93+
uint256 _nbVotes
94+
) public override {
95+
(address tokenGate, , ) = _extraDataToTokenInfo(_extraData);
96+
if (!supportedTokens[tokenGate]) revert TokenNotSupported(tokenGate);
97+
98+
// super.createDispute() ensures access control onlyByCore.
99+
super.createDispute(_coreDisputeID, _coreRoundID, _numberOfChoices, _extraData, _nbVotes);
100+
}
101+
65102
// ************************************* //
66103
// * Internal * //
67104
// ************************************* //
@@ -78,7 +115,7 @@ contract DisputeKitGated is DisputeKitClassicBase {
78115
/// @return tokenId The token ID for ERC-1155 tokens (ignored for ERC-20/ERC-721).
79116
function _extraDataToTokenInfo(
80117
bytes memory _extraData
81-
) public pure returns (address tokenGate, bool isERC1155, uint256 tokenId) {
118+
) internal pure returns (address tokenGate, bool isERC1155, uint256 tokenId) {
82119
// Need at least 160 bytes to safely read the parameters
83120
if (_extraData.length < 160) return (address(0), false, 0);
84121

@@ -107,7 +144,7 @@ contract DisputeKitGated is DisputeKitClassicBase {
107144
(address tokenGate, bool isERC1155, uint256 tokenId) = _extraDataToTokenInfo(dispute.extraData);
108145

109146
// If no token gate is specified, allow all jurors
110-
if (tokenGate == address(0)) return true;
147+
if (tokenGate == NO_TOKEN_GATE) return true;
111148

112149
// Check juror's token balance
113150
if (isERC1155) {
@@ -116,4 +153,10 @@ contract DisputeKitGated is DisputeKitClassicBase {
116153
return IBalanceHolder(tokenGate).balanceOf(_juror) > 0;
117154
}
118155
}
156+
157+
// ************************************* //
158+
// * Errors * //
159+
// ************************************* //
160+
161+
error TokenNotSupported(address tokenGate);
119162
}

contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@ interface IBalanceHolderERC1155 {
3030
contract DisputeKitGatedShutter is DisputeKitClassicBase {
3131
string public constant override version = "2.0.0";
3232

33+
address private constant NO_TOKEN_GATE = address(0);
34+
3335
// ************************************* //
3436
// * Storage * //
3537
// ************************************* //
3638

39+
mapping(address token => bool supported) public supportedTokens; // Whether the token is supported or not.
3740
mapping(uint256 localDisputeID => mapping(uint256 localRoundID => mapping(uint256 voteID => bytes32 justificationCommitment)))
3841
public justificationCommitments;
3942

@@ -84,6 +87,7 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase {
8487
uint256 _jumpDisputeKitID
8588
) external initializer {
8689
__DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID);
90+
supportedTokens[NO_TOKEN_GATE] = true; // Allows disputes without token gating
8791
}
8892

8993
// ************************ //
@@ -96,10 +100,34 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase {
96100
// NOP
97101
}
98102

103+
/// @notice Changes the supported tokens.
104+
/// @param _tokens The tokens to support.
105+
/// @param _supported Whether the tokens are supported or not.
106+
function changeSupportedTokens(address[] memory _tokens, bool _supported) external onlyByOwner {
107+
for (uint256 i = 0; i < _tokens.length; i++) {
108+
supportedTokens[_tokens[i]] = _supported;
109+
}
110+
}
111+
99112
// ************************************* //
100113
// * State Modifiers * //
101114
// ************************************* //
102115

116+
/// @inheritdoc DisputeKitClassicBase
117+
function createDispute(
118+
uint256 _coreDisputeID,
119+
uint256 _coreRoundID,
120+
uint256 _numberOfChoices,
121+
bytes calldata _extraData,
122+
uint256 _nbVotes
123+
) public override {
124+
(address tokenGate, , ) = _extraDataToTokenInfo(_extraData);
125+
if (!supportedTokens[tokenGate]) revert TokenNotSupported(tokenGate);
126+
127+
// super.createDispute() ensures access control onlyByCore.
128+
super.createDispute(_coreDisputeID, _coreRoundID, _numberOfChoices, _extraData, _nbVotes);
129+
}
130+
103131
/// @notice Sets the caller's commit for the specified votes.
104132
///
105133
/// @dev It can be called multiple times during the commit period, each call overrides the commits of the previous one.
@@ -212,7 +240,7 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase {
212240
/// @return tokenGate The address of the token contract used for gating access.
213241
/// @return isERC1155 True if the token is an ERC-1155, false for ERC-20/ERC-721.
214242
/// @return tokenId The token ID for ERC-1155 tokens (ignored for ERC-20/ERC-721).
215-
function __extraDataToTokenInfo(
243+
function _extraDataToTokenInfo(
216244
bytes memory _extraData
217245
) internal pure returns (address tokenGate, bool isERC1155, uint256 tokenId) {
218246
// Need at least 160 bytes to safely read the parameters
@@ -240,10 +268,10 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase {
240268
// Get the local dispute and extract token info from extraData
241269
uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
242270
Dispute storage dispute = disputes[localDisputeID];
243-
(address tokenGate, bool isERC1155, uint256 tokenId) = __extraDataToTokenInfo(dispute.extraData);
271+
(address tokenGate, bool isERC1155, uint256 tokenId) = _extraDataToTokenInfo(dispute.extraData);
244272

245273
// If no token gate is specified, allow all jurors
246-
if (tokenGate == address(0)) return true;
274+
if (tokenGate == NO_TOKEN_GATE) return true;
247275

248276
// Check juror's token balance
249277
if (isERC1155) {
@@ -257,6 +285,7 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase {
257285
// * Errors * //
258286
// ************************************* //
259287

288+
error TokenNotSupported(address tokenGate);
260289
error EmptyJustificationCommit();
261290
error WrongJustification();
262291
}
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 DisputeKitGatedMock
8+
/// DisputeKitGated with view functions to use in the 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+
}
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/DisputeKitGatedShutter.sol";
6+
7+
/// @title DisputeKitGatedShutterMock
8+
/// DisputeKitGatedShutter with view functions to use in the tests.
9+
contract DisputeKitGatedShutterMock is DisputeKitGatedShutter {
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+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import {
2+
setupTokenGatedTest,
3+
testTokenWhitelistManagement,
4+
testAccessControl,
5+
testUnsupportedTokenErrors,
6+
testERC20Gating,
7+
testERC721Gating,
8+
testERC1155Gating,
9+
testWhitelistIntegration,
10+
testNoTokenGateAddress,
11+
TokenGatedTestContext,
12+
} from "./helpers/dispute-kit-gated-common";
13+
import {
14+
setupShutterTest,
15+
testCommitPhase,
16+
testNormalFlowBotReveals,
17+
testRecoveryFlowJurorReveals,
18+
testEdgeCasesAndSecurity,
19+
ShutterTestContext,
20+
} from "./helpers/dispute-kit-shutter-common";
21+
22+
/* eslint-disable no-unused-vars */
23+
/* eslint-disable no-unused-expressions */
24+
25+
/**
26+
* Test suite for DisputeKitGatedShutter - a dispute kit that requires jurors to hold
27+
* specific tokens (ERC20, ERC721, or ERC1155) to participate in disputes, with additional
28+
* Shutter functionality for commit-reveal voting.
29+
*
30+
* Tests cover:
31+
* - All DisputeKitGated functionality (via shared tests)
32+
* - Shutter-specific commit/reveal mechanism
33+
* - Recovery commits for juror vote recovery
34+
* - Integration between token gating and Shutter features
35+
*/
36+
describe("DisputeKitGatedShutter", async () => {
37+
describe("Token Gating Features", async () => {
38+
let tokenContext: TokenGatedTestContext;
39+
40+
beforeEach("Setup", async () => {
41+
tokenContext = await setupTokenGatedTest({ contractName: "DisputeKitGatedShutterMock" });
42+
});
43+
44+
// Run all shared token gating tests
45+
testTokenWhitelistManagement(() => tokenContext);
46+
testAccessControl(() => tokenContext);
47+
testUnsupportedTokenErrors(() => tokenContext);
48+
testERC20Gating(() => tokenContext);
49+
testERC721Gating(() => tokenContext);
50+
testERC1155Gating(() => tokenContext);
51+
testWhitelistIntegration(() => tokenContext);
52+
testNoTokenGateAddress(() => tokenContext);
53+
});
54+
55+
describe("Shutter Features", async () => {
56+
let shutterContext: ShutterTestContext;
57+
58+
beforeEach("Setup", async () => {
59+
// Setup DisputeKitGatedShutter with token gating enabled
60+
shutterContext = await setupShutterTest({
61+
contractName: "DisputeKitGatedShutter",
62+
isGated: true, // Enable token gating for DAI
63+
});
64+
});
65+
66+
// Run all shared Shutter tests
67+
testCommitPhase(() => shutterContext);
68+
testNormalFlowBotReveals(() => shutterContext);
69+
testRecoveryFlowJurorReveals(() => shutterContext);
70+
testEdgeCasesAndSecurity(() => shutterContext);
71+
});
72+
});

0 commit comments

Comments
 (0)