Skip to content

Conversation

@tractorss
Copy link
Contributor

@tractorss tractorss commented Nov 20, 2025

Includes:

  • LeaderboardOffset contract
  • Verification tooling
  • Subgraph update to index LeaderboardOffset

Pending:

  • Frontend update to use the new leaderboardOffset property, pending subgraph deployment

closes #2178


PR-Codex overview

This PR focuses on implementing a new LeaderboardOffset contract to manage user leaderboard scores through offset events, along with related tests and configurations.

Detailed summary

  • Added .bin, *.wasm, and .latest.json to .gitignore.
  • Updated matchstick.yaml to specify test and libs folders.
  • Introduced leaderboardOffset to User entity in User.ts.
  • Updated schema.graphql to include leaderboardOffset.
  • Created LeaderboardOffset.ts to handle offset events and update user scores.
  • Modified subgraph.yaml and subgraph.template.yaml to integrate LeaderboardOffset.
  • Added tests in leaderboard-offset.test.ts for positive and negative offsets.
  • Created utility functions in leaderboard-offset-utils.ts for event and user creation.
  • Implemented LeaderboardOffset.sol contract with event emissions and governor management.
  • Added verification task in verify-all.ts for deployed contracts.
  • Updated package.json scripts for testing and verification.
  • Adjusted yarn.lock for new dependencies.

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

Summary by CodeRabbit

  • New Features

    • Governance-controlled leaderboard offset system with on-chain offset events and subgraph indexing
    • Real-time leaderboardOffset field added to user profiles and subgraph data source
  • Improvements

    • Enhanced deployment and verification workflow for contracts (new verify-all task)
  • Tests

    • Unit tests and test utilities for leaderboard offset handling
  • Chores

    • Updated config/scripts and repository ignores for build/test/verification tooling

✏️ Tip: You can customize this high-level summary in your review settings.

@tractorss tractorss requested review from a team and jaybuidl as code owners November 20, 2025 10:51
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 20, 2025

Walkthrough

Adds a governor-controlled LeaderboardOffset Solidity contract with deployment and verification tooling, integrates an Offset event into the subgraph (schema, mapping, manifest, tests), and introduces utilities and config for verifying and testing the new feature.

Changes

Cohort / File(s) Summary
LeaderboardOffset Contract
contracts/src/governance/LeaderboardOffset.sol
New contract exposing governor, setOffset, updateGovernor, Offset and GovernorUpdated events, and onlyGovernor access control.
Contract Deployment & Artifact
contracts/deploy/00-home-chain-leaderboard-offset.ts, contracts/deployments/arbitrumSepoliaDevnet/LeaderboardOffset.json
New Hardhat deploy script (tagged LeaderboardOffset, conditional skip on non-home chains) and deployed artifact for Arbitrum Sepolia Devnet.
Hardhat Verification Tooling
contracts/hardhat.config.ts, contracts/tasks/verify-all.ts, contracts/package.json
Adds @nomicfoundation/hardhat-verify, new verify-all task, etherscan config entry, and updates etherscan-verify script to verify-all.
Subgraph Schema & Entity Changes
subgraph/core/schema.graphql, subgraph/core/src/entities/User.ts
Adds leaderboardOffset: BigInt! to User and initializes user.leaderboardOffset = ZERO on creation.
Subgraph Mapping & Manifest
subgraph/core/src/LeaderboardOffset.ts, subgraph/core/subgraph.template.yaml, subgraph/core/subgraph.yaml
New mapping handleLeaderboardOffset for Offset event, new data source entry and ABI wiring.
Subgraph Tests & Utilities
subgraph/core/tests/leaderboard-offset-utils.ts, subgraph/core/tests/leaderboard-offset.test.ts
Test helpers to create events/users and unit tests for positive/negative offset handling.
Subgraph Config & Misc
subgraph/.gitignore, subgraph/matchstick.yaml, subgraph/package.json
Adds .gitignore entries, matchstick test paths, and updates test:core script to run graph test at repo root.

Sequence Diagram

sequenceDiagram
    participant Governor
    participant LeaderboardOffset as LeaderboardOffset\nContract
    participant Blockchain
    participant Subgraph
    participant User as User\nEntity

    Governor->>LeaderboardOffset: setOffset(juror, offset, arbitrator)
    activate LeaderboardOffset
    LeaderboardOffset->>LeaderboardOffset: onlyGovernor check
    LeaderboardOffset->>Blockchain: emit Offset(user, offset, arbitrator)
    deactivate LeaderboardOffset

    Blockchain->>Subgraph: Offset event indexed
    activate Subgraph
    Subgraph->>Subgraph: handleLeaderboardOffset(event)
    Subgraph->>User: load(userId)
    alt user exists
        Subgraph->>User: user.leaderboardOffset += event.params.offset
        Subgraph->>User: save()
    else missing user
        Subgraph->>Subgraph: log error (missing user)
    end
    deactivate Subgraph
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas needing extra attention:
    • Governor validation (zero-address guard) and onlyGovernor modifier correctness.
    • Deployment script's chain filtering / HomeChains lookup and fallback signer logic.
    • Subgraph handler behavior when User is missing and numeric overflow/BigInt handling.
    • verify-all task error handling for already-verified contracts and proxy skipping.

