Skip to content

Conversation

@unknownunknown1
Copy link
Contributor

@unknownunknown1 unknownunknown1 commented Nov 29, 2025

PR-Codex overview

This PR focuses on enhancing the SortitionModule and KlerosCore contracts by implementing the updateTotalStake function, which manages the total stake of PNK tokens. It also includes tests to validate the new functionality and its effects on the staking process.

Detailed summary

  • Added updateTotalStake function in SortitionModule and ISortitionModule to manage total stake adjustments.
  • Updated KlerosCore to call updateTotalStake when setting stakes.
  • Implemented tests for total staked amount adjustments in various scenarios.
  • Created helper functions for testing juror balance and staking processes.

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

Summary by CodeRabbit

  • Bug Fixes

    • Total staked bookkeeping now synchronizes on deposits and withdrawals to keep aggregate stake accurate and avoid inflation/inconsistency during delayed stake changes.
  • Tests

    • Added tests covering multi-participant total stake updates and edge cases (delayed stake execution, overflow/maximum conditions) to validate aggregate staking behavior.

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

@netlify
Copy link

netlify bot commented Nov 29, 2025

Deploy Preview for kleros-v2-testnet ready!

Name Link
🔨 Latest commit a2b5d76
🔍 Latest deploy log https://app.netlify.com/projects/kleros-v2-testnet/deploys/692ef0fbb8ac1a000871117e
😎 Deploy Preview https://deploy-preview-2199--kleros-v2-testnet.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.

@netlify
Copy link

netlify bot commented Nov 29, 2025

Deploy Preview for kleros-v2-neo ready!

Name Link
🔨 Latest commit a2b5d76
🔍 Latest deploy log https://app.netlify.com/projects/kleros-v2-neo/deploys/692ef0fbca6b600008c391dc
😎 Deploy Preview https://deploy-preview-2199--kleros-v2-neo.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
Contributor

coderabbitai bot commented Nov 29, 2025

Walkthrough

Decouples total staked PNK bookkeeping from in-place updates: KlerosCore now calls updateTotalStake(pnkDeposit, pnkWithdrawal) on the sortition module after setting a juror's stake; SortitionModule gains updateTotalStake and removes direct totalStaked updates from _validateStake. Tests added to cover totalStaked behaviors.

Changes

Cohort / File(s) Summary
Core integration & interface
contracts/src/arbitration/KlerosCore.sol, contracts/src/arbitration/interfaces/ISortitionModule.sol
Added updateTotalStake(uint256 _pnkDeposit, uint256 _pnkWithdrawal) to ISortitionModule; KlerosCore calls this on the sortition module after setStake to synchronize total stake.
SortitionModule implementation
contracts/src/arbitration/SortitionModule.sol
Added external updateTotalStake (restricted onlyByCore) that adjusts totalStaked based on deposit/withdrawal args; removed direct totalStaked mutations from _validateStake stake-set branches.
University variant
contracts/src/arbitration/university/SortitionModuleUniversity.sol
Added updateTotalStake stub (external, onlyByCore) as a no-op to satisfy the interface.
Tests — staking
contracts/test/foundry/KlerosCore_Staking.t.sol
Added test_setStake_totalStaked() to assert aggregate totalStaked across multiple stakers for stake increases and decreases.
Tests — execution / edge cases
contracts/test/foundry/KlerosCore_Execution.t.sol
Added test_inflatedTotalStaked_whenDelayedStakeExecute_whenJurorHasNoFunds() and several helper functions to exercise delayed stake execution, inflation to type(uint256).max, and subsequent staking behavior; also added console.sol import.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas to focus review on:
    • Ensure every code path that previously mutated totalStaked still leads to updateTotalStake being invoked (no missed call sites).
    • Validate arithmetic in SortitionModule.updateTotalStake (deposit vs withdrawal branching, zero handling, overflow/underflow considerations).
    • Confirm SortitionModuleUniversity.updateTotalStake no-op is intentional for that deployment variant and won't desynchronize state.
    • Review new tests for correctness and sufficient coverage of edge cases (inflated total, delayed execution paths).

Possibly related PRs

Suggested labels

