Skip to content

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

Open
wants to merge 5 commits into
base: feat/shutter-dispute-kit
Choose a base branch
from

Conversation

kemuru
Copy link
Contributor

@kemuru kemuru commented May 6, 2025

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:

  - kind: ethereum
    name: DisputeKitShutter
    network: arbitrum-sepolia
    source:
      address: "0x..."
      abi: DisputeKitShutter
      startBlock: 99999999
    mapping:
      kind: ethereum/events
      ...

PR-Codex overview

This PR focuses on updating the subgraph configurations and improving the handling of disputes and contributions within the Kleros system. It includes version updates, new data sources, and modifications to entity handling to accommodate additional dispute kits.

Detailed summary

  • Updated version in package.json from 0.15.2 to 0.16.0.
  • Added support for core-neo and core-university subgraphs.
  • Enhanced ClassicVote entity handling for ClassicDisputeKit and ShutterDisputeKit.
  • Introduced extractDisputeKitIDFromExtraData function in utils.ts.
  • Updated subgraph.yaml files to include new data sources and configurations.
  • Adjusted createClassicDisputeFromEvent to accept disputeKitID and roundIndex.
  • Modified event handlers in DisputeKitClassic.ts to support new dispute kits.
  • Updated ClassicContribution and ClassicRound entities to handle dispute kit IDs.
  • Added new event handlers in SortitionModule for better stake management.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added support for the Shutter Dispute Kit on the arbitrum-sepolia network, enabling tracking of disputes, rounds, votes, and contributions for this new contract.
    • Introduced new subgraph manifests for expanded protocol coverage including core-neo and core-university configurations.
  • Bug Fixes

    • Improved safety with additional null checks to prevent issues when required entities are missing.
  • Refactor

    • Replaced fixed dispute kit ID usage with dynamic extraction from event data, enhancing compatibility with multiple dispute kits.
    • Adjusted event handling and ID construction to use dynamically obtained dispute kit IDs.
  • Documentation

    • Updated comments to clarify that certain entities and logic apply to both Classic and Shutter dispute kits.
  • Chores

    • Updated the package version to 0.16.0.
    • Enhanced deployment scripts to prune undeployed contracts from subgraph configurations automatically.

@kemuru kemuru requested a review from a team as a code owner May 6, 2025 22:14
Copy link
Contributor

coderabbitai bot commented May 6, 2025

Walkthrough

The 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

Files/Paths Change Summary
subgraph/core/src/DisputeKitClassic.ts, subgraph/core/src/entities/ClassicDispute.ts, subgraph/core/src/entities/ClassicRound.ts, subgraph/core/src/entities/ClassicContribution.ts Replaced hardcoded dispute kit ID with dynamic extraction from event data or entities; updated entity ID construction and function signatures to accept/use dynamic dispute kit IDs; added null checks for entity existence in handlers; adjusted control flow for round creation and entity saving.
subgraph/core/src/utils.ts Added utility function extractDisputeKitIDFromExtraData to extract dispute kit ID from event extra data bytes.
subgraph/core/subgraph.yaml Added new data source for DisputeKitShutter on arbitrum-sepolia with event handlers mirroring DisputeKitClassic, enabling support for multiple dispute kits.
subgraph/core/src/KlerosCore.ts Updated comment to clarify that ClassicVote applies to both Classic and Shutter dispute kits; no logic changes.
subgraph/package.json Bumped package version from 0.15.2 to 0.16.0.

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, roundIndex)
    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
Loading

Poem

In the warren where subgraphs grow,
IDs once fixed now flex and flow.
Classic and Shutter, side by side,
Disputes and rounds with dynamic pride.
No more static, now they roam,
Rabbits cheer, “Multi-kit support is home!”
🐇✨

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@kemuru kemuru linked an issue May 6, 2025 that may be closed by this pull request
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 accepts disputeKitID, 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: Persist classicRound after answer update only once

Inside updateChoiceFundingFromContributionEvent you answer.save() and then classicRound.save() immediately after.
Because classicRound isn’t modified in this function, the latter save() is unnecessary and costs an extra store write.

-  answer.save();
-  classicRound.save();
+  answer.save();  // classicRound untouched
subgraph/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 the Dispute or ClassicDispute entity (e.g., disputeKit field) would let subsequent handlers fetch it directly instead of re-computing / re-loading Round each time, reducing store look-ups.


41-50: Repeated pattern → extract helper

The 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

📥 Commits

Reviewing files that changed from the base of the PR and between ee2ceb6 and 317aed6.

