-
Notifications
You must be signed in to change notification settings - Fork 49
feat: subgraph support for shutter disputekit in devnet #1966
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
base: feat/shutter-dispute-kit
Are you sure you want to change the base?
feat: subgraph support for shutter disputekit in devnet #1966
Conversation
WalkthroughThe changes introduce dynamic handling of dispute kit IDs throughout the subgraph code, replacing hardcoded values. Entity ID construction and event processing now extract the dispute kit ID from event data or related entities. A new data source for the Shutter dispute kit is added, and relevant function signatures are updated for flexibility and safety. Changes
Sequence Diagram(s)sequenceDiagram
participant Event
participant Utils
participant Dispute
participant Round
participant ClassicDispute
participant ClassicRound
Event->>Utils: extractDisputeKitIDFromExtraData(_extraData)
Utils-->>Event: disputeKitID
Event->>ClassicDispute: createClassicDisputeFromEvent(event, disputeKitID)
ClassicDispute->>ClassicDispute: Construct ID using disputeKitID
Event->>ClassicRound: createClassicRound(disputeID, choices, roundIndex, disputeKitID)
ClassicRound->>ClassicRound: Construct ID using disputeKitID
Event->>Dispute: Load Dispute by coreDisputeID
Dispute->>Round: Load Round by disputeID and roundIndex
Round-->>Event: disputeKitID for further entity ID construction
Possibly related issues
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
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 (4)
subgraph/core/src/entities/ClassicRound.ts (2)
6-13
: Missing JSDoc / comments after signature change
createClassicRound
now acceptsdisputeKitID
, but the function header or surrounding docs were not updated. A brief comment clarifying the ID format (<disputeKitID>-<coreDisputeID>-<roundIndex>
) will save future readers time.
85-97
: PersistclassicRound
after answer update only onceInside
updateChoiceFundingFromContributionEvent
youanswer.save()
and thenclassicRound.save()
immediately after.
BecauseclassicRound
isn’t modified in this function, the lattersave()
is unnecessary and costs an extra store write.- answer.save(); - classicRound.save(); + answer.save(); // classicRound untouchedsubgraph/core/src/DisputeKitClassic.ts (2)
34-38
: Consider caching the extracted dispute-kit ID
extractDisputeKitIDFromExtraData
is inexpensive, yet the value never changes for this dispute.
Persisting it once in theDispute
orClassicDispute
entity (e.g.,disputeKit
field) would let subsequent handlers fetch it directly instead of re-computing / re-loadingRound
each time, reducing store look-ups.
41-50
: Repeated pattern → extract helperThe sequence
load Dispute → load current Round → read disputeKit
is duplicated in several handlers (handleCommitCast
,handleVoteCast
,handleChoiceFunded
,handleWithdrawal
). Factor it into a small utility such as:export function getDisputeKitID(coreDisputeID: string): string | null { ... }to improve readability and reduce the chance of future inconsistencies.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
subgraph/core/src/DisputeKitClassic.ts
(8 hunks)subgraph/core/src/KlerosCore.ts
(1 hunks)subgraph/core/src/entities/ClassicContribution.ts
(1 hunks)subgraph/core/src/entities/ClassicDispute.ts
(1 hunks)subgraph/core/src/entities/ClassicRound.ts
(2 hunks)subgraph/core/src/utils.ts
(1 hunks)subgraph/core/subgraph.yaml
(1 hunks)subgraph/package.json
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
subgraph/core/src/DisputeKitClassic.ts (4)
subgraph/core/src/KlerosCore.ts (1)
handleDisputeCreation
(77-97)subgraph/core/src/utils.ts (3)
extractDisputeKitIDFromExtraData
(6-10)ZERO
(3-3)ONE
(4-4)subgraph/core/src/entities/ClassicDispute.ts (1)
createClassicDisputeFromEvent
(6-15)subgraph/core/src/entities/ClassicRound.ts (1)
createClassicRound
(6-25)
🪛 Biome (1.9.4)
subgraph/core/src/utils.ts
[error] 1-1: Do not shadow the global "BigInt" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
🔇 Additional comments (8)
subgraph/core/src/utils.ts (2)
1-1
: Biome static analysis issue can be safely ignored.The static analysis reports a potential issue with shadowing the global
BigInt
property, but this is a standard and necessary pattern in The Graph's AssemblyScript SDK. This import is correct and follows the standard practices.🧰 Tools
🪛 Biome (1.9.4)
[error] 1-1: Do not shadow the global "BigInt" property.
Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.
(lint/suspicious/noShadowRestrictedNames)
6-10
: Solid utility function for extracting dispute kit IDs.The function correctly extracts the dispute kit ID by:
- Getting the last 32 bytes from the extraData
- Reversing the byte order (little-endian to big-endian)
- Converting to a BigInt and returning as string
This approach enables dynamic handling of dispute kit IDs throughout the subgraph.
subgraph/package.json (1)
3-3
: Version bump is appropriate for this feature addition.The version increment from 0.15.2 to 0.16.0 follows semantic versioning principles, correctly indicating a new feature (adding support for ShutterDisputeKit).
subgraph/core/src/KlerosCore.ts (1)
159-159
: Updated comment accurately reflects expanded dispute kit support.The comment now correctly indicates that the ClassicVote entity works for both the ClassicDisputeKit and the new ShutterDisputeKit, aligning with the PR's goal of supporting multiple dispute kits.
subgraph/core/src/entities/ClassicDispute.ts (2)
1-1
: Added necessary Bytes import.The import of
Bytes
is now required since this file interacts with the new utility function that processes byte data.
6-8
: Well-implemented dynamic dispute kit ID handling.The function signature now accepts a
disputeKitID
parameter and properly uses it to construct the ClassicDispute entity ID, replacing the previously hardcoded ID prefix. This change enables consistent entity identification across multiple dispute kits.subgraph/core/src/entities/ClassicRound.ts (1)
74-86
:❌ Incorrect review comment
Null-check
disputeKitID
before using itSame concern as in
ClassicContribution
: ifcoreRound.disputeKit
isnull
the derivedroundID
is malformed.- const disputeKitID = coreRound.disputeKit; + const disputeKitID = coreRound.disputeKit; + if (!disputeKitID) return; // abort – underlying data is incomplete
Here’s some targeted searching to confirm whether the
disputeKit
field onRound
can actually benull
(and whether a guard is needed):
🏁 Script executed:
#!/bin/bash # 1) Check GraphQL schema for Round entity and disputeKit nullability rg "type Round" -n -g "*.graphql" rg "disputeKit" -n -g "*.graphql" # 2) Inspect the generated TS entity for Round to see how disputeKit is typed rg "class Round" -n rg "disputeKit" -C3 -nLength of output: 96599
DisputeKitID Is Always Defined – No Guard Needed
Based on
schema.graphql
and the generatedRound
entity,disputeKit
is non-nullable:
- In
schema.graphql
:
type Round @entity { … disputeKit: DisputeKit! … }
- In
subgraph/core/src/entities/Round.ts
:It’s always set before the entity is saved, and the generated AssemblyScriptround.disputeKit = roundInfo.disputeKitID.toString();get disputeKit(): string
cannot returnnull
.The same pattern is used throughout (e.g. in
ClassicContribution.ts
) without issue. You can safely omit the additionalif (!disputeKitID) return;
check here.Likely an incorrect or invalid review comment.
subgraph/core/subgraph.yaml (1)
123-158
: Validate ABI/event signature parity between Classic & Shutter
DisputeKitShutter
re-uses theDisputeKitClassic.ts
handlers. This will only work if every event signature in the Shutter ABI exactly matches the Classic ABI.Please double-check that:
- The ABI file at
../../contracts/deployments/arbitrumSepoliaDevnet/DisputeKitShutter.json
contains all six events with identical parameter ordering/types.- The
startBlock
(148 194 178) predates the first emitted event; otherwise initial events will be missed.A quick grep / diff against the Classic ABI before deploying would prevent runtime failures.
const coreDispute = Dispute.load(coreDisputeID); | ||
if (!coreDispute) return null; | ||
|
||
const roundId = `${coreDisputeID}-${coreRoundIndex}`; | ||
const coreRound = Round.load(roundId); | ||
if (!coreRound) return null; | ||
|
||
const disputeKitID = coreRound.disputeKit; | ||
|
||
const roundID = `${disputeKitID}-${coreDisputeID}-${coreRoundIndex}`; | ||
|
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.
💡 Verification agent
🧩 Analysis chain
Guard against a missing disputeKit
reference
coreRound.disputeKit
is assumed to be present and non-empty. If the Round entity was created before the refactor (or gets corrupted), this will evaluate to null
and the constructed roundID
becomes "null-<coreDisputeID>-<coreRoundIndex>"
, causing silent data fragmentation.
- const disputeKitID = coreRound.disputeKit;
+ const disputeKitID = coreRound.disputeKit;
+ if (!disputeKitID) {
+ // Inconsistent data – abort processing to avoid polluting the store
+ return null;
+ }
Fail-fast avoids persisting wrongly-keyed entities and makes the issue visible during indexing.
🏁 Script executed:
#!/bin/bash
echo "=== GraphQL schema files ==="
find . -type f -name "*.graphql"
echo ""
echo "=== 'type Round' and 'disputeKit' in GraphQL schemas ==="
rg -n "type Round" -C2 -g "*.graphql" || true
rg -n "disputeKit" -C2 -g "*.graphql" || true
echo ""
echo "=== Locate and inspect Round entity class ==="
round_files=$(rg -l "export class Round" -g "*.ts")
echo "Found Round.ts files:"
echo "$round_files"
for file in $round_files; do
echo ""
echo "------ $file ------"
rg -n "export class Round" -C3 "$file"
rg -n "get disputeKit" -C5 "$file" || true
done
Length of output: 3678
Guard against a missing disputeKit
reference
The GraphQL schema defines Round.disputeKit
as non-nullable, but legacy or corrupted data may still omit it. Add a null check immediately after loading the Round to avoid constructing IDs like "null-<coreDisputeID>-<coreRoundIndex>"
and silently polluting the store.
• File: subgraph/core/src/entities/ClassicContribution.ts
• Context: right after assigning disputeKitID
- const disputeKitID = coreRound.disputeKit;
+ const disputeKitID = coreRound.disputeKit;
+ if (!disputeKitID) {
+ // Inconsistent data – abort processing to avoid polluting the store
+ return null;
+ }
This fail-fast guard ensures any missing disputeKit
is surfaced during indexing rather than resulting in fragmented or mis-keyed entities.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const coreDispute = Dispute.load(coreDisputeID); | |
if (!coreDispute) return null; | |
const roundId = `${coreDisputeID}-${coreRoundIndex}`; | |
const coreRound = Round.load(roundId); | |
if (!coreRound) return null; | |
const disputeKitID = coreRound.disputeKit; | |
const roundID = `${disputeKitID}-${coreDisputeID}-${coreRoundIndex}`; | |
const coreDispute = Dispute.load(coreDisputeID); | |
if (!coreDispute) return null; | |
const roundId = `${coreDisputeID}-${coreRoundIndex}`; | |
const coreRound = Round.load(roundId); | |
if (!coreRound) return null; | |
const disputeKitID = coreRound.disputeKit; | |
if (!disputeKitID) { | |
// Inconsistent data – abort processing to avoid polluting the store | |
return null; | |
} | |
const roundID = `${disputeKitID}-${coreDisputeID}-${coreRoundIndex}`; |
const coreDispute = Dispute.load(coreDisputeID); | ||
if (!coreDispute) return; | ||
|
||
const roundId = `${coreDisputeID}-${coreRoundIndex}`; | ||
const coreRound = Round.load(roundId); | ||
if (!coreRound) return; | ||
const disputeKitID = coreRound.disputeKit; | ||
|
||
const roundID = `${disputeKitID}-${coreDisputeID}-${coreRoundIndex}`; |
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.
Defensive check for disputeKitID
Replicating the guard here avoids malformed IDs during withdrawals.
const disputeKitID = coreRound.disputeKit;
-
+ if (!disputeKitID) return;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const coreDispute = Dispute.load(coreDisputeID); | |
if (!coreDispute) return; | |
const roundId = `${coreDisputeID}-${coreRoundIndex}`; | |
const coreRound = Round.load(roundId); | |
if (!coreRound) return; | |
const disputeKitID = coreRound.disputeKit; | |
const roundID = `${disputeKitID}-${coreDisputeID}-${coreRoundIndex}`; | |
const coreDispute = Dispute.load(coreDisputeID); | |
if (!coreDispute) return; | |
const roundId = `${coreDisputeID}-${coreRoundIndex}`; | |
const coreRound = Round.load(roundId); | |
if (!coreRound) return; | |
const disputeKitID = coreRound.disputeKit; | |
if (!disputeKitID) return; | |
const roundID = `${disputeKitID}-${coreDisputeID}-${coreRoundIndex}`; |
const coreDispute = Dispute.load(coreDisputeID); | ||
if (!coreDispute) return; | ||
|
||
const roundId = `${coreDisputeID}-${coreRoundIndex}`; | ||
const coreRound = Round.load(roundId); | ||
if (!coreRound) return; | ||
const disputeKitID = coreRound.disputeKit; | ||
|
||
const roundID = `${disputeKitID}-${coreDisputeID}-${coreRoundIndex}`; |
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.
Early exit when disputeKitID
missing
Same defensive check as earlier – without it, roundID
may start with "null-"
.
const disputeKitID = coreRound.disputeKit;
-
+ if (!disputeKitID) return;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const coreDispute = Dispute.load(coreDisputeID); | |
if (!coreDispute) return; | |
const roundId = `${coreDisputeID}-${coreRoundIndex}`; | |
const coreRound = Round.load(roundId); | |
if (!coreRound) return; | |
const disputeKitID = coreRound.disputeKit; | |
const roundID = `${disputeKitID}-${coreDisputeID}-${coreRoundIndex}`; | |
const coreDispute = Dispute.load(coreDisputeID); | |
if (!coreDispute) return; | |
const roundId = `${coreDisputeID}-${coreRoundIndex}`; | |
const coreRound = Round.load(roundId); | |
if (!coreRound) return; | |
const disputeKitID = coreRound.disputeKit; | |
if (!disputeKitID) return; | |
const roundID = `${disputeKitID}-${coreDisputeID}-${coreRoundIndex}`; |
you can test this here: https://api.studio.thegraph.com/query/108432/kleros-v2-devnet/version/latest/graphql?que[…]pute+%7B%0A++++++++id%0A++++++%7D%0A++++%7D%0A++%7D%0A%7D
This only supports Devnet.
For Testnet support: would need a script adjustment to tweak the DisputeKitShutter address and deployment block in the subgraph.yaml file (since Devnet and Testnet share the same subgraph.yaml file)
For Neo support: it would be just adding the DisputeKitShutter address and deployment block in its subgraph.yaml file, since that file is independent from Devnet and Testnet so doesn't need script adjustment, add it like:
PR-Codex overview
This PR updates the
@kleros/kleros-v2-subgraph
package version and enhances the handling of dispute kits in various functions, allowing support for multiple dispute kits. It introduces a new utility function and modifies existing functions to accommodate the new structure.Detailed summary
version
insubgraph/package.json
from0.15.2
to0.16.0
.KlerosCore.ts
to reflect support forClassicDisputeKit
andShutterDisputeKit
.extractDisputeKitIDFromExtraData
function inutils.ts
.createClassicDisputeFromEvent
to acceptdisputeKitID
.ensureClassicContributionFromEvent
to loadDispute
andRound
entities, modifying theroundID
format.disputeKitID
parameter tocreateClassicRound
.handleDisputeCreation
to extractdisputeKitID
and pass it to functions.handleCommitCast
,handleVoteCast
, andhandleChoiceFunded
to usedisputeKitID
when loading disputes and rounds.handleWithdrawal
to accommodate the newdisputeKitID
structure.Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Documentation
Chores