Type: Enhancement :sparkles:, Package: Contracts, Compatibility: ABI change 🗯

Suggested reviewers

  • jaybuidl

Poem

🐰 I hopped through ledgers, soft and fleet,
I nudged the stakes to keep math neat.
Total and parts now take their cue,
One gentle call to make them true.
Tiny paws, tidy book—hooray!

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 accurately summarizes the main change: adding and implementing a total stake update mechanism across the SortitionModule, ISortitionModule, and related contracts.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/total-stake-update

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@netlify
Copy link

netlify bot commented Nov 29, 2025

Deploy Preview for kleros-v2-testnet-devtools failed. Why did it fail? →

Name Link
🔨 Latest commit a2b5d76
🔍 Latest deploy log https://app.netlify.com/projects/kleros-v2-testnet-devtools/deploys/692ef0fbca6b600008c391e0

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

🧹 Nitpick comments (1)
contracts/src/arbitration/SortitionModule.sol (1)

325-333: Consider a more defensive implementation for clarity.

The current if/else logic relies on the invariant that _pnkDeposit and _pnkWithdrawal are mutually exclusive (only one is non-zero at a time). While this is enforced by validateStake, the function itself doesn't verify this assumption.

A more explicit and defensive approach:

 function updateTotalStake(uint256 _pnkDeposit, uint256 _pnkWithdrawal) external override onlyByCore {
     // Note that we don't update totalStake in setStake() function because it doesn't always change total (e.g. during rewards/penalties).
     if (_pnkDeposit > 0) {
         totalStaked += _pnkDeposit;
-    } else {
+    }
+    if (_pnkWithdrawal > 0) {
         totalStaked -= _pnkWithdrawal;
     }
 }

This handles all cases correctly without relying on the caller's invariant, and also avoids the unnecessary totalStaked -= 0 when both parameters are zero.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 592243f and 1006d82.