📒 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:

  1. Getting the last 32 bytes from the extraData
  2. Reversing the byte order (little-endian to big-endian)
  3. 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 it

Same concern as in ClassicContribution: if coreRound.disputeKit is null the derived roundID 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 on Round can actually be null (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 -n

Length of output: 96599


DisputeKitID Is Always Defined – No Guard Needed

Based on schema.graphql and the generated Round entity, disputeKit is non-nullable:

  • In schema.graphql:
    type Round @entity { … disputeKit: DisputeKit! … }
  • In subgraph/core/src/entities/Round.ts:
    round.disputeKit = roundInfo.disputeKitID.toString();
    It’s always set before the entity is saved, and the generated AssemblyScript get disputeKit(): string cannot return null.

The same pattern is used throughout (e.g. in ClassicContribution.ts) without issue. You can safely omit the additional if (!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 the DisputeKitClassic.ts handlers. This will only work if every event signature in the Shutter ABI exactly matches the Classic ABI.

Please double-check that:

  1. The ABI file at ../../contracts/deployments/arbitrumSepoliaDevnet/DisputeKitShutter.json contains all six events with identical parameter ordering/types.
  2. 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.

Comment on lines +10 to +20
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}`;

Copy link
Contributor

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.

Suggested change
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}`;

Comment on lines +184 to +192
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}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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}`;

Comment on lines +128 to +136
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}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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}`;

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (1)
subgraph/core/src/entities/ClassicDispute.ts (1)

5-14: Consider adding documentation for improved maintainability.

While the code changes are well-implemented, adding function-level documentation that explains the purpose of the new parameters would improve maintainability, especially for new developers who might work on this codebase in the future.

+/**
+ * Creates a ClassicDispute entity from a DisputeCreation event
+ * @param event The DisputeCreation event
+ * @param disputeKitID The ID of the dispute kit (e.g., "1" for Classic, "2" for Shutter)
+ * @param roundIndex The initial round index for this dispute
+ */
 export function createClassicDisputeFromEvent(event: DisputeCreation, disputeKitID: string, roundIndex: BigInt): void {
   const coreDisputeID = event.params._coreDisputeID.toString();
   const classicDispute = new ClassicDispute(`${disputeKitID}-${coreDisputeID}`);
   classicDispute.coreDispute = coreDisputeID;
   classicDispute.currentLocalRoundIndex = roundIndex;
   classicDispute.numberOfChoices = event.params._numberOfChoices;
   classicDispute.extraData = event.params._extraData;
   classicDispute.timestamp = event.block.timestamp;
   classicDispute.save();
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 317aed6 and 05dcdb0.

📒 Files selected for processing (2)
  • subgraph/core/src/DisputeKitClassic.ts (8 hunks)
  • subgraph/core/src/entities/ClassicDispute.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • subgraph/core/src/DisputeKitClassic.ts
🧰 Additional context used
🪛 Biome (1.9.4)
subgraph/core/src/entities/ClassicDispute.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)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: SonarCloud
🔇 Additional comments (4)
subgraph/core/src/entities/ClassicDispute.ts (4)

1-1: Note on static analysis warning about BigInt import.

The static analysis warning about shadowing the global "BigInt" property can be safely ignored in this context. This is standard practice in The Graph's subgraph development, where we import the specialized BigInt type from the graph-ts library.

🧰 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)


5-5: Good enhancement: Added parameters for flexible dispute kit handling.

Adding the disputeKitID and roundIndex parameters to the function signature is a good enhancement that improves code flexibility by removing hardcoded values. This change aligns with the PR objective to support multiple dispute kits, specifically the DisputeKitShutter.


7-7: Proper update of entity ID to use dynamic dispute kit ID.

Replacing the previously hardcoded prefix with a dynamic disputeKitID parameter is a necessary change to support multiple dispute kits. This ensures unique IDs across different dispute kit implementations while maintaining a consistent entity structure.


9-9: Good improvement: Dynamic initialization of currentLocalRoundIndex.

Using the provided roundIndex parameter to initialize currentLocalRoundIndex instead of a hardcoded value improves flexibility and supports different dispute kit implementations that might start with different round indices.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (2)
subgraph/core/src/DisputeKitClassic.ts (2)

38-52: Same guard needed in handleCommitCast

The previous review already highlighted the risk of generating IDs that start with "null-"; the safeguard still hasn’t been added here.
Please short-circuit when disputeKitID is falsy.


73-82: Repeat of missing disputeKitID validation in handleVoteCast

