Skip to content

Conversation

@tractorss
Copy link
Contributor

@tractorss tractorss commented Oct 6, 2025

WIP

Summary by CodeRabbit

  • Breaking Changes

    • Some governance actions now require owner-level control and an event signature was changed — integrations may need updates.
  • Improvements

    • Safer fund transfers with automatic fallback to wrapped-native when native transfers fail.
    • Clearer, explicit error handling replacing generic reverts.
  • New Features

    • Wrapped-native support and associated deployment artifacts.
    • UI list cards now delegate opening to a parent modal handler and URL-driven list activation.
  • Chores

    • Updated verification tooling and scripts, added a verify-all task, docs, env examples, and dependency bumps.

@netlify
Copy link

netlify bot commented Oct 6, 2025

Deploy Preview for kleros-governor-v2 ready!

Name Link
🔨 Latest commit e280f78
🔍 Latest deploy log https://app.netlify.com/projects/kleros-governor-v2/deploys/691d791b1987e70008f8a254
😎 Deploy Preview https://deploy-preview-16--kleros-governor-v2.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Oct 6, 2025

Walkthrough

Refactors governor for owner-only control and custom errors, adds SafeSend with wNative support for robust ETH transfers, updates factory/deploy scripts and artifacts to pass wNative, introduces verify-all Hardhat task and verification script changes, lifts modal state in frontend, and updates dependencies and docs.

Changes

Cohort / File(s) Summary of Changes
Package / deps
contracts/package.json
Bumped @nomicfoundation/hardhat-verify ^2.0.0 → ^2.0.14 and @kleros/kleros-v2-contracts ^0.9.3 → ^2.0.0-rc.1; changed scripts.etherscan-verify to run hardhat verify-all.
Governor contract & SafeSend
contracts/src/KlerosGovernor.sol, contracts/src/libraries/SafeSend.sol
Added address public wNative; integrated SafeSend.safeSend(...) for ETH transfers; replaced many string require reverts with custom errors; switched several governor checks to owner-only semantics; added mapping arbitratorDisputeIDToSessionIndex.
Factory & deploy scripts
contracts/src/GovernorFactory.sol, contracts/deploy/00-governor-v2.ts, contracts/deploy/utils/disputeTemplate.ts, contracts/deploy/utils/getContracts.ts
GovernorFactory.deploy now accepts and forwards _wNative; deploy script retrieves WETH and passes wNative.target; dispute template mappings now call arbitratorDisputeIDToSessionIndex; NETWORK_TO_DEPLOYMENT for arbitrum maps to "mainnet".
Deployment artifacts
contracts/deployments/arbitrum/.chainId, contracts/deployments/arbitrum/WETH.json, contracts/deployments/arbitrumSepoliaDevnet/WETH.json
Added Arbitrum chain id file and WETH deployment artifacts (addresses, ABIs, and full metadata for Sepolia-devnet artifact).
Hardhat / verification / scripts
contracts/hardhat.config.ts, contracts/scripts/verifyProxies.sh, contracts/scripts/dotenv.sh, contracts/tasks/verify-all.ts
Added hardhat-verify import and new verify-all task; introduced top-level etherscan config and standardized ETHERSCAN_API_KEY usage; verifyProxies.sh now accepts chainId and targets etherscan v2; added dotenv helper script.
Docs / env / gitignore
contracts/README.md, contracts/README.md.template, contracts/.env.example, contracts/.gitignore
Added comprehensive contracts README and updated template; added .env.example placeholders; updated .gitignore to allow tracking .env.example.
Frontend: modal state & constants
web/src/app/(main)/governor/.../ActiveLists/ListCard.tsx, web/src/app/(main)/governor/.../ActiveLists/index.tsx, web/src/consts/governors.ts
Lifted modal state: ListCard no longer manages its own modal and now accepts setIsOpen; ActiveLists handles openList, URL syncing and renders ExamineModal; updated three governor addresses to a shared address.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Gov as KlerosGovernor
  participant SS as SafeSend (lib)
  participant W as WethLike
  participant Rec as Recipient

  User->>Gov: trigger payout/refund (ETH)
  Gov->>SS: safeSend(Rec, value, wNative)
  alt native send succeeds
    SS-->>Rec: native ETH transferred
  else native send fails
    SS->>W: deposit{value: value}()
    SS->>W: transfer(Rec, value)
    W-->>Rec: WETH tokens transferred
  end