Suggested labels

Type: Feature🗿, Package: Contracts, Package: Subgraph

Suggested reviewers

  • jaybuidl
  • alcercu

Poem

🐰✨ A governor nudges scores with care,
Events hop on-chain through crisp night air.
Subgraphs tally, users gain their props,
Tiny offsets, giant hops! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title 'Feat/leaderboard offset' is vague and uses a generic feature flag format that does not clearly convey the specific change being made. Consider using a more descriptive title like 'Add LeaderboardOffset contract for governance-controlled score adjustments' to better communicate the primary change to reviewers.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR successfully implements all requirements from issue #2178: introduces LeaderboardOffset contract for emitting offset events, integrates subgraph indexing, and adds comprehensive testing and verification tooling.
Out of Scope Changes check ✅ Passed All changes align with the linked issue scope: smart contract deployment, subgraph integration, verification tooling, and test infrastructure are all within the stated objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/leaderboard-offset

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4553827 and 9d8c385.

📒 Files selected for processing (2)
  • contracts/deploy/00-home-chain-leaderboard-offset.ts (1 hunks)
  • subgraph/core/subgraph.yaml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
contracts/deploy/00-home-chain-leaderboard-offset.ts (2)
contracts/deploy/utils/getContractOrDeploy.ts (1)
  • getContractOrDeploy (6-20)
contracts/deploy/utils/index.ts (1)
  • isSkipped (32-38)
⏰ 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). (1)
  • GitHub Check: SonarCloud
🔇 Additional comments (2)
subgraph/core/subgraph.yaml (1)

277-296: LeaderboardOffset data source wiring looks consistent with existing setup

The new LeaderboardOffset data source mirrors the structure of the other sources: network, ABI path, startBlock, entities (User), and the OffsethandleLeaderboardOffset mapping all look coherent and aligned with the intended User-level leaderboardOffset field. Assuming subgraph.template.yaml contains the same block so update.sh remains the source of truth, this configuration looks good.

contracts/deploy/00-home-chain-leaderboard-offset.ts (1)

6-24: Deploy script is coherent; confirm deployer-as-governor assumption

The deploy script looks clean: deployer selection with a fallback to the first signer, HomeChains-based skip logic, and use of getContractOrDeploy are all consistent with the existing deployment utilities and patterns. The only point to double‑check is that passing deployer as the sole constructor argument to LeaderboardOffset is indeed the intended initial governor/owner for controlling offsets; if governance should be held by a different account or contract (e.g., a multisig or a governor contract), it may be better to pass that address instead.


Comment @coderabbitai help to get the list of available commands and usage tips.

@tractorss tractorss linked an issue Nov 20, 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: 2

🧹 Nitpick comments (5)
contracts/src/governance/LeaderboardOffset.sol (1)

48-50: Consider validating address parameters in setOffset.

The function only emits an event, but accepting zero addresses for juror or arbitrator could lead to invalid data being indexed by the subgraph and confusion in off-chain systems.

Consider adding validation:

 function setOffset(address juror, int256 offset, address arbitrator) external onlyGovernor {
+    if (juror == address(0) || arbitrator == address(0)) revert InvalidGovernor();
     emit Offset(juror, offset, arbitrator);
 }

Alternatively, define a more specific error like InvalidAddress() if you prefer clearer error messages.

subgraph/core/tests/leaderboard-offset.test.ts (1)

13-25: Consider expanding test coverage for edge cases.

The current tests cover basic positive and negative offset scenarios effectively. Consider adding tests for:

  • Zero offset: Verify that applying an offset of 0 leaves the leaderboard unchanged.
  • Multiple offsets: Test cumulative behavior when multiple offset events are applied to the same user.
  • Non-existent user: Verify the error logging path when handleLeaderboardOffset is called for a user that doesn't exist (currently line 7-9 in LeaderboardOffset.ts logs an error but this path is untested).

Example test for the non-existent user scenario:

+  test("Should log error when user does not exist", () => {
+    const nonExistentAddress = Address.fromString("0x0000000000000000000000000000000000000999");
+    const event = createOffsetEvent(nonExistentAddress, BigInt.fromI32(100));
+    handleLeaderboardOffset(event);
+    // Note: matchstick provides logStore assertions to verify error logs
+  });
subgraph/core/tests/leaderboard-offset-utils.ts (1)