This block is vulnerable to the exact same issue as above – add the early exit to prevent malformed IDs.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 05dcdb0 and 44ea55e.

📒 Files selected for processing (1)
  • subgraph/core/src/DisputeKitClassic.ts (8 hunks)

Comment on lines +186 to 195
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}`;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Missing defensive check in handleWithdrawal

coreRound.disputeKit can be empty for legacy rounds or if an earlier handler failed to set it.
Add the same guard used elsewhere to avoid "null-" IDs and the subsequent failed ClassicRound.load.

Comment on lines +160 to +170

const localDispute = ClassicDispute.load(`${disputeKitID}-${coreDisputeID}`);
if (!localDispute) return;
const newRoundIndex = localDispute.currentLocalRoundIndex.plus(ONE);
const numberOfChoices = localDispute.numberOfChoices;
localDispute.currentLocalRoundIndex = newRoundIndex;
localDispute.save();
createClassicRound(coreDisputeID, numberOfChoices, newRoundIndex);

if (BigInt.fromString(disputeKitID) === newDisputeKitID) {
const newRoundIndex = localDispute.currentLocalRoundIndex.plus(ONE);
const numberOfChoices = localDispute.numberOfChoices;
localDispute.currentLocalRoundIndex = newRoundIndex;
localDispute.save();
createClassicRound(coreDisputeID, numberOfChoices, newRoundIndex, disputeKitID);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Incorrect BigInt comparison – use .equals() instead of ===

BigInt is a reference type in AssemblyScript; === compares object identity, not numeric value, and will always return false for two distinct instances representing the same number.

-    if (BigInt.fromString(disputeKitID) === newDisputeKitID) {
+    if (BigInt.fromString(disputeKitID).equals(newDisputeKitID)) {

Without this fix, the new round will never be created after an appeal when the dispute kit stays the same, breaking the appeal flow.

📝 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.

Suggested change
const localDispute = ClassicDispute.load(`${disputeKitID}-${coreDisputeID}`);
if (!localDispute) return;
const newRoundIndex = localDispute.currentLocalRoundIndex.plus(ONE);
const numberOfChoices = localDispute.numberOfChoices;
localDispute.currentLocalRoundIndex = newRoundIndex;
localDispute.save();
createClassicRound(coreDisputeID, numberOfChoices, newRoundIndex);
if (BigInt.fromString(disputeKitID) === newDisputeKitID) {
const newRoundIndex = localDispute.currentLocalRoundIndex.plus(ONE);
const numberOfChoices = localDispute.numberOfChoices;
localDispute.currentLocalRoundIndex = newRoundIndex;
localDispute.save();
createClassicRound(coreDisputeID, numberOfChoices, newRoundIndex, disputeKitID);
}
const localDispute = ClassicDispute.load(`${disputeKitID}-${coreDisputeID}`);
if (!localDispute) return;
if (BigInt.fromString(disputeKitID).equals(newDisputeKitID)) {
const newRoundIndex = localDispute.currentLocalRoundIndex.plus(ONE);
const numberOfChoices = localDispute.numberOfChoices;
localDispute.currentLocalRoundIndex = newRoundIndex;
localDispute.save();
createClassicRound(coreDisputeID, numberOfChoices, newRoundIndex, disputeKitID);
}

Comment on lines +26 to 35
const disputeKitID = extractDisputeKitIDFromExtraData(event.params._extraData);

const disputeKitClassic = DisputeKitClassic.bind(event.address);
const klerosCore = KlerosCore.bind(disputeKitClassic.core());
const totalRounds = klerosCore.getNumberOfRounds(event.params._coreDisputeID);
const newRoundIndex = totalRounds.minus(ONE);

createClassicDisputeFromEvent(event, disputeKitID, newRoundIndex);
createClassicRound(disputeID, event.params._numberOfChoices, newRoundIndex, disputeKitID);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add a null / empty check for disputeKitID before composing entity IDs

extractDisputeKitIDFromExtraData() can legitimately fail (e.g. malformed or zero-length extraData) and return an empty string.
Using that value immediately creates IDs such as "null-123" which later break .load() look-ups and triggers hard-to-trace “entity not found” errors.

-  const disputeKitID = extractDisputeKitIDFromExtraData(event.params._extraData);
+  const disputeKitID = extractDisputeKitIDFromExtraData(event.params._extraData);
+  if (!disputeKitID) {
+    // Guard against malformed events – nothing to index
+    return;
+  }

This defensive guard is especially important now that the same code path indexes multiple kits.

📝 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.

Suggested change
const disputeKitID = extractDisputeKitIDFromExtraData(event.params._extraData);
const disputeKitClassic = DisputeKitClassic.bind(event.address);
const klerosCore = KlerosCore.bind(disputeKitClassic.core());
const totalRounds = klerosCore.getNumberOfRounds(event.params._coreDisputeID);
const newRoundIndex = totalRounds.minus(ONE);
createClassicDisputeFromEvent(event, disputeKitID, newRoundIndex);
createClassicRound(disputeID, event.params._numberOfChoices, newRoundIndex, disputeKitID);
}
const disputeKitID = extractDisputeKitIDFromExtraData(event.params._extraData);
if (!disputeKitID) {
// Guard against malformed events – nothing to index
return;
}
const disputeKitClassic = DisputeKitClassic.bind(event.address);
const klerosCore = KlerosCore.bind(disputeKitClassic.core());
const totalRounds = klerosCore.getNumberOfRounds(event.params._coreDisputeID);
const newRoundIndex = totalRounds.minus(ONE);
createClassicDisputeFromEvent(event, disputeKitID, newRoundIndex);
createClassicRound(disputeID, event.params._numberOfChoices, newRoundIndex, disputeKitID);
}

…ata sources not deployed

e.g. DisputeKitShutter on testnet
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (10)
subgraph/core/subgraph.template.yaml (2)

132-139: Consolidate shared entity and ABI definitions
The Classic and Shutter dispute kit data sources share identical entities and abis blocks. To reduce duplication across subgraph.template.yaml files, consider extracting these common definitions into a reusable snippet or templating function.


155-158: Clarify mapping file reuse
The Shutter subgraph references ./src/DisputeKitClassic.ts as its mapping file, which could be confusing. Consider renaming or aliasing the AssemblyScript file (e.g., DisputeKitShutter.ts) to improve discoverability and maintainability.

subgraph/core-neo/subgraph.template.yaml (2)

137-143: Extract common configuration blocks
Classic and Shutter kits in the Neo manifest duplicate the same handlers and entity definitions. Consider templating or script-generating these sections to prevent drift and simplify future additions.


152-153: Disambiguate mapping file naming
Referencing DisputeKitClassic.ts for the Shutter data source may confuse future readers. A mapping file named DisputeKitShutter.ts would better reflect its purpose.

subgraph/scripts/update.sh (4)

3-3: Separate assignment for clarity
The SCRIPT_DIR assignment embeds command substitution within the declaration. For readability and to avoid masking errors, consider splitting this into separate declaration and assignment steps.


13-13: Quote command substitution to prevent splitting
The unquoted $(yq ...) in the basename invocation may split on whitespace in paths. Wrap it in quotes to ensure the full string is treated as a single argument.

🧰 Tools
🪛 Shellcheck (0.10.0)

[warning] 13-13: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 13-13: Quote this to prevent word splitting.

(SC2046)


49-52: Quote loop variables to avoid globbing
In the for f in $(yq …) loop and subsequent sed/yq calls, unquoted $f can lead to word splitting or globbing. Use an array or quote $f to handle filenames robustly.


79-79: Quote array addition to prevent splitting
The expression notDeployedSourceIndices+=($i) may misbehave if $i ever contains whitespace. Prefer notDeployedSourceIndices+=("$i") to ensure each index is added correctly.

🧰 Tools
🪛 Shellcheck (0.10.0)

[warning] 79-79: Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.

(SC2206)

subgraph/core-university/subgraph.template.yaml (2)

7-28: Document placeholder replacement process
The fields network, address, and startBlock are left as _PLACEHOLDER_. It would be helpful to add a top‐of‐file comment or reference a script in your CI/docs to explain how these placeholders should be populated for different environments.


84-117: Add DisputeKitShutter data source template
This PR adds support for the Shutter DisputeKit on Devnet. Consider including a template section here, similar to DisputeKitClassic, so users can plug in the Shutter contract details:

+  - kind: ethereum
+    name: DisputeKitShutter
+    network: _PLACEHOLDER_
+    source:
+      address: "_PLACEHOLDER_"
+      abi: DisputeKitShutter
+      startBlock: _PLACEHOLDER_
+    mapping:
+      kind: ethereum/events
+      apiVersion: 0.0.7
+      language: wasm/assemblyscript
+      entities:
+        - ShutterDispute
+        - ShutterRound
+      abis:
+        - name: DisputeKitShutter
+          file: ../../contracts/deployments/_PLACEHOLDER_/DisputeKitShutterUniversity.json
+        - name: KlerosCore
+          file: ../../contracts/deployments/_PLACEHOLDER_/KlerosCoreUniversity.json
+      eventHandlers:
+        - event: DisputeCreation(indexed uint256,uint256,uint8)
+          handler: handleShutterDisputeCreation
+        # Add other Shutter-specific events as needed
+      file: ./src/DisputeKitShutter.ts
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 44ea55e and a995e1e.

📒 Files selected for processing (10)
  • subgraph/core-neo/subgraph.template.yaml (1 hunks)
  • subgraph/core-neo/subgraph.yaml (1 hunks)
  • subgraph/core-university/subgraph.template.yaml (1 hunks)
  • subgraph/core-university/subgraph.yaml (3 hunks)
  • subgraph/core/subgraph.template.yaml (1 hunks)
  • subgraph/core/subgraph.yaml (2 hunks)
  • subgraph/dispute-template-registry/subgraph.template.yaml (1 hunks)
  • subgraph/dispute-template-registry/subgraph.yaml (1 hunks)
  • subgraph/scripts/all.sh (1 hunks)
  • subgraph/scripts/update.sh (3 hunks)
✅ Files skipped from review due to trivial changes (3)
  • subgraph/core-neo/subgraph.yaml
  • subgraph/dispute-template-registry/subgraph.yaml
  • subgraph/dispute-template-registry/subgraph.template.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • subgraph/core/subgraph.yaml
🧰 Additional context used
🪛 Shellcheck (0.10.0)
subgraph/scripts/update.sh

[warning] 13-13: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 13-13: Quote this to prevent word splitting.

(SC2046)


[warning] 79-79: Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.

(SC2206)

🔇 Additional comments (11)
subgraph/core/subgraph.template.yaml (1)

123-131: Add DisputeKitShutter data source
The new DisputeKitShutter section correctly mirrors the Classic kit configuration, enabling event handling for the Shutter dispute kit in Devnet. Ensure that the shared mapping in DisputeKitClassic.ts dynamically dispatches based on the extracted disputeKitID from event data.

subgraph/core-neo/subgraph.template.yaml (1)

119-127: Introduce DisputeKitShutter for Neo subgraph
The DisputeKitShutter data source is correctly added alongside the Classic kit, maintaining consistency with the core manifest. This ensures the Neo deployment can index Shutter dispute kit events.

subgraph/scripts/all.sh (1)

13-17: Include new subgraphs in the build loop
The for subgraph in core core-neo core-university drt update ensures that the newly added core-neo and core-university manifests are built and deployed alongside existing ones.

subgraph/core-university/subgraph.yaml (2)

68-70: Update PolicyRegistry address and startBlock
The PolicyRegistry contract address and startBlock for arbitrum-sepolia have been updated to the University deployment values. Please confirm these match the intended on-chain deployment.


123-127: Update EvidenceModule address and startBlock
The EvidenceModule address and startBlock are now configured for the University environment. Recommend verifying against the actual deployment to ensure accurate indexing.

subgraph/core-university/subgraph.template.yaml (6)

1-5: Validate specVersion and schema path
Ensure that the specified specVersion: 0.0.5 is supported by your Graph Node setup and that ./schema.graphql correctly resolves at runtime.


29-35: Verify ABI file paths
Confirm that the relative paths under abis: (e.g., ../../contracts/deployments/_PLACEHOLDER_/SortitionModuleUniversity.json) match your mono-repo structure and that the placeholders are replaced correctly during deployment.


36-62: Confirm event signatures and handler mappings
Double-check that each event: signature exactly matches the on-chain ABI (including indexed parameters and their order) and that the AssemblyScript mapping functions (e.g., handleDisputeCreation) exist with the correct signatures.


63-83: Review PolicyRegistry configuration
The PolicyRegistry data source currently only indexes the Court entity via PolicyUpdate events. Verify whether other entities or events from PolicyRegistry should be captured in this subgraph.


118-138: Check EvidenceModule entity alignment
Make sure that the entities ClassicEvidenceGroup and ClassicEvidence correspond exactly to what handleEvidenceEvent emits in AssemblyScript, and that no additional fields or groups are missing.


139-160: Standardize SortitionModule event handlers
Verify the signatures of StakeLocked and StakeSet against the on-chain contract ABI (parameter types/order), and ensure the handlers handleStakeLocked and handleStakeSet are implemented accordingly.

Copy link

sonarqubecloud bot commented May 9, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Subgraph: support for Classic-like Dispute Kits at id > 1
2 participants