Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ src = 'src'
out = 'out'
libs = ['../node_modules', 'lib']

[lint]
severity = ['high', 'medium']

[rpc_endpoints]
arbitrumSepolia = "https://sepolia-rollup.arbitrum.io/rpc"
arbitrum = "https://arb1.arbitrum.io/rpc"
Expand Down
135 changes: 121 additions & 14 deletions contracts/src/arbitration/KlerosCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
uint256 jurorsForCourtJump; // The appeal after the one that reaches this number of jurors will go to the parent court if any.
uint256[4] timesPerPeriod; // The time allotted to each dispute period in the form `timesPerPeriod[period]`.
mapping(uint256 disputeKitId => bool) supportedDisputeKits; // True if DK with this ID is supported by the court. Note that each court must support classic dispute kit.
bool disabled; // True if the court is disabled. Unused for now, will be implemented later.
uint256[10] __gap; // Reserved slots for future upgrades.
}

struct Dispute {
Expand All @@ -52,7 +52,8 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
Period period; // The current period of the dispute.
bool ruled; // True if the ruling has been executed, false otherwise.
uint256 lastPeriodChange; // The last time the period was changed.
Round[] rounds;
Round[] rounds; // Rounds of the dispute.
uint256[10] __gap; // Reserved slots for future upgrades.
}

struct Round {
Expand All @@ -68,6 +69,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
uint256 sumPnkRewardPaid; // Total sum of PNK paid to coherent jurors as a reward in this round.
IERC20 feeToken; // The token used for paying fees in this round.
uint256 drawIterations; // The number of iterations passed drawing the jurors for this round.
uint256[10] __gap; // Reserved slots for future upgrades.
}

// Workaround "stack too deep" errors
Expand All @@ -83,9 +85,9 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
}

struct CurrencyRate {
bool feePaymentAccepted;
uint64 rateInEth;
uint8 rateDecimals;
bool feePaymentAccepted; // True if this token is supported as payment method.
uint64 rateInEth; // Rate of the fee token in ETH.
uint8 rateDecimals; // Decimals of the fee token rate.
}

// ************************************* //
Expand Down Expand Up @@ -113,10 +115,38 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
// * Events * //
// ************************************* //

/// @dev Emitted when period is passed.
/// @param _disputeID ID of the related dispute.
/// @param _period The new period.
event NewPeriod(uint256 indexed _disputeID, Period _period);

/// @dev Emitted when appeal period starts.
/// @param _disputeID ID of the related dispute.
/// @param _arbitrable The arbitrable contract.
event AppealPossible(uint256 indexed _disputeID, IArbitrableV2 indexed _arbitrable);

/// @dev Emitted when the dispute is successfully appealed.
/// @param _disputeID ID of the related dispute.
/// @param _arbitrable The arbitrable contract.
event AppealDecision(uint256 indexed _disputeID, IArbitrableV2 indexed _arbitrable);

/// @dev Emitted when an address is successfully drawn.
/// @param _address The drawn address.
/// @param _disputeID ID of the related dispute.
/// @param _roundID ID of the related round.
/// @param _voteID ID of the vote given to the drawn juror.
event Draw(address indexed _address, uint256 indexed _disputeID, uint256 _roundID, uint256 _voteID);

/// @dev Emitted when a new court is created.
/// @param _courtID ID of the new court.
/// @param _parent ID of the parent court.
/// @param _hiddenVotes Whether the court has hidden votes or not.
/// @param _minStake The `minStake` property value of the court.
/// @param _alpha The `alpha` property value of the court.
/// @param _feeForJuror The `feeForJuror` property value of the court.
/// @param _jurorsForCourtJump The `jurorsForCourtJump` property value of the court.
/// @param _timesPerPeriod The `timesPerPeriod` property value of the court.
/// @param _supportedDisputeKits Indexes of dispute kits that this court will support.
event CourtCreated(
uint96 indexed _courtID,
uint96 indexed _parent,
Expand All @@ -128,6 +158,15 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
uint256[4] _timesPerPeriod,
uint256[] _supportedDisputeKits
);

