-
Notifications
You must be signed in to change notification settings - Fork 51
Certora Audit Remediations #2209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
a3759ef
b8af085
ad3006d
104d89b
8189a3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -311,15 +311,13 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { | |
| // Current phase is Staking: set stakes. | ||
| if (stakeIncrease) { | ||
| pnkDeposit = stakeChange; | ||
| totalStaked += stakeChange; | ||
| } else { | ||
| pnkWithdrawal = stakeChange; | ||
| uint256 possibleWithdrawal = juror.stakedPnk > juror.lockedPnk ? juror.stakedPnk - juror.lockedPnk : 0; | ||
| if (pnkWithdrawal > possibleWithdrawal) { | ||
| // Ensure locked tokens remain in the contract. They can only be released during Execution. | ||
| pnkWithdrawal = possibleWithdrawal; | ||
| } | ||
| totalStaked -= pnkWithdrawal; | ||
| } | ||
| return (pnkDeposit, pnkWithdrawal, StakingResult.Successful); | ||
| } | ||
|
|
@@ -372,6 +370,12 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { | |
| if (currentStake == 0) return false; // Juror has been unstaked, don't increase their stake. | ||
|
|
||
| uint256 newStake = currentStake + _reward; | ||
|
|
||
| // Transfer reward directly to the juror so the max stake is not exceeded. | ||
| if (jurors[_account].stakedPnk + _reward > maxStakePerJuror || newStake > maxStakePerJuror) { | ||
| return false; | ||
| } | ||
|
|
||
| _setStake(_account, _courtID, _reward, 0, newStake); | ||
| return true; | ||
|
Comment on lines
+373
to
380
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find and examine SortitionModule.sol to verify the review comment concern
find . -name "SortitionModule.sol" -type f 2>/dev/null | head -1Repository: kleros/kleros-v2 Length of output: 107 🏁 Script executed: #!/bin/bash
# Read the SortitionModule.sol file to verify the review comment
head -n 450 ./contracts/src/arbitration/SortitionModule.sol | tail -n +290Repository: kleros/kleros-v2 Length of output: 6440 Missing The Add the check before calling if (totalStaked + _reward > maxTotalStaked) {
return false;
}🤖 Prompt for AI Agents |
||
| } | ||
|
|
@@ -391,8 +395,10 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { | |
| } | ||
| // Increase juror's balance by deposited amount. | ||
| juror.stakedPnk += _pnkDeposit; | ||
| totalStaked += _pnkDeposit; | ||
| } else { | ||
| juror.stakedPnk -= _pnkWithdrawal; | ||
| totalStaked -= _pnkWithdrawal; | ||
| if (_newStake == 0) { | ||
| // Cleanup | ||
| for (uint256 i = juror.courtIDs.length; i > 0; i--) { | ||
|
|
@@ -460,6 +466,7 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { | |
| uint256 amount = getJurorLeftoverPNK(_account); | ||
| if (amount == 0) revert NotEligibleForWithdrawal(); | ||
| jurors[_account].stakedPnk = 0; | ||
| totalStaked -= amount; | ||
| core.transferBySortitionModule(_account, amount); | ||
| emit LeftoverPNKWithdrawn(_account, amount); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| pragma solidity ^0.8.24; | ||
|
|
||
| import {IArbitratorV2, IDisputeTemplateRegistry, IERC20, ArbitrableExample} from "../arbitration/arbitrables/ArbitrableExample.sol"; | ||
|
|
||
| /// @title MaliciousArbitrableMock | ||
| /// A mock contract to check intentional rule() revert. | ||
| contract MaliciousArbitrableMock is ArbitrableExample { | ||
| bool public doRevert; | ||
|
|
||
| function changeBehaviour(bool _doRevert) external { | ||
| doRevert = _doRevert; | ||
| } | ||
|
|
||
| constructor( | ||
| IArbitratorV2 _arbitrator, | ||
| string memory _templateData, | ||
| string memory _templateDataMappings, | ||
| bytes memory _arbitratorExtraData, | ||
| IDisputeTemplateRegistry _templateRegistry, | ||
| IERC20 _weth | ||
| ) | ||
| ArbitrableExample( | ||
| _arbitrator, | ||
| _templateData, | ||
| _templateDataMappings, | ||
| _arbitratorExtraData, | ||
| _templateRegistry, | ||
| _weth | ||
| ) | ||
| { | ||
| doRevert = true; | ||
| } | ||
|
|
||
| function rule(uint256 _arbitratorDisputeID, uint256 _ruling) external override { | ||
| if (doRevert) revert RuleReverted(); | ||
|
|
||
| uint256 localDisputeID = externalIDtoLocalID[_arbitratorDisputeID]; | ||
| DisputeStruct storage dispute = disputes[localDisputeID]; | ||
| if (msg.sender != address(arbitrator)) revert ArbitratorOnly(); | ||
| if (_ruling > dispute.numberOfRulingOptions) revert RulingOutOfBounds(); | ||
| if (dispute.isRuled) revert DisputeAlreadyRuled(); | ||
|
|
||
| dispute.isRuled = true; | ||
| dispute.ruling = _ruling; | ||
|
|
||
| emit Ruling(IArbitratorV2(msg.sender), _arbitratorDisputeID, dispute.ruling); | ||
| } | ||
|
|
||
| error RuleReverted(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verify consistent error handling for all
safeTransfercalls.The explicit return value check is appropriate given the SafeERC20 modifications. However, there are other
safeTransfercalls in this file that don't check return values:pinakion.safeTransfer(owner, _params.pnkPenaltiesInRound);pinakion.safeTransfer(account, pnkReward);pinakion.safeTransfer(owner, leftoverPnkReward);_feeToken.safeTransfer(_recipient, _amount);If SafeERC20 now returns false instead of reverting on failure, these unchecked calls could fail silently, leading to incorrect state (e.g., rewards not distributed but marked as paid).
Run this script to verify all
safeTransferusage patterns:🤖 Prompt for AI Agents