📒 Files selected for processing (5)
  • contracts/src/arbitration/KlerosCore.sol (1 hunks)
  • contracts/src/arbitration/SortitionModule.sol (1 hunks)
  • contracts/src/arbitration/interfaces/ISortitionModule.sol (1 hunks)
  • contracts/src/arbitration/university/SortitionModuleUniversity.sol (1 hunks)
  • contracts/test/foundry/KlerosCore_Staking.t.sol (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: jaybuidl
Repo: kleros/kleros-v2 PR: 1746
File: contracts/config/courts.v2.mainnet-neo.json:167-170
Timestamp: 2024-11-19T16:31:08.965Z
Learning: In `contracts/config/courts.v2.mainnet-neo.json`, the `minStake` parameter is denominated in PNK, not ETH.
📚 Learning: 2025-09-03T22:48:32.972Z
Learnt from: jaybuidl
Repo: kleros/kleros-v2 PR: 0
File: :0-0
Timestamp: 2025-09-03T22:48:32.972Z
Learning: In the Kleros v2 codebase, the team prioritizes gas optimization over strict CEI pattern compliance when dealing with trusted contracts. For penalty execution logic, they prefer batching storage writes (`round.pnkPenalties`) rather than updating incrementally after each penalty calculation to save gas costs, as the risk is extremely low between trusted contracts.

Applied to files:

  • contracts/src/arbitration/KlerosCore.sol
📚 Learning: 2025-09-30T17:18:12.895Z
Learnt from: jaybuidl
Repo: kleros/kleros-v2 PR: 2145
File: contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol:277-286
Timestamp: 2025-09-30T17:18:12.895Z
Learning: In DisputeKitClassicBase.sol's castCommit function, jurors are allowed to re-submit commits during the commit period. The implementation uses a commitCount variable to track only first-time commits (where commit == bytes32(0)) so that totalCommitted is not incremented when a juror updates their existing commit.

Applied to files:

  • contracts/src/arbitration/KlerosCore.sol
📚 Learning: 2024-11-19T16:31:08.965Z
Learnt from: jaybuidl
Repo: kleros/kleros-v2 PR: 1746
File: contracts/config/courts.v2.mainnet-neo.json:167-170
Timestamp: 2024-11-19T16:31:08.965Z
Learning: In the court hierarchy, child courts' `minStake` must be greater than or equal to their parent court's `minStake`.

Applied to files:

  • contracts/src/arbitration/KlerosCore.sol
📚 Learning: 2024-11-19T16:31:08.965Z
Learnt from: jaybuidl
Repo: kleros/kleros-v2 PR: 1746
File: contracts/config/courts.v2.mainnet-neo.json:167-170
Timestamp: 2024-11-19T16:31:08.965Z
Learning: In `contracts/config/courts.v2.mainnet-neo.json`, the `minStake` parameter is denominated in PNK, not ETH.

Applied to files:

  • contracts/src/arbitration/interfaces/ISortitionModule.sol
📚 Learning: 2025-09-03T19:34:58.056Z
Learnt from: jaybuidl
Repo: kleros/kleros-v2 PR: 2107
File: contracts/src/arbitration/university/KlerosCoreUniversity.sol:1083-1092
Timestamp: 2025-09-03T19:34:58.056Z
Learning: KlerosCoreUniversity and SortitionModuleUniversity do not have phases, unlike KlerosCoreBase and SortitionModuleBase. Therefore, validateStake in the University contracts will never return StakingResult.Delayed, only Successful or other failure states.

Applied to files:

  • contracts/src/arbitration/university/SortitionModuleUniversity.sol
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Analyze (javascript)
  • GitHub Check: hardhat-tests
🔇 Additional comments (3)
contracts/src/arbitration/KlerosCore.sol (1)

1365-1367: LGTM! Correctly integrates total stake update into the staking flow.

The call is properly placed after setStake completes and only executes for successful, non-delayed stakes. The flow ensures totalStaked bookkeeping is synchronized with actual PNK transfers.

contracts/src/arbitration/university/SortitionModuleUniversity.sol (1)

133-136: LGTM! Appropriate no-op implementation for the University variant.

The stub correctly satisfies the interface contract while maintaining access control. Based on learnings, SortitionModuleUniversity doesn't track phases or total stake, so a no-op is the correct implementation here.

contracts/src/arbitration/interfaces/ISortitionModule.sol (1)

60-63: LGTM! Clean interface declaration with appropriate documentation.

The function is well-placed adjacent to validateStake and has clear NatSpec describing its purpose.

Comment on lines +144 to +168
function test_setStake_totalStaked() public {
// Increase
vm.prank(staker1);
core.setStake(GENERAL_COURT, 4000);
vm.prank(staker1);
core.setStake(GENERAL_COURT, 5001);
vm.prank(staker2);
core.setStake(GENERAL_COURT, 1000);
vm.prank(staker2);
core.setStake(GENERAL_COURT, 1500);

assertEq(sortitionModule.totalStaked(), 6501, "Wrong totalStaked");

// Decrease
vm.prank(staker1);
core.setStake(GENERAL_COURT, 3000);
vm.prank(staker1);
core.setStake(GENERAL_COURT, 2500);
vm.prank(staker2);
core.setStake(GENERAL_COURT, 1400);
vm.prank(staker2);
core.setStake(GENERAL_COURT, 1200);

assertEq(sortitionModule.totalStaked(), 3700, "Wrong totalStaked");
}
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, search for totalStaked references in the test file
rg -n "totalStaked" contracts/test/foundry/KlerosCore_Staking.t.sol -B2 -A2

Repository: kleros/kleros-v2

Length of output: 7231


🏁 Script executed:

#!/bin/bash
# Search for tests that combine delayed stakes with totalStaked checks
rg -n "delayedStake.*totalStaked|totalStaked.*delayedStake" contracts/test/foundry/ -i

Repository: kleros/kleros-v2

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Search for all test functions that mention delayed stake
rg -n "function test.*[Dd]elayed" contracts/test/foundry/KlerosCore_Staking.t.sol

Repository: kleros/kleros-v2

Length of output: 182


🏁 Script executed:

#!/bin/bash
# Check the entire context around delayed stake tests to see if totalStaked is tested
rg -n "test_delayedStake" contracts/test/foundry/KlerosCore_Staking.t.sol -A 50 | head -100

Repository: kleros/kleros-v2

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Read the test_executeDelayedStakes function
sed -n '324,450p' contracts/test/foundry/KlerosCore_Staking.t.sol

Repository: kleros/kleros-v2

Length of output: 6960


🏁 Script executed:

#!/bin/bash
# Find the SortitionModule contract file
fd -t f "SortitionModule\.sol" contracts/

Repository: kleros/kleros-v2

Length of output: 163


🏁 Script executed:

#!/bin/bash
# Search for the totalStaked function/variable definition
rg -n "totalStaked" contracts/src/ -B2 -A5 | head -100

Repository: kleros/kleros-v2

Length of output: 7695


🏁 Script executed:

#!/bin/bash
# Search for executeDelayedStakes implementation
rg -n "function executeDelayedStakes" contracts/src/ -A 30

Repository: kleros/kleros-v2

Length of output: 9796


🏁 Script executed:

#!/bin/bash
# Search for setStakeBySortitionModule in KlerosCore
rg -n "function setStakeBySortitionModule" contracts/src/ -A 15

Repository: kleros/kleros-v2

Length of output: 3466


🏁 Script executed:

#!/bin/bash
# Confirm _setStake calls the totalStaked update
rg -n "function _setStake" contracts/src/arbitration/KlerosCore.sol -A 40 | head -60

Repository: kleros/kleros-v2

Length of output: 2059


Test coverage for delayed stakes affecting totalStaked should be added.

The verification confirms the review comment's concern is valid. While the current test_setStake_totalStaked() correctly validates immediate stakes, there is a test coverage gap:

  • Delayed stakes do NOT immediately update sortitionModule.totalStaked() (they return early in _setStake() at line 1351)
  • When executeDelayedStakes() is called during the Staking phase, it invokes setStakeBySortitionModule() which calls _setStake() with _noDelay=true, which then calls updateTotalStake() to update the global total
  • The existing test_executeDelayedStakes() validates delayed stake execution but never checks that sortitionModule.totalStaked() is correctly updated after execution

A test case should verify that executing delayed stakes properly increments the global totalStaked metric.

🤖 Prompt for AI Agents
In contracts/test/foundry/KlerosCore_Staking.t.sol around lines 144 to 168, add
a test that verifies delayed stakes do not immediately affect
sortitionModule.totalStaked and that calling executeDelayedStakes during the
Staking phase updates totalStaked appropriately: create delayed stakes (use
callers that will trigger the delayed path), assert
sortitionModule.totalStaked() remains unchanged, advance the contract to the
Staking phase (or ensure executeDelayedStakes is callable), call
core.executeDelayedStakes(), then assert sortitionModule.totalStaked() equals
the expected new total after delayed stakes are applied.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Dec 2, 2025

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1006d82 and a2b5d76.

📒 Files selected for processing (1)
  • contracts/test/foundry/KlerosCore_Execution.t.sol (3 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-04T23:36:16.415Z
Learnt from: jaybuidl
Repo: kleros/kleros-v2 PR: 2126
File: contracts/src/arbitration/KlerosCore.sol:472-489
Timestamp: 2025-09-04T23:36:16.415Z
Learning: In this repo, KlerosCore emits AcceptedFeeToken and NewCurrencyRate events that are declared in contracts/src/arbitration/interfaces/IArbitratorV2.sol; implementations don’t need to redeclare these events.

Applied to files:

  • contracts/test/foundry/KlerosCore_Execution.t.sol
📚 Learning: 2024-11-19T05:31:48.701Z
Learnt from: Harman-singh-waraich
Repo: kleros/kleros-v2 PR: 1744
File: web/src/hooks/useGenesisBlock.ts:9-31
Timestamp: 2024-11-19T05:31:48.701Z
Learning: In `useGenesisBlock.ts`, within the `useEffect` hook, the conditions (`isKlerosUniversity`, `isKlerosNeo`, `isTestnetDeployment`) are mutually exclusive, so multiple imports won't execute simultaneously, and race conditions are not a concern.

Applied to files:

  • contracts/test/foundry/KlerosCore_Execution.t.sol
📚 Learning: 2025-09-30T17:18:12.895Z
Learnt from: jaybuidl
Repo: kleros/kleros-v2 PR: 2145
File: contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol:277-286
Timestamp: 2025-09-30T17:18:12.895Z
Learning: In DisputeKitClassicBase.sol's castCommit function, jurors are allowed to re-submit commits during the commit period. The implementation uses a commitCount variable to track only first-time commits (where commit == bytes32(0)) so that totalCommitted is not incremented when a juror updates their existing commit.

Applied to files:

  • contracts/test/foundry/KlerosCore_Execution.t.sol
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Header rules - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Pages changed - kleros-v2-testnet
  • GitHub Check: Redirect rules - kleros-v2-testnet-devtools
  • GitHub Check: Header rules - kleros-v2-testnet-devtools
  • GitHub Check: Pages changed - kleros-v2-testnet-devtools
  • GitHub Check: Redirect rules - kleros-v2-neo
  • GitHub Check: Header rules - kleros-v2-neo
  • GitHub Check: Pages changed - kleros-v2-neo
  • GitHub Check: hardhat-tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
contracts/test/foundry/KlerosCore_Execution.t.sol (3)

11-11: LGTM! Console import for test debugging.

The forge-std console import is appropriate for test files and is used effectively to log totalStaked values during test execution.


916-953: LGTM! Helper functions are well-structured.

The helper functions _stakeBalanceForJuror, _stakePnk_createDispute_moveToDrawingPhase, _drawJurors_advancePeriodToVoting, and _vote_execute are well-designed test utilities that properly encapsulate common test setup patterns. They improve test readability and reduce code duplication.


753-797: This test needs assertions to validate the fix or error expectations if documenting known buggy behavior.

The test ends with _stakeBalanceForJuror(staker2, 20000) (line 796) but provides no assertions afterward. The comment indicates staker2's staking should revert with overflow, yet there is no vm.expectRevert() wrapper. Either add assertions confirming totalStaked does not overflow after the fix (if this validates the fix), or wrap the staker2 call with vm.expectRevert() if intentionally documenting the pre-fix bug. Additionally, remove or clarify the "post condition: inflated totalStaked" comment to reflect whether this test expects the bug to exist or to be fixed.

Comment on lines +753 to +797
function test_inflatedTotalStaked_whenDelayedStakeExecute_whenJurorHasNoFunds() public {
// pre conditions
// 1. there is a dispute in drawing phase
// 2. juror call setStake with an amount greater than his PNK balance
// 3. draw jurors, move to voting phase and execute voting
// 4. move sortition to staking phase
uint256 disputeID = 0;
uint256 amountToStake = 20000;
_stakePnk_createDispute_moveToDrawingPhase(disputeID, staker1, amountToStake);

KlerosCore.Round memory round = core.getRoundInfo(disputeID, 0);
uint256 pnkAtStakePerJuror = round.pnkAtStakePerJuror;
_stakeBalanceForJuror(staker1, type(uint256).max);
_drawJurors_advancePeriodToVoting(disputeID);
_vote_execute(disputeID, staker1);
sortitionModule.passPhase(); // set it to staking phase
_assertJurorBalance(
disputeID,
staker1,
amountToStake,
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
amountToStake,
1
);

console.log("totalStaked before: %e", sortitionModule.totalStaked());

// execution: execute delayed stake
sortitionModule.executeDelayedStakes(1);

// post condition: inflated totalStaked
console.log("totalStaked after: %e", sortitionModule.totalStaked());
_assertJurorBalance(
disputeID,
staker1,
amountToStake,
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
amountToStake,
1
);

// new juror tries to stake but totalStaked already reached type(uint256).max
// it reverts with "arithmetic underflow or overflow (0x11)"
_stakeBalanceForJuror(staker2, 20000);
}
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 | 🟠 Major

Incomplete test: Missing assertions or revert expectation.

The test comment on line 795 states that staker2's stake attempt "reverts with arithmetic underflow or overflow," but line 796 calls _stakeBalanceForJuror(staker2, 20000) without a preceding vm.expectRevert() call and without any subsequent assertions. This leaves the test's behavior and purpose unclear.

Additionally, the test relies on the _assertJurorBalance helper which currently has a critical bug (see separate comment), so the existing assertions at lines 769-776 and 785-792 are not functioning correctly.

Please clarify the test's intent and add appropriate assertions:

If the stake should revert:

     console.log("totalStaked after: %e", sortitionModule.totalStaked());
     _assertJurorBalance(
         disputeID,
         staker1,
         amountToStake,
         pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
         amountToStake,
         1
     );
 
     // new juror tries to stake but totalStaked already reached type(uint256).max
-    // it reverts with "arithmetic underflow or overflow (0x11)"
+    vm.expectRevert(); // or specify the exact error
     _stakeBalanceForJuror(staker2, 20000);

If the stake should succeed:

     // new juror tries to stake but totalStaked already reached type(uint256).max
-    // it reverts with "arithmetic underflow or overflow (0x11)"
     _stakeBalanceForJuror(staker2, 20000);
+    // Add assertions about staker2's balance and totalStaked
+    assertEq(sortitionModule.totalStaked(), expectedValue, "totalStaked should be...");
📝 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
function test_inflatedTotalStaked_whenDelayedStakeExecute_whenJurorHasNoFunds() public {
// pre conditions
// 1. there is a dispute in drawing phase
// 2. juror call setStake with an amount greater than his PNK balance
// 3. draw jurors, move to voting phase and execute voting
// 4. move sortition to staking phase
uint256 disputeID = 0;
uint256 amountToStake = 20000;
_stakePnk_createDispute_moveToDrawingPhase(disputeID, staker1, amountToStake);
KlerosCore.Round memory round = core.getRoundInfo(disputeID, 0);
uint256 pnkAtStakePerJuror = round.pnkAtStakePerJuror;
_stakeBalanceForJuror(staker1, type(uint256).max);
_drawJurors_advancePeriodToVoting(disputeID);
_vote_execute(disputeID, staker1);
sortitionModule.passPhase(); // set it to staking phase
_assertJurorBalance(
disputeID,
staker1,
amountToStake,
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
amountToStake,
1
);
console.log("totalStaked before: %e", sortitionModule.totalStaked());
// execution: execute delayed stake
sortitionModule.executeDelayedStakes(1);
// post condition: inflated totalStaked
console.log("totalStaked after: %e", sortitionModule.totalStaked());
_assertJurorBalance(
disputeID,
staker1,
amountToStake,
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
amountToStake,
1
);
// new juror tries to stake but totalStaked already reached type(uint256).max
// it reverts with "arithmetic underflow or overflow (0x11)"
_stakeBalanceForJuror(staker2, 20000);
}
function test_inflatedTotalStaked_whenDelayedStakeExecute_whenJurorHasNoFunds() public {
// pre conditions
// 1. there is a dispute in drawing phase
// 2. juror call setStake with an amount greater than his PNK balance
// 3. draw jurors, move to voting phase and execute voting
// 4. move sortition to staking phase
uint256 disputeID = 0;
uint256 amountToStake = 20000;
_stakePnk_createDispute_moveToDrawingPhase(disputeID, staker1, amountToStake);
KlerosCore.Round memory round = core.getRoundInfo(disputeID, 0);
uint256 pnkAtStakePerJuror = round.pnkAtStakePerJuror;
_stakeBalanceForJuror(staker1, type(uint256).max);
_drawJurors_advancePeriodToVoting(disputeID);
_vote_execute(disputeID, staker1);
sortitionModule.passPhase(); // set it to staking phase
_assertJurorBalance(
disputeID,
staker1,
amountToStake,
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
amountToStake,
1
);
console.log("totalStaked before: %e", sortitionModule.totalStaked());
// execution: execute delayed stake
sortitionModule.executeDelayedStakes(1);
// post condition: inflated totalStaked
console.log("totalStaked after: %e", sortitionModule.totalStaked());
_assertJurorBalance(
disputeID,
staker1,
amountToStake,
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
amountToStake,
1
);
// new juror tries to stake but totalStaked already reached type(uint256).max
vm.expectRevert();
_stakeBalanceForJuror(staker2, 20000);
}
Suggested change
function test_inflatedTotalStaked_whenDelayedStakeExecute_whenJurorHasNoFunds() public {
// pre conditions
// 1. there is a dispute in drawing phase
// 2. juror call setStake with an amount greater than his PNK balance
// 3. draw jurors, move to voting phase and execute voting
// 4. move sortition to staking phase
uint256 disputeID = 0;
uint256 amountToStake = 20000;
_stakePnk_createDispute_moveToDrawingPhase(disputeID, staker1, amountToStake);
KlerosCore.Round memory round = core.getRoundInfo(disputeID, 0);
uint256 pnkAtStakePerJuror = round.pnkAtStakePerJuror;
_stakeBalanceForJuror(staker1, type(uint256).max);
_drawJurors_advancePeriodToVoting(disputeID);
_vote_execute(disputeID, staker1);
sortitionModule.passPhase(); // set it to staking phase
_assertJurorBalance(
disputeID,
staker1,
amountToStake,
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
amountToStake,
1
);
console.log("totalStaked before: %e", sortitionModule.totalStaked());
// execution: execute delayed stake
sortitionModule.executeDelayedStakes(1);
// post condition: inflated totalStaked
console.log("totalStaked after: %e", sortitionModule.totalStaked());
_assertJurorBalance(
disputeID,
staker1,
amountToStake,
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
amountToStake,
1
);
// new juror tries to stake but totalStaked already reached type(uint256).max
// it reverts with "arithmetic underflow or overflow (0x11)"
_stakeBalanceForJuror(staker2, 20000);
}
function test_inflatedTotalStaked_whenDelayedStakeExecute_whenJurorHasNoFunds() public {
// pre conditions
// 1. there is a dispute in drawing phase
// 2. juror call setStake with an amount greater than his PNK balance
// 3. draw jurors, move to voting phase and execute voting
// 4. move sortition to staking phase
uint256 disputeID = 0;
uint256 amountToStake = 20000;
_stakePnk_createDispute_moveToDrawingPhase(disputeID, staker1, amountToStake);
KlerosCore.Round memory round = core.getRoundInfo(disputeID, 0);
uint256 pnkAtStakePerJuror = round.pnkAtStakePerJuror;
_stakeBalanceForJuror(staker1, type(uint256).max);
_drawJurors_advancePeriodToVoting(disputeID);
_vote_execute(disputeID, staker1);
sortitionModule.passPhase(); // set it to staking phase
_assertJurorBalance(
disputeID,
staker1,
amountToStake,
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
amountToStake,
1
);
console.log("totalStaked before: %e", sortitionModule.totalStaked());
// execution: execute delayed stake
sortitionModule.executeDelayedStakes(1);
// post condition: inflated totalStaked
console.log("totalStaked after: %e", sortitionModule.totalStaked());
_assertJurorBalance(
disputeID,
staker1,
amountToStake,
pnkAtStakePerJuror * DEFAULT_NB_OF_JURORS,
amountToStake,
1
);
_stakeBalanceForJuror(staker2, 20000);
// Add assertions about staker2's balance and totalStaked
assertEq(sortitionModule.totalStaked(), expectedValue, "totalStaked should be...");
}

Comment on lines +900 to +914
function _assertJurorBalance(
uint256 disputeID,
address juror,
uint256 totalStakedPnk,
uint256 totalLocked,
uint256 stakedInCourt,
uint256 nbCourts
) internal {
(uint256 totalStakedPnk, uint256 totalLocked, uint256 stakedInCourt, uint256 nbCourts) = sortitionModule
.getJurorBalance(juror, GENERAL_COURT);
assertEq(totalStakedPnk, totalStakedPnk, "Wrong totalStakedPnk"); // jurors total staked a.k.a juror.stakedPnk
assertEq(totalLocked, totalLocked, "Wrong totalLocked");
assertEq(stakedInCourt, stakedInCourt, "Wrong stakedInCourt"); // juror staked in court a.k.a _stakeOf
assertEq(nbCourts, nbCourts, "Wrong nbCourts");
}
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 | 🔴 Critical

Critical: Variable shadowing makes all assertions useless.

The tuple destructuring on lines 908-909 redeclares variables with the same names as the function parameters, causing variable shadowing. This means all assertions on lines 910-913 compare values against themselves and will always pass, regardless of the actual vs. expected values. The expected values passed as parameters are never used.

Apply this diff to fix the variable shadowing:

 function _assertJurorBalance(
     uint256 disputeID,
     address juror,
-    uint256 totalStakedPnk,
-    uint256 totalLocked,
-    uint256 stakedInCourt,
-    uint256 nbCourts
+    uint256 expectedTotalStakedPnk,
+    uint256 expectedTotalLocked,
+    uint256 expectedStakedInCourt,
+    uint256 expectedNbCourts
 ) internal {
     (uint256 totalStakedPnk, uint256 totalLocked, uint256 stakedInCourt, uint256 nbCourts) = sortitionModule
         .getJurorBalance(juror, GENERAL_COURT);
-    assertEq(totalStakedPnk, totalStakedPnk, "Wrong totalStakedPnk"); // jurors total staked a.k.a juror.stakedPnk
-    assertEq(totalLocked, totalLocked, "Wrong totalLocked");
-    assertEq(stakedInCourt, stakedInCourt, "Wrong stakedInCourt"); // juror staked in court a.k.a _stakeOf
-    assertEq(nbCourts, nbCourts, "Wrong nbCourts");
+    assertEq(totalStakedPnk, expectedTotalStakedPnk, "Wrong totalStakedPnk");
+    assertEq(totalLocked, expectedTotalLocked, "Wrong totalLocked");
+    assertEq(stakedInCourt, expectedStakedInCourt, "Wrong stakedInCourt");
+    assertEq(nbCourts, expectedNbCourts, "Wrong nbCourts");
 }
📝 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
function _assertJurorBalance(
uint256 disputeID,
address juror,
uint256 totalStakedPnk,
uint256 totalLocked,
uint256 stakedInCourt,
uint256 nbCourts
) internal {
(uint256 totalStakedPnk, uint256 totalLocked, uint256 stakedInCourt, uint256 nbCourts) = sortitionModule
.getJurorBalance(juror, GENERAL_COURT);
assertEq(totalStakedPnk, totalStakedPnk, "Wrong totalStakedPnk"); // jurors total staked a.k.a juror.stakedPnk
assertEq(totalLocked, totalLocked, "Wrong totalLocked");
assertEq(stakedInCourt, stakedInCourt, "Wrong stakedInCourt"); // juror staked in court a.k.a _stakeOf
assertEq(nbCourts, nbCourts, "Wrong nbCourts");
}
function _assertJurorBalance(
uint256 disputeID,
address juror,
uint256 expectedTotalStakedPnk,
uint256 expectedTotalLocked,
uint256 expectedStakedInCourt,
uint256 expectedNbCourts
) internal {
(uint256 totalStakedPnk, uint256 totalLocked, uint256 stakedInCourt, uint256 nbCourts) = sortitionModule
.getJurorBalance(juror, GENERAL_COURT);
assertEq(totalStakedPnk, expectedTotalStakedPnk, "Wrong totalStakedPnk"); // jurors total staked a.k.a juror.stakedPnk
assertEq(totalLocked, expectedTotalLocked, "Wrong totalLocked");
assertEq(stakedInCourt, expectedStakedInCourt, "Wrong stakedInCourt"); // juror staked in court a.k.a _stakeOf
assertEq(nbCourts, expectedNbCourts, "Wrong nbCourts");
}
🤖 Prompt for AI Agents
In contracts/test/foundry/KlerosCore_Execution.t.sol around lines 900-914, the
tuple destructuring currently redeclares variables with the same names as the
function parameters causing shadowing and useless self-comparisons; change the
destructuring to capture the returned values into distinct "actual" variables
(e.g. actualTotalStakedPnk, actualTotalLocked, actualStakedInCourt,
actualNbCourts) or remove the types to assign into the existing parameter names,
then compare expected parameters to those actual variables in the assertEq calls
(assertEq(actual..., expected..., "...")). Ensure you do not redeclare names so
assertions validate the expected vs actual values.

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.

2 participants