Loading
sequenceDiagram
  autonumber
  actor Deployer
  participant W as WETH artifact
  participant TF as GovernorFactory
  participant Gov as KlerosGovernor

  Deployer->>W: getContract("WETH")
  Deployer->>TF: deploy(..., wNative.target)
  TF->>Gov: new KlerosGovernor(..., _wNative)
  Gov-->>TF: deployed (wNative stored)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Files/areas needing extra attention:
    • contracts/src/KlerosGovernor.sol — access-control changes, custom errors, payment flow, and constructor parameter addition.
    • contracts/src/libraries/SafeSend.sol — payable handling, fallback wrap-to-WETH correctness and edge cases.
    • contracts/src/GovernorFactory.sol and contracts/deploy/00-governor-v2.ts — parameter ordering and deployment argument propagation.
    • Verification/Hardhat changes (contracts/hardhat.config.ts, tasks/verify-all.ts, scripts/verifyProxies.sh) — ensure API key handling and network mapping are correct.
    • Deployment artifacts (WETH.json) — validate addresses/ABI match intended networks.

Possibly related PRs

  • feat: governor-functionality #10 — modifies KlerosGovernor.sol and session/submission state; strong overlap with contract-level refactors here.
  • Feat/homepage #8 — touches frontend components (ActiveLists/ListCard) and governor constants; related to modal/state changes and address updates.

Suggested reviewers

  • alcercu

Poem

A rabbit hops with bytes and cheer,
Wrapping coin when transfers fear,
Owner guards the rules anew,
SafeSend bounds if sends won’t do.
Merge the patch — carrots for the crew. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'chore: sync-changes-from-governor-in-kleros-v2' accurately describes the main objective of syncing changes related to Governor with Kleros V2, which aligns with the extensive updates across contracts and deployment configurations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ 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 chore/contracts-sync-with-kleros-v2

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

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebe7aa6 and 16b1997.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • contracts/package.json (1 hunks)
  • contracts/src/KlerosGovernor.sol (16 hunks)
  • contracts/src/libraries/SafeSend.sol (1 hunks)
⏰ 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-governor-v2
  • GitHub Check: Header rules - kleros-governor-v2
  • GitHub Check: Pages changed - kleros-governor-v2

@tractorss tractorss marked this pull request as ready for review November 19, 2025 06:30
Copy link

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/consts/governors.ts (1)

21-43: Three governors sharing the same address breaks chain-specific resolution

Verification confirms the issue. All three governor entries (lines 22, 30, 38) use the identical address 0x52e6766e6C4fB05Caec92e5318668b3E366D649a:

export const getGovernor = (address: string) => governors.find((governor) => governor.address === address);

Since getGovernor searches by address alone and .find() returns the first match, any lookup for that address will always return the Arbitrum Sepolia entry. Additionally, generateStaticParams() will only create a single route for that shared address, making the Gnosis and mainnet entries unreachable.

Smart contracts typically deploy to different addresses on different networks. Either:

  • Update each governor entry with its correct network-specific address, or
  • Modify getGovernor to also accept and filter by chain if there is legitimate address reuse.

Without this fix, the Gnosis and mainnet governors cannot be accessed, and any route to that address will serve incorrect chain metadata.

♻️ Duplicate comments (2)
contracts/src/KlerosGovernor.sol (2)

55-60: Add a zero‑address guard for _wNative to avoid bricking SafeSend refunds

wNative is stored directly from the constructor argument with no validation:

address public wNative; // line 58
...
constructor(..., uint256 _withdrawTimeout, address _wNative) {
    ...
    wNative = _wNative;
}

If _wNative is ever passed as address(0), every SafeSend fallback path will revert when trying to interact with the WETH‑like contract, breaking refunds to contracts (e.g. Safes) whenever a plain ETH send fails.

Add a dedicated error and guard in the constructor:

@@
-    address public wNative; // The wrapped native token for safeSend().
+    address public wNative; // The wrapped native token for safeSend().
@@
-        uint256 _withdrawTimeout,
-        address _wNative
+        uint256 _withdrawTimeout,
+        address _wNative
     ) {
@@
-        arbitratorExtraData = _arbitratorExtraData;
-        wNative = _wNative;
+        arbitratorExtraData = _arbitratorExtraData;
+        if (_wNative == address(0)) revert InvalidWNativeAddress();
+        wNative = _wNative;
@@
-    error SubmissionTimeHasEnded();
+    error SubmissionTimeHasEnded();
@@
-    error AlreadyExecuted();
+    error AlreadyExecuted();
+    error InvalidWNativeAddress();

This is a one‑time check but prevents subtle failures in all paths relying on SafeSend.

Also applies to: 127-142, 460-478


90-93: onlyByOwner currently blocks all external governance calls

The onlyByOwner modifier still checks against address(this):

modifier onlyByOwner() {
    if (address(this) != msg.sender) revert OwnerOnly();
    _;
}

All governance setters (e.g., changeSubmissionDeposit, changeSubmissionTimeout, changeExecutionTimeout, changeWithdrawTimeout, changeArbitrator, changeDisputeTemplate) are now effectively callable only by the contract itself, making them unusable after deployment.

Introduce an explicit owner variable and set it in the constructor, then check msg.sender against that:

 contract KlerosGovernor is IArbitrableV2 {
+    address public owner;
@@
-    modifier onlyByOwner() {
-        if (address(this) != msg.sender) revert OwnerOnly();
-        _;
-    }
+    modifier onlyByOwner() {
+        if (msg.sender != owner) revert OwnerOnly();
+        _;
+    }
@@ constructor
-    ) {
-        arbitrator = _arbitrator;
+    ) {
+        owner = msg.sender;
+        arbitrator = _arbitrator;

Without this fix, all the governance configuration endpoints are effectively dead.

Also applies to: 163-208

🧹 Nitpick comments (8)
contracts/scripts/verifyProxies.sh (1)

5-5: Etherscan v2 wiring looks good; consider tightening the proxy file loop

The switch to the unified https://api.etherscan.io/v2/api endpoint with an explicit chainid and ETHERSCAN_API_KEY plus the added key check all look sane.

The only thing I’d tweak is the for f in $(ls …) pattern, which ShellCheck flagged (SC2045) and can misbehave with spaces / no matches. You can avoid ls and rely on globs directly:

 function verify() { #deploymentDir #explorerApiUrl #apiKey #chainId
@@
-    echo "verifying proxies on $(basename $deploymentDir)"
-    for f in $(ls -1 $deploymentDir/*_Proxy.json 2>/dev/null); do
+    echo "verifying proxies on $(basename "$deploymentDir")"
+    shopt -s nullglob
+    for f in "$deploymentDir"/*_Proxy.json; do
         contractName=$(basename $f .json)
@@
-        echo
-    done
+        echo
+    done
+    shopt -u nullglob
 }

This keeps behavior the same while being more robust.

Also applies to: 7-22, 24-28, 30-34

contracts/.env.example (1)

1-5: Align .env format with dotenv expectations and README, avoid export syntax

Using export VAR = "" is unconventional for .env files and may not be parsed as expected by dotenv, and it also conflicts with the linter output. Since contracts/scripts/dotenv.sh loads this via dotenv.config, I’d strongly suggest switching to plain KEY= lines and adding the optional keys mentioned in the README:

-export PRIVATE_KEY = ""
-export MAINNET_PRIVATE_KEY = ""
-export INFURA_API_KEY = ""
-export ETHERSCAN_API_KEY_FIX=""
-export ETHERSCAN_API_KEY=""
+PRIVATE_KEY=
+MAINNET_PRIVATE_KEY=
+INFURA_API_KEY=
+ETHERSCAN_API_KEY_FIX=
+ETHERSCAN_API_KEY=
+ARBISCAN_API_KEY=
+GNOSISSCAN_API_KEY=

This matches typical .env conventions, fixes the linter warnings, and keeps the example in sync with the docs.

contracts/scripts/dotenv.sh (1)

3-15: Simplify quoting around the Node dotenv call to avoid SC2027-style pitfalls

Functionally this works, but the current nested quoting ("$SCRIPT_DIR"/../.env inside a double-quoted node -e string) is fragile and triggered ShellCheck (SC2027). You can make it more robust by passing the path and key as arguments and keeping the Node snippet single‑quoted:

-node -e "
-  require('dotenv').config({ path: '"$SCRIPT_DIR"/../.env' })
-  console.log(process.env.$varKey)
-"
+node -e '
+  const [path, key] = process.argv.slice(2);
+  require("dotenv").config({ path });
+  console.log(process.env[key] ?? "");
+' "$SCRIPT_DIR/../.env" "$varKey"

This removes shell interpolation inside the JS, makes the script easier to maintain, and keeps behavior (load .env, print the requested key).

contracts/README.md (1)

69-80: Keep env var docs and .env.example in sync (ARBISCAN / GNOSISSCAN keys)

The README lists ARBISCAN_API_KEY and GNOSISSCAN_API_KEY as optional verification keys, but contracts/.env.example currently only defines PRIVATE_KEY, MAINNET_PRIVATE_KEY, INFURA_API_KEY, ETHERSCAN_API_KEY_FIX, and ETHERSCAN_API_KEY.

To avoid confusion for integrators, consider adding placeholders for ARBISCAN_API_KEY and GNOSISSCAN_API_KEY in .env.example (and/or updating this section if those keys are no longer used in the current Hardhat/verify setup).

web/src/app/(main)/governor/[governorAddress]/ActiveLists/index.tsx (1)

35-42: Modal open/close wiring looks good; minor optional cleanups

The handleOpen callback and closePopup with router.replace(window.location.pathname, { scroll: false }) correctly centralize modal state and clear the listId param on close.

Two optional polish points (no need to block on them):

  • ExamineModal is only rendered when openList is truthy, so isOpen={!isUndefined(openList)} is always true in that branch; you can hardcode isOpen or derive it once to reduce redundancy.
  • If you want list opens from ListCard to be shareable via URL (like those coming from the dispute template), you could also push ?listId=… in handleOpen for consistency.
web/src/app/(main)/governor/[governorAddress]/ActiveLists/ListCard.tsx (1)

19-25: Externalizing open behavior via setIsOpen looks solid (with one minor prop nit)

The switch to a parent‑supplied setIsOpen(list) on hover click cleanly removes modal state from ListCard and keeps the component focused on display concerns.

Minor optional tweak: IListCard still requires governorAddress, but ListCard no longer uses it. If nothing else depends on that prop, you can drop it from the interface and call sites to avoid unused‑parameter noise:

-interface IListCard {
-  list: Submission;
-  governorAddress: Address;
-  setIsOpen: (list: Submission) => void;
-}
-const ListCard: React.FC<IListCard> = ({ list, setIsOpen }) => {
+interface IListCard {
+  list: Submission;
+  setIsOpen: (list: Submission) => void;
+}
+const ListCard: React.FC<IListCard> = ({ list, setIsOpen }) => {

Also applies to: 60-69

contracts/deploy/00-governor-v2.ts (1)

21-45: wNative deployment wiring is consistent; gfArgs can be simplified

The script correctly:

  • Pulls WETH via ethers.getContract("WETH").
  • Passes wNative.target into both GovernorFactory.deploy(...) and the KlerosGovernor constructor (kgArgs).
  • Uses kgArgs again for constructor args during Etherscan verification, which should keep deploy/verify in sync as you evolve constructor parameters.

One small cleanup opportunity: gfArgs is only used to initialize kgArgs and never for GovernorFactory itself. You can inline or rename it to avoid confusion about its purpose, e.g.:

-  const gfArgs = [
+  const kgArgs = [
     klerosCore.target,
     extraData,
     disputeTemplateRegistry.target,
     disputeTemplate,
     dataMappings,
     0,
     600,
     600,
-    600, // feeTimeout: 10 minutes
+    600, // feeTimeout: 10 minutes
     wNative.target,
   ];
-
-  await governorFactory.deploy(
+  await governorFactory.deploy(
     klerosCore.target,
     extraData,
     disputeTemplateRegistry.target,
     disputeTemplate,
     dataMappings,
     0,
     600,
     600,
     600, // feeTimeout: 10 minutes,
     wNative.target
   );
-
-  const kgArgs = gfArgs;

Purely cosmetic, but it makes the intent of kgArgs clearer.

Also applies to: 59-67, 68-92

contracts/src/KlerosGovernor.sol (1)

273-291: Consider using safeSend for withdrawal refunds for consistency

withdrawTransactionList still refunds the deposit with a raw .transfer:

reservedETH -= submission.deposit;
payable(msg.sender).transfer(submission.deposit);

Every other ETH outbound path in this contract now goes through SafeSend to avoid bricking calls when the recipient is a contract without a payable receive/fallback. For consistency and resilience, you could align this path as well:

-        reservedETH -= submission.deposit;
-        payable(msg.sender).transfer(submission.deposit);
+        reservedETH -= submission.deposit;
+        payable(msg.sender).safeSend(submission.deposit, wNative);

Not strictly a regression, but it makes refunds behave the same way as the rest of the payout logic.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74efbcd and 6cefd0c.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (14)
  • contracts/.env.example (1 hunks)
  • contracts/.gitignore (1 hunks)
  • contracts/README.md (1 hunks)
  • contracts/deploy/00-governor-v2.ts (2 hunks)
  • contracts/deploy/utils/disputeTemplate.ts (2 hunks)
  • contracts/deploy/utils/getContracts.ts (1 hunks)
  • contracts/hardhat.config.ts (6 hunks)
  • contracts/package.json (2 hunks)
  • contracts/scripts/dotenv.sh (1 hunks)
  • contracts/scripts/verifyProxies.sh (1 hunks)
  • contracts/src/KlerosGovernor.sol (16 hunks)
  • web/src/app/(main)/governor/[governorAddress]/ActiveLists/ListCard.tsx (3 hunks)
  • web/src/app/(main)/governor/[governorAddress]/ActiveLists/index.tsx (3 hunks)
  • web/src/consts/governors.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • contracts/.gitignore
  • contracts/hardhat.config.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-15T18:23:26.791Z
Learnt from: tractorss
Repo: kleros/governor-v2 PR: 10
File: contracts/deploy/utils/disputeTemplate.ts:42-44
Timestamp: 2025-06-15T18:23:26.791Z
Learning: In Kleros Governor dispute template data mappings, the "seek" array values should be strings (e.g., "2") rather than numbers because they represent path strings that the SDK splits on, not direct array indices for indexing.

Applied to files:

  • contracts/deploy/utils/disputeTemplate.ts
📚 Learning: 2025-06-15T17:21:38.772Z
Learnt from: tractorss
Repo: kleros/governor-v2 PR: 10
File: web/src/utils/txnBuilder/constructSubmissionData.ts:5-11
Timestamp: 2025-06-15T17:21:38.772Z
Learning: In the Kleros Governor v2 web application, input validation is handled at the form level rather than in utility functions like constructSubmissionData. The team prefers to centralize validation at entry points and fix any validation leaks at the form level when discovered.

Applied to files:

  • contracts/src/KlerosGovernor.sol
🧬 Code graph analysis (3)
web/src/app/(main)/governor/[governorAddress]/ActiveLists/ListCard.tsx (1)
web/src/hooks/useFetchSubmittedLists.ts (1)
  • Submission (23-33)
web/src/app/(main)/governor/[governorAddress]/ActiveLists/index.tsx (2)
web/src/hooks/useFetchSubmittedLists.ts (2)
  • Submission (23-33)
  • useFetchSubmittedLists (36-88)
web/src/utils/index.ts (1)
  • isUndefined (39-40)
contracts/deploy/00-governor-v2.ts (2)
contracts/deploy/utils/getContracts.ts (1)
  • getArbitratorContracts (11-20)
contracts/deploy/utils/disputeTemplate.ts (2)
  • templateFn (2-30)
  • dataMappings (32-65)
🪛 dotenv-linter (4.0.0)
contracts/.env.example

[warning] 1-1: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)


[warning] 2-2: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)


[warning] 2-2: [UnorderedKey] The MAINNET_PRIVATE_KEY key should go before the PRIVATE_KEY key

(UnorderedKey)


[warning] 3-3: [SpaceCharacter] The line has spaces around equal sign

(SpaceCharacter)


[warning] 3-3: [UnorderedKey] The INFURA_API_KEY key should go before the MAINNET_PRIVATE_KEY key

(UnorderedKey)


[warning] 4-4: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 4-4: [UnorderedKey] The ETHERSCAN_API_KEY_FIX key should go before the INFURA_API_KEY key

(UnorderedKey)


[warning] 5-5: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 5-5: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 5-5: [UnorderedKey] The ETHERSCAN_API_KEY key should go before the ETHERSCAN_API_KEY_FIX key

(UnorderedKey)

🪛 markdownlint-cli2 (0.18.1)
contracts/README.md

85-85: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


91-91: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


99-99: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


107-107: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


129-129: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


135-135: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🪛 Shellcheck (0.11.0)
contracts/scripts/verifyProxies.sh

[warning] 13-13: Iterating over ls output is fragile. Use globs.

(SC2045)

contracts/scripts/dotenv.sh

[warning] 13-13: The surrounding quotes actually unquote this. Remove or escape them.

(SC2027)

🔇 Additional comments (9)
contracts/package.json (1)

60-60: Version bumps look consistent with the rest of the PR

Updating @nomicfoundation/hardhat-verify and @kleros/kleros-v2-contracts aligns with the new deployment/verification and arbitration flows; I don’t see issues in this file itself. Please just confirm that the new deployment names / APIs in @kleros/kleros-v2-contracts@^2.0.0-rc.1 (e.g. mainnet vs mainnetNeo) match how you’re using them in getContracts.ts and deploy scripts.

Also applies to: 93-93

contracts/deploy/utils/getContracts.ts (1)

5-9: arbitrummainnet deployment mapping: confirm it matches kleros-v2 contract artifacts

Switching the NETWORK_TO_DEPLOYMENT entry for arbitrum to "mainnet" makes sense with the new @kleros/kleros-v2-contracts version, but it does tightly couple this file to the deployment naming in that package. If mainnetNeo artifacts still exist or naming changes again, getArbitratorContracts will start throwing.

Please double‑check that:

  • DeploymentName for Arbitrum mainnet is indeed "mainnet" in @kleros/kleros-v2-contracts@^2.0.0-rc.1, and
  • The deployments/arbitrum folder here lines up with that expectation.

Otherwise this file looks good.

contracts/deploy/utils/disputeTemplate.ts (1)

5-6: New sessionIndex mapping is consistent with governor changes; confirm "value" seek path

The template update to reference sessionIndex in the description and the new dataMappings entry that:

  • calls arbitratorDisputeIDToSessionIndex(arbitratorDisputeID),
  • populates "sessionIndex", and
  • then feeds {{{sessionIndex}}} into the existing getSession call

is conceptually sound and lines up with the new arbitratorDisputeIDToSessionIndex mapping on the contract.

Also, using string values in seek (["value"] for the first call and ["2"] for getSession) follows the earlier guidance that these are path strings, not numeric indices. Just make sure "value" matches the actual response shape expected by the dispute-template SDK for abi/call results.

Overall, this looks correct.

Based on learnings

Also applies to: 34-56

web/src/app/(main)/governor/[governorAddress]/ActiveLists/index.tsx (2)

59-60: ListCard integration aligns with externalized open handler

Passing setIsOpen={handleOpen} into ListCard for both last‑session and active lists matches the new pattern of delegating modal control to the parent. The props spread { governorAddress, list } remains type‑safe and backward compatible.

Also applies to: 75-76


77-84: ExamineModal props wiring is coherent with new state

Conditionally rendering ExamineModal when openList is set and passing the current list plus toggleIsOpen={closePopup} cleanly reflects the view state. This should work well with the deep‑link auto‑open behavior from the effect above once listId parsing is hardened.

contracts/src/KlerosGovernor.sol (4)

215-271: submitList checks and duplicate detection look correct now

The revamped submitList logic reads clean and defensive:

  • Length mismatches _target.length != _value.length / _target.length != _dataSize.length correctly revert with specific errors.
  • The deposit is computed as submissionBaseDeposit + arbitrator.arbitrationCost(arbitratorExtraData) and enforced via InsufficientDeposit.
  • The hash chain listHash and the duplicate guard:
if (alreadySubmitted[sessions.length - 1][listHash]) revert ListAlreadySubmitted();
alreadySubmitted[sessions.length - 1][listHash] = true;

correctly prevent resubmitting the same list in a session.

  • Remainder refunds use safeSend(remainder, wNative) to avoid .transfer pitfalls for contracts.

This section now matches the intended behavior and integrates wNative/SafeSend safely.


293-327: Dispute flow updates (mapping + SafeSend) are coherent

The dispute/approval flow changes appear consistent:

  • executeSubmissions now:

    • Uses AlreadyDisputed when session.status != Status.NoDispute.
    • Pays the single‑list submitter with submission.submitter.safeSend(sumDeposit, wNative);.
    • Records arbitratorDisputeIDToSessionIndex[session.disputeID] = sessions.length - 1; when creating a dispute, matching the new view mapping consumed by dataMappings.
  • rule:

    • Restricts entry to msg.sender == address(arbitrator) with OnlyArbitratorAllowed.
    • Enforces _ruling <= session.submittedLists.length and session.status == Status.DisputeCreated.
    • Uses safeSend(session.sumDeposit, wNative) to pay the winning submitter, then correctly zeroes session.sumDeposit and decreases reservedETH.

The flow remains single‑dispute‑at‑a‑time (via session status), and the introduced mapping is a useful read‑path hook without affecting core logic.

Also applies to: 329-356


362-378: Execution and expendable‑funds helpers look correct under stated invariants

  • executeTransactionList now:

    • Checks SubmissionNotApproved and TimeToExecuteHasPassed via custom errors.
    • Uses getExpendableFunds() per iteration, and guards against re‑entrancy by reverting with AlreadyExecuted if transaction.executed is set during the external call.
  • getExpendableFunds returns address(this).balance - reservedETH, which is correct assuming reservedETH is always kept ≤ balance as you already do when adjusting it in submission/withdraw/dispute flows.

Given those invariants, the new logic is sound and doesn’t introduce obvious re‑entrancy or accounting issues.

Also applies to: 380-387


389-455: View helpers and custom errors are a solid API improvement

The added view functions (getTransactionInfo, getSubmittedLists, getSubmission, getNumberOfTransactions, getNumberOfCreatedLists, getCurrentSessionNumber, getSession, getTotalSubmissionDeposit) give off‑chain code a clean, gas‑efficient way to inspect state without reconstructing it manually.

Grouping all the new custom errors at the bottom also improves readability and gas behavior versus string reverts.

No issues from a correctness perspective here.

Also applies to: 456-478

Copy link

@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)
web/src/app/(main)/governor/[governorAddress]/ActiveLists/index.tsx (4)

19-23: URL-driven auto-open logic is now safe; consider making URL the single source of truth

The guarded BigInt(listId) with try/catch and !isUndefined checks looks solid and avoids crashes from malformed or missing listId. However, openList is only ever set (never cleared) in this effect, so the modal can stay open even if the user navigates to a URL without listId or with an invalid one (e.g., via browser back or manual URL edits).

If you want the URL to fully control which list is open, consider extending the effect to also clear openList whenever listId is null/invalid or no matching list is found, and, conversely, have handleOpen/closePopup update listId in the URL so state and URL can’t drift apart.

Also applies to: 27-36


38-41: Optionally sync handleOpen with the listId query param

handleOpen correctly centralizes modal control via openList, but it doesn’t update the URL, while the effect reads from listId to auto-open. This asymmetry means deep-links work, but clicking a card doesn’t produce a shareable URL.

If that’s desirable UX, consider having handleOpen also set listId in the query string (e.g., via router.replace/push), using list.listId.toString(). That would keep the URL and UI in sync and make list openings reproducible via navigation.


42-45: Avoid blowing away unrelated query params when closing the modal

closePopup currently calls router.replace(window.location.pathname, { scroll: false }), which clears all query parameters, not just listId. That can be surprising if other filters/search params are added later.

Consider deriving the current pathname/search from Next helpers and only removing listId, e.g. using usePathname + useSearchParams to rebuild a URL without listId, then router.replace(newUrl, { scroll: false }). This keeps modal state decoupled from any future query parameters.


80-87: Simplify isOpen handling for ExamineModal

Given the surrounding conditional {openList ? ( ... ) : null}, openList is guaranteed to be truthy when ExamineModal renders, so isOpen={!isUndefined(openList)} will always be true in practice.

You can either:

  • Derive a boolean once (e.g. const isModalOpen = !isUndefined(openList);) and use it both for the conditional and isOpen, or
  • Rely solely on the conditional render and hardcode isOpen={true} (if ExamineModal doesn’t need a separate closed-but-mounted state).

Either approach reduces duplicated state and makes the intent clearer.

contracts/.env.example (1)

1-5: Env example matches config; linter warnings are purely stylistic

The variable names line up with hardhat.config.ts, so this is functionally fine. If dotenv-linter warnings are enforced in CI, consider reordering keys alphabetically and adding a trailing newline to silence the UnorderedKey/EndingBlankLine notices; otherwise you can safely ignore them.

contracts/README.md (1)

69-79: Clarify the roles of ETHERSCAN_API_KEY vs ETHERSCAN_API_KEY_FIX

Both keys are documented with the same description, which makes it unclear when each is actually used (e.g., verify-all vs proxy verification or other flows). Consider briefly explaining which tooling reads each variable, or consolidating if only one is needed, so users don’t have to guess which key to set.

contracts/README.md.template (1)

63-65: Keep template aligned with README and clarify ETHERSCAN env vars

The updated template correctly mirrors the KlerosGovernor tag and new ETHERSCAN variables, but it carries the same ambiguity as the main README about how ETHERSCAN_API_KEY vs ETHERSCAN_API_KEY_FIX are used. Since this file is the source for generated docs, it’s a good place to add a short note explaining which tooling reads each variable so downstream READMEs stay clear.

Also applies to: 80-81, 104-105, 118-119

contracts/package.json (1)

41-41: Verify-all wiring and RC dependency bump

The etherscan-verify script now cleanly targets the new verify-all task, which matches the README examples. The bump to @nomicfoundation/hardhat-verify and @kleros/kleros-v2-contracts@^2.0.0-rc.1 looks intentional, but please double-check compatibility (especially with Hardhat 2.22.x) and that you’re comfortable depending on an RC before tagging a stable release.

Also applies to: 60-60, 93-93

contracts/tasks/verify-all.ts (1)

1-42: verify-all task looks solid; consider skipping imported deployments and hardening error match

The task correctly walks Hardhat-Deploy deployments, supports an optional --contract filter, and skips proxies by naming convention, which is a reasonable default. Two optional refinements you might consider:

  • If Hardhat-Deploy exposes a way to distinguish imported/external deployments (e.g. coming from @kleros/kleros-v2-contracts via external.deployments), you may want to skip those to avoid noisy failures or redundant verification attempts.
  • The "Already Verified" check is brittle against small message changes; a case-insensitive match or checking for a shorter, stable substring (like "already verified") would make the script more resilient across plugin versions.
contracts/hardhat.config.ts (1)

5-5: Etherscan config and verify-all wiring look correct; watch the dual API key envs

Importing @nomicfoundation/hardhat-verify and ./tasks/verify-all correctly wires up the new verification flow, and the top-level etherscan.apiKey using ETHERSCAN_API_KEY_FIX matches the new env/example setup. Network verify.etherscan.apiKey blocks still use ETHERSCAN_API_KEY, which is fine but means you now rely on two different env vars for verification depending on the path (verify-all vs other verify usages/proxy script).

It’s worth confirming that:

  • Both env vars are set appropriately in local and CI environments, and
  • This split is intentional (e.g., one key via a new gateway vs legacy per-network keys).

If the distinction is purely historical, you might eventually simplify to a single env var to reduce configuration drift.

Also applies to: 16-16, 69-71, 83-85, 95-97, 133-135, 148-155

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6cefd0c and e280f78.

📒 Files selected for processing (8)
  • contracts/.env.example (1 hunks)
  • contracts/README.md (1 hunks)
  • contracts/README.md.template (4 hunks)
  • contracts/deploy/00-governor-v2.ts (3 hunks)
  • contracts/hardhat.config.ts (7 hunks)
  • contracts/package.json (3 hunks)
  • contracts/tasks/verify-all.ts (1 hunks)
  • web/src/app/(main)/governor/[governorAddress]/ActiveLists/index.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • contracts/deploy/00-governor-v2.ts
🧰 Additional context used
🧬 Code graph analysis (1)
web/src/app/(main)/governor/[governorAddress]/ActiveLists/index.tsx (2)
web/src/hooks/useFetchSubmittedLists.ts (2)
  • Submission (23-33)
  • useFetchSubmittedLists (36-88)
web/src/utils/index.ts (1)
  • isUndefined (39-40)
🪛 dotenv-linter (4.0.0)
contracts/.env.example

[warning] 1-1: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 2-2: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 2-2: [UnorderedKey] The MAINNET_PRIVATE_KEY key should go before the PRIVATE_KEY key

(UnorderedKey)


[warning] 3-3: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 3-3: [UnorderedKey] The INFURA_API_KEY key should go before the MAINNET_PRIVATE_KEY key

(UnorderedKey)


[warning] 4-4: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 4-4: [UnorderedKey] The ETHERSCAN_API_KEY_FIX key should go before the INFURA_API_KEY key

(UnorderedKey)


[warning] 5-5: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)


[warning] 5-5: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 5-5: [UnorderedKey] The ETHERSCAN_API_KEY key should go before the ETHERSCAN_API_KEY_FIX key

(UnorderedKey)

🪛 markdownlint-cli2 (0.18.1)
contracts/README.md

84-84: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


90-90: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


98-98: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


106-106: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


128-128: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


134-134: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🔇 Additional comments (1)
web/src/app/(main)/governor/[governorAddress]/ActiveLists/index.tsx (1)

61-63: Centralizing modal open via setIsOpen={handleOpen} looks good

Passing setIsOpen={handleOpen} to ListCard for both last session and active lists is a nice consolidation of modal behavior. It keeps list cards dumb and moves the open/close logic into a single place (ActiveLists), which will make future changes around opening behavior much easier.

Also applies to: 75-79

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.

3 participants