7-31: LGTM with minor observation.

The createOffsetEvent utility function is well-structured. Note that Line 24 sets the arbitrator parameter to the same address as the user parameter. While this is acceptable for testing purposes and keeps the test setup simple, it doesn't reflect real-world scenarios where the arbitrator would typically be a different address (e.g., the KlerosCore contract address).

Consider adding an optional arbitrator parameter if future tests need to distinguish between user and arbitrator addresses.

Optional enhancement:

-export const createOffsetEvent = (user: Address, offset: BigInt): Offset => {
+export const createOffsetEvent = (user: Address, offset: BigInt, arbitrator?: Address): Offset => {
   let mockEvent = newMockEvent();
   let offsetEvent = new Offset(
     mockEvent.address,
     mockEvent.logIndex,
     mockEvent.transactionLogIndex,
     mockEvent.logType,
     mockEvent.block,
     mockEvent.transaction,
     mockEvent.parameters,
     mockEvent.receipt
   );

   offsetEvent.parameters = new Array();

   let userParam = new ethereum.EventParam("user", ethereum.Value.fromAddress(user));
   let offsetParam = new ethereum.EventParam("offset", ethereum.Value.fromSignedBigInt(offset));
-  let arbitratorParam = new ethereum.EventParam("arbitrator", ethereum.Value.fromAddress(user));
+  let arbitratorParam = new ethereum.EventParam("arbitrator", ethereum.Value.fromAddress(arbitrator || user));

   offsetEvent.parameters.push(userParam);
   offsetEvent.parameters.push(offsetParam);
   offsetEvent.parameters.push(arbitratorParam);

   return offsetEvent;
 };
contracts/tasks/verify-all.ts (2)

17-21: Consider more robust proxy detection.

The current implementation skips proxies based solely on the naming convention endsWith("_Proxy"). This approach is fragile and may not catch all proxy deployments, especially if naming conventions change or if proxies use different suffixes (e.g., _Implementation, Proxy, etc.).

Consider checking deployment metadata or contract type for more reliable proxy detection, if available through hardhat-deploy.

Alternative approach:

-      // skip proxy files (we only skip by naming convention)
-      if (name.endsWith("_Proxy")) {
-        console.log(`Skipping proxy: ${name}`);
+      // skip proxy files (check both naming convention and deployment metadata)
+      if (name.endsWith("_Proxy") || name.includes("Proxy") || deployment.implementation) {
+        console.log(`Skipping proxy/implementation: ${name}`);
         continue;
       }

14-41: Consider adding progress indication for user experience.

For repositories with many deployments, verification can take considerable time. Adding progress indication would improve the user experience.

     const allDeployments = await deployments.all();
     const entries = Object.entries(allDeployments);
+    const total = entries.length;
+    let current = 0;

     for (const [name, deployment] of entries) {
+      current++;
+      console.log(`\n[${current}/${total}] Verifying ${name}...`);
       if (contract && name !== contract) continue;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59a67a0 and 4553827.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (16)
  • contracts/deploy/00-home-chain-leaderboard-offset.ts (1 hunks)
  • contracts/deployments/arbitrumSepoliaDevnet/LeaderboardOffset.json (1 hunks)
  • contracts/hardhat.config.ts (3 hunks)
  • contracts/package.json (2 hunks)
  • contracts/src/governance/LeaderboardOffset.sol (1 hunks)
  • contracts/tasks/verify-all.ts (1 hunks)
  • subgraph/.gitignore (1 hunks)
  • subgraph/core/schema.graphql (1 hunks)
  • subgraph/core/src/LeaderboardOffset.ts (1 hunks)
  • subgraph/core/src/entities/User.ts (1 hunks)
  • subgraph/core/subgraph.template.yaml (1 hunks)
  • subgraph/core/subgraph.yaml (1 hunks)
  • subgraph/core/tests/leaderboard-offset-utils.ts (1 hunks)
  • subgraph/core/tests/leaderboard-offset.test.ts (1 hunks)
  • subgraph/matchstick.yaml (1 hunks)
  • subgraph/package.json (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-11-07T10:48:16.774Z
Learnt from: Harman-singh-waraich
Repo: kleros/kleros-v2 PR: 1739
File: web/src/pages/Home/TopJurors/JurorCard/Coherency.tsx:22-26
Timestamp: 2024-11-07T10:48:16.774Z
Learning: In the `Coherency` component (`web/src/pages/Home/TopJurors/JurorCard/Coherency.tsx`), `totalResolvedVotes` is always greater than or equal to `totalCoherentVotes`. When both are zero, `0/0` results in `NaN`, which is acceptable in this context.

Applied to files:

  • subgraph/core/schema.graphql
🧬 Code graph analysis (3)
contracts/tasks/verify-all.ts (1)
contracts/deployments/utils.ts (1)
  • deployments (3-16)
subgraph/core/tests/leaderboard-offset.test.ts (2)
subgraph/core/tests/leaderboard-offset-utils.ts (2)
  • createUser (33-51)
  • createOffsetEvent (7-31)
subgraph/core/src/LeaderboardOffset.ts (1)
  • handleLeaderboardOffset (5-13)
contracts/deploy/00-home-chain-leaderboard-offset.ts (2)
contracts/deploy/utils/getContractOrDeploy.ts (1)
  • getContractOrDeploy (6-20)
contracts/deploy/utils/index.ts (1)
  • isSkipped (32-38)
🪛 Biome (2.1.2)
subgraph/core/tests/leaderboard-offset-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)

subgraph/core/tests/leaderboard-offset.test.ts

[error] 2-2: 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. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: SonarCloud
🔇 Additional comments (13)
subgraph/.gitignore (1)

1-3: LGTM! Standard subgraph artifact exclusions.

The ignore patterns appropriately exclude build artifacts and generated files.

subgraph/matchstick.yaml (1)

1-3: LGTM! Matchstick configuration aligns with project structure.

The path configuration correctly references the core subgraph tests and manifest.

subgraph/package.json (1)

13-13: LGTM! Test script simplified via matchstick.yaml configuration.

The removal of cd core is correct since matchstick.yaml now specifies the test paths.

subgraph/core/src/entities/User.ts (1)

43-43: LGTM! Proper initialization of leaderboardOffset field.

The field is correctly initialized to ZERO, consistent with other User entity fields and the schema requirement.

contracts/src/governance/LeaderboardOffset.sol (1)

1-68: Clean and focused governance contract.

The contract design is minimal and appropriate for its purpose of emitting offset events. The governor access control, validation in constructor and updateGovernor, and error handling are all correctly implemented.

subgraph/core/schema.graphql (1)

82-82: LGTM! Schema field consistent with entity initialization.

The non-null BigInt type is appropriate for the leaderboardOffset field, which is always initialized to ZERO.

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

276-295: LGTM! LeaderboardOffset data source properly configured.

The event signature matches the contract definition, and the mapping configuration follows the established pattern for other data sources.

contracts/package.json (1)

83-83: Dependency version verification complete — no issues found.

The @nomicfoundation/hardhat-verify@^2.0.14 dependency is correctly pinned for Hardhat 2.26.5. No security vulnerabilities exist for this package. Upgrading to 3.0.7 would require migrating to Hardhat 3, which is outside this change's scope.

subgraph/core/src/LeaderboardOffset.ts (1)

5-13: LGTM!

The handler implementation is correct and straightforward:

  • Proper error handling for missing users with early return
  • Correct use of plus() for BigInt arithmetic to accumulate offsets
  • Saves the updated entity appropriately
contracts/hardhat.config.ts (1)

303-305: LGTM!

The addition of the etherscan configuration section at the root level is correct for the @nomicfoundation/hardhat-verify plugin. The dual configuration (both verify.etherscan at lines 298-302 and etherscan at lines 303-305) appears intentional to support both hardhat-deploy's verification and the newer hardhat-verify plugin.

subgraph/core/tests/leaderboard-offset-utils.ts (1)

33-51: LGTM!

The createUser utility properly initializes all User entity fields with appropriate default values, including the new leaderboardOffset field set to ZERO. This provides a clean baseline for testing offset behavior.

contracts/deploy/00-home-chain-leaderboard-offset.ts (1)

14-18: Document governance transfer expectations.

The deployment sets the deployer as the initial governor (line 16), granting them full control over offset emission. Ensure this is intentional and document the expected governance transfer process if the governor role should be transferred to a multisig or DAO after deployment.

Consider adding a comment in the deployment script or updating documentation to clarify:

  1. Whether the deployer is intended to remain the permanent governor
  2. If not, the process and timeline for transferring governance to the appropriate entity
   await getContractOrDeploy(hre, "LeaderboardOffset", {
     from: deployer,
+    // Note: deployer is set as initial governor; transfer governance using updateGovernor() if needed
     args: [deployer],
     log: true,
   });
contracts/deployments/arbitrumSepoliaDevnet/LeaderboardOffset.json (1)

1-194: Deployment artifact looks correct.

This is an auto-generated deployment artifact file. The ABI, deployment address (0x811eC94d73445Df262D3Bf43571B85caD122bBD7), and start block (217101551) correctly match the configuration in subgraph/core/subgraph.yaml (lines 281 and 283).

@sonarqubecloud
Copy link

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.

Leaderboard score offsets under the governor's control

2 participants