/// @dev Emitted when court's parameters are changed.
/// @param _courtID ID of the court.
/// @param _hiddenVotes Whether the court has hidden votes or not.
/// @param _minStake The `minStake` property value of the court.
/// @param _alpha The `alpha` property value of the court.
/// @param _feeForJuror The `feeForJuror` property value of the court.
/// @param _jurorsForCourtJump The `jurorsForCourtJump` property value of the court.
/// @param _timesPerPeriod The `timesPerPeriod` property value of the court.
event CourtModified(
uint96 indexed _courtID,
bool _hiddenVotes,
Expand All @@ -137,37 +176,80 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
uint256 _jurorsForCourtJump,
uint256[4] _timesPerPeriod
);

/// @dev Emitted when a dispute kit is created.
/// @param _disputeKitID ID of the new dispute kit.
/// @param _disputeKitAddress Address of the new dispute kit.
event DisputeKitCreated(uint256 indexed _disputeKitID, IDisputeKit indexed _disputeKitAddress);

/// @dev Emitted when a dispute kit is enabled/disabled in a court.
/// @param _courtID ID of the related court.
/// @param _disputeKitID ID of the dispute kit.
/// @param _enable Whether the dispute kit has been enabled or disabled.
event DisputeKitEnabled(uint96 indexed _courtID, uint256 indexed _disputeKitID, bool indexed _enable);

/// @dev Emitted when a dispute jumps to a new court.
/// @param _disputeID ID of the dispute.
/// @param _roundID ID of the round.
/// @param _fromCourtID ID of the previous court.
/// @param _toCourtID ID of the new court.
event CourtJump(
uint256 indexed _disputeID,
uint256 indexed _roundID,
uint96 indexed _fromCourtID,
uint96 _toCourtID
);

/// @dev Emitted when a dispute jumps to a new dispute kit.
/// @param _disputeID ID of the dispute.
/// @param _roundID ID of the round.
/// @param _fromDisputeKitID ID of the previous dispute kit.
/// @param _toDisputeKitID ID of the new dispute kit.
event DisputeKitJump(
uint256 indexed _disputeID,
uint256 indexed _roundID,
uint256 indexed _fromDisputeKitID,
uint256 _toDisputeKitID
);

/// @dev Emitted when juror's balance shifts after penalties/rewards has been processed.
/// @param _account Juror's address.
/// @param _disputeID ID of the dispute.
/// @param _roundID ID of the round.
/// @param _degreeOfCoherencyPnk Juror's degree of coherency in this round applied to PNK.
/// @param _degreeOfCoherencyFee Juror's degree of coherency in this round applied to the dispute fee.
/// @param _amountPnk Amount of PNK shifted.
/// @param _amountFee Amount of fee shifted.
/// @param _feeToken Address of the fee token.
event TokenAndETHShift(
address indexed _account,
uint256 indexed _disputeID,
uint256 indexed _roundID,
uint256 _degreeOfCoherency,
int256 _pnkAmount,
int256 _feeAmount,
uint256 _degreeOfCoherencyPnk,
uint256 _degreeOfCoherencyFee,
int256 _amountPnk,
int256 _amountFee,
IERC20 _feeToken
);

/// @dev Emitted when leftover reward sent to owner.
/// @param _disputeID ID of the dispute.
/// @param _roundID ID of the round.
/// @param _amountPnk Amount of PNK sent.
/// @param _amountFee Amount of fee sent.
/// @param _feeToken Address of the fee token.
event LeftoverRewardSent(
uint256 indexed _disputeID,
uint256 indexed _roundID,
uint256 _pnkAmount,
uint256 _feeAmount,
uint256 _amountPnk,
uint256 _amountFee,
IERC20 _feeToken
);

/// @dev Emitted when this contract is paused.
event Paused();

/// @dev Emitted when this contract is unpaused.
event Unpaused();

// ************************************* //
Expand Down Expand Up @@ -413,6 +495,14 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
);
}

/// @dev Changes the parameters of the court.
/// @param _courtID ID of the court.
/// @param _hiddenVotes The `hiddenVotes` property value of the court.
/// @param _minStake The `minStake` property value of the court.
/// @param _alpha The `alpha` property value of the court.
/// @param _feeForJuror The `feeForJuror` property value of the court.
/// @param _jurorsForCourtJump The `jurorsForCourtJump` property value of the court.
/// @param _timesPerPeriod The `timesPerPeriod` property value of the court.
function changeCourtParameters(
uint96 _courtID,
bool _hiddenVotes,
Expand Down Expand Up @@ -795,7 +885,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
}
}
if (round.pnkPenalties != pnkPenaltiesInRound) {
round.pnkPenalties = pnkPenaltiesInRound; // Reentrancy risk: breaks Check-Effect-Interact
round.pnkPenalties = pnkPenaltiesInRound; // Note: Check-Effect-Interaction pattern is compromised here, but in the current state it doesn't cause any issues.
}
}

