-
Notifications
You must be signed in to change notification settings - Fork 50
Token whitelist for DisputeKitGated and DisputeKitGatedShutter #2176
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
Conversation
✅ Deploy Preview for kleros-v2-testnet ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
❌ Deploy Preview for kleros-v2-testnet-devtools failed. Why did it fail? →
|
WalkthroughcreateDispute visibility made overridable in DisputeKitClassicBase. DisputeKitGated and DisputeKitGatedShutter add token-gating state and checks (supportedTokens, changeSupportedTokens, TokenNotSupported) and override createDispute to validate tokenGate extracted from extraData. Tests refactored to shared helpers and add mocks exposing token parsing. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Core
participant Gated as DisputeKitGated
participant Base as DisputeKitClassicBase
Core->>Gated: createDispute(coreDisputeID, coreRoundID, choices, extraData, nbVotes)
note right of Gated: extract tokenGate via _extraDataToTokenInfo(extraData)
alt tokenGate not supported
Gated-->>Core: revert TokenNotSupported(tokenGate)
else supported
Gated->>Base: super.createDispute(...)
Base-->>Core: dispute created
end
sequenceDiagram
autonumber
actor Core
participant GatedShutter as DisputeKitGatedShutter
participant Base as DisputeKitClassicBase
Core->>GatedShutter: createDispute(..., extraData, ...)
GatedShutter->>GatedShutter: _extraDataToTokenInfo(extraData)
alt unsupported
GatedShutter-->>Core: revert TokenNotSupported
else supported
GatedShutter->>Base: super.createDispute(...)
Base-->>Core: success
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
✅ Deploy Preview for kleros-v2-neo ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
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.
Actionable comments posted: 0
🧹 Nitpick comments (5)
contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol (2)
100-107
: Emit events for governance transparency.The
changeSupportedTokens
function modifies critical state but doesn't emit events. Consider emitting an event to track token whitelist changes for transparency and off-chain monitoring.Apply this diff:
+ event SupportedTokensChanged(address indexed token, bool supported); + /// @notice Changes the supported tokens. /// @param _tokens The tokens to support. /// @param _supported Whether the tokens are supported or not. function changeSupportedTokens(address[] memory _tokens, bool _supported) external onlyByOwner { for (uint256 i = 0; i < _tokens.length; i++) { supportedTokens[_tokens[i]] = _supported; + emit SupportedTokensChanged(_tokens[i], _supported); } }
100-107
: Validate token addresses.The function doesn't validate that token addresses are non-zero. While
address(0)
might be intentionally supported for "no gating," explicit validation or documentation would prevent accidental misconfigurations.Consider adding validation:
function changeSupportedTokens(address[] memory _tokens, bool _supported) external onlyByOwner { for (uint256 i = 0; i < _tokens.length; i++) { + if (_tokens[i] == address(0) && _supported) revert InvalidTokenAddress(); supportedTokens[_tokens[i]] = _supported; } }
contracts/test/arbitration/dispute-kit-gated.ts (1)
162-301
: Test coverage gap: whitelist functionality not tested.The tests verify token-gating behavior (jurors with/without tokens) but don't cover the new
changeSupportedTokens
governance function or the scenario where a token is not whitelisted (should revert withTokenNotSupported
).Consider adding tests for:
- Calling
changeSupportedTokens
to whitelist tokens- Attempting to create a dispute with an unsupported token (should revert)
- Removing tokens from the whitelist
Would you like me to generate these additional test cases?
contracts/src/arbitration/dispute-kits/DisputeKitGated.sol (2)
71-78
: Emit events for governance transparency.The
changeSupportedTokens
function modifies critical state but doesn't emit events. Consider emitting an event to track token whitelist changes for transparency and off-chain monitoring.Apply this diff:
+ event SupportedTokensChanged(address indexed token, bool supported); + /// @notice Changes the supported tokens. /// @param _tokens The tokens to support. /// @param _supported Whether the tokens are supported or not. function changeSupportedTokens(address[] memory _tokens, bool _supported) external onlyByOwner { for (uint256 i = 0; i < _tokens.length; i++) { supportedTokens[_tokens[i]] = _supported; + emit SupportedTokensChanged(_tokens[i], _supported); } }
71-78
: Validate token addresses.The function doesn't validate that token addresses are non-zero. While
address(0)
might be intentionally supported for "no gating," explicit validation or documentation would prevent accidental misconfigurations.Consider adding validation:
function changeSupportedTokens(address[] memory _tokens, bool _supported) external onlyByOwner { for (uint256 i = 0; i < _tokens.length; i++) { + if (_tokens[i] == address(0) && _supported) revert InvalidTokenAddress(); supportedTokens[_tokens[i]] = _supported; } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol
(1 hunks)contracts/src/arbitration/dispute-kits/DisputeKitGated.sol
(4 hunks)contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol
(5 hunks)contracts/test/arbitration/dispute-kit-gated.ts
(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
contracts/test/arbitration/dispute-kit-gated.ts (1)
contracts/deploy/utils/deployUpgradable.ts (1)
deployUpgradable
(37-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: Redirect rules - kleros-v2-neo
- GitHub Check: Header rules - kleros-v2-neo
- GitHub Check: Pages changed - kleros-v2-neo
- GitHub Check: Redirect rules - kleros-v2-testnet-devtools
- GitHub Check: Header rules - kleros-v2-testnet-devtools
- GitHub Check: Pages changed - kleros-v2-testnet-devtools
- GitHub Check: Redirect rules - kleros-v2-testnet
- GitHub Check: Redirect rules - kleros-v2-testnet
- GitHub Check: Header rules - kleros-v2-testnet
- GitHub Check: Header rules - kleros-v2-testnet
- GitHub Check: Pages changed - kleros-v2-testnet
- GitHub Check: Pages changed - kleros-v2-testnet
- GitHub Check: Analyze (javascript)
- GitHub Check: hardhat-tests
🔇 Additional comments (8)
contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol (1)
201-207
: LGTM! Visibility change enables token-gating pattern.Changing from
external
topublic virtual
correctly allows derived dispute kits (DisputeKitGated, DisputeKitGatedShutter) to override this function, perform token validation, then delegate to the base implementation viasuper.createDispute()
. TheonlyByCore
modifier is preserved, ensuring access control remains intact.contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol (3)
113-126
: Token gating logic is correct.The override properly extracts the token address from
extraData
, validates it against the whitelist, and delegates to the base implementation. The comment correctly notes thatsuper.createDispute()
enforcesonlyByCore
access control.
237-252
: Visibility change tointernal
is appropriate.Changing
_extraDataToTokenInfo
frompublic
tointernal
is correct since it's an implementation detail only needed by internal logic and overriding contracts. Tests now use a mock that exposes this publicly.
37-37
: Consider initializing supported tokens.The
supportedTokens
mapping is not initialized in theinitialize
function. This means all tokens start as unsupported, requiring explicit whitelisting through governance after deployment. Consider whether any tokens should be supported by default.Do you want to initialize any tokens as supported by default, or is the empty whitelist intentional?
contracts/test/arbitration/dispute-kit-gated.ts (1)
58-68
: Deployment pattern is correct.The switch to using
deployUpgradable
andDisputeKitGatedMock
aligns with the upgradeable proxy pattern and enables testing of internal functions through the mock contract.contracts/src/arbitration/dispute-kits/DisputeKitGated.sol (3)
84-97
: Token gating logic is correct.The override properly extracts the token address from
extraData
, validates it against the whitelist, and delegates to the base implementation. The comment correctly notes thatsuper.createDispute()
enforcesonlyByCore
access control.
113-128
: Visibility change tointernal
is appropriate.Changing
_extraDataToTokenInfo
frompublic
tointernal
is correct since it's an implementation detail only needed by internal logic and overriding contracts. Tests now use a mock that exposes this publicly.
36-36
: Consider initializing supported tokens.The
supportedTokens
mapping is not initialized in theinitialize
function. This means all tokens start as unsupported, requiring explicit whitelisting through governance after deployment. Consider whether any tokens should be supported by default.Do you want to initialize any tokens as supported by default, or is the empty whitelist intentional?
17c684f
to
bcbe228
Compare
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.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
contracts/src/test/DisputeKitGatedMock.sol
(1 hunks)contracts/test/arbitration/dispute-kit-gated.ts
(13 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
contracts/test/arbitration/dispute-kit-gated.ts (2)
contracts/deploy/utils/index.ts (1)
PNK
(40-40)contracts/deploy/utils/deployUpgradable.ts (1)
deployUpgradable
(37-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Analyze (javascript)
- GitHub Check: hardhat-tests
🔇 Additional comments (8)
contracts/test/arbitration/dispute-kit-gated.ts (8)
22-31
: LGTM!The documentation comment provides a clear overview of the test coverage, which is helpful for understanding the test suite structure.
69-80
: LGTM!The deployment using
deployUpgradable
correctly initializes the mock contract with proper proxy setup, adds it to the core, and enables it for the GENERAL court.
99-100
: LGTM!Whitelisting all tokens by default is a pragmatic approach that allows existing tests to continue working without modification while new tests can explicitly test whitelist behavior.
106-145
: LGTM!The helper functions are well-designed, reduce code duplication, and provide a clean API for test operations. The type handling for
string | Addressable
is appropriate and flexible.
202-273
: LGTM!The whitelist management tests provide comprehensive coverage including:
- Single and batch operations
- Add and remove operations
- Mixed operations
- Idempotency (duplicate operations)
- Access control
The test structure is well-organized with clear descriptions.
275-312
: LGTM!The error handling tests thoroughly verify the
TokenNotSupported
error for all token types (ERC20, ERC721, ERC1155) and include a state transition test that validates the full lifecycle of removing and re-adding tokens.
455-510
: LGTM!The integration tests validate the complete workflow:
- Token whitelisting → dispute creation → juror drawing
- Token removal → dispute rejection
- State consistency across multiple operations
These tests ensure the whitelist feature integrates correctly with the existing dispute system.
180-180
: LGTM!The call is correctly updated to use the public
extraDataToTokenInfo
wrapper fromDisputeKitGatedMock
instead of the now-internal_extraDataToTokenInfo
.
|
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.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol (1)
258-279
: Validate token is a contract to prevent draw-time revertsIf a non-contract or wrong interface address is whitelisted, _postDrawCheck may revert on balanceOf. Add a code-size check when gating is active to fail early during dispute creation.
function createDispute( @@ - (address tokenGate, , ) = _extraDataToTokenInfo(_extraData); + (address tokenGate, , ) = _extraDataToTokenInfo(_extraData); if (!supportedTokens[tokenGate]) revert TokenNotSupported(tokenGate); + if (tokenGate != NO_TOKEN_GATE) { + // Prevent footguns: require a contract address + if (tokenGate.code.length == 0) revert TokenNotSupported(tokenGate); + }
🧹 Nitpick comments (11)
contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol (3)
103-111
: Emit whitelist change events and consider zero-address policy
- No event is emitted on whitelist updates; governance changes are silent on-chain.
- Clarify whether toggling NO_TOKEN_GATE (address(0)) is allowed; currently possible via changeSupportedTokens.
Add an event and emit per token; optionally guard address(0) if you want to keep “no-gate” always enabled.
+ // * Events * // + event SupportedTokenChanged(address indexed token, bool supported); @@ function changeSupportedTokens(address[] memory _tokens, bool _supported) external onlyByOwner { for (uint256 i = 0; i < _tokens.length; i++) { - supportedTokens[_tokens[i]] = _supported; + address token = _tokens[i]; + supportedTokens[token] = _supported; + emit SupportedTokenChanged(token, _supported); } }
116-129
: createDispute pre-check OK; consider early “no-gate” fast pathLogic is correct. Minor optimization/readability: early-return when tokenGate == NO_TOKEN_GATE to skip mapping lookup.
240-255
: Avoid calldata→memory copy for extraData parsingThis is called from createDispute(bytes calldata _extraData) but accepts bytes memory, incurring a copy. Consider overloading with a calldata variant to save gas at hot paths.
- function _extraDataToTokenInfo( - bytes memory _extraData - ) internal pure returns (address tokenGate, bool isERC1155, uint256 tokenId) { + function _extraDataToTokenInfoCalldata( + bytes calldata _extraData + ) internal pure returns (address tokenGate, bool isERC1155, uint256 tokenId) { // Need at least 160 bytes to safely read the parameters if (_extraData.length < 160) return (address(0), false, 0); assembly { let packedTokenGateIsERC1155 := calldataload(_extraData.offset + 0x60) // 4th param (128) tokenId := calldataload(_extraData.offset + 0x80) // 5th param (160) tokenGate := and(packedTokenGateIsERC1155, 0xffffffffffffffffffffffffffffffffffffffff) isERC1155 := and(shr(160, packedTokenGateIsERC1155), 1) } }Then use the calldata version in createDispute; keep the memory version for storage/memory call sites.
contracts/src/arbitration/dispute-kits/DisputeKitGated.sol (3)
74-82
: Emit events for whitelist changesMirror the event in the non-Shutter variant to keep observability consistent.
+ event SupportedTokenChanged(address indexed token, bool supported); @@ function changeSupportedTokens(address[] memory _tokens, bool _supported) external onlyByOwner { for (uint256 i = 0; i < _tokens.length; i++) { - supportedTokens[_tokens[i]] = _supported; + address token = _tokens[i]; + supportedTokens[token] = _supported; + emit SupportedTokenChanged(token, _supported); } }
87-100
: Fast-path and contract-code guard (same as Shutter)
- Optional: fast-path when tokenGate == NO_TOKEN_GATE.
- Recommended: require tokenGate.code.length > 0 when gating is active to avoid draw-time failures.
116-131
: extraData parsing: avoid calldata→memory copySame suggestion as in Shutter file: provide a calldata overload for _extraDataToTokenInfo and call it from createDispute.
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (2)
76-85
: whitelistTokens signer assumptionFunction relies on disputeKit being connected to the owner. If tests ever change signer context, pass the signer explicitly to avoid flakiness.
108-162
: stakeAndDraw: disputeId hardcoded to 0This relies on fresh fixtures per test. If suites evolve to reuse state, derive the disputeId from the CreateDispute event or core state.
contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (3)
185-201
: Remove the arbitrary 10‑vote cap when collecting vote IDsA fixed cap can miss valid votes and cause flakiness. Stop when getVoteInfo reverts, but keep a reasonable safety cap.
Apply this diff:
- const voteIDs: bigint[] = []; - const maxVotes = 10; // Reasonable limit for testing - - for (let i = 0; i < maxVotes; i++) { + const voteIDs: bigint[] = []; + const SAFETY_CAP = 64; // upper bound to avoid infinite loops + for (let i = 0; i < SAFETY_CAP; i++) { try { const voteInfo = await context.disputeKit.getVoteInfo(disputeId, roundIndex, i); if (voteInfo[0] === juror.address) { // account is at index 0 voteIDs.push(BigInt(i)); } } catch { // No more votes break; } }
53-56
: Narrow the config contractName to a string unionPrevents typos and improves DX.
Apply this diff:
-export interface ShutterTestConfig { - contractName: string; // "DisputeKitShutter" or "DisputeKitGatedShutter" +export interface ShutterTestConfig { + contractName: "DisputeKitShutter" | "DisputeKitGatedShutter"; isGated?: boolean; // Whether to setup token gating }
139-141
: Optional: use Hardhat network-helpers for time/mineSwitch to @nomicfoundation/hardhat-network-helpers (time.increase, mine) for readability and consistency with Hardhat v3 runtime.
Based on learnings
Also applies to: 165-167
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
contracts/src/arbitration/dispute-kits/DisputeKitGated.sol
(6 hunks)contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol
(6 hunks)contracts/src/test/DisputeKitGatedShutterMock.sol
(1 hunks)contracts/test/arbitration/dispute-kit-gated-shutter.ts
(1 hunks)contracts/test/arbitration/dispute-kit-gated.ts
(1 hunks)contracts/test/arbitration/dispute-kit-shutter.ts
(1 hunks)contracts/test/arbitration/helpers/dispute-kit-gated-common.ts
(1 hunks)contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
contracts/test/arbitration/dispute-kit-shutter.ts (1)
contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (7)
ShutterTestContext
(25-50)setupShutterTest
(206-325)testCommitPhase
(329-443)testNormalFlowBotReveals
(445-582)testRecoveryFlowJurorReveals
(584-730)testHashFunctionBehavior
(732-772)testEdgeCasesAndSecurity
(774-875)
contracts/test/arbitration/dispute-kit-gated.ts (1)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (9)
TokenGatedTestContext
(27-46)setupTokenGatedTest
(165-236)testTokenWhitelistManagement
(240-305)testAccessControl
(307-323)testUnsupportedTokenErrors
(325-368)testERC20Gating
(370-431)testERC721Gating
(433-494)testERC1155Gating
(496-557)testWhitelistIntegration
(559-625)
contracts/test/arbitration/dispute-kit-gated-shutter.ts (2)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (9)
TokenGatedTestContext
(27-46)setupTokenGatedTest
(165-236)testTokenWhitelistManagement
(240-305)testAccessControl
(307-323)testUnsupportedTokenErrors
(325-368)testERC20Gating
(370-431)testERC721Gating
(433-494)testERC1155Gating
(496-557)testWhitelistIntegration
(559-625)contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (7)
ShutterTestContext
(25-50)setupShutterTest
(206-325)testCommitPhase
(329-443)testNormalFlowBotReveals
(445-582)testRecoveryFlowJurorReveals
(584-730)testHashFunctionBehavior
(732-772)testEdgeCasesAndSecurity
(774-875)
contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (2)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (1)
encodeExtraData
(59-74)contracts/deploy/utils/deployUpgradable.ts (1)
deployUpgradable
(37-89)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (4)
contracts/deploy/utils/index.ts (1)
PNK
(40-40)contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (1)
encodeExtraData
(71-72)contracts/deploy/utils/deployUpgradable.ts (1)
deployUpgradable
(37-89)contracts/deploy/utils/deployTokens.ts (2)
deployERC721
(55-67)deployERC1155
(69-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Redirect rules - kleros-v2-testnet
- GitHub Check: Redirect rules - kleros-v2-testnet
- GitHub Check: Header rules - kleros-v2-testnet
- GitHub Check: Header rules - kleros-v2-testnet
- GitHub Check: Pages changed - kleros-v2-testnet
- GitHub Check: Pages changed - kleros-v2-testnet
- GitHub Check: Redirect rules - kleros-v2-neo
- GitHub Check: Header rules - kleros-v2-neo
- GitHub Check: Pages changed - kleros-v2-neo
- GitHub Check: Analyze (javascript)
- GitHub Check: hardhat-tests
🔇 Additional comments (7)
contracts/src/arbitration/dispute-kits/DisputeKitGated.sol (1)
146-155
: Post-draw check assumes prior whitelist validationOK to skip supportedTokens check here. If you plan to allow dynamic whitelist changes mid-dispute, consider caching tokenGate per dispute at creation to avoid state drift. Otherwise current approach is fine.
contracts/src/test/DisputeKitGatedShutterMock.sol (1)
10-14
: Mock wrapper is cleanPublic wrapper correctly exposes parsing for tests; no concerns.
contracts/test/arbitration/dispute-kit-gated-shutter.ts (1)
36-72
: Shared test harness usage LGTMGood split between token-gating and Shutter suites with contexts and mocks. No issues spotted.
contracts/test/arbitration/dispute-kit-shutter.ts (1)
12-38
: Refactor to shared helpers LGTMCleaner suite via shared helpers; maintains coverage and readability.
contracts/test/arbitration/dispute-kit-gated.ts (1)
14-41
: Adoption of shared token-gated helpers LGTMContext-driven tests cover whitelist, access control, and gating paths comprehensively.
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (1)
58-74
: Packed encoding matches on-chain parsingThe uint88 + bool + address packing aligns with assembly extraction (addr in low 160 bits, bool at bit 160). Good.
contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (1)
343-349
: Verify/align test event args with the dispute‑kit ABI (CommitCastShutter / VoteCast)
- IDisputeKit.VoteCast matches the test shape: (uint256 indexed coreDisputeID, address indexed juror, uint256[] voteIDs, uint256 indexed choice, string justification).
- CommitCastShutter is inconsistent across implementations/ABIs in this repo: some variants include a recoveryCommit bytes32 (6 args), others do not (5 args). The test currently expects the 6‑arg form (commit, recoveryCommit, identity, encryptedVote).
- Action: ensure the test deploys/uses the dispute‑kit implementation whose ABI includes recoveryCommit, or update the test to expect the actual emitted event arguments (remove recoveryCommit from withArgs or assert it separately if present).
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.
Actionable comments posted: 3
🧹 Nitpick comments (2)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (1)
386-389
: Improve type safety for log filtering.The log filtering uses
any
type and optional chaining (log.fragment?.name
). While functional, this reduces type safety and could hide issues.Consider adding a type guard or helper function:
// At the top of the file, add a helper for type-safe log filtering const isDrawLog = (log: any): log is { fragment: { name: string }, address: string, args: any[] } => { return log.fragment?.name === "Draw"; }; // Then use it in tests: const drawLogs = tx?.logs.filter((log) => isDrawLog(log) && log.address === ctx.core.target) || [];This pattern is repeated throughout the test suites (lines 407, 449, 470, 512, 533, 581, 655, 684), so a shared helper would improve consistency and type safety.
contracts/test/arbitration/dispute-kit-gated.ts (1)
14-16
: Combine ESLint disable comments.The two separate ESLint disable comments can be combined into a single comment for consistency.
/* eslint-disable no-unused-vars */ -/* eslint-disable no-unused-expressions */ - +/* eslint-disable no-unused-expressions */ // https://github.com/standard/standard/issues/690#issuecomment-278533482 +
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
contracts/test/arbitration/dispute-kit-gated-shutter.ts
(1 hunks)contracts/test/arbitration/dispute-kit-gated.ts
(1 hunks)contracts/test/arbitration/helpers/dispute-kit-gated-common.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- contracts/test/arbitration/dispute-kit-gated-shutter.ts
🧰 Additional context used
🧬 Code graph analysis (2)
contracts/test/arbitration/dispute-kit-gated.ts (1)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (10)
TokenGatedTestContext
(27-46)setupTokenGatedTest
(165-236)testTokenWhitelistManagement
(240-305)testAccessControl
(307-323)testUnsupportedTokenErrors
(325-368)testERC20Gating
(370-431)testERC721Gating
(433-494)testERC1155Gating
(496-557)testWhitelistIntegration
(559-625)testNoTokenGateAddress
(627-704)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (4)
contracts/deploy/utils/index.ts (1)
PNK
(40-40)contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (1)
encodeExtraData
(71-72)contracts/deploy/utils/deployUpgradable.ts (1)
deployUpgradable
(37-89)contracts/deploy/utils/deployTokens.ts (2)
deployERC721
(55-67)deployERC1155
(69-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Redirect rules - kleros-v2-testnet
- GitHub Check: Header rules - kleros-v2-testnet
- GitHub Check: Pages changed - kleros-v2-testnet
🔇 Additional comments (5)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (4)
1-51
: LGTM: Well-structured type definitions.The type definitions, interfaces, and imports are well-organized and comprehensive. The
TokenGatedTestContext
interface captures all necessary test state, and theDisputeKitGatedType
union type correctly handles both gated variants.
108-162
: Note the test limitation regarding EOA-created disputes.The comment on line 150 correctly notes that the dispute "cannot be executed, in reality it should be created by an arbitrable contract, not an EOA." This is an acceptable test limitation, but it means these tests don't fully validate the end-to-end dispute execution flow.
Additionally, the hardcoded gas limits (lines 125, 130, 161) might be fragile across different EVM versions or network conditions. Consider using automatic gas estimation or documenting why specific limits are needed.
238-704
: Comprehensive test coverage for token gating.The test suites provide excellent coverage of token gating functionality:
- Whitelist management (add/remove single/multiple tokens, duplicates)
- Access control (owner-only operations)
- Error handling for unsupported tokens
- Token gating for ERC20, ERC721, and ERC1155
- Whitelist state management across operations
- Edge case handling for address(0) as "no token gate"
The tests are well-structured, clearly documented, and cover both positive and negative scenarios.
59-74
: Token packing format matches Solidity unpacking. Verified thatencodeExtraData
’s solidityPacked(["uint88","bool","address"]) aligns with_extraDataToTokenInfo
’s assembly logic in both DisputeKitGated.sol and DisputeKitGatedShutter.sol.contracts/test/arbitration/dispute-kit-gated.ts (1)
1-43
: Excellent refactoring to shared test helpers.This test file demonstrates good software engineering practices:
DRY principle: Test logic is centralized in the common helper file, eliminating duplication between DisputeKitGated and DisputeKitGatedShutter tests.
Clear structure: The test file is easy to understand—it sets up context once and delegates to focused test suite functions.
Maintainability: Changes to test logic only need to be made in one place (the common helper), while contract-specific behavior can still be tested separately if needed.
Documentation: The JSDoc comment clearly explains what the test suite covers.
The context factory pattern (
() => context
) is used correctly to ensure each test gets fresh context via thebeforeEach
hook.
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.
Actionable comments posted: 2
🧹 Nitpick comments (7)
contracts/test/arbitration/dispute-kit-gated-shutter.ts (1)
37-39
: Nit: avoid async on describe callbacks.Mocha ignores async return on describe; keep async on hooks/tests only.
contracts/src/arbitration/dispute-kits/DisputeKitGated.sol (3)
74-81
: Emit events for whitelist changes.Governance actions should be observable. Add an event and emit per updated token.
Apply:
+ /// @notice Emitted when supported token status changes. + event SupportedTokenChanged(address indexed token, bool supported); + function changeSupportedTokens(address[] memory _tokens, bool _supported) external onlyByOwner { for (uint256 i = 0; i < _tokens.length; i++) { supportedTokens[_tokens[i]] = _supported; + emit SupportedTokenChanged(_tokens[i], _supported); } }
87-101
: Consider enforcing access control first (minor).Currently the whitelist check runs before super.createDispute’s onlyByCore. Calling super first would fail fast for unauthorized callers. Not critical since the pre-check is cheap and data is public anyway.
149-154
: Harden balance checks to avoid revert on bad whitelist entries.If a non-contract or non-compliant token is whitelisted, interface calls may revert and block draws. Prefer safe staticcall; treat failures as zero balance.
Example replacement (helper functions to add elsewhere in the contract):
function _hasERC20or721Balance(address token, address owner) internal view returns (bool) { (bool ok, bytes memory ret) = token.staticcall(abi.encodeWithSignature("balanceOf(address)", owner)); if (!ok || ret.length < 32) return false; return abi.decode(ret, (uint256)) > 0; } function _hasERC1155Balance(address token, address owner, uint256 id) internal view returns (bool) { (bool ok, bytes memory ret) = token.staticcall(abi.encodeWithSignature("balanceOf(address,uint256)", owner, id)); if (!ok || ret.length < 32) return false; return abi.decode(ret, (uint256)) > 0; }Then within this block:
- if (isERC1155) { - return IBalanceHolderERC1155(tokenGate).balanceOf(_juror, tokenId) > 0; - } else { - return IBalanceHolder(tokenGate).balanceOf(_juror) > 0; - } + return isERC1155 + ? _hasERC1155Balance(tokenGate, _juror, tokenId) + : _hasERC20or721Balance(tokenGate, _juror);contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol (2)
39-41
: Emit events when whitelist changes for auditabilityMapping flips are silent. Emit an event per token to ease monitoring and off-chain indexing, and to support revert-free idempotent updates.
Example:
+ event SupportedTokenChanged(address indexed token, bool supported); function changeSupportedTokens(address[] memory _tokens, bool _supported) external onlyByOwner { for (uint256 i = 0; i < _tokens.length; i++) { supportedTokens[_tokens[i]] = _supported; + emit SupportedTokenChanged(_tokens[i], _supported); } }Also applies to: 103-111
116-129
: Access control check runs after gating logicYou rely on super.createDispute for onlyByCore. Minor: consider moving access-control earlier (e.g., replicate the modifier or a lightweight check) to fail fast on unauthorized callers, then run gating. Not critical.
Confirm base enforces onlyByCore and no state is mutated before the revert in this override.
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (1)
76-85
: Normalize AddressLike instead of toString()Using toString() on Addressable can yield non-address strings. Prefer AddressLike and ethers.getAddress for safety.
-export const whitelistTokens = async ( - context: TokenGatedTestContext, - tokens: (string | Addressable)[], - supported: boolean = true -) => { - const tokenAddresses = tokens.map((token) => (typeof token === "string" ? token : token.toString())); +export const whitelistTokens = async ( + context: TokenGatedTestContext, + tokens: readonly ethers.AddressLike[], + supported: boolean = true +) => { + const tokenAddresses = tokens.map((t) => ethers.getAddress(t as string)); return context.disputeKit.changeSupportedTokens(tokenAddresses, supported); };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
contracts/src/arbitration/dispute-kits/DisputeKitGated.sol
(6 hunks)contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol
(6 hunks)contracts/src/test/DisputeKitGatedShutterMock.sol
(1 hunks)contracts/test/arbitration/dispute-kit-gated-shutter.ts
(1 hunks)contracts/test/arbitration/dispute-kit-gated.ts
(1 hunks)contracts/test/arbitration/dispute-kit-shutter.ts
(1 hunks)contracts/test/arbitration/helpers/dispute-kit-gated-common.ts
(1 hunks)contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
contracts/test/arbitration/dispute-kit-gated-shutter.ts (2)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (2)
TokenGatedTestContext
(27-46)setupTokenGatedTest
(165-236)contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (2)
ShutterTestContext
(25-50)setupShutterTest
(206-325)
contracts/test/arbitration/dispute-kit-gated.ts (1)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (10)
TokenGatedTestContext
(27-46)setupTokenGatedTest
(165-236)testTokenWhitelistManagement
(240-305)testAccessControl
(307-323)testUnsupportedTokenErrors
(325-368)testERC20Gating
(370-431)testERC721Gating
(433-494)testERC1155Gating
(496-557)testWhitelistIntegration
(559-625)testNoTokenGateAddress
(627-704)
contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (2)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (1)
encodeExtraData
(59-74)contracts/deploy/utils/deployUpgradable.ts (1)
deployUpgradable
(37-89)
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (4)
contracts/deploy/utils/index.ts (1)
PNK
(40-40)contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (1)
encodeExtraData
(71-72)contracts/deploy/utils/deployUpgradable.ts (1)
deployUpgradable
(37-89)contracts/deploy/utils/deployTokens.ts (2)
deployERC721
(55-67)deployERC1155
(69-81)
contracts/test/arbitration/dispute-kit-shutter.ts (1)
contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (7)
ShutterTestContext
(25-50)setupShutterTest
(206-325)testCommitPhase
(329-443)testNormalFlowBotReveals
(445-582)testRecoveryFlowJurorReveals
(584-730)testHashFunctionBehavior
(732-772)testEdgeCasesAndSecurity
(774-875)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: Redirect rules - kleros-v2-neo
- GitHub Check: Header rules - kleros-v2-neo
- GitHub Check: Redirect rules - kleros-v2-testnet-devtools
- GitHub Check: Header rules - kleros-v2-testnet-devtools
- GitHub Check: Pages changed - kleros-v2-neo
- GitHub Check: Pages changed - kleros-v2-testnet-devtools
- GitHub Check: hardhat-tests
- GitHub Check: Analyze (javascript)
- GitHub Check: Redirect rules - kleros-v2-testnet
- GitHub Check: Redirect rules - kleros-v2-testnet
- GitHub Check: Header rules - kleros-v2-testnet
- GitHub Check: Header rules - kleros-v2-testnet
- GitHub Check: Pages changed - kleros-v2-testnet
- GitHub Check: Pages changed - kleros-v2-testnet
🔇 Additional comments (13)
contracts/src/test/DisputeKitGatedShutterMock.sol (1)
10-14
: Good: test-only wrapper for internal decoder.Simple, pure passthrough is appropriate for tests.
contracts/test/arbitration/dispute-kit-shutter.ts (1)
32-37
: Refactor to shared suites looks good.Centralizing commit/reveal tests via common helpers reduces duplication.
contracts/test/arbitration/dispute-kit-gated-shutter.ts (2)
45-53
: Token-gating shared tests wired correctly.Good coverage across whitelist, access control, and gating variants.
67-73
: Shutter shared tests wired correctly under gated context.Nice reuse of the same suites with isGated=true.
contracts/test/arbitration/dispute-kit-gated.ts (1)
34-42
: Shared gated tests integration looks good.Concise orchestration; promotes reuse and clarity.
contracts/src/arbitration/dispute-kits/DisputeKitGated.sol (1)
116-131
: Docs match decoding layout; no update needed.contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol (3)
240-255
: Decoder offsets and bit unpacking look correctLength guard, offsets (0x80/0xA0 with bytes length slot accounted), and bit-160 flag extraction match the packed layout used in tests.
258-279
: Gating check in _postDrawCheck is soundNO_TOKEN_GATE shortcut and ERC20/721 vs ERC1155 balance checks are correct. Dependent on createDispute-time whitelist only, which is fine.
If whitelist may change mid‑dispute, confirm the intended behavior is to gate juror eligibility independent of later whitelist flips.
90-91
: Allowing address(0) by default is sensibleKeeps non‑gated disputes possible. Intentional and matches tests.
contracts/test/arbitration/helpers/dispute-kit-gated-common.ts (2)
59-75
: Encoder matches on-chain decoderPacked [uint88,bool,address] into bytes32 and ABI-encodes at slot 4 with tokenId at slot 5; aligns with contract decoding.
232-236
: Global default whitelist in setup is finePre-whitelisting test tokens keeps legacy tests green. Consider asserting ZeroAddress remains supported if you rely on non-gated flows.
Add a quick check here: expectTokenSupported(ctx, ethers.ZeroAddress, true);
contracts/test/arbitration/helpers/dispute-kit-shutter-common.ts (2)
70-73
: Ungated encoder OKABI shape [uint256,uint256,uint256] matches on-chain expectations.
224-278
: Setup path selection logic is consistentUsing the Mock for gated shutter and enabling hidden votes on General court is reasonable for tests.
Ensure Court IDs and DisputeKit IDs (1=Classic, 2=Shutter) match current Core deployment constants.
PR-Codex overview
This PR introduces enhancements to the
DisputeKitGated
andDisputeKitGatedShutter
contracts, including new functionality for token gating in dispute creation and improvements in testing frameworks for both kits.Detailed summary
createDispute
visibility fromexternal
topublic
inDisputeKitClassicBase
.changeSupportedTokens
function to manage token support inDisputeKitGated
andDisputeKitGatedShutter
._extraDataToTokenInfo
to beinternal
in both kits.TokenNotSupported
error for unsupported tokens.DisputeKitGatedMock
andDisputeKitGatedShutterMock
for testing.DisputeKitGated
andDisputeKitGatedShutter
to cover token gating and access control scenarios.dispute-kit-gated-common.ts
for setting up tests and managing token whitelisting.address(0)
as a token gate, allowing disputes without token gating.Summary by CodeRabbit
New Features
Behavior Changes
Tests