Expand All @@ -816,7 +906,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
_params.pnkAtStakePerJurorInRound
);

// Guard against degree exceeding 1, though it should be ensured by the dispute kit.
// Extra check to guard against degree exceeding 1, though it should be ensured by the dispute kit.
if (coherence > ONE_BASIS_POINT) {
coherence = ONE_BASIS_POINT;
}
Expand All @@ -841,6 +931,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
_params.disputeID,
_params.round,
coherence,
0,
-int256(availablePenalty),
0,
round.feeToken
Expand Down Expand Up @@ -885,7 +976,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
_params.pnkAtStakePerJurorInRound
);

// Guard against degree exceeding 1, though it should be ensured by the dispute kit.
// Extra check to guard against degree exceeding 1, though it should be ensured by the dispute kit.
if (pnkCoherence > ONE_BASIS_POINT) {
pnkCoherence = ONE_BASIS_POINT;
}
Expand All @@ -908,7 +999,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
// Transfer the fee reward
_transferFeeToken(round.feeToken, payable(account), feeReward);

// Stake the PNK reward if possible, by-passes delayed stakes and other checks usually done by validateStake()
// Stake the PNK reward if possible, bypasses delayed stakes and other checks done by validateStake()
if (!sortitionModule.setStakeReward(account, dispute.courtID, pnkReward)) {
pinakion.safeTransfer(account, pnkReward);
}
Expand All @@ -918,6 +1009,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
_params.disputeID,
_params.round,
pnkCoherence,
feeCoherence,
int256(pnkReward),
int256(feeReward),
round.feeToken
Expand Down Expand Up @@ -1102,10 +1194,16 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
return !courts[court.parent].supportedDisputeKits[round.disputeKitID];
}

/// @dev Returns the length of disputeKits array.
/// @return disputeKits length.
function getDisputeKitsLength() external view returns (uint256) {
return disputeKits.length;
}

/// @dev Converts ETH into tokens.
/// @param _toToken The token to convert ETH into.
/// @param _amountInEth ETH amount.
/// @return Amount of tokens.
function convertEthToTokenAmount(IERC20 _toToken, uint256 _amountInEth) public view returns (uint256) {
return (_amountInEth * 10 ** currencyRates[_toToken].rateDecimals) / currencyRates[_toToken].rateInEth;
}
Expand All @@ -1127,6 +1225,15 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
disputeKits[_round.disputeKitID].earlyCourtJump(_disputeID) || _round.nbVotes >= _court.jurorsForCourtJump;
}

/// @dev Checks whether a dispute will jump to new court/DK, and returns new court and DK.
/// @param _dispute Dispute data.
/// @param _round Round ID.
/// @param _court Current court ID.
/// @param _disputeID Dispute ID.
/// @return newCourtID Court ID after jump.
/// @return newDisputeKitID Dispute kit ID after jump.
/// @return courtJump Whether the dispute jumps to a new court or not.
/// @return disputeKitJump Whether the dispute jumps to a new dispute kit or not.
function _getCourtAndDisputeKitJumps(
Dispute storage _dispute,
Round storage _round,
Expand Down
2 changes: 1 addition & 1 deletion contracts/src/arbitration/KlerosGovernor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ contract KlerosGovernor is IArbitrableV2 {
// Check in case arbitration cost increased after the submission. It's unlikely that its increase won't be covered by the base deposit, but technically possible.
session.sumDeposit = session.sumDeposit > arbitrationCost ? session.sumDeposit - arbitrationCost : 0;
reservedETH = reservedETH > arbitrationCost ? reservedETH - arbitrationCost : 0;
emit DisputeRequest(arbitrator, session.disputeID, sessions.length - 1, templateId, "");
emit DisputeRequest(arbitrator, session.disputeID, sessions.length - 1, templateId);
}
}

Expand Down
Loading
Loading