diff --git a/contracts/CHANGELOG.md b/contracts/CHANGELOG.md index 2e61786fb..299b686d0 100644 --- a/contracts/CHANGELOG.md +++ b/contracts/CHANGELOG.md @@ -8,10 +8,11 @@ The format is based on [Common Changelog](https://common-changelog.org/). ### Changed +- **Breaking:** Add event parameter `KlerosCore.TokenAndETHShift._degreeOfCoherencyFee` and renamed the other parameters to `_degreeOfCoherencyPnk`, `_amountPnk`, `_amountFee` ([#2097](https://github.com/kleros/kleros-v2/issues/2097)) +- **Breaking:** Move state variable `DisputeKitClassicBase.nbVotes` to the `Round` struct ([#2097](https://github.com/kleros/kleros-v2/issues/2097)) - **Breaking:** Stake the juror's PNK rewards instead of transferring them out ([#2099](https://github.com/kleros/kleros-v2/issues/2099)) - **Breaking:** Replace `require()` with `revert()` and custom errors outside KlerosCore for consistency and smaller bytecode ([#2084](https://github.com/kleros/kleros-v2/issues/2084)) - **Breaking:** Rename the interface from `RNG` to `IRNG` ([#2054](https://github.com/kleros/kleros-v2/issues/2054)) -- **Breaking:** Remove the `_block` parameter from `IRNG.requestRandomness()` and `IRNG.receiveRandomness()`, not needed for the primary VRF-based RNG ([#2054](https://github.com/kleros/kleros-v2/issues/2054)) - **Breaking:** Rename `governor` to `owner` in order to comply with the lightweight ownership standard [ERC-5313](https://eipsinsight.com/ercs/erc-5313) ([#2112](https://github.com/kleros/kleros-v2/issues/2112)) - **Breaking:** Apply the penalties to the stakes in the Sortition Tree ([#2107](https://github.com/kleros/kleros-v2/issues/2107)) - **Breaking:** Make `SortitionModule.getJurorBalance().stakedInCourt` include the penalties ([#2107](https://github.com/kleros/kleros-v2/issues/2107)) @@ -32,6 +33,8 @@ The format is based on [Common Changelog](https://common-changelog.org/). ### Added +- **Breaking:** Add storage gap arrays to `KlerosCore` structs `Round`, `Dispute` and `Court` ([#2097](https://github.com/kleros/kleros-v2/issues/2097)) +- **Breaking:** Add storage gap arrays to `DisputeKitClassicBase` state variables and to the structs `Round`, `Dispute` and `Vote` ([#2097](https://github.com/kleros/kleros-v2/issues/2097)) - **Breaking:** Add a new field `drawnJurorFromCourtIDs` to the `Round` struct in `KlerosCoreBase` and `KlerosCoreUniversity` ([#2107](https://github.com/kleros/kleros-v2/issues/2107)) - **Breaking:** Add a new state variable `jumpDisputeKitID` to the `DisputeKitClassicBase` contract ([#2114](https://github.com/kleros/kleros-v2/issues/2114)) - **Breaking:** Add a parameter `_recoveryCommit` to the event `DisputeKitShutter.CommitCastShutter` ([#2100](https://github.com/kleros/kleros-v2/issues/2100)) @@ -41,6 +44,13 @@ The format is based on [Common Changelog](https://common-changelog.org/). - Allow the dispute kits to specify which new dispute kit to use when a court jump occurs ([#2114](https://github.com/kleros/kleros-v2/issues/2114)) - Allow stake changes to by-pass delayed stakes when initiated by the SortitionModule by setting the `_noDelay` parameter to `true` in `SortitionModule.validateStake()` ([#2107](https://github.com/kleros/kleros-v2/issues/2107)) +### Removed + +- **Breaking:** Remove unused event parameters `IArbitrableV2.DisputeRequest._templateUri` ([#2097](https://github.com/kleros/kleros-v2/issues/2097)) +- **Breaking:** Remove deprecated `SortitionModule` state variables `alreadyTransferred`, `randomNumberRequestBlock`, `rngLookahead` and `latestDelayedStakeIndex` ([#2097](https://github.com/kleros/kleros-v2/issues/2097)) +- **Breaking:** Remove struct variable `DisputeKitClassicBase.Round.nbVotes` ([#2097](https://github.com/kleros/kleros-v2/issues/2097)) +- **Breaking:** Remove the `_block` parameter from `IRNG.requestRandomness()` and `IRNG.receiveRandomness()`, not needed for the primary VRF-based RNG ([#2054](https://github.com/kleros/kleros-v2/issues/2054)) + ### Fixed - Do not pass to Voting period if all the commits are cast because it breaks the current Shutter auto-reveal process. ([#2085](https://github.com/kleros/kleros-v2/issues/2085)) diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 2dcdd2711..7c2d48987 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -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" diff --git a/contracts/package.json b/contracts/package.json index fd40b404d..ad8174fb6 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -87,8 +87,6 @@ "watch": "hardhat watch", "natspec-smells": "natspec-smells", "docgen": "hardhat docgen", - "docserve": "scripts/docPreprocess.sh && forge doc --serve", - "docbuild": "scripts/docPreprocess.sh && forge doc --build --out dist && scripts/docPostprocess.sh", "populate:courts:devnet": "hardhat populate:courts --from v2_devnet --network arbitrumSepoliaDevnet", "populate:courts:testnet": "hardhat populate:courts --from v2_testnet --network arbitrumSepolia", "populate:courts:mainnet": "hardhat populate:courts --from v2_mainnet --network arbitrum", diff --git a/contracts/src/arbitration/DisputeTemplateRegistry.sol b/contracts/src/arbitration/DisputeTemplateRegistry.sol index 8d3d68d58..2abbe1911 100644 --- a/contracts/src/arbitration/DisputeTemplateRegistry.sol +++ b/contracts/src/arbitration/DisputeTemplateRegistry.sol @@ -6,7 +6,7 @@ import "../proxy/Initializable.sol"; import "./interfaces/IDisputeTemplateRegistry.sol"; /// @title Dispute Template Registry -/// @dev A contract to maintain a registry of dispute templates. +/// @notice A contract to maintain a registry of dispute templates. contract DisputeTemplateRegistry is IDisputeTemplateRegistry, UUPSProxiable, Initializable { string public constant override version = "2.0.0"; @@ -14,10 +14,10 @@ contract DisputeTemplateRegistry is IDisputeTemplateRegistry, UUPSProxiable, Ini // * Storage * // // ************************************* // - /// @dev The owner of the contract. + /// @notice The owner of the contract. address public owner; - /// @dev The number of templates. + /// @notice The number of templates. uint256 public templates; // ************************************* // @@ -38,7 +38,7 @@ contract DisputeTemplateRegistry is IDisputeTemplateRegistry, UUPSProxiable, Ini _disableInitializers(); } - /// @dev Initializer + /// @notice Initializer /// @param _owner Owner of the contract. function initialize(address _owner) external initializer { owner = _owner; @@ -54,7 +54,7 @@ contract DisputeTemplateRegistry is IDisputeTemplateRegistry, UUPSProxiable, Ini // NOP } - /// @dev Changes the owner of the contract. + /// @notice Changes the owner of the contract. /// @param _owner The new owner. function changeOwner(address _owner) external onlyByOwner { owner = _owner; @@ -64,10 +64,7 @@ contract DisputeTemplateRegistry is IDisputeTemplateRegistry, UUPSProxiable, Ini // * State Modifiers * // // ************************************* // - /// @dev Registers a new dispute template. - /// @param _templateTag The tag of the template (optional). - /// @param _templateData The data of the template. - /// @param _templateDataMappings The data mappings of the template. + /// @inheritdoc IDisputeTemplateRegistry function setDisputeTemplate( string memory _templateTag, string memory _templateData, diff --git a/contracts/src/arbitration/KlerosCore.sol b/contracts/src/arbitration/KlerosCore.sol index a8c78a910..fab1f2882 100644 --- a/contracts/src/arbitration/KlerosCore.sol +++ b/contracts/src/arbitration/KlerosCore.sol @@ -13,8 +13,8 @@ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "../libraries/Constants.sol"; /// @title KlerosCore -/// Core arbitrator contract for Kleros v2. -/// Note that this contract trusts the PNK token, the dispute kit and the sortition module contracts. +/// @notice Core arbitrator contract for Kleros v2. +/// @dev This contract trusts the PNK token, the dispute kits and the sortition module contracts. contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { using SafeERC20 for IERC20; using SafeSend for address payable; @@ -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 { @@ -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 { @@ -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 @@ -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. } // ************************************* // @@ -113,10 +115,38 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { // * Events * // // ************************************* // + /// @notice Emitted when period is passed. + /// @param _disputeID ID of the related dispute. + /// @param _period The new period. event NewPeriod(uint256 indexed _disputeID, Period _period); + + /// @notice 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); + + /// @notice 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); + + /// @notice 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); + + /// @notice 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, @@ -128,6 +158,15 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { uint256[4] _timesPerPeriod, uint256[] _supportedDisputeKits ); + + /// @notice 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, @@ -137,37 +176,80 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { uint256 _jurorsForCourtJump, uint256[4] _timesPerPeriod ); + + /// @notice 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); + + /// @notice 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); + + /// @notice 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 ); + + /// @notice 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 ); + + /// @notice 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 ); + + /// @notice 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 ); + + /// @notice Emitted when this contract is paused. event Paused(); + + /// @notice Emitted when this contract is unpaused. event Unpaused(); // ************************************* // @@ -203,7 +285,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { _disableInitializers(); } - /// @dev Initializer (constructor equivalent for upgradable contracts). + /// @notice Initializer (constructor equivalent for upgradable contracts). /// @param _owner The owner's address. /// @param _guardian The guardian's address. /// @param _pinakion The address of the token contract. @@ -290,19 +372,19 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { // NOP } - /// @dev Pause staking and reward execution. Can only be done by guardian or owner. + /// @notice Pause staking and reward execution. Can only be done by guardian or owner. function pause() external onlyByGuardianOrOwner whenNotPaused { paused = true; emit Paused(); } - /// @dev Unpause staking and reward execution. Can only be done by owner. + /// @notice Unpause staking and reward execution. Can only be done by owner. function unpause() external onlyByOwner whenPaused { paused = false; emit Unpaused(); } - /// @dev Allows the owner to call anything on behalf of the contract. + /// @notice Allows the owner to call anything on behalf of the contract. /// @param _destination The destination of the call. /// @param _amount The value sent with the call. /// @param _data The data sent with the call. @@ -311,38 +393,38 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { if (!success) revert UnsuccessfulCall(); } - /// @dev Changes the `owner` storage variable. + /// @notice Changes the `owner` storage variable. /// @param _owner The new value for the `owner` storage variable. function changeOwner(address payable _owner) external onlyByOwner { owner = _owner; } - /// @dev Changes the `guardian` storage variable. + /// @notice Changes the `guardian` storage variable. /// @param _guardian The new value for the `guardian` storage variable. function changeGuardian(address _guardian) external onlyByOwner { guardian = _guardian; } - /// @dev Changes the `pinakion` storage variable. + /// @notice Changes the `pinakion` storage variable. /// @param _pinakion The new value for the `pinakion` storage variable. function changePinakion(IERC20 _pinakion) external onlyByOwner { pinakion = _pinakion; } - /// @dev Changes the `jurorProsecutionModule` storage variable. + /// @notice Changes the `jurorProsecutionModule` storage variable. /// @param _jurorProsecutionModule The new value for the `jurorProsecutionModule` storage variable. function changeJurorProsecutionModule(address _jurorProsecutionModule) external onlyByOwner { jurorProsecutionModule = _jurorProsecutionModule; } - /// @dev Changes the `_sortitionModule` storage variable. + /// @notice Changes the `_sortitionModule` storage variable. /// Note that the new module should be initialized for all courts. /// @param _sortitionModule The new value for the `sortitionModule` storage variable. function changeSortitionModule(ISortitionModule _sortitionModule) external onlyByOwner { sortitionModule = _sortitionModule; } - /// @dev Add a new supported dispute kit module to the court. + /// @notice Add a new supported dispute kit module to the court. /// @param _disputeKitAddress The address of the dispute kit contract. function addNewDisputeKit(IDisputeKit _disputeKitAddress) external onlyByOwner { uint256 disputeKitID = disputeKits.length; @@ -350,7 +432,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { emit DisputeKitCreated(disputeKitID, _disputeKitAddress); } - /// @dev Creates a court under a specified parent court. + /// @notice Creates a court under a specified parent court. /// @param _parent The `parent` property value of the court. /// @param _hiddenVotes The `hiddenVotes` property value of the court. /// @param _minStake The `minStake` property value of the court. @@ -413,6 +495,14 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { ); } + /// @notice 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, @@ -448,7 +538,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { ); } - /// @dev Adds/removes court's support for specified dispute kits. + /// @notice Adds/removes court's support for specified dispute kits. /// @param _courtID The ID of the court. /// @param _disputeKitIDs The IDs of dispute kits which support should be added/removed. /// @param _enable Whether add or remove the dispute kits from the court. @@ -469,7 +559,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { } } - /// @dev Changes the supported fee tokens. + /// @notice Changes the supported fee tokens. /// @param _feeToken The fee token. /// @param _accepted Whether the token is supported or not as a method of fee payment. function changeAcceptedFeeTokens(IERC20 _feeToken, bool _accepted) external onlyByOwner { @@ -477,7 +567,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { emit AcceptedFeeToken(_feeToken, _accepted); } - /// @dev Changes the currency rate of a fee token. + /// @notice Changes the currency rate of a fee token. /// @param _feeToken The fee token. /// @param _rateInEth The new rate of the fee token in ETH. /// @param _rateDecimals The new decimals of the fee token rate. @@ -487,20 +577,20 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { emit NewCurrencyRate(_feeToken, _rateInEth, _rateDecimals); } - /// @dev Changes the `jurorNft` storage variable. + /// @notice Changes the `jurorNft` storage variable. /// @param _jurorNft The new value for the `jurorNft` storage variable. function changeJurorNft(IERC721 _jurorNft) external onlyByOwner { jurorNft = _jurorNft; } - /// @dev Adds or removes an arbitrable from whitelist. + /// @notice Adds or removes an arbitrable from whitelist. /// @param _arbitrable Arbitrable address. /// @param _allowed Whether add or remove permission. function changeArbitrableWhitelist(address _arbitrable, bool _allowed) external onlyByOwner { arbitrableWhitelist[_arbitrable] = _allowed; } - /// @dev Enables or disables the arbitrable whitelist. + /// @notice Enables or disables the arbitrable whitelist. function changeArbitrableWhitelistEnabled(bool _enabled) external onlyByOwner { arbitrableWhitelistEnabled = _enabled; } @@ -509,7 +599,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { // * State Modifiers * // // ************************************* // - /// @dev Sets the caller's stake in a court. + /// @notice Sets the caller's stake in a court. /// @param _courtID The ID of the court. /// @param _newStake The new stake. /// Note that the existing delayed stake will be nullified as non-relevant. @@ -518,7 +608,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { _setStake(msg.sender, _courtID, _newStake, false, OnError.Revert); } - /// @dev Sets the stake of a specified account in a court without delaying stake changes, typically to apply a delayed stake or unstake inactive jurors. + /// @notice Sets the stake of a specified account in a court without delaying stake changes, typically to apply a delayed stake or unstake inactive jurors. /// @param _account The account whose stake is being set. /// @param _courtID The ID of the court. /// @param _newStake The new stake. @@ -527,7 +617,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { _setStake(_account, _courtID, _newStake, true, OnError.Return); } - /// @dev Transfers PNK to the juror by SortitionModule. + /// @notice Transfers PNK to the juror by SortitionModule. /// @param _account The account of the juror whose PNK to transfer. /// @param _amount The amount to transfer. function transferBySortitionModule(address _account, uint256 _amount) external { @@ -596,7 +686,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { emit DisputeCreation(disputeID, IArbitrableV2(msg.sender)); } - /// @dev Passes the period of a specified dispute. + /// @notice Passes the period of a specified dispute. /// @param _disputeID The ID of the dispute. function passPeriod(uint256 _disputeID) external { Dispute storage dispute = disputes[_disputeID]; @@ -644,7 +734,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { emit NewPeriod(_disputeID, dispute.period); } - /// @dev Draws jurors for the dispute. Can be called in parts. + /// @notice Draws jurors for the dispute. Can be called in parts. /// @param _disputeID The ID of the dispute. /// @param _iterations The number of iterations to run. /// @return nbDrawnJurors The total number of jurors drawn in the round. @@ -675,8 +765,8 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { return round.drawnJurors.length; } - /// @dev Appeals the ruling of a specified dispute. - /// Note: Access restricted to the Dispute Kit for this `disputeID`. + /// @notice Appeals the ruling of a specified dispute. + /// @dev Access restricted to the Dispute Kit for this `_disputeID`. /// @param _disputeID The ID of the dispute. /// @param _numberOfChoices Number of choices for the dispute. Can be required during court jump. /// @param _extraData Extradata for the dispute. Can be required during court jump. @@ -729,8 +819,8 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { emit NewPeriod(_disputeID, Period.evidence); } - /// @dev Distribute the PNKs at stake and the dispute fees for the specific round of the dispute. Can be called in parts. - /// Note: Reward distributions are forbidden during pause. + /// @notice Distribute the PNKs at stake and the dispute fees for the specific round of the dispute. Can be called in parts. + /// @dev Reward distributions are forbidden during pause. /// @param _disputeID The ID of the dispute. /// @param _round The appeal round. /// @param _iterations The number of iterations to run. @@ -795,11 +885,11 @@ 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. } } - /// @dev Distribute the PNKs at stake and the dispute fees for the specific round of the dispute, penalties only. + /// @notice Distribute the PNKs at stake and the dispute fees for the specific round of the dispute, penalties only. /// @param _params The parameters for the execution, see `ExecuteParams`. /// @return pnkPenaltiesInRoundCache The updated penalties in round cache. function _executePenalties(ExecuteParams memory _params) internal returns (uint256) { @@ -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; } @@ -841,6 +931,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { _params.disputeID, _params.round, coherence, + 0, -int256(availablePenalty), 0, round.feeToken @@ -869,7 +960,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { return _params.pnkPenaltiesInRound; } - /// @dev Distribute the PNKs at stake and the dispute fees for the specific round of the dispute, rewards only. + /// @notice Distribute the PNKs at stake and the dispute fees for the specific round of the dispute, rewards only. /// @param _params The parameters for the execution, see `ExecuteParams`. function _executeRewards(ExecuteParams memory _params) internal { Dispute storage dispute = disputes[_params.disputeID]; @@ -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; } @@ -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); } @@ -918,6 +1009,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { _params.disputeID, _params.round, pnkCoherence, + feeCoherence, int256(pnkReward), int256(feeReward), round.feeToken @@ -945,7 +1037,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { } } - /// @dev Executes a specified dispute's ruling. + /// @notice Executes a specified dispute's ruling. /// @param _disputeID The ID of the dispute. function executeRuling(uint256 _disputeID) external { Dispute storage dispute = disputes[_disputeID]; @@ -962,25 +1054,18 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { // * Public Views * // // ************************************* // - /// @dev Compute the cost of arbitration denominated in ETH. - /// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation. - /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes). - /// @return cost The arbitration cost in ETH. + /// @inheritdoc IArbitratorV2 function arbitrationCost(bytes memory _extraData) public view override returns (uint256 cost) { (uint96 courtID, uint256 minJurors, ) = _extraDataToCourtIDMinJurorsDisputeKit(_extraData); cost = courts[courtID].feeForJuror * minJurors; } - /// @dev Compute the cost of arbitration denominated in `_feeToken`. - /// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation. - /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes). - /// @param _feeToken The ERC20 token used to pay fees. - /// @return cost The arbitration cost in `_feeToken`. + /// @inheritdoc IArbitratorV2 function arbitrationCost(bytes calldata _extraData, IERC20 _feeToken) public view override returns (uint256 cost) { cost = convertEthToTokenAmount(_feeToken, arbitrationCost(_extraData)); } - /// @dev Gets the cost of appealing a specified dispute. + /// @notice Gets the cost of appealing a specified dispute. /// @param _disputeID The ID of the dispute. /// @return cost The appeal cost. function appealCost(uint256 _disputeID) public view returns (uint256 cost) { @@ -1009,7 +1094,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { } } - /// @dev Gets the start and the end of a specified dispute's current appeal period. + /// @notice Gets the start and the end of a specified dispute's current appeal period. /// @param _disputeID The ID of the dispute. /// @return start The start of the appeal period. /// @return end The end of the appeal period. @@ -1024,11 +1109,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { } } - /// @dev Gets the current ruling of a specified dispute. - /// @param _disputeID The ID of the dispute. - /// @return ruling The current ruling. - /// @return tied Whether it's a tie or not. - /// @return overridden Whether the ruling was overridden by appeal funding or not. + /// @inheritdoc IArbitratorV2 function currentRuling(uint256 _disputeID) public view returns (uint256 ruling, bool tied, bool overridden) { Dispute storage dispute = disputes[_disputeID]; Round storage round = dispute.rounds[dispute.rounds.length - 1]; @@ -1036,7 +1117,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { (ruling, tied, overridden) = disputeKit.currentRuling(_disputeID); } - /// @dev Gets the round info for a specified dispute and round. + /// @notice Gets the round info for a specified dispute and round. /// @dev This function must not be called from a non-view function because it returns a dynamic array which might be very large, theoretically exceeding the block gas limit. /// @param _disputeID The ID of the dispute. /// @param _round The round to get the info for. @@ -1045,7 +1126,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { return disputes[_disputeID].rounds[_round]; } - /// @dev Gets the PNK at stake per juror for a specified dispute and round. + /// @notice Gets the PNK at stake per juror for a specified dispute and round. /// @param _disputeID The ID of the dispute. /// @param _round The round to get the info for. /// @return pnkAtStakePerJuror The PNK at stake per juror. @@ -1053,14 +1134,14 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { return disputes[_disputeID].rounds[_round].pnkAtStakePerJuror; } - /// @dev Gets the number of rounds for a specified dispute. + /// @notice Gets the number of rounds for a specified dispute. /// @param _disputeID The ID of the dispute. /// @return The number of rounds. function getNumberOfRounds(uint256 _disputeID) external view returns (uint256) { return disputes[_disputeID].rounds.length; } - /// @dev Checks if a given dispute kit is supported by a given court. + /// @notice Checks if a given dispute kit is supported by a given court. /// @param _courtID The ID of the court to check the support for. /// @param _disputeKitID The ID of the dispute kit to check the support for. /// @return Whether the dispute kit is supported or not. @@ -1068,7 +1149,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { return courts[_courtID].supportedDisputeKits[_disputeKitID]; } - /// @dev Gets the timesPerPeriod array for a given court. + /// @notice Gets the timesPerPeriod array for a given court. /// @param _courtID The ID of the court to get the times from. /// @return timesPerPeriod The timesPerPeriod array for the given court. function getTimesPerPeriod(uint96 _courtID) external view returns (uint256[4] memory timesPerPeriod) { @@ -1079,14 +1160,14 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { // * Public Views for Dispute Kits * // // ************************************* // - /// @dev Gets the number of votes permitted for the specified dispute in the latest round. + /// @notice Gets the number of votes permitted for the specified dispute in the latest round. /// @param _disputeID The ID of the dispute. function getNumberOfVotes(uint256 _disputeID) external view returns (uint256) { Dispute storage dispute = disputes[_disputeID]; return dispute.rounds[dispute.rounds.length - 1].nbVotes; } - /// @dev Returns true if the dispute kit will be switched to a parent DK. + /// @notice Returns true if the dispute kit will be switched to a parent DK. /// @param _disputeID The ID of the dispute. /// @return Whether DK will be switched or not. function isDisputeKitJumping(uint256 _disputeID) external view returns (bool) { @@ -1102,10 +1183,16 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { return !courts[court.parent].supportedDisputeKits[round.disputeKitID]; } + /// @notice Returns the length of disputeKits array. + /// @return disputeKits length. function getDisputeKitsLength() external view returns (uint256) { return disputeKits.length; } + /// @notice 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; } @@ -1114,7 +1201,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { // * Internal * // // ************************************* // - /// @dev Returns true if the round is jumping to a parent court. + /// @notice Returns true if the round is jumping to a parent court. /// @param _round The round to check. /// @param _court The court to check. /// @return Whether the round is jumping to a parent court or not. @@ -1127,6 +1214,15 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { disputeKits[_round.disputeKitID].earlyCourtJump(_disputeID) || _round.nbVotes >= _court.jurorsForCourtJump; } + /// @notice 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, @@ -1153,7 +1249,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { } } - /// @dev Internal function to transfer fee tokens (ETH or ERC20) + /// @notice Internal function to transfer fee tokens (ETH or ERC20) /// @param _feeToken The token to transfer (NATIVE_CURRENCY for ETH). /// @param _recipient The recipient address. /// @param _amount The amount to transfer. @@ -1165,7 +1261,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { } } - /// @dev Applies degree of coherence to an amount + /// @notice Applies degree of coherence to an amount /// @param _amount The base amount to apply coherence to. /// @param _coherence The degree of coherence in basis points. /// @return The amount after applying the degree of coherence. @@ -1173,7 +1269,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { return (_amount * _coherence) / ONE_BASIS_POINT; } - /// @dev Calculates PNK at stake per juror based on court parameters + /// @notice Calculates PNK at stake per juror based on court parameters /// @param _minStake The minimum stake for the court. /// @param _alpha The alpha parameter for the court in basis points. /// @return The amount of PNK at stake per juror. @@ -1181,7 +1277,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { return (_minStake * _alpha) / ONE_BASIS_POINT; } - /// @dev Toggles the dispute kit support for a given court. + /// @notice Toggles the dispute kit support for a given court. /// @param _courtID The ID of the court to toggle the support for. /// @param _disputeKitID The ID of the dispute kit to toggle the support for. /// @param _enable Whether to enable or disable the support. Note that classic dispute kit should always be enabled. @@ -1190,7 +1286,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { emit DisputeKitEnabled(_courtID, _disputeKitID, _enable); } - /// @dev If called only once then set _onError to Revert, otherwise set it to Return + /// @notice If called only once then set _onError to Revert, otherwise set it to Return /// @param _account The account to set the stake for. /// @param _courtID The ID of the court to set the stake for. /// @param _newStake The new stake. @@ -1241,7 +1337,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { return true; } - /// @dev It may revert depending on the _onError parameter. + /// @notice It may revert depending on the _onError parameter. function _stakingFailed(OnError _onError, StakingResult _result) internal pure { if (_onError == OnError.Return) return; if (_result == StakingResult.StakingTransferFailed) revert StakingTransferFailed(); @@ -1254,8 +1350,8 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable { if (_result == StakingResult.CannotStakeMoreThanMaxTotalStaked) revert StakingMoreThanMaxTotalStaked(); } - /// @dev Gets a court ID, the minimum number of jurors and an ID of a dispute kit from a specified extra data bytes array. - /// Note that if extradata contains an incorrect value then this value will be switched to default. + /// @notice Gets a court ID, the minimum number of jurors and an ID of a dispute kit from a specified extra data bytes array. + /// @dev If `_extraData` contains an incorrect value then this value will be switched to default. /// @param _extraData The extra data bytes array. The first 32 bytes are the court ID, the next are the minimum number of jurors and the last are the dispute kit ID. /// @return courtID The court ID. /// @return minJurors The minimum number of jurors required. diff --git a/contracts/src/arbitration/KlerosGovernor.sol b/contracts/src/arbitration/KlerosGovernor.sol index 11957a745..37f857c80 100644 --- a/contracts/src/arbitration/KlerosGovernor.sol +++ b/contracts/src/arbitration/KlerosGovernor.sol @@ -6,7 +6,8 @@ import {IArbitrableV2, IArbitratorV2} from "./interfaces/IArbitrableV2.sol"; import {SafeSend} from "../libraries/SafeSend.sol"; import "./interfaces/IDisputeTemplateRegistry.sol"; -/// @title KlerosGovernor for V2. Note that appeal functionality and evidence submission will be handled by the court. +/// @title KlerosGovernor for V2. +/// @dev Appeal and evidence submission is handled by the court. contract KlerosGovernor is IArbitrableV2 { using SafeSend for address payable; @@ -89,7 +90,7 @@ contract KlerosGovernor is IArbitrableV2 { // * Events * // // ************************************* // - /// @dev Emitted when a new list is submitted. + /// @notice Emitted when a new list is submitted. /// @param _listID The index of the transaction list in the array of lists. /// @param _submitter The address that submitted the list. /// @param _session The number of the current session. @@ -106,7 +107,7 @@ contract KlerosGovernor is IArbitrableV2 { // * Constructor * // // ************************************* // - /// @dev Constructor. + /// @notice Constructor. /// @param _arbitrator The arbitrator of the contract. /// @param _arbitratorExtraData Extra data for the arbitrator. /// @param _templateData The dispute template data. @@ -145,32 +146,34 @@ contract KlerosGovernor is IArbitrableV2 { // * Governance * // // ************************************* // - /// @dev Changes the value of the base deposit required for submitting a list. + /// @notice Changes the value of the base deposit required for submitting a list. /// @param _submissionBaseDeposit The new value of the base deposit, in wei. function changeSubmissionDeposit(uint256 _submissionBaseDeposit) external onlyByOwner { submissionBaseDeposit = _submissionBaseDeposit; } - /// @dev Changes the time allocated for submission. Note that it can't be changed during approval period because there can be an active dispute in the old arbitrator contract + /// @notice Changes the time allocated for submission. + /// @dev It cannot be changed during approval period because there can be an active dispute in the old arbitrator contract /// and prolonging submission timeout might switch it back to submission period. /// @param _submissionTimeout The new duration of the submission period, in seconds. function changeSubmissionTimeout(uint256 _submissionTimeout) external onlyByOwner duringSubmissionPeriod { submissionTimeout = _submissionTimeout; } - /// @dev Changes the time allocated for list's execution. + /// @notice Changes the time allocated for list's execution. /// @param _executionTimeout The new duration of the execution timeout, in seconds. function changeExecutionTimeout(uint256 _executionTimeout) external onlyByOwner { executionTimeout = _executionTimeout; } - /// @dev Changes list withdrawal timeout. Note that withdrawals are only possible in the first half of the submission period. + /// @notice Changes list withdrawal timeout. Note that withdrawals are only possible in the first half of the submission period. /// @param _withdrawTimeout The new duration of withdraw period, in seconds. function changeWithdrawTimeout(uint256 _withdrawTimeout) external onlyByOwner { withdrawTimeout = _withdrawTimeout; } - /// @dev Changes the arbitrator of the contract. Note that it can't be changed during approval period because there can be an active dispute in the old arbitrator contract. + /// @notice Changes the arbitrator of the contract. + /// @dev It cannot be changed during approval period because there can be an active dispute in the old arbitrator contract. /// @param _arbitrator The new trusted arbitrator. /// @param _arbitratorExtraData The extra data used by the new arbitrator. function changeArbitrator( @@ -181,7 +184,7 @@ contract KlerosGovernor is IArbitrableV2 { arbitratorExtraData = _arbitratorExtraData; } - /// @dev Update the dispute template data. + /// @notice Update the dispute template data. /// @param _templateData The new dispute template data. /// @param _templateDataMappings The new dispute template data mappings. function changeDisputeTemplate( @@ -195,7 +198,7 @@ contract KlerosGovernor is IArbitrableV2 { // * State Modifiers * // // ************************************* // - /// @dev Creates transaction list based on input parameters and submits it for potential approval and execution. + /// @notice Creates transaction list based on input parameters and submits it for potential approval and execution. /// @param _target List of addresses to call. /// @param _value List of values required for respective addresses. /// @param _data Concatenated calldata of all transactions of this list. @@ -249,8 +252,8 @@ contract KlerosGovernor is IArbitrableV2 { reservedETH += submission.deposit; } - /// @dev Withdraws submitted transaction list. Reimburses submission deposit. - /// Withdrawal is only possible during the first half of the submission period and during withdrawTimeout after the submission is made. + /// @notice Withdraws submitted transaction list. Reimburses submission deposit. + /// @dev Withdrawal is only possible during the first half of the submission period and during withdrawTimeout after the submission is made. /// @param _submissionID Submission's index in the array of submitted lists of the current sesssion. /// @param _listHash Hash of a withdrawing list. function withdrawTransactionList(uint256 _submissionID, bytes32 _listHash) external { @@ -269,8 +272,8 @@ contract KlerosGovernor is IArbitrableV2 { payable(msg.sender).transfer(submission.deposit); } - /// @dev Approves a transaction list or creates a dispute if more than one list was submitted. - /// If nothing was submitted changes session. + /// @notice Approves a transaction list or creates a dispute if more than one list was submitted. + /// @dev If nothing was submitted changes session. function executeSubmissions() external duringApprovalPeriod { Session storage session = sessions[sessions.length - 1]; if (session.status != Status.NoDispute) revert AlreadyDisputed(); @@ -300,11 +303,12 @@ 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); } } - /// @dev Gives a ruling for a dispute. Must be called by the arbitrator. + /// @notice Gives a ruling for a dispute. + /// @dev Must be called by the arbitrator. /// @param _disputeID ID of the dispute in the Arbitrator contract. /// @param _ruling Ruling given by the arbitrator. Note that 0 is reserved for "Refuse to arbitrate". /// Note If the final ruling is "0" nothing is approved and deposits will stay locked in the contract. @@ -332,7 +336,7 @@ contract KlerosGovernor is IArbitrableV2 { emit Ruling(IArbitratorV2(msg.sender), _disputeID, _ruling); } - /// @dev Executes selected transactions of the list. + /// @notice Executes selected transactions of the list. /// @param _listID The index of the transaction list in the array of lists. /// @param _cursor Index of the transaction from which to start executing. /// @param _count Number of transactions to execute. Executes until the end if set to "0" or number higher than number of transactions in the list. @@ -354,16 +358,16 @@ contract KlerosGovernor is IArbitrableV2 { } } - /// @dev Receive function to receive funds for the execution of transactions. + /// @notice Receive function to receive funds for the execution of transactions. receive() external payable {} - /// @dev Gets the sum of contract funds that are used for the execution of transactions. + /// @notice Gets the sum of contract funds that are used for the execution of transactions. /// @return Contract balance without reserved ETH. function getExpendableFunds() public view returns (uint256) { return address(this).balance - reservedETH; } - /// @dev Gets the info of the specific transaction in the specific list. + /// @notice Gets the info of the specific transaction in the specific list. /// @param _listID The index of the transaction list in the array of lists. /// @param _transactionIndex The index of the transaction. /// @return target The target of the transaction. @@ -379,8 +383,11 @@ contract KlerosGovernor is IArbitrableV2 { return (transaction.target, transaction.value, transaction.data, transaction.executed); } - /// @dev Gets the array of submitted lists in the session. - /// Note that this function is O(n), where n is the number of submissions in the session. This could exceed the gas limit, therefore this function should only be used for interface display and not by other contracts. + /// @notice Gets the array of submitted lists in the session. + /// + /// @dev This function is O(n), where `n` is the number of submissions in the session. + /// This could exceed the gas limit, therefore this function should only be used for interface display and not by other contracts. + /// /// @param _session The ID of the session. /// @return submittedLists Indexes of lists that were submitted during the session. function getSubmittedLists(uint256 _session) external view returns (uint256[] memory submittedLists) { @@ -388,7 +395,7 @@ contract KlerosGovernor is IArbitrableV2 { submittedLists = session.submittedLists; } - /// @dev Gets the number of transactions in the list. + /// @notice Gets the number of transactions in the list. /// @param _listID The index of the transaction list in the array of lists. /// @return txCount The number of transactions in the list. function getNumberOfTransactions(uint256 _listID) external view returns (uint256 txCount) { @@ -396,13 +403,13 @@ contract KlerosGovernor is IArbitrableV2 { return submission.txs.length; } - /// @dev Gets the number of lists created in contract's lifetime. + /// @notice Gets the number of lists created in contract's lifetime. /// @return The number of created lists. function getNumberOfCreatedLists() external view returns (uint256) { return submissions.length; } - /// @dev Gets the number of the ongoing session. + /// @notice Gets the number of the ongoing session. /// @return The number of the ongoing session. function getCurrentSessionNumber() external view returns (uint256) { return sessions.length - 1; diff --git a/contracts/src/arbitration/PolicyRegistry.sol b/contracts/src/arbitration/PolicyRegistry.sol index 9c8d09f3e..ac8e6f44c 100644 --- a/contracts/src/arbitration/PolicyRegistry.sol +++ b/contracts/src/arbitration/PolicyRegistry.sol @@ -5,7 +5,7 @@ import "../proxy/UUPSProxiable.sol"; import "../proxy/Initializable.sol"; /// @title PolicyRegistry -/// @dev A contract to maintain a policy for each court. +/// @notice A contract to maintain a policy for each court. contract PolicyRegistry is UUPSProxiable, Initializable { string public constant override version = "2.0.0"; @@ -13,7 +13,7 @@ contract PolicyRegistry is UUPSProxiable, Initializable { // * Events * // // ************************************* // - /// @dev Emitted when a policy is updated. + /// @notice Emitted when a policy is updated. /// @param _courtID The ID of the policy's court. /// @param _courtName The name of the policy's court. /// @param _policy The URI of the policy JSON. @@ -30,7 +30,7 @@ contract PolicyRegistry is UUPSProxiable, Initializable { // * Function Modifiers * // // ************************************* // - /// @dev Requires that the sender is the owner. + /// @notice Requires that the sender is the owner. modifier onlyByOwner() { if (owner != msg.sender) revert OwnerOnly(); _; @@ -45,7 +45,7 @@ contract PolicyRegistry is UUPSProxiable, Initializable { _disableInitializers(); } - /// @dev Constructs the `PolicyRegistry` contract. + /// @notice Constructs the `PolicyRegistry` contract. /// @param _owner The owner's address. function initialize(address _owner) external initializer { owner = _owner; @@ -63,7 +63,7 @@ contract PolicyRegistry is UUPSProxiable, Initializable { // NOP } - /// @dev Changes the `owner` storage variable. + /// @notice Changes the `owner` storage variable. /// @param _owner The new value for the `owner` storage variable. function changeOwner(address _owner) external onlyByOwner { owner = _owner; @@ -73,7 +73,7 @@ contract PolicyRegistry is UUPSProxiable, Initializable { // * State Modifiers * // // ************************************* // - /// @dev Sets the policy for the specified court. + /// @notice Sets the policy for the specified court. /// @param _courtID The ID of the specified court. /// @param _courtName The name of the specified court. /// @param _policy The URI of the policy JSON. diff --git a/contracts/src/arbitration/SortitionModule.sol b/contracts/src/arbitration/SortitionModule.sol index bf61255ae..e46753f0a 100644 --- a/contracts/src/arbitration/SortitionModule.sol +++ b/contracts/src/arbitration/SortitionModule.sol @@ -12,7 +12,7 @@ import {IRNG} from "../rng/IRNG.sol"; import "../libraries/Constants.sol"; /// @title SortitionModule -/// @dev A factory of trees that keeps track of staked values for sortition. +/// @notice A factory of trees that keeps track of staked values for sortition. contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { using SortitionTrees for SortitionTrees.Tree; using SortitionTrees for mapping(TreeKey key => SortitionTrees.Tree); @@ -27,7 +27,6 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { address account; // The address of the juror. uint96 courtID; // The ID of the court. uint256 stake; // The new stake. - bool alreadyTransferred; // DEPRECATED. True if tokens were already transferred before delayed stake's execution. } struct Juror { @@ -46,20 +45,17 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { uint256 public minStakingTime; // The time after which the phase can be switched to Drawing if there are open disputes. uint256 public maxDrawingTime; // The time after which the phase can be switched back to Staking. uint256 public lastPhaseChange; // The last time the phase was changed. - uint256 public randomNumberRequestBlock; // DEPRECATED: to be removed in the next redeploy uint256 public disputesWithoutJurors; // The number of disputes that have not finished drawing jurors. IRNG public rng; // The random number generator. uint256 public randomNumber; // Random number returned by RNG. - uint256 public rngLookahead; // DEPRECATED: to be removed in the next redeploy uint256 public delayedStakeWriteIndex; // The index of the last `delayedStake` item that was written to the array. 0 index is skipped. uint256 public delayedStakeReadIndex; // The index of the next `delayedStake` item that should be processed. Starts at 1 because 0 index is skipped. mapping(TreeKey key => SortitionTrees.Tree) sortitionSumTrees; // The mapping of sortition trees by keys. mapping(address account => Juror) public jurors; // The jurors. mapping(uint256 => DelayedStake) public delayedStakes; // Stores the stakes that were changed during Drawing phase, to update them when the phase is switched to Staking. - mapping(address jurorAccount => mapping(uint96 courtId => uint256)) public latestDelayedStakeIndex; // DEPRECATED. Maps the juror to its latest delayed stake. If there is already a delayed stake for this juror then it'll be replaced. latestDelayedStakeIndex[juror][courtID]. - uint256 public maxStakePerJuror; - uint256 public maxTotalStaked; - uint256 public totalStaked; + uint256 public maxStakePerJuror; // The maximum amount of PNK a juror can stake in a court. + uint256 public maxTotalStaked; // The maximum amount of PNK that can be staked in all courts. + uint256 public totalStaked; // The amount that is currently staked in all courts. // ************************************* // // * Events * // @@ -84,12 +80,12 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { /// @param _unlock Whether the stake is locked or unlocked. event StakeLocked(address indexed _address, uint256 _relativeAmount, bool _unlock); - /// @dev Emitted when leftover PNK is available. + /// @notice Emitted when leftover PNK is available. /// @param _account The account of the juror. /// @param _amount The amount of PNK available. event LeftoverPNK(address indexed _account, uint256 _amount); - /// @dev Emitted when leftover PNK is withdrawn. + /// @notice Emitted when leftover PNK is withdrawn. /// @param _account The account of the juror withdrawing PNK. /// @param _amount The amount of PNK withdrawn. event LeftoverPNKWithdrawn(address indexed _account, uint256 _amount); @@ -103,7 +99,7 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { _disableInitializers(); } - /// @dev Initializer (constructor equivalent for upgradable contracts). + /// @notice Initializer (constructor equivalent for upgradable contracts). /// @param _owner The owner. /// @param _core The KlerosCore. /// @param _minStakingTime Minimal time to stake @@ -155,25 +151,25 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { // NOP } - /// @dev Changes the owner of the contract. + /// @notice Changes the owner of the contract. /// @param _owner The new owner. function changeOwner(address _owner) external onlyByOwner { owner = _owner; } - /// @dev Changes the `minStakingTime` storage variable. + /// @notice Changes the `minStakingTime` storage variable. /// @param _minStakingTime The new value for the `minStakingTime` storage variable. function changeMinStakingTime(uint256 _minStakingTime) external onlyByOwner { minStakingTime = _minStakingTime; } - /// @dev Changes the `maxDrawingTime` storage variable. + /// @notice Changes the `maxDrawingTime` storage variable. /// @param _maxDrawingTime The new value for the `maxDrawingTime` storage variable. function changeMaxDrawingTime(uint256 _maxDrawingTime) external onlyByOwner { maxDrawingTime = _maxDrawingTime; } - /// @dev Changes the `rng` storage variable. + /// @notice Changes the `rng` storage variable. /// @param _rng The new random number generator. function changeRandomNumberGenerator(IRNG _rng) external onlyByOwner { rng = _rng; @@ -182,10 +178,14 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { } } + /// @notice Changes the `maxStakePerJuror` storage variable. + /// @param _maxStakePerJuror The new `maxStakePerJuror` storage variable. function changeMaxStakePerJuror(uint256 _maxStakePerJuror) external onlyByOwner { maxStakePerJuror = _maxStakePerJuror; } + /// @notice Changes the `maxTotalStaked` storage variable. + /// @param _maxTotalStaked The new `maxTotalStaked` storage variable. function changeMaxTotalStaked(uint256 _maxTotalStaked) external onlyByOwner { maxTotalStaked = _maxTotalStaked; } @@ -194,7 +194,8 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { // * State Modifiers * // // ************************************* // - function passPhase() external { + /// @inheritdoc ISortitionModule + function passPhase() external override { if (phase == Phase.staking) { if (block.timestamp - lastPhaseChange < minStakingTime) revert MinStakingTimeNotPassed(); if (disputesWithoutJurors == 0) revert NoDisputesThatNeedJurors(); @@ -215,18 +216,15 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { emit NewPhase(phase); } - /// @dev Create a sortition sum tree at the specified key. - /// @param _courtID The ID of the court. - /// @param _extraData Extra data that contains the number of children each node in the tree should have. + /// @inheritdoc ISortitionModule function createTree(uint96 _courtID, bytes memory _extraData) external override onlyByCore { TreeKey key = CourtID.wrap(_courtID).toTreeKey(); uint256 K = _extraDataToTreeK(_extraData); sortitionSumTrees.createTree(key, K); } - /// @dev Executes the next delayed stakes. - /// @param _iterations The number of delayed stakes to execute. - function executeDelayedStakes(uint256 _iterations) external { + /// @inheritdoc ISortitionModule + function executeDelayedStakes(uint256 _iterations) external override { if (phase != Phase.staking) revert NotStakingPhase(); if (delayedStakeWriteIndex < delayedStakeReadIndex) revert NoDelayedStakeToExecute(); @@ -243,27 +241,17 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { delayedStakeReadIndex = newDelayedStakeReadIndex; } + /// @inheritdoc ISortitionModule function createDisputeHook(uint256 /*_disputeID*/, uint256 /*_roundID*/) external override onlyByCore { disputesWithoutJurors++; } + /// @inheritdoc ISortitionModule function postDrawHook(uint256 /*_disputeID*/, uint256 /*_roundID*/) external override onlyByCore { disputesWithoutJurors--; } - /// @dev Saves the random number to use it in sortition. Not used by this contract because the storing of the number is inlined in passPhase(). - /// @param _randomNumber Random number returned by RNG contract. - function notifyRandomNumber(uint256 _randomNumber) public override {} - - /// @dev Validate the specified juror's new stake for a court. - /// Note: no state changes should be made when returning stakingResult != Successful, otherwise delayed stakes might break invariants. - /// @param _account The address of the juror. - /// @param _courtID The ID of the court. - /// @param _newStake The new stake. - /// @param _noDelay True if the stake change should not be delayed. - /// @return pnkDeposit The amount of PNK to be deposited. - /// @return pnkWithdrawal The amount of PNK to be withdrawn. - /// @return stakingResult The result of the staking operation. + /// @inheritdoc ISortitionModule function validateStake( address _account, uint96 _courtID, @@ -280,7 +268,7 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { bool _noDelay ) internal returns (uint256 pnkDeposit, uint256 pnkWithdrawal, StakingResult stakingResult) { Juror storage juror = jurors[_account]; - uint256 currentStake = stakeOf(_account, _courtID); + uint256 currentStake = _stakeOf(_account, _courtID); bool stakeIncrease = _newStake > currentStake; uint256 stakeChange = stakeIncrease ? _newStake - currentStake : currentStake - _newStake; @@ -329,17 +317,7 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { return (pnkDeposit, pnkWithdrawal, StakingResult.Successful); } - /// @dev Update the state of the stakes, called by KC at the end of setStake flow. - /// `O(n + p * log_k(j))` where - /// `n` is the number of courts the juror has staked in, - /// `p` is the depth of the court tree, - /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, - /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. - /// @param _account The address of the juror. - /// @param _courtID The ID of the court. - /// @param _pnkDeposit The amount of PNK to be deposited. - /// @param _pnkWithdrawal The amount of PNK to be withdrawn. - /// @param _newStake The new stake. + /// @inheritdoc ISortitionModule function setStake( address _account, uint96 _courtID, @@ -350,18 +328,7 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { _setStake(_account, _courtID, _pnkDeposit, _pnkWithdrawal, _newStake); } - /// @dev Update the state of the stakes with a PNK reward deposit, called by KC during rewards execution. - /// `O(n + p * log_k(j))` where - /// `n` is the number of courts the juror has staked in, - /// `p` is the depth of the court tree, - /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, - /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. - /// @param _account The address of the juror. - /// @param _courtID The ID of the court. - /// @param _penalty The amount of PNK to be deducted. - /// @return pnkBalance The updated total PNK balance of the juror, including the penalty. - /// @return newCourtStake The updated stake of the juror in the court. - /// @return availablePenalty The amount of PNK that was actually deducted. + /// @inheritdoc ISortitionModule function setStakePenalty( address _account, uint96 _courtID, @@ -369,32 +336,24 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { ) external override onlyByCore returns (uint256 pnkBalance, uint256 newCourtStake, uint256 availablePenalty) { Juror storage juror = jurors[_account]; availablePenalty = _penalty; - newCourtStake = stakeOf(_account, _courtID); + newCourtStake = _stakeOf(_account, _courtID); if (juror.stakedPnk < _penalty) { availablePenalty = juror.stakedPnk; } if (availablePenalty == 0) return (juror.stakedPnk, newCourtStake, 0); // No penalty to apply. - uint256 currentStake = stakeOf(_account, _courtID); + uint256 currentStake = _stakeOf(_account, _courtID); uint256 newStake = 0; if (currentStake >= availablePenalty) { newStake = currentStake - availablePenalty; } _setStake(_account, _courtID, 0, availablePenalty, newStake); pnkBalance = juror.stakedPnk; // updated by _setStake() - newCourtStake = stakeOf(_account, _courtID); // updated by _setStake() + newCourtStake = _stakeOf(_account, _courtID); // updated by _setStake() } - /// @dev Update the state of the stakes with a PNK reward deposit, called by KC during rewards execution. - /// `O(n + p * log_k(j))` where - /// `n` is the number of courts the juror has staked in, - /// `p` is the depth of the court tree, - /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, - /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. - /// @param _account The address of the juror. - /// @param _courtID The ID of the court. - /// @param _reward The amount of PNK to be deposited as a reward. + /// @inheritdoc ISortitionModule function setStakeReward( address _account, uint96 _courtID, @@ -402,7 +361,7 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { ) external override onlyByCore returns (bool success) { if (_reward == 0) return true; // No reward to add. - uint256 currentStake = stakeOf(_account, _courtID); + uint256 currentStake = _stakeOf(_account, _courtID); if (currentStake == 0) return false; // Juror has been unstaked, don't increase their stake. uint256 newStake = currentStake + _reward; @@ -419,7 +378,7 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { ) internal { Juror storage juror = jurors[_account]; if (_pnkDeposit > 0) { - uint256 currentStake = stakeOf(_account, _courtID); + uint256 currentStake = _stakeOf(_account, _courtID); if (currentStake == 0) { juror.courtIDs.push(_courtID); } @@ -450,17 +409,19 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { if (currentCourtID == GENERAL_COURT) { finished = true; } else { - (currentCourtID, , , , , , ) = core.courts(currentCourtID); // Get the parent court. + (currentCourtID, , , , , ) = core.courts(currentCourtID); // Get the parent court. } } emit StakeSet(_account, _courtID, _newStake, juror.stakedPnk); } + /// @inheritdoc ISortitionModule function lockStake(address _account, uint256 _relativeAmount) external override onlyByCore { jurors[_account].lockedPnk += _relativeAmount; emit StakeLocked(_account, _relativeAmount, false); } + /// @inheritdoc ISortitionModule function unlockStake(address _account, uint256 _relativeAmount) external override onlyByCore { Juror storage juror = jurors[_account]; juror.lockedPnk -= _relativeAmount; @@ -472,13 +433,7 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { } } - /// @dev Unstakes the inactive juror from all courts. - /// `O(n * (p * log_k(j)) )` where - /// `n` is the number of courts the juror has staked in, - /// `p` is the depth of the court tree, - /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, - /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. - /// @param _account The juror to unstake. + /// @inheritdoc ISortitionModule function forcedUnstakeAllCourts(address _account) external override onlyByCore { uint96[] memory courtIDs = getJurorCourtIDs(_account); for (uint256 j = courtIDs.length; j > 0; j--) { @@ -486,24 +441,12 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { } } - /// @dev Unstakes the inactive juror from a specific court. - /// `O(n * (p * log_k(j)) )` where - /// `n` is the number of courts the juror has staked in, - /// `p` is the depth of the court tree, - /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, - /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. - /// @param _account The juror to unstake. - /// @param _courtID The ID of the court. + /// @inheritdoc ISortitionModule function forcedUnstake(address _account, uint96 _courtID) external override onlyByCore { core.setStakeBySortitionModule(_account, _courtID, 0); } - /// @dev Gives back the locked PNKs in case the juror fully unstaked earlier. - /// Note that since locked and staked PNK are async it is possible for the juror to have positive staked PNK balance - /// while having 0 stake in courts and 0 locked tokens (eg. when the juror fully unstaked during dispute and later got his tokens unlocked). - /// In this case the juror can use this function to withdraw the leftover tokens. - /// Also note that if the juror has some leftover PNK while not fully unstaked he'll have to manually unstake from all courts to trigger this function. - /// @param _account The juror whose PNK to withdraw. + /// @inheritdoc ISortitionModule function withdrawLeftoverPNK(address _account) external override { // Can withdraw the leftover PNK if fully unstaked, has no tokens locked and has positive balance. // This withdrawal can't be triggered by calling setStake() in KlerosCore because current stake is technically 0, thus it is done via separate function. @@ -518,15 +461,7 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { // * Public Views * // // ************************************* // - /// @dev Draw an ID from a tree using a number. - /// Note that this function reverts if the sum of all values in the tree is 0. - /// @param _courtID The ID of the court. - /// @param _coreDisputeID Index of the dispute in Kleros Core. - /// @param _nonce Nonce to hash with random number. - /// @return drawnAddress The drawn address. - /// `O(k * log_k(n))` where - /// `k` is the maximum number of children per node in the tree, - /// and `n` is the maximum number of nodes ever appended. + /// @inheritdoc ISortitionModule function draw( uint96 _courtID, uint256 _coreDisputeID, @@ -538,23 +473,7 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { (drawnAddress, fromSubcourtID) = sortitionSumTrees[key].draw(_coreDisputeID, _nonce, randomNumber); } - /// @dev Get the stake of a juror in a court. - /// @param _juror The address of the juror. - /// @param _courtID The ID of the court. - /// @return value The stake of the juror in the court. - function stakeOf(address _juror, uint96 _courtID) public view returns (uint256) { - bytes32 stakePathID = SortitionTrees.toStakePathID(_juror, _courtID); - TreeKey key = CourtID.wrap(_courtID).toTreeKey(); - return sortitionSumTrees[key].stakeOf(stakePathID); - } - - /// @dev Gets the balance of a juror in a court. - /// @param _juror The address of the juror. - /// @param _courtID The ID of the court. - /// @return totalStaked The total amount of tokens staked including locked tokens and penalty deductions. Equivalent to the effective stake in the General court. - /// @return totalLocked The total amount of tokens locked in disputes. - /// @return stakedInCourt The amount of tokens staked in the specified court including locked tokens and penalty deductions. - /// @return nbCourts The number of courts the juror has directly staked in. + /// @inheritdoc ISortitionModule function getJurorBalance( address _juror, uint96 _courtID @@ -562,25 +481,26 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { external view override - returns (uint256 totalStaked, uint256 totalLocked, uint256 stakedInCourt, uint256 nbCourts) + returns (uint256 totalStakedPnk, uint256 totalLocked, uint256 stakedInCourt, uint256 nbCourts) { Juror storage juror = jurors[_juror]; - totalStaked = juror.stakedPnk; + totalStakedPnk = juror.stakedPnk; totalLocked = juror.lockedPnk; - stakedInCourt = stakeOf(_juror, _courtID); + stakedInCourt = _stakeOf(_juror, _courtID); nbCourts = juror.courtIDs.length; } - /// @dev Gets the court identifiers where a specific `_juror` has staked. - /// @param _juror The address of the juror. + /// @inheritdoc ISortitionModule function getJurorCourtIDs(address _juror) public view override returns (uint96[] memory) { return jurors[_juror].courtIDs; } + /// @inheritdoc ISortitionModule function isJurorStaked(address _juror) external view override returns (bool) { return jurors[_juror].stakedPnk > 0; } + /// @inheritdoc ISortitionModule function getJurorLeftoverPNK(address _juror) public view override returns (uint256) { Juror storage juror = jurors[_juror]; if (juror.courtIDs.length == 0 && juror.lockedPnk == 0) { @@ -594,6 +514,19 @@ contract SortitionModule is ISortitionModule, Initializable, UUPSProxiable { // * Internal * // // ************************************* // + /// @notice Get the stake of a juror in a court. + /// @param _juror The address of the juror. + /// @param _courtID The ID of the court. + /// @return value The stake of the juror in the court. + function _stakeOf(address _juror, uint96 _courtID) internal view returns (uint256) { + bytes32 stakePathID = SortitionTrees.toStakePathID(_juror, _courtID); + TreeKey key = CourtID.wrap(_courtID).toTreeKey(); + return sortitionSumTrees[key].stakeOf(stakePathID); + } + + /// @notice Converts sortition extradata into K value of sortition tree. + /// @param _extraData Sortition extra data. + /// @return K The value of K. function _extraDataToTreeK(bytes memory _extraData) internal pure returns (uint256 K) { if (_extraData.length >= 32) { assembly { diff --git a/contracts/src/arbitration/arbitrables/ArbitrableExample.sol b/contracts/src/arbitration/arbitrables/ArbitrableExample.sol index 4dd60cb82..c0f052202 100644 --- a/contracts/src/arbitration/arbitrables/ArbitrableExample.sol +++ b/contracts/src/arbitration/arbitrables/ArbitrableExample.sol @@ -7,7 +7,7 @@ import "../interfaces/IDisputeTemplateRegistry.sol"; import "../../libraries/SafeERC20.sol"; /// @title ArbitrableExample -/// An example of an arbitrable contract which connects to the arbitator that implements the updated interface. +/// @notice An example of an arbitrable contract which connects to the arbitator that implements the updated interface. contract ArbitrableExample is IArbitrableV2 { using SafeERC20 for IERC20; @@ -21,8 +21,16 @@ contract ArbitrableExample is IArbitrableV2 { uint256 numberOfRulingOptions; // The number of choices the arbitrator can give. } + // ************************************* // + // * Events * // + // ************************************* // + event Action(string indexed _action); + // ************************************* // + // * Storage * // + // ************************************* // + address public immutable owner; IArbitratorV2 public arbitrator; // Arbitrator is set in constructor. IDisputeTemplateRegistry public templateRegistry; // The dispute template registry. @@ -45,7 +53,7 @@ contract ArbitrableExample is IArbitrableV2 { // * Constructor * // // ************************************* // - /// @dev Constructor + /// @notice Constructor /// @param _arbitrator The arbitrator to rule on created disputes. /// @param _templateData The dispute template data. /// @param _templateDataMappings The dispute template data mappings. @@ -96,8 +104,8 @@ contract ArbitrableExample is IArbitrableV2 { // * State Modifiers * // // ************************************* // - /// @dev Calls createDispute function of the specified arbitrator to create a dispute. - /// Note that we don’t need to check that msg.value is enough to pay arbitration fees as it’s the responsibility of the arbitrator contract. + /// @notice Calls createDispute function of the specified arbitrator to create a dispute. + /// @dev No need to check that msg.value is enough to pay arbitration fees as it’s the responsibility of the arbitrator contract. /// @param _action The action that requires arbitration. /// @return disputeID Dispute id (on arbitrator side) of the dispute created. function createDispute(string calldata _action) external payable returns (uint256 disputeID) { @@ -111,11 +119,11 @@ contract ArbitrableExample is IArbitrableV2 { externalIDtoLocalID[disputeID] = localDisputeID; uint256 externalDisputeID = uint256(keccak256(abi.encodePacked(_action))); - emit DisputeRequest(arbitrator, disputeID, externalDisputeID, templateId, ""); + emit DisputeRequest(arbitrator, disputeID, externalDisputeID, templateId); } - /// @dev Calls createDispute function of the specified arbitrator to create a dispute. - /// Note that we don’t need to check that msg.value is enough to pay arbitration fees as it’s the responsibility of the arbitrator contract. + /// @notice Calls createDispute function of the specified arbitrator to create a dispute. + /// @dev No need to check that msg.value is enough to pay arbitration fees as it’s the responsibility of the arbitrator contract. /// @param _action The action that requires arbitration. /// @param _feeInWeth Amount of fees in WETH for the arbitrator. /// @return disputeID Dispute id (on arbitrator side) of the dispute created. @@ -133,12 +141,10 @@ contract ArbitrableExample is IArbitrableV2 { externalIDtoLocalID[disputeID] = localDisputeID; uint256 externalDisputeID = uint256(keccak256(abi.encodePacked(_action))); - emit DisputeRequest(arbitrator, disputeID, externalDisputeID, templateId, ""); + emit DisputeRequest(arbitrator, disputeID, externalDisputeID, templateId); } - /// @dev To be called by the arbitrator of the dispute, to declare the winning ruling. - /// @param _arbitratorDisputeID ID of the dispute in arbitrator contract. - /// @param _ruling The ruling choice of the arbitration. + /// @inheritdoc IArbitrableV2 function rule(uint256 _arbitratorDisputeID, uint256 _ruling) external override { uint256 localDisputeID = externalIDtoLocalID[_arbitratorDisputeID]; DisputeStruct storage dispute = disputes[localDisputeID]; diff --git a/contracts/src/arbitration/arbitrables/DisputeResolver.sol b/contracts/src/arbitration/arbitrables/DisputeResolver.sol index 949d282b2..64fef2ee6 100644 --- a/contracts/src/arbitration/arbitrables/DisputeResolver.sol +++ b/contracts/src/arbitration/arbitrables/DisputeResolver.sol @@ -6,7 +6,8 @@ import "../interfaces/IDisputeTemplateRegistry.sol"; pragma solidity ^0.8.24; /// @title DisputeResolver -/// DisputeResolver contract adapted for V2 from https://github.com/kleros/arbitrable-proxy-contracts/blob/master/contracts/ArbitrableProxy.sol. +/// @notice DisputeResolver contract +/// @dev Adapted for V2 from https://github.com/kleros/arbitrable-proxy-contracts/blob/master/contracts/ArbitrableProxy.sol. contract DisputeResolver is IArbitrableV2 { // ************************************* // // * Enums / Structs * // @@ -33,7 +34,7 @@ contract DisputeResolver is IArbitrableV2 { // * Constructor * // // ************************************* // - /// @dev Constructor + /// @notice Constructor /// @param _arbitrator Target global arbitrator for any disputes. constructor(IArbitratorV2 _arbitrator, IDisputeTemplateRegistry _templateRegistry) { owner = msg.sender; @@ -45,7 +46,7 @@ contract DisputeResolver is IArbitrableV2 { // * Governance * // // ************************************* // - /// @dev Changes the owner. + /// @notice Changes the owner. /// @param _owner The address of the new owner. function changeOwner(address _owner) external { if (owner != msg.sender) revert OwnerOnly(); @@ -66,8 +67,8 @@ contract DisputeResolver is IArbitrableV2 { // * State Modifiers * // // ************************************* // - /// @dev Calls createDispute function of the specified arbitrator to create a dispute. - /// Note that we don’t need to check that msg.value is enough to pay arbitration fees as it’s the responsibility of the arbitrator contract. + /// @notice Calls createDispute function of the specified arbitrator to create a dispute. + /// @dev No need to check that msg.value is enough to pay arbitration fees as it’s the responsibility of the arbitrator contract. /// @param _arbitratorExtraData Extra data for the arbitrator of the dispute. /// @param _disputeTemplate Dispute template. /// @param _disputeTemplateDataMappings The data mappings. @@ -84,28 +85,11 @@ contract DisputeResolver is IArbitrableV2 { _arbitratorExtraData, _disputeTemplate, _disputeTemplateDataMappings, - "", _numberOfRulingOptions ); } - /// @dev Calls createDispute function of the specified arbitrator to create a dispute. - /// Note that we don’t need to check that msg.value is enough to pay arbitration fees as it’s the responsibility of the arbitrator contract. - /// @param _arbitratorExtraData Extra data for the arbitrator of the dispute. - /// @param _disputeTemplateUri The URI to the dispute template. For example on IPFS: starting with '/ipfs/'. - /// @param _numberOfRulingOptions Number of ruling options. - /// @return disputeID Dispute id (on arbitrator side) of the created dispute. - function createDisputeForTemplateUri( - bytes calldata _arbitratorExtraData, - string calldata _disputeTemplateUri, - uint256 _numberOfRulingOptions - ) external payable returns (uint256 disputeID) { - return _createDispute(_arbitratorExtraData, "", "", _disputeTemplateUri, _numberOfRulingOptions); - } - - /// @dev To be called by the arbitrator of the dispute, to declare the winning ruling. - /// @param _arbitratorDisputeID ID of the dispute in arbitrator contract. - /// @param _ruling The ruling choice of the arbitration. + /// @inheritdoc IArbitrableV2 function rule(uint256 _arbitratorDisputeID, uint256 _ruling) external override { uint256 localDisputeID = arbitratorDisputeIDToLocalID[_arbitratorDisputeID]; DisputeStruct storage dispute = disputes[localDisputeID]; @@ -127,7 +111,6 @@ contract DisputeResolver is IArbitrableV2 { bytes calldata _arbitratorExtraData, string memory _disputeTemplate, string memory _disputeTemplateDataMappings, - string memory _disputeTemplateUri, uint256 _numberOfRulingOptions ) internal virtual returns (uint256 arbitratorDisputeID) { if (_numberOfRulingOptions <= 1) revert ShouldBeAtLeastTwoRulingOptions(); @@ -144,7 +127,7 @@ contract DisputeResolver is IArbitrableV2 { ); arbitratorDisputeIDToLocalID[arbitratorDisputeID] = localDisputeID; uint256 templateId = templateRegistry.setDisputeTemplate("", _disputeTemplate, _disputeTemplateDataMappings); - emit DisputeRequest(arbitrator, arbitratorDisputeID, localDisputeID, templateId, _disputeTemplateUri); + emit DisputeRequest(arbitrator, arbitratorDisputeID, localDisputeID, templateId); } // ************************************* // diff --git a/contracts/src/arbitration/devtools/DisputeResolverRuler.sol b/contracts/src/arbitration/devtools/DisputeResolverRuler.sol index eed2c8092..3ca5607ea 100644 --- a/contracts/src/arbitration/devtools/DisputeResolverRuler.sol +++ b/contracts/src/arbitration/devtools/DisputeResolverRuler.sol @@ -9,14 +9,14 @@ interface IKlerosCoreRulerFragment { } /// @title DisputeResolverRuler -/// It extends DisputeResolver for testing purposes of the automatic ruling modes. -/// The arbitrator disputeID must be known before dispute creation, otherwise the dispute cannot be retrieved during the immediate call to rule(). +/// @notice Extension of the DisputeResolver for development tooling and testing of the automatic ruling modes. +/// @dev The arbitrator disputeID must be known before dispute creation, otherwise the dispute cannot be retrieved during the immediate call to rule(). contract DisputeResolverRuler is DisputeResolver { // ************************************* // // * Constructor * // // ************************************* // - /// @dev Constructor + /// @notice Constructor /// @param _arbitrator Target global arbitrator for any disputes. constructor( IArbitratorV2 _arbitrator, @@ -33,7 +33,6 @@ contract DisputeResolverRuler is DisputeResolver { bytes calldata _arbitratorExtraData, string memory _disputeTemplate, string memory _disputeTemplateDataMappings, - string memory _disputeTemplateUri, uint256 _numberOfRulingOptions ) internal override returns (uint256 arbitratorDisputeID) { if (_numberOfRulingOptions <= 1) revert ShouldBeAtLeastTwoRulingOptions(); @@ -47,7 +46,7 @@ contract DisputeResolverRuler is DisputeResolver { arbitratorDisputeID = IKlerosCoreRulerFragment(address(arbitrator)).getNextDisputeID(); arbitratorDisputeIDToLocalID[arbitratorDisputeID] = localDisputeID; uint256 templateId = templateRegistry.setDisputeTemplate("", _disputeTemplate, _disputeTemplateDataMappings); - emit DisputeRequest(arbitrator, arbitratorDisputeID, localDisputeID, templateId, _disputeTemplateUri); + emit DisputeRequest(arbitrator, arbitratorDisputeID, localDisputeID, templateId); arbitrator.createDispute{value: msg.value}(_numberOfRulingOptions, _arbitratorExtraData); } diff --git a/contracts/src/arbitration/devtools/KlerosCoreRuler.sol b/contracts/src/arbitration/devtools/KlerosCoreRuler.sol index 42a428299..4ea1be3ee 100644 --- a/contracts/src/arbitration/devtools/KlerosCoreRuler.sol +++ b/contracts/src/arbitration/devtools/KlerosCoreRuler.sol @@ -130,16 +130,17 @@ contract KlerosCoreRuler is IArbitratorV2, UUPSProxiable, Initializable { 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 ); event LeftoverRewardSent( uint256 indexed _disputeID, uint256 indexed _roundID, - uint256 _pnkAmount, - uint256 _feeAmount, + uint256 _amountPnk, + uint256 _amountFee, IERC20 _feeToken ); event AutoRuled( @@ -518,7 +519,16 @@ contract KlerosCoreRuler is IArbitratorV2, UUPSProxiable, Initializable { // The dispute fees were paid in ERC20 round.feeToken.safeTransfer(account, feeReward); } - emit TokenAndETHShift(account, _disputeID, _round, 1, int256(0), int256(feeReward), round.feeToken); + emit TokenAndETHShift( + account, + _disputeID, + _round, + ONE_BASIS_POINT, + ONE_BASIS_POINT, + int256(0), + int256(feeReward), + round.feeToken + ); } /// @dev Executes a specified dispute's ruling. diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol b/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol index dd2edbab8..95f551a48 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.24; import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol"; /// @title DisputeKitClassic -/// Dispute kit implementation of the Kleros v1 features including: +/// @notice Dispute kit implementation of the Kleros v1 features including: /// - a drawing system: proportional to staked PNK, /// - a vote aggregation system: plurality, /// - an incentive system: equal split between coherent votes, @@ -22,7 +22,7 @@ contract DisputeKitClassic is DisputeKitClassicBase { _disableInitializers(); } - /// @dev Initializer. + /// @notice Initializer. /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol b/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol index 5dc1c3522..912155986 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol @@ -9,7 +9,7 @@ import {SafeSend} from "../../libraries/SafeSend.sol"; import {ONE_BASIS_POINT, DISPUTE_KIT_CLASSIC} from "../../libraries/Constants.sol"; /// @title DisputeKitClassicBase -/// Abstract Dispute kit classic implementation of the Kleros v1 features including: +/// @notice Abstract Dispute kit classic implementation of the Kleros v1 features including: /// - a drawing system: proportional to staked PNK, /// - a vote aggregation system: plurality, /// - an incentive system: equal split between coherent votes, @@ -27,6 +27,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi bool jumped; // True if dispute jumped to a parent dispute kit and won't be handled by this DK anymore. mapping(uint256 => uint256) coreRoundIDToLocal; // Maps id of the round in the core contract to the index of the round of related local dispute. bytes extraData; // Extradata for the dispute. + uint256[10] __gap; // Reserved slots for future upgrades. } struct Round { @@ -41,14 +42,16 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi mapping(address account => mapping(uint256 choiceId => uint256)) contributions; // Maps contributors to their contributions for each choice. uint256 feeRewards; // Sum of reimbursable appeal fees available to the parties that made contributions to the ruling that ultimately wins a dispute. uint256[] fundedChoices; // Stores the choices that are fully funded. - uint256 nbVotes; // Maximal number of votes this dispute can get. + mapping(address drawnAddress => bool) alreadyDrawn; // True if the address has already been drawn, false by default. + uint256[10] __gap; // Reserved slots for future upgrades. } struct Vote { + bool voted; // True if the vote has been cast. address account; // The address of the juror. bytes32 commit; // The commit of the juror. For courts with hidden votes. uint256 choice; // The choice of the juror. - bool voted; // True if the vote has been cast. + uint256[10] __gap; // Reserved slots for future upgrades. } // ************************************* // @@ -64,30 +67,30 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi Dispute[] public disputes; // Array of the locally created disputes. mapping(uint256 => uint256) public coreDisputeIDToLocal; // Maps the dispute ID in Kleros Core to the local dispute ID. bool public singleDrawPerJuror; // Whether each juror can only draw once per dispute, false by default. - mapping(uint256 localDisputeID => mapping(uint256 localRoundID => mapping(address drawnAddress => bool))) - public alreadyDrawn; // True if the address has already been drawn, false by default. To be added to the Round struct when fully redeploying rather than upgrading. mapping(uint256 coreDisputeID => bool) public coreDisputeIDToActive; // True if this dispute kit is active for this core dispute ID. address public wNative; // The wrapped native token for safeSend(). uint256 public jumpDisputeKitID; // The ID of the dispute kit in Kleros Core disputeKits array that the dispute should switch to after the court jump, in case the new court doesn't support this dispute kit. + uint256[50] private __gap; // Reserved slots for future upgrades. + // ************************************* // // * Events * // // ************************************* // - /// @dev To be emitted when a dispute is created. + /// @notice To be emitted when a dispute is created. /// @param _coreDisputeID The identifier of the dispute in the Arbitrator contract. /// @param _numberOfChoices The number of choices available in the dispute. /// @param _extraData The extra data for the dispute. event DisputeCreation(uint256 indexed _coreDisputeID, uint256 _numberOfChoices, bytes _extraData); - /// @dev To be emitted when a vote commitment is cast. + /// @notice To be emitted when a vote commitment is cast. /// @param _coreDisputeID The identifier of the dispute in the Arbitrator contract. /// @param _juror The address of the juror casting the vote commitment. /// @param _voteIDs The identifiers of the votes in the dispute. /// @param _commit The commitment of the juror. event CommitCast(uint256 indexed _coreDisputeID, address indexed _juror, uint256[] _voteIDs, bytes32 _commit); - /// @dev To be emitted when a funding contribution is made. + /// @notice To be emitted when a funding contribution is made. /// @param _coreDisputeID The identifier of the dispute in the Arbitrator contract. /// @param _coreRoundID The identifier of the round in the Arbitrator contract. /// @param _choice The choice that is being funded. @@ -101,7 +104,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi uint256 _amount ); - /// @dev To be emitted when the contributed funds are withdrawn. + /// @notice To be emitted when the contributed funds are withdrawn. /// @param _coreDisputeID The identifier of the dispute in the Arbitrator contract. /// @param _coreRoundID The identifier of the round in the Arbitrator contract. /// @param _choice The choice that is being funded. @@ -115,7 +118,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi uint256 _amount ); - /// @dev To be emitted when a choice is fully funded for an appeal. + /// @notice To be emitted when a choice is fully funded for an appeal. /// @param _coreDisputeID The identifier of the dispute in the Arbitrator contract. /// @param _coreRoundID The identifier of the round in the Arbitrator contract. /// @param _choice The choice that is being funded. @@ -144,7 +147,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi // * Constructor * // // ************************************* // - /// @dev Initializer. + /// @notice Initializer. /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. @@ -165,7 +168,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi // * Governance * // // ************************ // - /// @dev Allows the owner to call anything on behalf of the contract. + /// @notice Allows the owner to call anything on behalf of the contract. /// @param _destination The destination of the call. /// @param _amount The value sent with the call. /// @param _data The data sent with the call. @@ -174,19 +177,19 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi if (!success) revert UnsuccessfulCall(); } - /// @dev Changes the `owner` storage variable. + /// @notice Changes the `owner` storage variable. /// @param _owner The new value for the `owner` storage variable. function changeOwner(address payable _owner) external onlyByOwner { owner = _owner; } - /// @dev Changes the `core` storage variable. + /// @notice Changes the `core` storage variable. /// @param _core The new value for the `core` storage variable. function changeCore(address _core) external onlyByOwner { core = KlerosCore(_core); } - /// @dev Changes the dispute kit ID used for the jump. + /// @notice Changes the dispute kit ID used for the jump. /// @param _jumpDisputeKitID The new value for the `jumpDisputeKitID` storage variable. function changeJumpDisputeKitID(uint256 _jumpDisputeKitID) external onlyByOwner { jumpDisputeKitID = _jumpDisputeKitID; @@ -196,17 +199,12 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi // * State Modifiers * // // ************************************* // - /// @dev Creates a local dispute and maps it to the dispute ID in the Core contract. - /// Note: Access restricted to Kleros Core only. - /// @param _coreDisputeID The ID of the dispute in Kleros Core. - /// @param _numberOfChoices Number of choices of the dispute - /// @param _extraData Additional info about the dispute, for possible use in future dispute kits. - /// @param _nbVotes Number of votes for this dispute. + /// @inheritdoc IDisputeKit function createDispute( uint256 _coreDisputeID, uint256 _numberOfChoices, bytes calldata _extraData, - uint256 _nbVotes + uint256 /*_nbVotes*/ ) external override onlyByCore { uint256 localDisputeID = disputes.length; Dispute storage dispute = disputes.push(); @@ -218,7 +216,6 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi dispute.coreRoundIDToLocal[core.getNumberOfRounds(_coreDisputeID) - 1] = dispute.rounds.length; Round storage round = dispute.rounds.push(); - round.nbVotes = _nbVotes; round.tied = true; coreDisputeIDToLocal[_coreDisputeID] = localDisputeID; @@ -226,11 +223,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi emit DisputeCreation(_coreDisputeID, _numberOfChoices, _extraData); } - /// @dev Draws the juror from the sortition tree. The drawn address is picked up by Kleros Core. - /// Note: Access restricted to Kleros Core only. - /// @param _coreDisputeID The ID of the dispute in Kleros Core. - /// @param _nonce Nonce of the drawing iteration. - /// @return drawnAddress The drawn address. + /// @inheritdoc IDisputeKit function draw( uint256 _coreDisputeID, uint256 _nonce @@ -249,17 +242,19 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi } if (_postDrawCheck(round, _coreDisputeID, drawnAddress)) { - round.votes.push(Vote({account: drawnAddress, commit: bytes32(0), choice: 0, voted: false})); - alreadyDrawn[localDisputeID][localRoundID][drawnAddress] = true; + Vote storage vote = round.votes.push(); + vote.account = drawnAddress; + round.alreadyDrawn[drawnAddress] = true; } else { drawnAddress = address(0); } } - /// @dev Sets the caller's commit for the specified votes. It can be called multiple times during the - /// commit period, each call overrides the commits of the previous one. - /// `O(n)` where - /// `n` is the number of votes. + /// @notice Sets the caller's commit for the specified votes. + /// + /// @dev It can be called multiple times during the commit period, each call overrides the commits of the previous one. + /// `O(n)` where `n` is the number of votes. + /// /// @param _coreDisputeID The ID of the dispute in Kleros Core. /// @param _voteIDs The IDs of the votes. /// @param _commit The commitment hash. @@ -287,9 +282,10 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi emit CommitCast(_coreDisputeID, msg.sender, _voteIDs, _commit); } - /// @dev Sets the caller's choices for the specified votes. - /// `O(n)` where - /// `n` is the number of votes. + /// @notice Sets the caller's choices for the specified votes. + /// + /// @dev `O(n)` where `n` is the number of votes. + /// /// @param _coreDisputeID The ID of the dispute in Kleros Core. /// @param _voteIDs The IDs of the votes. /// @param _choice The choice. @@ -326,7 +322,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi Round storage round = dispute.rounds[localRoundID]; { (uint96 courtID, , , , ) = core.disputes(_coreDisputeID); - (, bool hiddenVotes, , , , , ) = core.courts(courtID); + (, bool hiddenVotes, , , , ) = core.courts(courtID); bytes32 actualVoteHash = hashVote(_choice, _salt, _justification); // Save the votes. @@ -359,7 +355,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi emit VoteCast(_coreDisputeID, _juror, _voteIDs, _choice, _justification); } - /// @dev Manages contributions, and appeals a dispute if at least two choices are fully funded. + /// @notice Manages contributions, and appeals a dispute if at least two choices are fully funded. /// Note that the surplus deposit will be reimbursed. /// @param _coreDisputeID Index of the dispute in Kleros Core. /// @param _choice A choice that receives funding. @@ -422,7 +418,6 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi dispute.coreRoundIDToLocal[coreRoundID + 1] = dispute.rounds.length; Round storage newRound = dispute.rounds.push(); - newRound.nbVotes = core.getNumberOfVotes(_coreDisputeID); newRound.tied = true; } core.appeal{value: appealCost}(_coreDisputeID, dispute.numberOfChoices, dispute.extraData); @@ -431,8 +426,8 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi if (msg.value > contribution) payable(msg.sender).safeSend(msg.value - contribution, wNative); } - /// @dev Allows those contributors who attempted to fund an appeal round to withdraw any reimbursable fees or rewards after the dispute gets resolved. - /// Note that withdrawals are not possible if the core contract is paused. + /// @notice Allows those contributors who attempted to fund an appeal round to withdraw any reimbursable fees or rewards after the dispute gets resolved. + /// @dev Withdrawals are not possible if the core contract is paused. /// @param _coreDisputeID Index of the dispute in Kleros Core contract. /// @param _beneficiary The address whose rewards to withdraw. /// @param _coreRoundID The round in the Kleros Core contract the caller wants to withdraw from. @@ -482,13 +477,11 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi // * Public Views * // // ************************************* // - /** - * @dev Computes the hash of a vote using ABI encoding - * @dev The unused parameters may be used by overriding contracts. - * @param _choice The choice being voted for - * @param _salt A random salt for commitment - * @return bytes32 The hash of the encoded vote parameters - */ + /// @notice Computes the hash of a vote using ABI encoding + /// @dev The unused parameters may be used by overriding contracts. + /// @param _choice The choice being voted for + /// @param _salt A random salt for commitment + /// @return bytes32 The hash of the encoded vote parameters function hashVote( uint256 _choice, uint256 _salt, @@ -497,17 +490,16 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi return keccak256(abi.encodePacked(_choice, _salt)); } + /// @notice Returns the rulings that were fully funded in the latest appeal round. + /// @param _coreDisputeID The ID of the dispute in Kleros Core. + /// @return fundedChoices Fully funded rulings. function getFundedChoices(uint256 _coreDisputeID) public view returns (uint256[] memory fundedChoices) { Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]]; Round storage lastRound = dispute.rounds[dispute.rounds.length - 1]; return lastRound.fundedChoices; } - /// @dev Gets the current ruling of a specified dispute. - /// @param _coreDisputeID The ID of the dispute in Kleros Core. - /// @return ruling The current ruling. - /// @return tied Whether it's a tie or not. - /// @return overridden Whether the ruling was overridden by appeal funding or not. + /// @inheritdoc IDisputeKit function currentRuling( uint256 _coreDisputeID ) external view override returns (uint256 ruling, bool tied, bool overridden) { @@ -527,12 +519,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi } } - /// @dev Gets the degree of coherence of a particular voter. This function is called by Kleros Core in order to determine the amount of the reward. - /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. - /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. - /// @param _voteID The ID of the vote. - /// @return pnkCoherence The degree of coherence in basis points for the dispute PNK reward. - /// @return feeCoherence The degree of coherence in basis points for the dispute fee reward. + /// @inheritdoc IDisputeKit function getDegreeOfCoherenceReward( uint256 _coreDisputeID, uint256 _coreRoundID, @@ -544,11 +531,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi return (coherence, coherence); } - /// @dev Gets the degree of coherence of a particular voter. This function is called by Kleros Core in order to determine the amount of the penalty. - /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. - /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. - /// @param _voteID The ID of the vote. - /// @return pnkCoherence The degree of coherence in basis points for the dispute PNK reward. + /// @inheritdoc IDisputeKit function getDegreeOfCoherencePenalty( uint256 _coreDisputeID, uint256 _coreRoundID, @@ -576,10 +559,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi } } - /// @dev Gets the number of jurors who are eligible to a reward in this round. - /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. - /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. - /// @return The number of coherent jurors. + /// @inheritdoc IDisputeKit function getCoherentCount(uint256 _coreDisputeID, uint256 _coreRoundID) external view override returns (uint256) { Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]]; Round storage currentRound = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]]; @@ -594,34 +574,26 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi } } - /// @dev Returns true if all of the jurors have cast their commits for the last round. - /// @param _coreDisputeID The ID of the dispute in Kleros Core. - /// @return Whether all of the jurors have cast their commits for the last round. + /// @inheritdoc IDisputeKit function areCommitsAllCast(uint256 _coreDisputeID) external view override returns (bool) { Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]]; Round storage round = dispute.rounds[dispute.rounds.length - 1]; return round.totalCommitted == round.votes.length; } - /// @dev Returns true if all of the jurors have cast their votes for the last round. - /// Note that this function is to be called directly by the core contract and is not for off-chain usage. - /// @param _coreDisputeID The ID of the dispute in Kleros Core. - /// @return Whether all of the jurors have cast their votes for the last round. + /// @inheritdoc IDisputeKit function areVotesAllCast(uint256 _coreDisputeID) external view override returns (bool) { Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]]; Round storage round = dispute.rounds[dispute.rounds.length - 1]; (uint96 courtID, , , , ) = core.disputes(_coreDisputeID); - (, bool hiddenVotes, , , , , ) = core.courts(courtID); + (, bool hiddenVotes, , , , ) = core.courts(courtID); uint256 expectedTotalVoted = hiddenVotes ? round.totalCommitted : round.votes.length; return round.totalVoted == expectedTotalVoted; } - /// @dev Returns true if the appeal funding is finished prematurely (e.g. when losing side didn't fund). - /// Note that this function is to be called directly by the core contract and is not for off-chain usage. - /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. - /// @return Whether the appeal funding is finished. + /// @inheritdoc IDisputeKit function isAppealFunded(uint256 _coreDisputeID) external view override returns (bool) { (uint256 appealPeriodStart, uint256 appealPeriodEnd) = core.appealPeriod(_coreDisputeID); @@ -632,15 +604,12 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi ((appealPeriodEnd - appealPeriodStart) * LOSER_APPEAL_PERIOD_MULTIPLIER) / ONE_BASIS_POINT); } - /// @dev Returns true if the dispute is jumping to a parent court. - /// @return Whether the dispute is jumping to a parent court or not. + /// @inheritdoc IDisputeKit function earlyCourtJump(uint256 /* _coreDisputeID */) external pure override returns (bool) { return false; } - /// @dev Returns the number of votes after the appeal. - /// @param _currentNbVotes The number of votes before the appeal. - /// @return The number of votes after the appeal. + /// @inheritdoc IDisputeKit function getNbVotesAfterAppeal( IDisputeKit /* _previousDisputeKit */, uint256 _currentNbVotes @@ -648,18 +617,13 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi return (_currentNbVotes * 2) + 1; } - /// @dev Returns the dispute kid ID be used after court jump by Kleros Core. - /// @return The ID of the dispute kit in Kleros Core disputeKits array. + /// @inheritdoc IDisputeKit function getJumpDisputeKitID() external view override returns (uint256) { // Fall back to classic DK in case the jump ID is not defined. return jumpDisputeKitID == 0 ? DISPUTE_KIT_CLASSIC : jumpDisputeKitID; } - /// @dev Returns true if the specified voter was active in this round. - /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. - /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. - /// @param _voteID The ID of the voter. - /// @return Whether the voter was active or not. + /// @inheritdoc IDisputeKit function isVoteActive( uint256 _coreDisputeID, uint256 _coreRoundID, @@ -670,6 +634,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi return vote.voted; } + /// @inheritdoc IDisputeKit function getRoundInfo( uint256 _coreDisputeID, uint256 _coreRoundID, @@ -682,7 +647,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi uint256 winningChoice, bool tied, uint256 totalVoted, - uint256 totalCommited, + uint256 totalCommitted, uint256 nbVoters, uint256 choiceCount ) @@ -699,14 +664,14 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi ); } - /// @dev Returns the number of rounds in a dispute. + /// @notice Returns the number of rounds in a dispute. /// @param _localDisputeID The ID of the dispute in the Dispute Kit. /// @return The number of rounds in the dispute. function getNumberOfRounds(uint256 _localDisputeID) external view returns (uint256) { return disputes[_localDisputeID].rounds.length; } - /// @dev Returns the local dispute ID and round ID for a given core dispute ID and core round ID. + /// @notice Returns the local dispute ID and round ID for a given core dispute ID and core round ID. /// @param _coreDisputeID The ID of the dispute in Kleros Core. /// @param _coreRoundID The ID of the round in Kleros Core. /// @return localDisputeID The ID of the dispute in the Dispute Kit. @@ -719,12 +684,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi localRoundID = disputes[localDisputeID].coreRoundIDToLocal[_coreRoundID]; } - /// @dev Returns the vote information for a given vote ID. - /// @param _coreDisputeID The ID of the dispute in Kleros Core. - /// @param _coreRoundID The ID of the round in Kleros Core. - /// @param _voteID The ID of the vote. - /// @return account The address of the juror who cast the vote. - /// @return commit The commit of the vote. + /// @inheritdoc IDisputeKit function getVoteInfo( uint256 _coreDisputeID, uint256 _coreRoundID, @@ -739,7 +699,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi // * Internal * // // ************************************* // - /// @dev Returns the expected vote hash for a given vote. + /// @notice Returns the expected vote hash for a given vote. /// @param _localDisputeID The ID of the dispute in the Dispute Kit. /// @param _localRoundID The ID of the round in the Dispute Kit. /// @param _voteID The ID of the vote. @@ -752,11 +712,13 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi return disputes[_localDisputeID].rounds[_localRoundID].votes[_voteID].commit; } - /// @dev Checks that the chosen address satisfies certain conditions for being drawn. - /// Note that we don't check the minStake requirement here because of the implicit staking in parent courts. + /// @notice Checks that the chosen address satisfies certain conditions for being drawn. + /// + /// @dev No need to check the minStake requirement here because of the implicit staking in parent courts. /// minStake is checked directly during staking process however it's possible for the juror to get drawn /// while having < minStake if it is later increased by governance. /// This issue is expected and harmless. + /// /// @param _coreDisputeID ID of the dispute in the core contract. /// @param _juror Chosen address. /// @return result Whether the address passes the check or not. @@ -768,8 +730,8 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi if (singleDrawPerJuror) { uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID]; Dispute storage dispute = disputes[localDisputeID]; - uint256 localRoundID = dispute.rounds.length - 1; - result = !alreadyDrawn[localDisputeID][localRoundID][_juror]; + Round storage round = dispute.rounds[dispute.rounds.length - 1]; + result = !round.alreadyDrawn[_juror]; } else { result = true; } diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol b/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol index 8abfdecac..1cbf21df8 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.24; import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol"; interface IBalanceHolder { - /// @dev Returns the number of tokens in `owner` account. + /// @notice Returns the number of tokens in `owner` account. /// @dev Compatible with ERC-20 and ERC-721. /// @param owner The address of the owner. /// @return balance The number of tokens in `owner` account. @@ -13,7 +13,7 @@ interface IBalanceHolder { } interface IBalanceHolderERC1155 { - /// @dev Returns the balance of an ERC-1155 token. + /// @notice Returns the balance of an ERC-1155 token. /// @param account The address of the token holder /// @param id ID of the token /// @return The token balance @@ -21,7 +21,7 @@ interface IBalanceHolderERC1155 { } /// @title DisputeKitGated -/// Dispute kit implementation adapted from DisputeKitClassic +/// @notice Dispute kit implementation adapted from DisputeKitClassic /// - a drawing system: proportional to staked PNK with a non-zero balance of `tokenGate` where `tokenGate` is an ERC20, ERC721 or ERC1155 /// - a vote aggregation system: plurality, /// - an incentive system: equal split between coherent votes, @@ -38,7 +38,7 @@ contract DisputeKitGated is DisputeKitClassicBase { _disableInitializers(); } - /// @dev Initializer. + /// @notice Initializer. /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. @@ -66,13 +66,13 @@ contract DisputeKitGated is DisputeKitClassicBase { // * Internal * // // ************************************* // - /// @dev Extracts token gating information from the extra data. + /// @notice Extracts token gating information from the extra data. /// @param _extraData The extra data bytes array with the following encoding: - /// - bytes 0-31: uint96 courtID, not used here - /// - bytes 32-63: uint256 minJurors, not used here - /// - bytes 64-95: uint256 disputeKitID, not used here - /// - bytes 96-127: uint256 packedTokenGateAndFlag (address tokenGate in bits 0-159, bool isERC1155 in bit 160) - /// - bytes 128-159: uint256 tokenId + /// - bytes 0-31: uint96 courtID, not used here + /// - bytes 32-63: uint256 minJurors, not used here + /// - bytes 64-95: uint256 disputeKitID, not used here + /// - bytes 96-127: uint256 packedTokenGateAndFlag (address tokenGate in bits 0-159, bool isERC1155 in bit 160) + /// - bytes 128-159: uint256 tokenId /// @return tokenGate The address of the token contract used for gating access. /// @return isERC1155 True if the token is an ERC-1155, false for ERC-20/ERC-721. /// @return tokenId The token ID for ERC-1155 tokens (ignored for ERC-20/ERC-721). diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol b/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol index 573f81f11..b408e27a4 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.24; import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol"; interface IBalanceHolder { - /// @dev Returns the number of tokens in `owner` account. + /// @notice Returns the number of tokens in `owner` account. /// @dev Compatible with ERC-20 and ERC-721. /// @param owner The address of the owner. /// @return balance The number of tokens in `owner` account. @@ -13,7 +13,7 @@ interface IBalanceHolder { } interface IBalanceHolderERC1155 { - /// @dev Returns the balance of an ERC-1155 token. + /// @notice Returns the balance of an ERC-1155 token. /// @param account The address of the token holder /// @param id ID of the token /// @return The token balance @@ -21,7 +21,7 @@ interface IBalanceHolderERC1155 { } /// @title DisputeKitGatedShutter -/// Added functionality: shielded voting. +/// @notice Added functionality: shielded voting. /// Dispute kit implementation adapted from DisputeKitClassic /// - a drawing system: proportional to staked PNK with a non-zero balance of `tokenGate` where `tokenGate` is an ERC20, ERC721 or ERC1155 /// - a vote aggregation system: plurality, @@ -72,7 +72,7 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase { _disableInitializers(); } - /// @dev Initializer. + /// @notice Initializer. /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. @@ -100,10 +100,11 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase { // * State Modifiers * // // ************************************* // - /// @dev Sets the caller's commit for the specified votes. It can be called multiple times during the - /// commit period, each call overrides the commits of the previous one. - /// `O(n)` where - /// `n` is the number of votes. + /// @notice Sets the caller's commit for the specified votes. + /// + /// @dev It can be called multiple times during the commit period, each call overrides the commits of the previous one. + /// `O(n)` where `n` is the number of votes. + /// /// @param _coreDisputeID The ID of the dispute in Kleros Core. /// @param _voteIDs The IDs of the votes. /// @param _commit The commitment hash including the justification. @@ -132,6 +133,13 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase { emit CommitCastShutter(_coreDisputeID, msg.sender, _commit, _recoveryCommit, _identity, _encryptedVote); } + /// @notice Version of the `castVote` function designed specifically for Shutter. + /// @dev `O(n)` where `n` is the number of votes. + /// @param _coreDisputeID The ID of the dispute in Kleros Core. + /// @param _voteIDs The IDs of the votes. + /// @param _choice The choice. + /// @param _salt The salt for the commit if the votes were hidden. + /// @param _justification Justification of the choice. function castVoteShutter( uint256 _coreDisputeID, uint256[] calldata _voteIDs, @@ -154,13 +162,11 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase { // * Public Views * // // ************************************* // - /** - * @dev Computes the hash of a vote using ABI encoding - * @param _choice The choice being voted for - * @param _justification The justification for the vote - * @param _salt A random salt for commitment - * @return bytes32 The hash of the encoded vote parameters - */ + /// @notice Computes the hash of a vote using ABI encoding + /// @param _choice The choice being voted for + /// @param _salt A random salt for commitment + /// @param _justification The justification for the vote + /// @return bytes32 The hash of the encoded vote parameters function hashVote( uint256 _choice, uint256 _salt, @@ -180,11 +186,7 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase { // * Internal * // // ************************************* // - /// @dev Returns the expected vote hash for a given vote. - /// @param _localDisputeID The ID of the dispute in the Dispute Kit. - /// @param _localRoundID The ID of the round in the Dispute Kit. - /// @param _voteID The ID of the vote. - /// @return The expected vote hash. + /// @inheritdoc DisputeKitClassicBase function _getExpectedVoteHash( uint256 _localDisputeID, uint256 _localRoundID, @@ -197,13 +199,13 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase { } } - /// @dev Extracts token gating information from the extra data. + /// @notice Extracts token gating information from the extra data. /// @param _extraData The extra data bytes array with the following encoding: - /// - bytes 0-31: uint96 courtID, not used here - /// - bytes 32-63: uint256 minJurors, not used here - /// - bytes 64-95: uint256 disputeKitID, not used here - /// - bytes 96-127: uint256 packedTokenGateAndFlag (address tokenGate in bits 0-159, bool isERC1155 in bit 160) - /// - bytes 128-159: uint256 tokenId + /// - bytes 0-31: uint96 courtID, not used here + /// - bytes 32-63: uint256 minJurors, not used here + /// - bytes 64-95: uint256 disputeKitID, not used here + /// - bytes 96-127: uint256 packedTokenGateAndFlag (address tokenGate in bits 0-159, bool isERC1155 in bit 160) + /// - bytes 128-159: uint256 tokenId /// @return tokenGate The address of the token contract used for gating access. /// @return isERC1155 True if the token is an ERC-1155, false for ERC-20/ERC-721. /// @return tokenId The token ID for ERC-1155 tokens (ignored for ERC-20/ERC-721). diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol b/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol index bbcb28b24..0ea8bdb41 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.28; import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol"; /// @title DisputeKitShutter -/// Added functionality: shielded voting. +/// @notice Added functionality: shielded voting. /// Dispute kit implementation of the Kleros v1 features including: /// - a drawing system: proportional to staked PNK, /// - a vote aggregation system: plurality, @@ -31,7 +31,7 @@ contract DisputeKitShutter is DisputeKitClassicBase { // * Events * // // ************************************* // - /// @dev Emitted when a vote is cast. + /// @notice Emitted when a vote is cast. /// @param _coreDisputeID The identifier of the dispute in the Arbitrator contract. /// @param _juror The address of the juror casting the vote commitment. /// @param _commit The commitment hash. @@ -56,7 +56,7 @@ contract DisputeKitShutter is DisputeKitClassicBase { _disableInitializers(); } - /// @dev Initializer. + /// @notice Initializer. /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. @@ -84,10 +84,11 @@ contract DisputeKitShutter is DisputeKitClassicBase { // * State Modifiers * // // ************************************* // - /// @dev Sets the caller's commit for the specified votes. It can be called multiple times during the - /// commit period, each call overrides the commits of the previous one. - /// `O(n)` where - /// `n` is the number of votes. + /// @notice Sets the caller's commit for the specified votes. + /// + /// @dev It can be called multiple times during the commit period, each call overrides the commits of the previous one. + /// `O(n)` where `n` is the number of votes. + /// /// @param _coreDisputeID The ID of the dispute in Kleros Core. /// @param _voteIDs The IDs of the votes. /// @param _commit The commitment hash including the justification. @@ -116,6 +117,13 @@ contract DisputeKitShutter is DisputeKitClassicBase { emit CommitCastShutter(_coreDisputeID, msg.sender, _commit, _recoveryCommit, _identity, _encryptedVote); } + /// @notice Version of `castVote` function designed specifically for Shutter. + /// @dev `O(n)` where `n` is the number of votes. + /// @param _coreDisputeID The ID of the dispute in Kleros Core. + /// @param _voteIDs The IDs of the votes. + /// @param _choice The choice. + /// @param _salt The salt for the commit if the votes were hidden. + /// @param _justification Justification of the choice. function castVoteShutter( uint256 _coreDisputeID, uint256[] calldata _voteIDs, @@ -138,13 +146,11 @@ contract DisputeKitShutter is DisputeKitClassicBase { // * Public Views * // // ************************************* // - /** - * @dev Computes the hash of a vote using ABI encoding - * @param _choice The choice being voted for - * @param _justification The justification for the vote - * @param _salt A random salt for commitment - * @return bytes32 The hash of the encoded vote parameters - */ + /// @notice Computes the hash of a vote using ABI encoding + /// @param _choice The choice being voted for + /// @param _salt A random salt for commitment + /// @param _justification The justification for the vote + /// @return bytes32 The hash of the encoded vote parameters function hashVote( uint256 _choice, uint256 _salt, @@ -164,7 +170,7 @@ contract DisputeKitShutter is DisputeKitClassicBase { // * Internal * // // ************************************* // - /// @dev Returns the expected vote hash for a given vote. + /// @notice Returns the expected vote hash for a given vote. /// @param _localDisputeID The ID of the dispute in the Dispute Kit. /// @param _localRoundID The ID of the round in the Dispute Kit. /// @param _voteID The ID of the vote. diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol index 10cc67c5c..983fb63df 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol @@ -5,14 +5,14 @@ pragma solidity ^0.8.24; import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol"; interface IProofOfHumanity { - /// @dev Return true if the submission is registered and not expired. + /// @notice Return true if the submission is registered and not expired. /// @param _submissionID The address of the submission. /// @return Whether the submission is registered or not. function isRegistered(address _submissionID) external view returns (bool); } /// @title DisputeKitSybilResistant -/// Dispute kit implementation adapted from DisputeKitClassic +/// @notice Dispute kit implementation adapted from DisputeKitClassic /// - a drawing system: at most 1 vote per juror registered on Proof of Humanity, /// - a vote aggregation system: plurality, /// - an incentive system: equal split between coherent votes, @@ -35,7 +35,7 @@ contract DisputeKitSybilResistant is DisputeKitClassicBase { _disableInitializers(); } - /// @dev Initializer. + /// @notice Initializer. /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _poh The Proof of Humanity registry. diff --git a/contracts/src/arbitration/evidence/EvidenceModule.sol b/contracts/src/arbitration/evidence/EvidenceModule.sol index 8321e55ba..e29e3a6ac 100644 --- a/contracts/src/arbitration/evidence/EvidenceModule.sol +++ b/contracts/src/arbitration/evidence/EvidenceModule.sol @@ -35,7 +35,7 @@ contract EvidenceModule is IEvidence, Initializable, UUPSProxiable { _disableInitializers(); } - /// @dev Initializer. + /// @notice Initializer. /// @param _owner The owner's address. function initialize(address _owner) external initializer { owner = _owner; @@ -57,9 +57,9 @@ contract EvidenceModule is IEvidence, Initializable, UUPSProxiable { // * Function Modifiers * // // ************************************* // - /// @dev Submits evidence for a dispute. - /// @param _externalDisputeID Unique identifier for this dispute outside Kleros. It's the submitter responsability to submit the right evidence group ID. - /// @param _evidence Stringified evidence object, example: '{"name" : "Justification", "description" : "Description", "fileURI" : "/ipfs/QmWQV5ZFFhEJiW8Lm7ay2zLxC2XS4wx1b2W7FfdrLMyQQc"}'. + /// @notice Submits evidence for a dispute. + /// @param _externalDisputeID Unique identifier for this dispute outside Kleros. It's the submitter responsibility to submit the right evidence group ID. + /// @param _evidence Stringified evidence object, example: `{"name" : "Justification", "description" : "Description", "fileURI" : "/ipfs/QmWQV5ZFFhEJiW8Lm7ay2zLxC2XS4wx1b2W7FfdrLMyQQc"}`. function submitEvidence(uint256 _externalDisputeID, string calldata _evidence) external { emit Evidence(_externalDisputeID, msg.sender, _evidence); } diff --git a/contracts/src/arbitration/evidence/ModeratedEvidenceModule.sol b/contracts/src/arbitration/evidence/ModeratedEvidenceModule.sol index b2b64a4d3..2ffd0b30b 100644 --- a/contracts/src/arbitration/evidence/ModeratedEvidenceModule.sol +++ b/contracts/src/arbitration/evidence/ModeratedEvidenceModule.sol @@ -70,9 +70,9 @@ contract ModeratedEvidenceModule is IArbitrableV2 { // * Events * // // ************************************* // - /// @dev To be raised when a moderated evidence is submitted. Should point to the resource (evidences are not to be stored on chain due to gas considerations). + /// @notice To be raised when a moderated evidence is submitted. Should point to the resource (evidences are not to be stored on chain due to gas considerations). /// @param _arbitrator The arbitrator of the contract. - /// @param _externalDisputeID Unique identifier for this dispute outside Kleros. It's the submitter responsability to submit the right evidence group ID. + /// @param _externalDisputeID Unique identifier for this dispute outside Kleros. It's the submitter responsibility to submit the right evidence group ID. /// @param _party The address of the party submiting the evidence. Note that 0x0 refers to evidence not submitted by any party. /// @param _evidence Stringified evidence object, example: '{"name" : "Justification", "description" : "Description", "fileURI" : "/ipfs/QmWQV5ZFFhEJiW8Lm7ay2zLxC2XS4wx1b2W7FfdrLMyQQc"}'. event ModeratedEvidence( @@ -82,7 +82,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { string _evidence ); - /// @dev Indicate that a party has to pay a fee or would otherwise be considered as losing. + /// @notice Indicate that a party has to pay a fee or would otherwise be considered as losing. /// @param _evidenceID The ID of the evidence being moderated. /// @param _currentWinner The party who is currently winning. event ModerationStatusChanged(bytes32 indexed _evidenceID, Party _currentWinner); @@ -91,7 +91,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { // * Constructor * // // ************************************* // - /// @dev Constructor. + /// @notice Constructor. /// @param _arbitrator The trusted arbitrator to resolve potential disputes. /// @param _owner The trusted owner of the contract. /// @param _totalCostMultiplier Multiplier of arbitration fees that must be ultimately paid as fee stake. In basis points. @@ -132,32 +132,32 @@ contract ModeratedEvidenceModule is IArbitrableV2 { // * Governance * // // ************************************* // - /// @dev Change the owner of the contract. + /// @notice Change the owner of the contract. /// @param _owner The address of the new owner. function changeOwner(address _owner) external onlyOwner { owner = _owner; } - /// @dev Change the proportion of arbitration fees that must be paid as fee stake by parties when there is no winner or loser (e.g. when the arbitrator refused to rule). + /// @notice Change the proportion of arbitration fees that must be paid as fee stake by parties when there is no winner or loser (e.g. when the arbitrator refused to rule). /// @param _initialDepositMultiplier Multiplier of arbitration fees that must be paid as fee stake. In basis points. function changeInitialDepositMultiplier(uint256 _initialDepositMultiplier) external onlyOwner { initialDepositMultiplier = _initialDepositMultiplier; } - /// @dev Change the proportion of arbitration fees that must be paid as fee stake by the winner of the previous round. + /// @notice Change the proportion of arbitration fees that must be paid as fee stake by the winner of the previous round. /// @param _totalCostMultiplier Multiplier of arbitration fees that must be paid as fee stake. In basis points. function changeTotalCostMultiplier(uint256 _totalCostMultiplier) external onlyOwner { totalCostMultiplier = _totalCostMultiplier; } - /// @dev Change the time window within which evidence submissions and removals can be contested. + /// @notice Change the time window within which evidence submissions and removals can be contested. /// Ongoing moderations will start using the latest bondTimeout available after calling moderate() again. /// @param _bondTimeout Multiplier of arbitration fees that must be paid as fee stake. In basis points. function changeBondTimeout(uint256 _bondTimeout) external onlyOwner { bondTimeout = _bondTimeout; } - /// @dev Update the dispute template data. + /// @notice Update the dispute template data. /// @param _templateData The new dispute template data. function changeDisputeTemplate( string calldata _templateData, @@ -173,7 +173,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { ); } - /// @dev Change the arbitrator to be used for disputes that may be raised in the next requests. The arbitrator is trusted to support appeal period and not reenter. + /// @notice Change the arbitrator to be used for disputes that may be raised in the next requests. The arbitrator is trusted to support appeal period and not reenter. /// @param _arbitratorExtraData The extra data used by the new arbitrator. function changeArbitratorExtraData(bytes calldata _arbitratorExtraData) external onlyOwner { ArbitratorData storage arbitratorData = arbitratorDataList[arbitratorDataList.length - 1]; @@ -189,8 +189,8 @@ contract ModeratedEvidenceModule is IArbitrableV2 { // * State Modifiers * // // ************************************* // - /// @dev Submits evidence. - /// @param _evidenceGroupID Unique identifier of the evidence group the evidence belongs to. It's the submitter responsability to submit the right evidence group ID. + /// @notice Submits evidence. + /// @param _evidenceGroupID Unique identifier of the evidence group the evidence belongs to. It's the submitter responsibility to submit the right evidence group ID. /// @param _evidence Stringified evidence object, example: '{"name" : "Justification", "description" : "Description", "fileURI" : "/ipfs/QmWQV5ZFFhEJiW8Lm7ay2zLxC2XS4wx1b2W7FfdrLMyQQc"}'. function submitEvidence(uint256 _evidenceGroupID, string calldata _evidence) external payable { // Optimization opportunity: map evidenceID to an incremental index that can be safely assumed to be less than a small uint. @@ -217,7 +217,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { emit ModeratedEvidence(arbitrator, _evidenceGroupID, msg.sender, _evidence); } - /// @dev Moderates an evidence submission. Requires the contester to at least double the accumulated stake of the oposing party. + /// @notice Moderates an evidence submission. Requires the contester to at least double the accumulated stake of the oposing party. /// Optimization opportunity: use `bytes calldata args` and compress _evidenceID and _side (only for optimistic rollups). /// @param _evidenceID Unique identifier of the evidence submission. /// @param _side The side to contribute to. @@ -269,8 +269,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { arbitrator, evidenceData.disputeID, uint256(_evidenceID), - arbitratorData.disputeTemplateId, - "" + arbitratorData.disputeTemplateId ); evidenceData.disputed = true; moderation.bondDeadline = 0; @@ -282,7 +281,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { emit ModerationStatusChanged(_evidenceID, moderation.currentWinner); } - /// @dev Resolves a moderation event once the timeout has passed. + /// @notice Resolves a moderation event once the timeout has passed. /// @param _evidenceID Unique identifier of the evidence submission. function resolveModerationMarket(bytes32 _evidenceID) external { // Moderation market resolutions are not final. @@ -298,7 +297,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { evidenceData.ruling = moderation.currentWinner; } - /// @dev Make a fee contribution. + /// @notice Make a fee contribution. /// @param _moderation The moderation to contribute to. /// @param _side The side to contribute to. /// @param _contributor The contributor. @@ -327,7 +326,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { return contribution; } - /// @dev Returns the contribution value and remainder from available ETH and required amount. + /// @notice Returns the contribution value and remainder from available ETH and required amount. /// @param _available The amount of ETH available for the contribution. /// @param _requiredAmount The amount of ETH required for the contribution. /// @return taken The amount of ETH taken. @@ -342,9 +341,11 @@ contract ModeratedEvidenceModule is IArbitrableV2 { return (_requiredAmount, remainder); } - /// @dev Withdraws contributions of moderations. Reimburses contributions if the appeal was not fully funded. - /// If the appeal was fully funded, sends the fee stake rewards and reimbursements proportional to the contributions made to the winner of a dispute. + /// @notice Withdraws contributions of moderations. Reimburses contributions if the appeal was not fully funded. + /// + /// @dev If the appeal was fully funded, sends the fee stake rewards and reimbursements proportional to the contributions made to the winner of a dispute. /// Optimization opportunity: use `bytes calldata args` and compress _evidenceID and _moderationID (only for optimistic rollups). + /// /// @param _beneficiary The address that made contributions. /// @param _evidenceID The ID of the associated evidence submission. /// @param _moderationID The ID of the moderatino occurence. @@ -379,10 +380,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { _beneficiary.send(reward); // It is the user responsibility to accept ETH. } - /// @dev Give a ruling for a dispute. Must be called by the arbitrator to enforce the final ruling. - /// The purpose of this function is to ensure that the address calling it has the right to rule on the contract. - /// @param _disputeID ID of the dispute in the Arbitrator contract. - /// @param _ruling Ruling given by the arbitrator. Note that 0 is reserved for "Not able/wanting to make a decision". + /// @inheritdoc IArbitrableV2 function rule(uint256 _disputeID, uint256 _ruling) public override { bytes32 evidenceID = disputeIDtoEvidenceID[_disputeID]; EvidenceData storage evidenceData = evidences[evidenceID]; @@ -405,7 +403,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { // * Public Views * // // ************************************* // - /// @dev Gets the number of moderation events of the specific evidence submission. + /// @notice Gets the number of moderation events of the specific evidence submission. /// @param _evidenceID The ID of the evidence submission. /// @return The number of moderations. function getNumberOfModerations(bytes32 _evidenceID) external view returns (uint256) { @@ -413,7 +411,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { return evidenceData.moderations.length; } - /// @dev Gets the contributions made by a party for a given moderation. + /// @notice Gets the contributions made by a party for a given moderation. /// @param _evidenceID The ID of the evidence submission. /// @param _moderationID The ID of the moderation occurence. /// @param _contributor The address of the contributor. @@ -428,7 +426,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { contributions = moderation.contributions[_contributor]; } - /// @dev Gets the information of a moderation event. + /// @notice Gets the information of a moderation event. /// @param _evidenceID The ID of the evidence submission. /// @param _moderationID The ID of the moderation occurence. /// @return paidFees currentWinner feeRewards The moderation information. @@ -441,7 +439,7 @@ contract ModeratedEvidenceModule is IArbitrableV2 { return (moderation.paidFees, moderation.currentWinner, moderation.feeRewards); } - /// @dev Gets the last arbitrator data index, which is used for current new submissions. + /// @notice Gets the last arbitrator data index, which is used for current new submissions. /// @return The last arbitrator data index. function getCurrentArbitratorIndex() external view returns (uint256) { return arbitratorDataList.length - 1; diff --git a/contracts/src/arbitration/interfaces/IArbitrableV2.sol b/contracts/src/arbitration/interfaces/IArbitrableV2.sol index 22dac6e4a..b371aa867 100644 --- a/contracts/src/arbitration/interfaces/IArbitrableV2.sol +++ b/contracts/src/arbitration/interfaces/IArbitrableV2.sol @@ -8,33 +8,41 @@ import "./IArbitratorV2.sol"; /// @notice Arbitrable interface. /// @dev When developing arbitrable contracts, we need to: /// - Define the action taken when a ruling is received by the contract. -/// - Allow dispute creation. For this a function must call arbitrator.createDispute{value: _fee}(_choices,_extraData); +/// - Allow dispute creation which calls `arbitrator.createDispute{value: _fee}(_choices,_extraData)`. interface IArbitrableV2 { - /// @dev To be emitted when a dispute is created to link the correct meta-evidence to the disputeID. + // ************************************* // + // * Events * // + // ************************************* // + + /// @notice To be emitted when a dispute is created to link the correct template to the disputeID. /// @param _arbitrator The arbitrator of the contract. /// @param _arbitratorDisputeID The identifier of the dispute in the Arbitrator contract. /// @param _externalDisputeID An identifier created outside Kleros by the protocol requesting arbitration. - /// @param _templateId The identifier of the dispute template. Should not be used with _templateUri. - /// @param _templateUri The URI to the dispute template. For example on IPFS: starting with '/ipfs/'. Should not be used with _templateId. + /// @param _templateId The identifier of the dispute template. event DisputeRequest( IArbitratorV2 indexed _arbitrator, uint256 indexed _arbitratorDisputeID, uint256 _externalDisputeID, - uint256 _templateId, - string _templateUri + uint256 _templateId ); - /// @dev To be raised when a ruling is given. + /// @notice To be raised when a ruling is given. /// @param _arbitrator The arbitrator giving the ruling. /// @param _disputeID The identifier of the dispute in the Arbitrator contract. /// @param _ruling The ruling which was given. event Ruling(IArbitratorV2 indexed _arbitrator, uint256 indexed _disputeID, uint256 _ruling); - /// @dev Give a ruling for a dispute. - /// Must be called by the arbitrator. - /// The purpose of this function is to ensure that the address calling it has the right to rule on the contract. + // ************************************* // + // * State Modifiers * // + // ************************************* // + + /// @notice Give a ruling for a dispute. + /// + /// @dev This is a callback function for the arbitrator to provide the ruling to this contract. + /// Only the arbitrator must be allowed to call this function. + /// Ruling 0 is reserved for "Not able/wanting to make a decision". + /// /// @param _disputeID The identifier of the dispute in the Arbitrator contract. /// @param _ruling Ruling given by the arbitrator. - /// Note that 0 is reserved for "Not able/wanting to make a decision". function rule(uint256 _disputeID, uint256 _ruling) external; } diff --git a/contracts/src/arbitration/interfaces/IArbitratorV2.sol b/contracts/src/arbitration/interfaces/IArbitratorV2.sol index 9559c81b3..125baab92 100644 --- a/contracts/src/arbitration/interfaces/IArbitratorV2.sol +++ b/contracts/src/arbitration/interfaces/IArbitratorV2.sol @@ -6,38 +6,41 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./IArbitrableV2.sol"; /// @title Arbitrator -/// Arbitrator interface that implements the new arbitration standard. -/// Unlike the ERC-792 this standard is not concerned with appeals, so each arbitrator can implement an appeal system that suits it the most. -/// When developing arbitrator contracts we need to: -/// - Define the functions for dispute creation (createDispute). Don't forget to store the arbitrated contract and the disputeID (which should be unique, may nbDisputes). -/// - Define the functions for cost display (arbitrationCost). -/// - Allow giving rulings. For this a function must call arbitrable.rule(disputeID, ruling). +/// @notice Arbitrator interface for the Kleros V2 protocol. +/// @dev Unlike the ERC-792 this standard is not concerned with appeals, so each arbitrator can implement an appeal system that suits it the most. interface IArbitratorV2 { - /// @dev To be emitted when a dispute is created. + // ************************************* // + // * Events * // + // ************************************* // + + /// @notice To be emitted when a dispute is created. /// @param _disputeID The identifier of the dispute in the Arbitrator contract. /// @param _arbitrable The contract which created the dispute. event DisputeCreation(uint256 indexed _disputeID, IArbitrableV2 indexed _arbitrable); - /// @dev To be raised when a ruling is given. + /// @notice To be raised when a ruling is given. /// @param _arbitrable The arbitrable receiving the ruling. /// @param _disputeID The identifier of the dispute in the Arbitrator contract. /// @param _ruling The ruling which was given. event Ruling(IArbitrableV2 indexed _arbitrable, uint256 indexed _disputeID, uint256 _ruling); - /// @dev To be emitted when an ERC20 token is added or removed as a method to pay fees. + /// @notice To be emitted when an ERC20 token is added or removed as a method to pay fees. /// @param _token The ERC20 token. /// @param _accepted Whether the token is accepted or not. event AcceptedFeeToken(IERC20 indexed _token, bool indexed _accepted); - /// @dev To be emitted when the fee for a particular ERC20 token is updated. + /// @notice To be emitted when the fee for a particular ERC20 token is updated. /// @param _feeToken The ERC20 token. /// @param _rateInEth The new rate of the fee token in ETH. /// @param _rateDecimals The new decimals of the fee token rate. event NewCurrencyRate(IERC20 indexed _feeToken, uint64 _rateInEth, uint8 _rateDecimals); - /// @dev Create a dispute and pay for the fees in the native currency, typically ETH. - /// Must be called by the arbitrable contract. - /// Must pay at least arbitrationCost(_extraData). + // ************************************* // + // * State Modifiers * // + // ************************************* // + + /// @notice Create a dispute and pay for the fees in the native currency, typically ETH. + /// @dev Must be called by the arbitrable contract and pay at least `arbitrationCost(_extraData)` in ETH. /// @param _numberOfChoices The number of choices the arbitrator can choose from in this dispute. /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes). /// @return disputeID The identifier of the dispute created. @@ -46,9 +49,8 @@ interface IArbitratorV2 { bytes calldata _extraData ) external payable returns (uint256 disputeID); - /// @dev Create a dispute and pay for the fees in a supported ERC20 token. - /// Must be called by the arbitrable contract. - /// Must pay at least arbitrationCost(_extraData). + /// @notice Create a dispute and pay for the fees in a supported ERC20 token. + /// @dev Must be called by the arbitrable contract and pay at least `arbitrationCost(_extraData)` in the supported ERC20 token. /// @param _numberOfChoices The number of choices the arbitrator can choose from in this dispute. /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes). /// @param _feeToken The ERC20 token used to pay fees. @@ -61,20 +63,24 @@ interface IArbitratorV2 { uint256 _feeAmount ) external returns (uint256 disputeID); - /// @dev Compute the cost of arbitration denominated in the native currency, typically ETH. - /// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation. + // ************************************* // + // * Public Views * // + // ************************************* // + + /// @notice Compute the cost of arbitration denominated in the native currency, typically ETH. + /// @dev It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation. /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes). /// @return cost The arbitration cost in ETH. function arbitrationCost(bytes calldata _extraData) external view returns (uint256 cost); - /// @dev Compute the cost of arbitration denominated in `_feeToken`. - /// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation. + /// @notice Compute the cost of arbitration denominated in `_feeToken`. + /// @dev It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation. /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes). /// @param _feeToken The ERC20 token used to pay fees. /// @return cost The arbitration cost in `_feeToken`. function arbitrationCost(bytes calldata _extraData, IERC20 _feeToken) external view returns (uint256 cost); - /// @dev Gets the current ruling of a specified dispute. + /// @notice Gets the current ruling of a specified dispute. /// @param _disputeID The ID of the dispute. /// @return ruling The current ruling. /// @return tied Whether it's a tie or not. diff --git a/contracts/src/arbitration/interfaces/IDisputeKit.sol b/contracts/src/arbitration/interfaces/IDisputeKit.sol index 90fbbd727..20e42a0d2 100644 --- a/contracts/src/arbitration/interfaces/IDisputeKit.sol +++ b/contracts/src/arbitration/interfaces/IDisputeKit.sol @@ -5,14 +5,14 @@ pragma solidity >=0.8.0 <0.9.0; import "./IArbitratorV2.sol"; /// @title IDisputeKit -/// An abstraction of the Dispute Kits intended for interfacing with KlerosCore. -/// It does not intend to abstract the interactions with the user (such as voting or appeal funding) to allow for implementation-specific parameters. +/// @notice An abstraction of the Dispute Kits intended for interfacing with KlerosCore. +/// @dev It does not intend to abstract the interactions with the user (such as voting or appeal funding) to allow for implementation-specific parameters. interface IDisputeKit { // ************************************ // // * Events * // // ************************************ // - /// @dev Emitted when casting a vote to provide the justification of juror's choice. + /// @notice Emitted when casting a vote to provide the justification of juror's choice. /// @param _coreDisputeID The identifier of the dispute in the Arbitrator contract. /// @param _juror Address of the juror. /// @param _voteIDs The identifiers of the votes in the dispute. @@ -30,12 +30,12 @@ interface IDisputeKit { // * State Modifiers * // // ************************************* // - /// @dev Creates a local dispute and maps it to the dispute ID in the Core contract. - /// Note: Access restricted to Kleros Core only. + /// @notice Creates a local dispute and maps it to the dispute ID in the Core contract. + /// @dev Access restricted to Kleros Core only. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @param _numberOfChoices Number of choices of the dispute /// @param _extraData Additional info about the dispute, for possible use in future dispute kits. - /// @param _nbVotes Maximal number of votes this dispute can get. DEPRECATED as we don't need to pass it now. KC handles the count. + /// @param _nbVotes Maximal number of votes this dispute can get. Added for future-proofing. function createDispute( uint256 _coreDisputeID, uint256 _numberOfChoices, @@ -43,8 +43,8 @@ interface IDisputeKit { uint256 _nbVotes ) external; - /// @dev Draws the juror from the sortition tree. The drawn address is picked up by Kleros Core. - /// Note: Access restricted to Kleros Core only. + /// @notice Draws the juror from the sortition tree. The drawn address is picked up by Kleros Core. + /// @dev Access restricted to Kleros Core only. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @param _nonce Nonce. /// @return drawnAddress The drawn address. @@ -57,14 +57,15 @@ interface IDisputeKit { // * Public Views * // // ************************************* // - /// @dev Gets the current ruling of a specified dispute. + /// @notice Gets the current ruling of a specified dispute. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @return ruling The current ruling. /// @return tied Whether it's a tie or not. /// @return overridden Whether the ruling was overridden by appeal funding or not. function currentRuling(uint256 _coreDisputeID) external view returns (uint256 ruling, bool tied, bool overridden); - /// @dev Gets the degree of coherence of a particular voter. This function is called by Kleros Core in order to determine the amount of the reward. + /// @notice Gets the degree of coherence of a particular voter. + /// @dev This function is called by Kleros Core in order to determine the amount of the reward. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. /// @param _voteID The ID of the vote. @@ -80,7 +81,8 @@ interface IDisputeKit { uint256 _pnkAtStakePerJuror ) external view returns (uint256 pnkCoherence, uint256 feeCoherence); - /// @dev Gets the degree of coherence of a particular voter. This function is called by Kleros Core in order to determine the amount of the penalty. + /// @notice Gets the degree of coherence of a particular voter. + /// @dev This function is called by Kleros Core in order to determine the amount of the penalty. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. /// @param _voteID The ID of the vote. @@ -95,23 +97,25 @@ interface IDisputeKit { uint256 _pnkAtStakePerJuror ) external view returns (uint256 pnkCoherence); - /// @dev Gets the number of jurors who are eligible to a reward in this round. + /// @notice Gets the number of jurors who are eligible to a reward in this round. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. /// @return The number of coherent jurors. function getCoherentCount(uint256 _coreDisputeID, uint256 _coreRoundID) external view returns (uint256); - /// @dev Returns true if all of the jurors have cast their commits for the last round. + /// @notice Returns true if all of the jurors have cast their commits for the last round. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @return Whether all of the jurors have cast their commits for the last round. function areCommitsAllCast(uint256 _coreDisputeID) external view returns (bool); - /// @dev Returns true if all of the jurors have cast their votes for the last round. + /// @notice Returns true if all of the jurors have cast their votes for the last round. + /// @dev This function is to be called directly by the core contract and is not for off-chain usage. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @return Whether all of the jurors have cast their votes for the last round. function areVotesAllCast(uint256 _coreDisputeID) external view returns (bool); - /// @dev Returns true if the appeal funding is finished prematurely (e.g. when losing side didn't fund). + /// @notice Returns true if the appeal funding is finished prematurely (e.g. when losing side didn't fund). + /// @dev This function is to be called directly by the core contract and is not for off-chain usage. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @return Whether the appeal funding is finished. function isAppealFunded(uint256 _coreDisputeID) external view returns (bool); @@ -121,26 +125,36 @@ interface IDisputeKit { /// @return Whether the dispute is jumping to a parent court or not. function earlyCourtJump(uint256 _coreDisputeID) external view returns (bool); - /// @dev Returns the number of votes after the appeal. + /// @notice Returns the number of votes after the appeal. /// @param _previousDisputeKit The previous Dispute Kit. /// @param _currentNbVotes The number of votes before the appeal. /// @return The number of votes after the appeal. function getNbVotesAfterAppeal( IDisputeKit _previousDisputeKit, uint256 _currentNbVotes - ) external view returns (uint256); // TODO: remove previousDisputeKit + ) external view returns (uint256); - /// @dev Returns the dispute kid ID be used after court jump by Kleros Core. + /// @notice Returns the dispute kit ID to be used after court jump by Kleros Core. /// @return The ID of the dispute kit in Kleros Core disputeKits array. function getJumpDisputeKitID() external view returns (uint256); - /// @dev Returns true if the specified voter was active in this round. + /// @notice Returns true if the specified voter was active in this round. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. /// @param _voteID The ID of the voter. /// @return Whether the voter was active or not. function isVoteActive(uint256 _coreDisputeID, uint256 _coreRoundID, uint256 _voteID) external view returns (bool); + /// @notice Returns the info of the specified round in the core contract. + /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. + /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. + /// @param _choice The choice to query. + /// @return winningChoice The winning choice of this round. + /// @return tied Whether it's a tie or not. + /// @return totalVoted Number of jurors who cast the vote already. + /// @return totalCommited Number of jurors who cast the commit already (only relevant for hidden votes). + /// @return nbVoters Total number of voters in this round. + /// @return choiceCount Number of votes cast for the queried choice. function getRoundInfo( uint256 _coreDisputeID, uint256 _coreRoundID, @@ -157,6 +171,14 @@ interface IDisputeKit { uint256 choiceCount ); + /// @notice Returns the vote information for a given vote ID. + /// @param _coreDisputeID The ID of the dispute in Kleros Core. + /// @param _coreRoundID The ID of the round in Kleros Core. + /// @param _voteID The ID of the vote. + /// @return account The address of the juror who cast the vote. + /// @return commit The commit of the vote. + /// @return choice The choice that got the vote. + /// @return voted Whether the vote was cast or not. function getVoteInfo( uint256 _coreDisputeID, uint256 _coreRoundID, diff --git a/contracts/src/arbitration/interfaces/IDisputeTemplateRegistry.sol b/contracts/src/arbitration/interfaces/IDisputeTemplateRegistry.sol index 23e09f8d5..cfa207448 100644 --- a/contracts/src/arbitration/interfaces/IDisputeTemplateRegistry.sol +++ b/contracts/src/arbitration/interfaces/IDisputeTemplateRegistry.sol @@ -5,7 +5,11 @@ pragma solidity >=0.8.0 <0.9.0; /// @title IDisputeTemplate /// @notice Dispute Template interface. interface IDisputeTemplateRegistry { - /// @dev To be emitted when a new dispute template is created. + // ************************************* // + // * Events * // + // ************************************* // + + /// @notice To be emitted when a new dispute template is created. /// @param _templateId The identifier of the dispute template. /// @param _templateTag An optional tag for the dispute template, such as "registration" or "removal". /// @param _templateData The template data. @@ -17,6 +21,15 @@ interface IDisputeTemplateRegistry { string _templateDataMappings ); + // ************************************* // + // * State Modifiers * // + // ************************************* // + + /// @notice Registers a new dispute template. + /// @param _templateTag An optional tag for the dispute template, such as "registration" or "removal". + /// @param _templateData The template data. + /// @param _templateDataMappings The data mappings for the template. + /// @return templateId The identifier of the dispute template. function setDisputeTemplate( string memory _templateTag, string memory _templateData, diff --git a/contracts/src/arbitration/interfaces/IEvidence.sol b/contracts/src/arbitration/interfaces/IEvidence.sol index a7683186a..4b6477ff2 100644 --- a/contracts/src/arbitration/interfaces/IEvidence.sol +++ b/contracts/src/arbitration/interfaces/IEvidence.sol @@ -4,8 +4,8 @@ pragma solidity >=0.8.0 <0.9.0; /// @title IEvidence interface IEvidence { - /// @dev To be raised when evidence is submitted. Should point to the resource (evidences are not to be stored on chain due to gas considerations). - /// @param _externalDisputeID Unique identifier for this dispute outside Kleros. It's the submitter responsability to submit the right external dispute ID. + /// @notice To be raised when evidence is submitted. Should point to the resource (evidences are not to be stored on chain due to gas considerations). + /// @param _externalDisputeID Unique identifier for this dispute outside Kleros. It's the submitter responsibility to submit the right external dispute ID. /// @param _party The address of the party submiting the evidence. Note that 0x0 refers to evidence not submitted by any party. /// @param _evidence Stringified evidence object, example: '{"name" : "Justification", "description" : "Description", "fileURI" : "/ipfs/QmWQV5ZFFhEJiW8Lm7ay2zLxC2XS4wx1b2W7FfdrLMyQQc"}'. event Evidence(uint256 indexed _externalDisputeID, address indexed _party, string _evidence); diff --git a/contracts/src/arbitration/interfaces/ISortitionModule.sol b/contracts/src/arbitration/interfaces/ISortitionModule.sol index d9fe7e485..0803666ff 100644 --- a/contracts/src/arbitration/interfaces/ISortitionModule.sol +++ b/contracts/src/arbitration/interfaces/ISortitionModule.sol @@ -4,17 +4,52 @@ pragma solidity >=0.8.0 <0.9.0; import "../../libraries/Constants.sol"; +/// @title ISortitionModule +/// @notice Interface for the SortitionModule contract. interface ISortitionModule { + // ************************************* // + // * Enums * // + // ************************************* // + enum Phase { staking, // Stake sum trees can be updated. Pass after `minStakingTime` passes and there is at least one dispute without jurors. generating, // Waiting for a random number. Pass as soon as it is ready. drawing // Jurors can be drawn. Pass after all disputes have jurors or `maxDrawingTime` passes. } + // ************************************* // + // * Events * // + // ************************************* // + + /// @notice Emitted when the phase is changed. + /// @param _phase The new phase. event NewPhase(Phase _phase); + // ************************************* // + // * State Modifiers * // + // ************************************* // + + /// @notice Passes the phase. + function passPhase() external; + + /// @notice Executes the next delayed stakes. + /// @param _iterations The number of delayed stakes to execute. + function executeDelayedStakes(uint256 _iterations) external; + + /// @notice Create a sortition sum tree at the specified key. + /// @param _courtID The ID of the court. + /// @param _extraData Extra data that contains the number of children each node in the tree should have. function createTree(uint96 _courtID, bytes memory _extraData) external; + /// @notice Validate the specified juror's new stake for a court. + /// @dev No state changes should be made when returning stakingResult != Successful, otherwise delayed stakes might break invariants. + /// @param _account The address of the juror. + /// @param _courtID The ID of the court. + /// @param _newStake The new stake. + /// @param _noDelay True if the stake change should not be delayed. + /// @return pnkDeposit The amount of PNK to be deposited. + /// @return pnkWithdrawal The amount of PNK to be withdrawn. + /// @return stakingResult The result of the staking operation. function validateStake( address _account, uint96 _courtID, @@ -22,6 +57,19 @@ interface ISortitionModule { bool _noDelay ) external returns (uint256 pnkDeposit, uint256 pnkWithdrawal, StakingResult stakingResult); + /// @notice Update the state of the stakes, called by KC at the end of setStake flow. + /// + /// @dev `O(n + p * log_k(j))` where + /// `n` is the number of courts the juror has staked in, + /// `p` is the depth of the court tree, + /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, + /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. + /// + /// @param _account The address of the juror. + /// @param _courtID The ID of the court. + /// @param _pnkDeposit The amount of PNK to be deposited. + /// @param _pnkWithdrawal The amount of PNK to be withdrawn. + /// @param _newStake The new stake. function setStake( address _account, uint96 _courtID, @@ -30,44 +78,139 @@ interface ISortitionModule { uint256 _newStake ) external; + /// @notice Update the state of the stakes with a PNK reward deposit, called by KC during rewards execution. + /// + /// @dev `O(n + p * log_k(j))` where + /// `n` is the number of courts the juror has staked in, + /// `p` is the depth of the court tree, + /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, + /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. + /// + /// @param _account The address of the juror. + /// @param _courtID The ID of the court. + /// @param _penalty The amount of PNK to be deducted. + /// @return pnkBalance The updated total PNK balance of the juror, including the penalty. + /// @return newCourtStake The updated stake of the juror in the court. + /// @return availablePenalty The amount of PNK that was actually deducted. function setStakePenalty( address _account, uint96 _courtID, uint256 _penalty ) external returns (uint256 pnkBalance, uint256 newCourtStake, uint256 availablePenalty); + /// @notice Update the state of the stakes with a PNK reward deposit, called by KC during rewards execution. + /// + /// @dev `O(n + p * log_k(j))` where + /// `O(n + p * log_k(j))` where + /// `n` is the number of courts the juror has staked in, + /// `p` is the depth of the court tree, + /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, + /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. + /// + /// @param _account The address of the juror. + /// @param _courtID The ID of the court. + /// @param _reward The amount of PNK to be deposited as a reward. + /// @return success True if the reward was added successfully. function setStakeReward(address _account, uint96 _courtID, uint256 _reward) external returns (bool success); + /// @notice Unstakes the inactive juror from all courts. + /// + /// @dev `O(n * (p * log_k(j)) )` where + /// `O(n * (p * log_k(j)) )` where + /// `n` is the number of courts the juror has staked in, + /// `p` is the depth of the court tree, + /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, + /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. + /// + /// @param _account The juror to unstake. function forcedUnstakeAllCourts(address _account) external; + /// @notice Unstakes the inactive juror from a specific court. + /// + /// @dev `O(n * (p * log_k(j)) )` where + /// `n` is the number of courts the juror has staked in, + /// `p` is the depth of the court tree, + /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, + /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. + /// + /// @param _account The juror to unstake. + /// @param _courtID The ID of the court. function forcedUnstake(address _account, uint96 _courtID) external; + /// @notice Locks the tokens of the drawn juror. + /// @param _account The address of the juror. + /// @param _relativeAmount The amount to lock. function lockStake(address _account, uint256 _relativeAmount) external; + /// @notice Unlocks the tokens of the drawn juror. + /// @param _account The address of the juror. + /// @param _relativeAmount The amount to unlock. function unlockStake(address _account, uint256 _relativeAmount) external; - function notifyRandomNumber(uint256 _drawnNumber) external; + /// @notice Triggers the state changes after dispute creation. + /// @param _disputeID The ID of the dispute. + /// @param _roundID The ID of the round. + function createDisputeHook(uint256 _disputeID, uint256 _roundID) external; + + /// @notice Triggers the state changes after drawing. + /// @param _disputeID The ID of the dispute. + /// @param _roundID The ID of the round. + function postDrawHook(uint256 _disputeID, uint256 _roundID) external; + + /// @notice Gives back the locked PNKs in case the juror fully unstaked earlier. + /// + /// @dev that since locked and staked PNK are async it is possible for the juror to have positive staked PNK balance + /// while having 0 stake in courts and 0 locked tokens (eg. when the juror fully unstaked during dispute and later got his tokens unlocked). + /// In this case the juror can use this function to withdraw the leftover tokens. + /// Also note that if the juror has some leftover PNK while not fully unstaked he'll have to manually unstake from all courts to trigger this function. + /// + /// @param _account The juror whose PNK to withdraw. + function withdrawLeftoverPNK(address _account) external; + // ************************************* // + // * Public Views * // + // ************************************* // + + /// @notice Draw an ID from a tree using a number. + /// + /// @dev that this function reverts if the sum of all values in the tree is 0. + /// `O(k * log_k(n))` where + /// `k` is the maximum number of children per node in the tree, + /// and `n` is the maximum number of nodes ever appended. + /// + /// @param _courtID The ID of the court. + /// @param _coreDisputeID Index of the dispute in Kleros Core. + /// @param _nonce Nonce to hash with random number. + /// @return drawnAddress The drawn address. function draw( uint96 _courtID, uint256 _coreDisputeID, uint256 _nonce ) external view returns (address drawnAddress, uint96 fromSubcourtID); + /// @notice Gets the balance of a juror in a court. + /// @param _juror The address of the juror. + /// @param _courtID The ID of the court. + /// @return totalStakedPnk The total amount of tokens staked including locked tokens and penalty deductions. Equivalent to the effective stake in the General court. + /// @return totalLocked The total amount of tokens locked in disputes. + /// @return stakedInCourt The amount of tokens staked in the specified court including locked tokens and penalty deductions. + /// @return nbCourts The number of courts the juror has directly staked in. function getJurorBalance( address _juror, uint96 _courtID - ) external view returns (uint256 totalStaked, uint256 totalLocked, uint256 stakedInCourt, uint256 nbCourts); + ) external view returns (uint256 totalStakedPnk, uint256 totalLocked, uint256 stakedInCourt, uint256 nbCourts); + /// @notice Gets the court identifiers where a specific `_juror` has staked. + /// @param _juror The address of the juror. function getJurorCourtIDs(address _juror) external view returns (uint96[] memory); + /// @notice Checks if the juror is staked in any court. + /// @param _juror The address of the juror. + /// @return Whether the juror is staked or not. function isJurorStaked(address _juror) external view returns (bool); + /// @notice Checks if the juror has any leftover PNK in the contract. + /// @param _juror The address of the juror. + /// @return Whether the juror has leftover PNK. function getJurorLeftoverPNK(address _juror) external view returns (uint256); - - function createDisputeHook(uint256 _disputeID, uint256 _roundID) external; - - function postDrawHook(uint256 _disputeID, uint256 _roundID) external; - - function withdrawLeftoverPNK(address _account) external; } diff --git a/contracts/src/arbitration/university/KlerosCoreUniversity.sol b/contracts/src/arbitration/university/KlerosCoreUniversity.sol index afe7fc4d4..2a29a54be 100644 --- a/contracts/src/arbitration/university/KlerosCoreUniversity.sol +++ b/contracts/src/arbitration/university/KlerosCoreUniversity.sol @@ -11,7 +11,7 @@ import {Initializable} from "../../proxy/Initializable.sol"; import "../../libraries/Constants.sol"; /// @title KlerosCoreUniversity -/// Core arbitrator contract for educational purposes. +/// @notice Core arbitrator contract for educational purposes. contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { using SafeERC20 for IERC20; @@ -39,7 +39,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { 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 { @@ -49,6 +49,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { bool ruled; // True if the ruling has been executed, false otherwise. uint256 lastPeriodChange; // The last time the period was changed. Round[] rounds; + uint256[10] __gap; // Reserved slots for future upgrades. } struct Round { @@ -64,6 +65,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { 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 @@ -146,16 +148,17 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { 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 ); event LeftoverRewardSent( uint256 indexed _disputeID, uint256 indexed _roundID, - uint256 _pnkAmount, - uint256 _feeAmount, + uint256 _amountPnk, + uint256 _amountFee, IERC20 _feeToken ); @@ -187,7 +190,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { _disableInitializers(); } - /// @dev Initializer (constructor equivalent for upgradable contracts). + /// @notice Initializer (constructor equivalent for upgradable contracts). /// @param _owner The owner's address. /// @param _instructor The address of the instructor. /// @param _pinakion The address of the token contract. @@ -262,7 +265,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { // NOP } - /// @dev Allows the owner to call anything on behalf of the contract. + /// @notice Allows the owner to call anything on behalf of the contract. /// @param _destination The destination of the call. /// @param _amount The value sent with the call. /// @param _data The data sent with the call. @@ -271,38 +274,38 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { if (!success) revert UnsuccessfulCall(); } - /// @dev Changes the `owner` storage variable. + /// @notice Changes the `owner` storage variable. /// @param _owner The new value for the `owner` storage variable. function changeOwner(address payable _owner) external onlyByOwner { owner = _owner; } - /// @dev Changes the `instructor` storage variable. + /// @notice Changes the `instructor` storage variable. /// @param _instructor The new value for the `instructor` storage variable. function changeInstructor(address _instructor) external onlyByOwnerOrInstructor { instructor = _instructor; } - /// @dev Changes the `pinakion` storage variable. + /// @notice Changes the `pinakion` storage variable. /// @param _pinakion The new value for the `pinakion` storage variable. function changePinakion(IERC20 _pinakion) external onlyByOwner { pinakion = _pinakion; } - /// @dev Changes the `jurorProsecutionModule` storage variable. + /// @notice Changes the `jurorProsecutionModule` storage variable. /// @param _jurorProsecutionModule The new value for the `jurorProsecutionModule` storage variable. function changeJurorProsecutionModule(address _jurorProsecutionModule) external onlyByOwner { jurorProsecutionModule = _jurorProsecutionModule; } - /// @dev Changes the `_sortitionModule` storage variable. + /// @notice Changes the `_sortitionModule` storage variable. /// Note that the new module should be initialized for all courts. /// @param _sortitionModule The new value for the `sortitionModule` storage variable. function changeSortitionModule(ISortitionModuleUniversity _sortitionModule) external onlyByOwner { sortitionModule = _sortitionModule; } - /// @dev Add a new supported dispute kit module to the court. + /// @notice Add a new supported dispute kit module to the court. /// @param _disputeKitAddress The address of the dispute kit contract. function addNewDisputeKit(IDisputeKit _disputeKitAddress) external onlyByOwner { uint256 disputeKitID = disputeKits.length; @@ -310,7 +313,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { emit DisputeKitCreated(disputeKitID, _disputeKitAddress); } - /// @dev Creates a court under a specified parent court. + /// @notice Creates a court under a specified parent court. /// @param _parent The `parent` property value of the court. /// @param _hiddenVotes The `hiddenVotes` property value of the court. /// @param _minStake The `minStake` property value of the court. @@ -404,7 +407,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { ); } - /// @dev Adds/removes court's support for specified dispute kits. + /// @notice Adds/removes court's support for specified dispute kits. /// @param _courtID The ID of the court. /// @param _disputeKitIDs The IDs of dispute kits which support should be added/removed. /// @param _enable Whether add or remove the dispute kits from the court. @@ -425,7 +428,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { } } - /// @dev Changes the supported fee tokens. + /// @notice Changes the supported fee tokens. /// @param _feeToken The fee token. /// @param _accepted Whether the token is supported or not as a method of fee payment. function changeAcceptedFeeTokens(IERC20 _feeToken, bool _accepted) external onlyByOwner { @@ -433,7 +436,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { emit AcceptedFeeToken(_feeToken, _accepted); } - /// @dev Changes the currency rate of a fee token. + /// @notice Changes the currency rate of a fee token. /// @param _feeToken The fee token. /// @param _rateInEth The new rate of the fee token in ETH. /// @param _rateDecimals The new decimals of the fee token rate. @@ -447,7 +450,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { // * State Modifiers * // // ************************************* // - /// @dev Sets the caller's stake in a court. + /// @notice Sets the caller's stake in a court. /// @param _courtID The ID of the court. /// @param _newStake The new stake. /// Note that the existing delayed stake will be nullified as non-relevant. @@ -455,7 +458,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { _setStake(msg.sender, _courtID, _newStake, false, OnError.Revert); } - /// @dev Sets the stake of a specified account in a court, typically to apply a delayed stake or unstake inactive jurors. + /// @notice Sets the stake of a specified account in a court, typically to apply a delayed stake or unstake inactive jurors. /// @param _account The account whose stake is being set. /// @param _courtID The ID of the court. /// @param _newStake The new stake. @@ -464,7 +467,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { _setStake(_account, _courtID, _newStake, true, OnError.Return); } - /// @dev Transfers PNK to the juror by SortitionModule. + /// @notice Transfers PNK to the juror by SortitionModule. /// @param _account The account of the juror whose PNK to transfer. /// @param _amount The amount to transfer. function transferBySortitionModule(address _account, uint256 _amount) external { @@ -532,7 +535,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { emit DisputeCreation(disputeID, IArbitrableV2(msg.sender)); } - /// @dev Passes the period of a specified dispute. + /// @notice Passes the period of a specified dispute. /// @param _disputeID The ID of the dispute. function passPeriod(uint256 _disputeID) external { Dispute storage dispute = disputes[_disputeID]; @@ -582,7 +585,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { emit NewPeriod(_disputeID, dispute.period); } - /// @dev Draws one juror for the dispute until the number votes paid for is reached. + /// @notice Draws one juror for the dispute until the number votes paid for is reached. /// @param _disputeID The ID of the dispute. /// @param _juror The address of the juror to draw. function draw(uint256 _disputeID, address _juror) external onlyByOwnerOrInstructor { @@ -612,8 +615,8 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { sortitionModule.setTransientJuror(address(0)); } - /// @dev Appeals the ruling of a specified dispute. - /// Note: Access restricted to the Dispute Kit for this `disputeID`. + /// @notice Appeals the ruling of a specified dispute. + /// @dev Access restricted to the Dispute Kit for this `disputeID`. /// @param _disputeID The ID of the dispute. /// @param _numberOfChoices Number of choices for the dispute. Can be required during court jump. /// @param _extraData Extradata for the dispute. Can be required during court jump. @@ -673,7 +676,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { emit NewPeriod(_disputeID, Period.evidence); } - /// @dev Distribute the PNKs at stake and the dispute fees for the specific round of the dispute. Can be called in parts. + /// @notice Distribute the PNKs at stake and the dispute fees for the specific round of the dispute. Can be called in parts. /// @param _disputeID The ID of the dispute. /// @param _round The appeal round. /// @param _iterations The number of iterations to run. @@ -742,7 +745,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { } } - /// @dev Distribute the PNKs at stake and the dispute fees for the specific round of the dispute, penalties only. + /// @notice Distribute the PNKs at stake and the dispute fees for the specific round of the dispute, penalties only. /// @param _params The parameters for the execution, see `ExecuteParams`. /// @return pnkPenaltiesInRoundCache The updated penalties in round cache. function _executePenalties(ExecuteParams memory _params) internal returns (uint256) { @@ -784,6 +787,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { _params.disputeID, _params.round, coherence, + coherence, -int256(availablePenalty), 0, round.feeToken @@ -818,7 +822,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { return _params.pnkPenaltiesInRound; } - /// @dev Distribute the PNKs at stake and the dispute fees for the specific round of the dispute, rewards only. + /// @notice Distribute the PNKs at stake and the dispute fees for the specific round of the dispute, rewards only. /// @param _params The parameters for the execution, see `ExecuteParams`. function _executeRewards(ExecuteParams memory _params) internal { Dispute storage dispute = disputes[_params.disputeID]; @@ -849,9 +853,9 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { sortitionModule.unlockStake(account, pnkLocked); // Compute the rewards - uint256 pnkReward = ((_params.pnkPenaltiesInRound / _params.coherentCount) * pnkCoherence) / ONE_BASIS_POINT; + uint256 pnkReward = ((_params.pnkPenaltiesInRound / _params.coherentCount) * pnkCoherence) / ONE_BASIS_POINT; /// forge-lint: disable-line(divide-before-multiply) round.sumPnkRewardPaid += pnkReward; - uint256 feeReward = ((round.totalFeesForJurors / _params.coherentCount) * feeCoherence) / ONE_BASIS_POINT; + uint256 feeReward = ((round.totalFeesForJurors / _params.coherentCount) * feeCoherence) / ONE_BASIS_POINT; /// forge-lint: disable-line(divide-before-multiply) round.sumFeeRewardPaid += feeReward; // Transfer the fee reward @@ -873,6 +877,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { _params.disputeID, _params.round, pnkCoherence, + feeCoherence, int256(pnkReward), int256(feeReward), round.feeToken @@ -906,7 +911,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { } } - /// @dev Executes a specified dispute's ruling. + /// @notice Executes a specified dispute's ruling. /// @param _disputeID The ID of the dispute. function executeRuling(uint256 _disputeID) external { Dispute storage dispute = disputes[_disputeID]; @@ -923,25 +928,18 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { // * Public Views * // // ************************************* // - /// @dev Compute the cost of arbitration denominated in ETH. - /// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation. - /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes). - /// @return cost The arbitration cost in ETH. + /// @inheritdoc IArbitratorV2 function arbitrationCost(bytes memory _extraData) public view override returns (uint256 cost) { (uint96 courtID, uint256 minJurors, ) = _extraDataToCourtIDMinJurorsDisputeKit(_extraData); cost = courts[courtID].feeForJuror * minJurors; } - /// @dev Compute the cost of arbitration denominated in `_feeToken`. - /// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation. - /// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes). - /// @param _feeToken The ERC20 token used to pay fees. - /// @return cost The arbitration cost in `_feeToken`. + /// @inheritdoc IArbitratorV2 function arbitrationCost(bytes calldata _extraData, IERC20 _feeToken) public view override returns (uint256 cost) { cost = convertEthToTokenAmount(_feeToken, arbitrationCost(_extraData)); } - /// @dev Gets the cost of appealing a specified dispute. + /// @notice Gets the cost of appealing a specified dispute. /// @param _disputeID The ID of the dispute. /// @return cost The appeal cost. function appealCost(uint256 _disputeID) public view returns (uint256 cost) { @@ -962,7 +960,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { } } - /// @dev Gets the start and the end of a specified dispute's current appeal period. + /// @notice Gets the start and the end of a specified dispute's current appeal period. /// @param _disputeID The ID of the dispute. /// @return start The start of the appeal period. /// @return end The end of the appeal period. @@ -977,11 +975,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { } } - /// @dev Gets the current ruling of a specified dispute. - /// @param _disputeID The ID of the dispute. - /// @return ruling The current ruling. - /// @return tied Whether it's a tie or not. - /// @return overridden Whether the ruling was overridden by appeal funding or not. + /// @inheritdoc IArbitratorV2 function currentRuling(uint256 _disputeID) public view returns (uint256 ruling, bool tied, bool overridden) { Dispute storage dispute = disputes[_disputeID]; Round storage round = dispute.rounds[dispute.rounds.length - 1]; @@ -989,7 +983,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { (ruling, tied, overridden) = disputeKit.currentRuling(_disputeID); } - /// @dev Gets the round info for a specified dispute and round. + /// @notice Gets the round info for a specified dispute and round. /// @dev This function must not be called from a non-view function because it returns a dynamic array which might be very large, theoretically exceeding the block gas limit. /// @param _disputeID The ID of the dispute. /// @param _round The round to get the info for. @@ -998,7 +992,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { return disputes[_disputeID].rounds[_round]; } - /// @dev Gets the PNK at stake per juror for a specified dispute and round. + /// @notice Gets the PNK at stake per juror for a specified dispute and round. /// @param _disputeID The ID of the dispute. /// @param _round The round to get the info for. /// @return pnkAtStakePerJuror The PNK at stake per juror. @@ -1006,14 +1000,14 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { return disputes[_disputeID].rounds[_round].pnkAtStakePerJuror; } - /// @dev Gets the number of rounds for a specified dispute. + /// @notice Gets the number of rounds for a specified dispute. /// @param _disputeID The ID of the dispute. /// @return The number of rounds. function getNumberOfRounds(uint256 _disputeID) external view returns (uint256) { return disputes[_disputeID].rounds.length; } - /// @dev Checks if a given dispute kit is supported by a given court. + /// @notice Checks if a given dispute kit is supported by a given court. /// @param _courtID The ID of the court to check the support for. /// @param _disputeKitID The ID of the dispute kit to check the support for. /// @return Whether the dispute kit is supported or not. @@ -1021,7 +1015,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { return courts[_courtID].supportedDisputeKits[_disputeKitID]; } - /// @dev Gets the timesPerPeriod array for a given court. + /// @notice Gets the timesPerPeriod array for a given court. /// @param _courtID The ID of the court to get the times from. /// @return timesPerPeriod The timesPerPeriod array for the given court. function getTimesPerPeriod(uint96 _courtID) external view returns (uint256[4] memory timesPerPeriod) { @@ -1032,14 +1026,14 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { // * Public Views for Dispute Kits * // // ************************************* // - /// @dev Gets the number of votes permitted for the specified dispute in the latest round. + /// @notice Gets the number of votes permitted for the specified dispute in the latest round. /// @param _disputeID The ID of the dispute. function getNumberOfVotes(uint256 _disputeID) external view returns (uint256) { Dispute storage dispute = disputes[_disputeID]; return dispute.rounds[dispute.rounds.length - 1].nbVotes; } - /// @dev Returns true if the dispute kit will be switched to a parent DK. + /// @notice Returns true if the dispute kit will be switched to a parent DK. /// @param _disputeID The ID of the dispute. /// @return Whether DK will be switched or not. function isDisputeKitJumping(uint256 _disputeID) external view returns (bool) { @@ -1067,7 +1061,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { // * Internal * // // ************************************* // - /// @dev Toggles the dispute kit support for a given court. + /// @notice Toggles the dispute kit support for a given court. /// @param _courtID The ID of the court to toggle the support for. /// @param _disputeKitID The ID of the dispute kit to toggle the support for. /// @param _enable Whether to enable or disable the support. Note that classic dispute kit should always be enabled. @@ -1076,7 +1070,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { emit DisputeKitEnabled(_courtID, _disputeKitID, _enable); } - /// @dev If called only once then set _onError to Revert, otherwise set it to Return + /// @notice If called only once then set _onError to Revert, otherwise set it to Return /// @param _account The account to set the stake for. /// @param _courtID The ID of the court to set the stake for. /// @param _newStake The new stake. @@ -1125,7 +1119,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { return true; } - /// @dev It may revert depending on the _onError parameter. + /// @notice It may revert depending on the _onError parameter. function _stakingFailed(OnError _onError, StakingResult _result) internal pure virtual { if (_onError == OnError.Return) return; if (_result == StakingResult.StakingTransferFailed) revert StakingTransferFailed(); @@ -1136,8 +1130,8 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable { if (_result == StakingResult.CannotStakeZeroWhenNoStake) revert StakingZeroWhenNoStake(); } - /// @dev Gets a court ID, the minimum number of jurors and an ID of a dispute kit from a specified extra data bytes array. - /// Note that if extradata contains an incorrect value then this value will be switched to default. + /// @notice Gets a court ID, the minimum number of jurors and an ID of a dispute kit from a specified extra data bytes array. + /// @dev If `_extraData` contains an incorrect value then this value will be switched to default. /// @param _extraData The extra data bytes array. The first 32 bytes are the court ID, the next are the minimum number of jurors and the last are the dispute kit ID. /// @return courtID The court ID. /// @return minJurors The minimum number of jurors required. diff --git a/contracts/src/arbitration/university/SortitionModuleUniversity.sol b/contracts/src/arbitration/university/SortitionModuleUniversity.sol index 66e815e4a..c1c1cd076 100644 --- a/contracts/src/arbitration/university/SortitionModuleUniversity.sol +++ b/contracts/src/arbitration/university/SortitionModuleUniversity.sol @@ -10,7 +10,7 @@ import "../../proxy/Initializable.sol"; import "../../libraries/Constants.sol"; /// @title SortitionModuleUniversity -/// @dev An adapted version of the SortitionModule contract for educational purposes. +/// @notice An adapted version of the SortitionModule contract for educational purposes. contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, Initializable { string public constant override version = "2.0.0"; @@ -52,12 +52,12 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, /// @param _unlock Whether the stake is locked or unlocked. event StakeLocked(address indexed _address, uint256 _relativeAmount, bool _unlock); - /// @dev Emitted when leftover PNK is available. + /// @notice Emitted when leftover PNK is available. /// @param _account The account of the juror. /// @param _amount The amount of PNK available. event LeftoverPNK(address indexed _account, uint256 _amount); - /// @dev Emitted when leftover PNK is withdrawn. + /// @notice Emitted when leftover PNK is withdrawn. /// @param _account The account of the juror withdrawing PNK. /// @param _amount The amount of PNK withdrawn. event LeftoverPNKWithdrawn(address indexed _account, uint256 _amount); @@ -85,7 +85,7 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, _disableInitializers(); } - /// @dev Initializer (constructor equivalent for upgradable contracts). + /// @notice Initializer (constructor equivalent for upgradable contracts). /// @param _core The KlerosCore. function initialize(address _owner, KlerosCoreUniversity _core) external initializer { owner = _owner; @@ -108,34 +108,37 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, // * State Modifiers * // // ************************************* // + /// @inheritdoc ISortitionModule + function passPhase() external override onlyByCore { + // NOP + } + + /// @inheritdoc ISortitionModule + function executeDelayedStakes(uint256 _iterations) external override onlyByCore { + // NOP + } + + /// @inheritdoc ISortitionModuleUniversity function setTransientJuror(address _juror) external override onlyByCore { transientJuror = _juror; } + /// @inheritdoc ISortitionModule function createTree(uint96 _courtID, bytes memory _extraData) external { // NOP } + /// @inheritdoc ISortitionModule function createDisputeHook(uint256 /*_disputeID*/, uint256 /*_roundID*/) external override onlyByCore { disputesWithoutJurors++; } + /// @inheritdoc ISortitionModule function postDrawHook(uint256 /*_disputeID*/, uint256 /*_roundID*/) external override onlyByCore { disputesWithoutJurors--; } - /// @dev Saves the random number to use it in sortition. Not used by this contract because the storing of the number is inlined in passPhase(). - /// @param _randomNumber Random number returned by RNG contract. - function notifyRandomNumber(uint256 _randomNumber) public override {} - - /// @dev Validate the specified juror's new stake for a court. - /// Note: no state changes should be made when returning stakingResult != Successful, otherwise delayed stakes might break invariants. - /// @param _account The address of the juror. - /// @param _courtID The ID of the court. - /// @param _newStake The new stake. - /// @return pnkDeposit The amount of PNK to be deposited. - /// @return pnkWithdrawal The amount of PNK to be withdrawn. - /// @return stakingResult The result of the staking operation. + /// @inheritdoc ISortitionModule function validateStake( address _account, uint96 _courtID, @@ -173,17 +176,7 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, return (pnkDeposit, pnkWithdrawal, StakingResult.Successful); } - /// @dev Update the state of the stakes, called by KC at the end of setStake flow. - /// `O(n + p * log_k(j))` where - /// `n` is the number of courts the juror has staked in, - /// `p` is the depth of the court tree, - /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, - /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. - /// @param _account The address of the juror. - /// @param _courtID The ID of the court. - /// @param _pnkDeposit The amount of PNK to be deposited. - /// @param _pnkWithdrawal The amount of PNK to be withdrawn. - /// @param _newStake The new stake. + /// @inheritdoc ISortitionModule function setStake( address _account, uint96 _courtID, @@ -218,15 +211,7 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, newCourtStake = _stakeOf(_account, _courtID); // updated by _setStake() } - /// @dev Update the state of the stakes with a PNK reward deposit, called by KC during rewards execution. - /// `O(n + p * log_k(j))` where - /// `n` is the number of courts the juror has staked in, - /// `p` is the depth of the court tree, - /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, - /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. - /// @param _account The address of the juror. - /// @param _courtID The ID of the court. - /// @param _reward The amount of PNK to be deposited as a reward. + /// @inheritdoc ISortitionModule function setStakeReward( address _account, uint96 _courtID, @@ -280,7 +265,7 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, if (currentCourtID == GENERAL_COURT) { finished = true; } else { - (currentCourtID, , , , , , ) = core.courts(currentCourtID); + (currentCourtID, , , , , ) = core.courts(currentCourtID); } } emit StakeSet(_account, _courtID, _newStake, juror.stakedPnk); @@ -296,13 +281,7 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, emit StakeLocked(_account, _relativeAmount, true); } - /// @dev Unstakes the inactive juror from all courts. - /// `O(n * (p * log_k(j)) )` where - /// `n` is the number of courts the juror has staked in, - /// `p` is the depth of the court tree, - /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, - /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. - /// @param _account The juror to unstake. + /// @inheritdoc ISortitionModule function forcedUnstakeAllCourts(address _account) external override onlyByCore { uint96[] memory courtIDs = getJurorCourtIDs(_account); for (uint256 j = courtIDs.length; j > 0; j--) { @@ -310,24 +289,12 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, } } - /// @dev Unstakes the inactive juror from a specific court. - /// `O(n * (p * log_k(j)) )` where - /// `n` is the number of courts the juror has staked in, - /// `p` is the depth of the court tree, - /// `k` is the minimum number of children per node of one of these courts' sortition sum tree, - /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously. - /// @param _account The juror to unstake. - /// @param _courtID The ID of the court. + /// @inheritdoc ISortitionModule function forcedUnstake(address _account, uint96 _courtID) external override onlyByCore { core.setStakeBySortitionModule(_account, _courtID, 0); } - /// @dev Gives back the locked PNKs in case the juror fully unstaked earlier. - /// Note that since locked and staked PNK are async it is possible for the juror to have positive staked PNK balance - /// while having 0 stake in courts and 0 locked tokens (eg. when the juror fully unstaked during dispute and later got his tokens unlocked). - /// In this case the juror can use this function to withdraw the leftover tokens. - /// Also note that if the juror has some leftover PNK while not fully unstaked he'll have to manually unstake from all courts to trigger this function. - /// @param _account The juror whose PNK to withdraw. + /// @inheritdoc ISortitionModule function withdrawLeftoverPNK(address _account) external override { // Can withdraw the leftover PNK if fully unstaked, has no tokens locked and has positive balance. // This withdrawal can't be triggered by calling setStake() in KlerosCore because current stake is technically 0, thus it is done via separate function. @@ -342,22 +309,12 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, // * Public Views * // // ************************************* // - /// @dev Draw an ID from a tree using a number. - /// Note that this function reverts if the sum of all values in the tree is 0. - /// @return drawnAddress The drawn address. + /// @inheritdoc ISortitionModule function draw(uint96, uint256, uint256) public view override returns (address drawnAddress, uint96 fromSubcourtID) { drawnAddress = transientJuror; } - /// @dev Gets the stake of a juror in a court. - /// Warning: `O(n)` complexity where `n` is the number of courts the juror has staked in - /// but acceptable for this educational implementation. - /// @param _juror The address of the juror. - /// @param _courtID The ID of the court. - /// @return totalStaked The total amount of tokens staked by the juror in the court. - /// @return totalLocked The total amount of tokens locked by the juror in the court. - /// @return stakedInCourt The amount of tokens staked by the juror in the court. - /// @return nbCourts The number of courts the juror has staked in. + /// @inheritdoc ISortitionModule function getJurorBalance( address _juror, uint96 _courtID @@ -379,16 +336,17 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, } } - /// @dev Gets the court identifiers where a specific `_juror` has staked. - /// @param _juror The address of the juror. + /// @inheritdoc ISortitionModule function getJurorCourtIDs(address _juror) public view override returns (uint96[] memory) { return jurors[_juror].courtIDs; } + /// @inheritdoc ISortitionModule function isJurorStaked(address _juror) external view override returns (bool) { return jurors[_juror].stakedPnk > 0; } + /// @inheritdoc ISortitionModule function getJurorLeftoverPNK(address _juror) public view override returns (uint256) { Juror storage juror = jurors[_juror]; if (juror.courtIDs.length == 0 && juror.lockedPnk == 0) { @@ -401,9 +359,11 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable, // * Internal * // // ************************************* // - /// @dev Gets the stake of a juror in a court. - /// Warning: `O(n)` complexity where `n` is the number of courts the juror has staked in + /// @notice Gets the stake of a juror in a court. + /// + /// @dev Warning: `O(n)` complexity where `n` is the number of courts the juror has staked in /// but acceptable for this educational implementation. + /// /// @param _juror The address of the juror. /// @param _courtID The ID of the court. /// @return stakedInCourt The amount of tokens staked by the juror in the court. diff --git a/contracts/src/arbitration/view/KlerosCoreSnapshotProxy.sol b/contracts/src/arbitration/view/KlerosCoreSnapshotProxy.sol index 70c3c77ce..82f5b90ac 100644 --- a/contracts/src/arbitration/view/KlerosCoreSnapshotProxy.sol +++ b/contracts/src/arbitration/view/KlerosCoreSnapshotProxy.sol @@ -9,7 +9,7 @@ interface IKlerosCore { } /// @title KlerosCoreSnapshotProxy -/// Proxy contract for V2 that exposes staked PNK with balanceOf() function for Snapshot voting. +/// @notice Proxy contract for V2 that exposes staked PNK with balanceOf() function for Snapshot voting. contract KlerosCoreSnapshotProxy { // ************************************* // // * State Modifiers * // @@ -34,7 +34,7 @@ contract KlerosCoreSnapshotProxy { // * Constructor * // // ************************************* // - /// @dev Constructor + /// @notice Constructor /// @param _owner The owner of the contract. /// @param _core KlerosCore to read the balance from. constructor(address _owner, IKlerosCore _core) { @@ -46,13 +46,13 @@ contract KlerosCoreSnapshotProxy { // * Governance * // // ************************************* // - /// @dev Changes the `owner` storage variable. + /// @notice Changes the `owner` storage variable. /// @param _owner The new value for the `owner` storage variable. function changeOwner(address _owner) external onlyByOwner { owner = _owner; } - /// @dev Changes the `core` storage variable. + /// @notice Changes the `core` storage variable. /// @param _core The new value for the `core` storage variable. function changeCore(IKlerosCore _core) external onlyByOwner { core = _core; @@ -62,8 +62,8 @@ contract KlerosCoreSnapshotProxy { // * Public Views * // // ************************************* // - /// @dev Returns the amount of PNK staked in KlerosV2 for a particular address. - /// Note: Proxy doesn't need to differentiate between courts so we pass 0 as courtID. + /// @notice Returns the amount of PNK staked in KlerosV2 for a particular address. + /// @dev Proxy doesn't need to differentiate between courts so we pass 0 as courtID. /// @param _account The address to query. /// @return totalStaked Total amount staked in V2 by the address. function balanceOf(address _account) external view returns (uint256 totalStaked) { diff --git a/contracts/src/gateway/ForeignGateway.sol b/contracts/src/gateway/ForeignGateway.sol index 366f3e6f0..76f17bb4e 100644 --- a/contracts/src/gateway/ForeignGateway.sol +++ b/contracts/src/gateway/ForeignGateway.sol @@ -7,8 +7,8 @@ import "../proxy/UUPSProxiable.sol"; import "../proxy/Initializable.sol"; import "../libraries/Constants.sol"; -/// Foreign Gateway -/// Counterpart of `HomeGateway` +/// @title Foreign Gateway +/// @notice Counterpart of `HomeGateway` contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable { string public constant override version = "0.8.0"; @@ -71,7 +71,7 @@ contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable { _disableInitializers(); } - /// @dev Constructs the `PolicyRegistry` contract. + /// @notice Constructs the `PolicyRegistry` contract. /// @param _owner The owner's address. /// @param _veaOutbox The address of the VeaOutbox. /// @param _homeChainID The chainID of the home chain. @@ -101,14 +101,14 @@ contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable { // NOP } - /// @dev Changes the owner. + /// @notice Changes the owner. /// @param _owner The address of the new owner. function changeOwner(address _owner) external { if (owner != msg.sender) revert OwnerOnly(); owner = _owner; } - /// @dev Changes the outbox. + /// @notice Changes the outbox. /// @param _veaOutbox The address of the new outbox. /// @param _gracePeriod The duration to accept messages from the deprecated bridge (if at all). function changeVea(address _veaOutbox, uint256 _gracePeriod) external onlyByOwner { @@ -118,14 +118,14 @@ contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable { veaOutbox = _veaOutbox; } - /// @dev Changes the home gateway. + /// @notice Changes the home gateway. /// @param _homeGateway The address of the new home gateway. function changeHomeGateway(address _homeGateway) external { if (owner != msg.sender) revert OwnerOnly(); homeGateway = _homeGateway; } - /// @dev Changes the `feeForJuror` property value of a specified court. + /// @notice Changes the `feeForJuror` property value of a specified court. /// @param _courtID The ID of the court on the v2 arbitrator. Not to be confused with the courtID on KlerosLiquid. /// @param _feeForJuror The new value for the `feeForJuror` property value. function changeCourtJurorFee(uint96 _courtID, uint256 _feeForJuror) external onlyByOwner { diff --git a/contracts/src/gateway/HomeGateway.sol b/contracts/src/gateway/HomeGateway.sol index 2a23913ef..19a2e4337 100644 --- a/contracts/src/gateway/HomeGateway.sol +++ b/contracts/src/gateway/HomeGateway.sol @@ -9,8 +9,8 @@ import "../libraries/Constants.sol"; import "../proxy/UUPSProxiable.sol"; import "../proxy/Initializable.sol"; -/// Home Gateway -/// Counterpart of `ForeignGateway` +/// @title Home Gateway +/// @notice Counterpart of `ForeignGateway` contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable { using SafeERC20 for IERC20; @@ -43,7 +43,7 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable { // * Function Modifiers * // // ************************************* // - /// @dev Requires that the sender is the owner. + /// @notice Requires that the sender is the owner. modifier onlyByOwner() { if (owner != msg.sender) revert OwnerOnly(); _; @@ -58,7 +58,7 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable { _disableInitializers(); } - /// @dev Constructs the `PolicyRegistry` contract. + /// @notice Constructs the `PolicyRegistry` contract. /// @param _owner The owner's address. /// @param _arbitrator The address of the arbitrator. /// @param _veaInbox The address of the vea inbox. @@ -93,31 +93,31 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable { // NOP } - /// @dev Changes the owner. + /// @notice Changes the owner. /// @param _owner The address of the new owner. function changeOwner(address _owner) external onlyByOwner { owner = _owner; } - /// @dev Changes the arbitrator. + /// @notice Changes the arbitrator. /// @param _arbitrator The address of the new arbitrator. function changeArbitrator(IArbitratorV2 _arbitrator) external onlyByOwner { arbitrator = _arbitrator; } - /// @dev Changes the vea inbox, useful to increase the claim deposit. + /// @notice Changes the vea inbox, useful to increase the claim deposit. /// @param _veaInbox The address of the new vea inbox. function changeVea(IVeaInbox _veaInbox) external onlyByOwner { veaInbox = _veaInbox; } - /// @dev Changes the foreign gateway. + /// @notice Changes the foreign gateway. /// @param _foreignGateway The address of the new foreign gateway. function changeForeignGateway(address _foreignGateway) external onlyByOwner { foreignGateway = _foreignGateway; } - /// @dev Changes the fee token. + /// @notice Changes the fee token. /// @param _feeToken The address of the new fee token. function changeFeeToken(IERC20 _feeToken) external onlyByOwner { feeToken = _feeToken; @@ -151,7 +151,7 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable { disputeHashtoID[disputeHash] = disputeID; relayedData.relayer = msg.sender; - emit DisputeRequest(arbitrator, disputeID, _params.externalDisputeID, _params.templateId, _params.templateUri); + emit DisputeRequest(arbitrator, disputeID, _params.externalDisputeID, _params.templateId); emit CrossChainDisputeIncoming( arbitrator, @@ -160,8 +160,7 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable { _params.foreignDisputeID, disputeID, _params.externalDisputeID, - _params.templateId, - _params.templateUri + _params.templateId ); } @@ -193,7 +192,7 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable { relayedData.relayer = msg.sender; // Not strictly necessary for functionality, only to satisfy IArbitrableV2 - emit DisputeRequest(arbitrator, disputeID, _params.externalDisputeID, _params.templateId, _params.templateUri); + emit DisputeRequest(arbitrator, disputeID, _params.externalDisputeID, _params.templateId); emit CrossChainDisputeIncoming( arbitrator, @@ -202,8 +201,7 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable { _params.foreignDisputeID, disputeID, _params.externalDisputeID, - _params.templateId, - _params.templateUri + _params.templateId ); } diff --git a/contracts/src/gateway/interfaces/IForeignGateway.sol b/contracts/src/gateway/interfaces/IForeignGateway.sol index 49f51e5de..845713440 100644 --- a/contracts/src/gateway/interfaces/IForeignGateway.sol +++ b/contracts/src/gateway/interfaces/IForeignGateway.sol @@ -5,8 +5,13 @@ pragma solidity >=0.8.0 <0.9.0; import "../../arbitration/interfaces/IArbitratorV2.sol"; import "@kleros/vea-contracts/src/interfaces/gateways/IReceiverGateway.sol"; +/// @title Foreign Gateway Interface interface IForeignGateway is IArbitratorV2, IReceiverGateway { - /// @dev To be emitted when a dispute is sent to the IHomeGateway. + // ************************************* // + // * Events * // + // ************************************* // + + /// @notice To be emitted when a dispute is sent to the IHomeGateway. /// @param _foreignBlockHash foreignBlockHash /// @param _foreignArbitrable The address of the Arbitrable contract. /// @param _foreignDisputeID The identifier of the dispute in the Arbitrable contract. @@ -20,20 +25,35 @@ interface IForeignGateway is IArbitratorV2, IReceiverGateway { bytes _extraData ); - /// Relay the rule call from the home gateway to the arbitrable. + // ************************************* // + // * State Modifiers * // + // ************************************* // + + /// @notice Relay the rule call from the home gateway to the arbitrable. + /// @param _messageSender The address of the message sender. + /// @param _disputeHash The dispute hash. + /// @param _ruling The ruling. + /// @param _forwarder The address of the forwarder. function relayRule(address _messageSender, bytes32 _disputeHash, uint256 _ruling, address _forwarder) external; - /// Reimburses the dispute fees to the relayer who paid for these fees on the home chain. + /// @notice Reimburses the dispute fees to the relayer who paid for these fees on the home chain. /// @param _disputeHash The dispute hash for which to withdraw the fees. function withdrawFees(bytes32 _disputeHash) external; - /// @dev Looks up the local foreign disputeID for a disputeHash - /// @param _disputeHash dispute hash + // ************************************* // + // * Public Views * // + // ************************************* // + + /// @notice Looks up the local foreign disputeID for a disputeHash + /// @param _disputeHash The dispute hash. + /// @return The local foreign disputeID. function disputeHashToForeignID(bytes32 _disputeHash) external view returns (uint256); + /// @notice Home chain identifier. /// @return The chain ID where the corresponding home gateway is deployed. function homeChainID() external view returns (uint256); + /// @notice Home gateway address. /// @return The address of the corresponding home gateway. function homeGateway() external view returns (address); } diff --git a/contracts/src/gateway/interfaces/IHomeGateway.sol b/contracts/src/gateway/interfaces/IHomeGateway.sol index b80f194d6..407d7b1ce 100644 --- a/contracts/src/gateway/interfaces/IHomeGateway.sol +++ b/contracts/src/gateway/interfaces/IHomeGateway.sol @@ -6,16 +6,19 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@kleros/vea-contracts/src/interfaces/gateways/ISenderGateway.sol"; import "../../arbitration/interfaces/IArbitrableV2.sol"; +/// @title Home Gateway Interface interface IHomeGateway is IArbitrableV2, ISenderGateway { - /// @dev To be emitted when a dispute is received from the IForeignGateway. + // ************************************* // + // * Events * // + // ************************************* // + + /// @notice To be emitted when a dispute is received from the IForeignGateway. /// @param _arbitrator The arbitrator of the contract. /// @param _arbitrableChainId The chain identifier where the Arbitrable contract is deployed. /// @param _arbitrable The address of the Arbitrable contract. /// @param _arbitrableDisputeID The identifier of the dispute in the Arbitrable contract. /// @param _arbitratorDisputeID The identifier of the dispute in the Arbitrator contract. /// @param _externalDisputeID An identifier created outside Kleros by the protocol requesting arbitration. - /// @param _templateId The identifier of the dispute template. Should not be used with _templateUri. - /// @param _templateUri IPFS path to the dispute template starting with '/ipfs/'. Should not be used with _templateId. event CrossChainDisputeIncoming( IArbitratorV2 _arbitrator, uint256 _arbitrableChainId, @@ -23,10 +26,13 @@ interface IHomeGateway is IArbitrableV2, ISenderGateway { uint256 indexed _arbitrableDisputeID, uint256 indexed _arbitratorDisputeID, uint256 _externalDisputeID, - uint256 _templateId, - string _templateUri + uint256 _templateId ); + // ************************************* // + // * Enums / Structs * // + // ************************************* // + // Workaround stack too deep for relayCreateDispute() struct RelayCreateDisputeParams { bytes32 foreignBlockHash; @@ -35,34 +41,48 @@ interface IHomeGateway is IArbitrableV2, ISenderGateway { uint256 foreignDisputeID; uint256 externalDisputeID; uint256 templateId; - string templateUri; uint256 choices; bytes extraData; } - /// @dev Relays a dispute creation from the ForeignGateway to the home arbitrator using the same parameters as the ones on the foreign chain. - /// Providing incorrect parameters will create a different hash than on the foreignChain and will not affect the actual dispute/arbitrable's ruling. - /// This function accepts the fees payment in the native currency of the home chain, typically ETH. + // ************************************* // + // * State Modifiers * // + // ************************************* // + + /// @notice Relays a dispute creation from the ForeignGateway to the home arbitrator using the same parameters as the ones on the foreign chain. + /// + /// @dev Providing incorrect parameters will create a different hash than on the foreignChain and will not affect the actual dispute/arbitrable's ruling. + /// This function accepts the fees payment in the native currency of the home chain, typically ETH. + /// /// @param _params The parameters of the dispute, see `RelayCreateDisputeParams`. function relayCreateDispute(RelayCreateDisputeParams memory _params) external payable; - /// @dev Relays a dispute creation from the ForeignGateway to the home arbitrator using the same parameters as the ones on the foreign chain. - /// Providing incorrect parameters will create a different hash than on the foreignChain and will not affect the actual dispute/arbitrable's ruling. - /// This function accepts the fees payment in the ERC20 `acceptedFeeToken()`. + /// @notice Relays a dispute creation from the ForeignGateway to the home arbitrator using the same parameters as the ones on the foreign chain. + /// + /// @dev Providing incorrect parameters will create a different hash than on the foreignChain and will not affect the actual dispute/arbitrable's ruling. + /// This function accepts the fees payment in the ERC20 `acceptedFeeToken()`. + /// /// @param _params The parameters of the dispute, see `RelayCreateDisputeParams`. function relayCreateDispute(RelayCreateDisputeParams memory _params, uint256 _feeAmount) external; - /// @dev Looks up the local home disputeID for a disputeHash + // ************************************* // + // * Public Views * // + // ************************************* // + + /// @notice Looks up the local home disputeID for a disputeHash /// @param _disputeHash dispute hash /// @return disputeID dispute identifier on the home chain function disputeHashToHomeID(bytes32 _disputeHash) external view returns (uint256); + /// @notice Foreign chain identifier. /// @return The chain ID where the corresponding foreign gateway is deployed. function foreignChainID() external view returns (uint256); + /// @notice Foreign gateway address. /// @return The address of the corresponding foreign gateway. function foreignGateway() external view returns (address); - /// return The fee token. + /// @notice Fee token. + /// @return The fee token. function feeToken() external view returns (IERC20); } diff --git a/contracts/src/libraries/SafeERC20.sol b/contracts/src/libraries/SafeERC20.sol index 35f538350..35d1401f9 100644 --- a/contracts/src/libraries/SafeERC20.sol +++ b/contracts/src/libraries/SafeERC20.sol @@ -6,14 +6,16 @@ pragma solidity ^0.8.24; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @title SafeERC20 -/// @dev Wrappers around ERC20 operations that throw on failure (when the token -/// contract returns false). Tokens that return no value (and instead revert or -/// throw on failure) are also supported, non-reverting calls are assumed to be -/// successful. +/// +/// @notice Wrappers around ERC20 operations +/// +/// @dev Throws on failure (when the token contract returns false). +/// Tokens that return no value (and instead revert or throw on failure) are also supported. +/// Non-reverting calls are assumed to be successful. /// To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, /// which allows you to call the safe operations as `token.safeTransfer(...)`, etc. library SafeERC20 { - /// @dev Increases the allowance granted to `spender` by the caller. + /// @notice Increases the allowance granted to `spender` by the caller. /// @param _token Token to transfer. /// @param _spender The address which will spend the funds. /// @param _addedValue The amount of tokens to increase the allowance by. @@ -22,7 +24,7 @@ library SafeERC20 { return true; } - /// @dev Calls transfer() without reverting. + /// @notice Calls transfer() without reverting. /// @param _token Token to transfer. /// @param _to Recipient address. /// @param _value Amount transferred. @@ -32,7 +34,7 @@ library SafeERC20 { return (success && (data.length == 0 || abi.decode(data, (bool)))); } - /// @dev Calls transferFrom() without reverting. + /// @notice Calls transferFrom() without reverting. /// @param _token Token to transfer. /// @param _from Sender address. /// @param _to Recipient address. diff --git a/contracts/src/libraries/SafeSend.sol b/contracts/src/libraries/SafeSend.sol index fcd02d815..850aaf0df 100644 --- a/contracts/src/libraries/SafeSend.sol +++ b/contracts/src/libraries/SafeSend.sol @@ -19,6 +19,6 @@ library SafeSend { if (_to.send(_value)) return; WethLike(_wethLike).deposit{value: _value}(); - WethLike(_wethLike).transfer(_to, _value); + WethLike(_wethLike).transfer(_to, _value); /// forge-lint: disable-line(erc20-unchecked-transfer) } } diff --git a/contracts/src/libraries/SortitionTrees.sol b/contracts/src/libraries/SortitionTrees.sol index f8d9706fe..b6db48e20 100644 --- a/contracts/src/libraries/SortitionTrees.sol +++ b/contracts/src/libraries/SortitionTrees.sol @@ -29,7 +29,7 @@ library SortitionTrees { // * State Modifiers * // // ************************************* // - /// @dev Create a sortition sum tree at the specified key. + /// @notice Create a sortition sum tree at the specified key. /// @param _trees The mapping of sortition sum trees. /// @param _key The key of the new tree. /// @param _k The maximum number of children per node. @@ -41,15 +41,17 @@ library SortitionTrees { tree.nodes.push(0); } - /// @dev Draw an ID from a tree using a number. - /// Note that this function reverts if the sum of all values in the tree is 0. + /// @notice Draw an ID from a tree using a number. + /// + /// @dev This function reverts if the sum of all values in the tree is 0. + /// `O(k * log_k(n))` where + /// `k` is the maximum number of children per node in the tree, + /// and `n` is the maximum number of nodes ever appended. + /// /// @param _tree The sortition sum tree. /// @param _coreDisputeID Index of the dispute in Kleros Core. /// @param _nonce Nonce to hash with random number. /// @return drawnAddress The drawn address. - /// `O(k * log_k(n))` where - /// `k` is the maximum number of children per node in the tree, - /// and `n` is the maximum number of nodes ever appended. function draw( Tree storage _tree, uint256 _coreDisputeID, @@ -86,13 +88,15 @@ library SortitionTrees { (drawnAddress, fromSubcourtID) = toAccountAndCourtID(stakePathID); } - /// @dev Set a value in a tree. + /// @notice Set a value in a tree. + /// + /// @dev `O(log_k(n))` where + /// `k` is the maximum number of children per node in the tree, + /// and `n` is the maximum number of nodes ever appended. + /// /// @param _tree The sortition sum tree. /// @param _value The new value. /// @param _stakePathID The ID of the value. - /// `O(log_k(n))` where - /// `k` is the maximum number of children per node in the tree, - /// and `n` is the maximum number of nodes ever appended. function set(Tree storage _tree, uint256 _value, bytes32 _stakePathID) internal { uint256 treeIndex = _tree.IDsToNodeIndexes[_stakePathID]; @@ -164,14 +168,16 @@ library SortitionTrees { } } - /// @dev Update all the parents of a node. + /// @notice Update all the parents of a node. + /// + /// @dev `O(log_k(n))` where + /// `k` is the maximum number of children per node in the tree, + /// and `n` is the maximum number of nodes ever appended. + /// /// @param _tree The sortition sum tree. /// @param _treeIndex The index of the node to start from. /// @param _plusOrMinus Whether to add (true) or substract (false). /// @param _value The value to add or substract. - /// `O(log_k(n))` where - /// `k` is the maximum number of children per node in the tree, - /// and `n` is the maximum number of nodes ever appended. function updateParents(Tree storage _tree, uint256 _treeIndex, bool _plusOrMinus, uint256 _value) private { uint256 parentIndex = _treeIndex; while (parentIndex != 0) { @@ -186,7 +192,7 @@ library SortitionTrees { // * Public Views * // // ************************************* // - /// @dev Get the stake of a juror in a court. + /// @notice Get the stake of a juror in a court. /// @param _tree The sortition sum tree. /// @param _stakePathID The stake path ID, corresponding to a juror. /// @return The stake of the juror in the court. @@ -198,7 +204,8 @@ library SortitionTrees { return _tree.nodes[treeIndex]; } - /// @dev Packs an account and a court ID into a stake path ID: [20 bytes of address][12 bytes of courtID] = 32 bytes total. + /// @notice Packs an account and a court ID into a stake path ID + /// @dev [20 bytes of address][12 bytes of courtID] = 32 bytes total. /// @param _account The address of the juror to pack. /// @param _courtID The court ID to pack. /// @return stakePathID The stake path ID. @@ -210,7 +217,7 @@ library SortitionTrees { } } - /// @dev Retrieves both juror's address and court ID from the stake path ID. + /// @notice Retrieves both juror's address and court ID from the stake path ID. /// @param _stakePathID The stake path ID to unpack. /// @return account The account. /// @return courtID The court ID. diff --git a/contracts/src/proxy/UUPSProxiable.sol b/contracts/src/proxy/UUPSProxiable.sol index 34504f686..57e28b3a1 100644 --- a/contracts/src/proxy/UUPSProxiable.sol +++ b/contracts/src/proxy/UUPSProxiable.sol @@ -4,7 +4,8 @@ pragma solidity ^0.8.24; /// @title UUPS Proxiable /// @author Simon Malatrait -/// @dev This contract implements an upgradeability mechanism designed for UUPS proxies. +/// @notice This contract implements an upgradeability mechanism designed for UUPS proxies. +/// /// @dev Adapted from /// The functions included here can perform an upgrade of an UUPS Proxy, when this contract is set as the implementation behind such a proxy. /// @@ -21,7 +22,7 @@ abstract contract UUPSProxiable { // * Event * // // ************************************* // - /// @dev Emitted when the `implementation` has been successfully upgraded. + /// @notice Emitted when the `implementation` has been successfully upgraded. /// @param newImplementation Address of the new implementation the proxy is now forwarding calls to. event Upgraded(address indexed newImplementation); @@ -29,13 +30,13 @@ abstract contract UUPSProxiable { // * Error * // // ************************************* // - /// @dev The call is from an unauthorized context. + /// @notice The call is from an unauthorized context. error UUPSUnauthorizedCallContext(); - /// @dev The storage `slot` is unsupported as a UUID. + /// @notice The storage `slot` is unsupported as a UUID. error UUPSUnsupportedProxiableUUID(bytes32 slot); - /// @dev The `implementation` is not UUPS-compliant + /// @notice The `implementation` is not UUPS-compliant error InvalidImplementation(address implementation); /// Failed Delegated call @@ -68,7 +69,7 @@ abstract contract UUPSProxiable { // * State Modifiers * // // ************************************* // - /// @dev Upgrade mechanism including access control and UUPS-compliance. + /// @notice Upgrade mechanism including access control and UUPS-compliance. /// @param newImplementation Address of the new implementation contract. /// @param data Data used in a delegate call to `newImplementation` if non-empty. This will typically be an encoded /// function call, and allows initializing the storage of the proxy like a Solidity constructor. @@ -110,10 +111,10 @@ abstract contract UUPSProxiable { // * Public Views * // // ************************************* // - /// @dev Implementation of the ERC1822 `proxiableUUID` function. This returns the storage slot used by the + /// @notice Implementation of the ERC1822 `proxiableUUID` function. This returns the storage slot used by the /// implementation. It is used to validate the implementation's compatibility when performing an upgrade. /// - /// IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks + /// @dev IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks /// bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this /// function revert if invoked through a proxy. This is guaranteed by the if statement. function proxiableUUID() external view virtual returns (bytes32) { @@ -124,7 +125,7 @@ abstract contract UUPSProxiable { return IMPLEMENTATION_SLOT; } - /// @dev Returns the version of the implementation. + /// @notice Returns the version of the implementation. /// @return Version string. function version() external view virtual returns (string memory); diff --git a/contracts/src/proxy/UUPSProxy.sol b/contracts/src/proxy/UUPSProxy.sol index 4f190158d..328b0381a 100644 --- a/contracts/src/proxy/UUPSProxy.sol +++ b/contracts/src/proxy/UUPSProxy.sol @@ -4,10 +4,11 @@ pragma solidity ^0.8.24; /// @title UUPS Proxy /// @author Simon Malatrait -/// @dev This contract implements a UUPS Proxy compliant with ERC-1967 & ERC-1822. +/// @notice This contract implements a UUPS Proxy compliant with ERC-1967 & ERC-1822. +/// /// @dev This contract delegates all calls to another contract (UUPS Proxiable) through a fallback function and the use of the `delegatecall` EVM instruction. -/// @dev We refer to the Proxiable contract (as per ERC-1822) with `implementation`. -/// @dev Adapted from +/// We refer to the Proxiable contract (as per ERC-1822) with `implementation`. +/// Adapted from contract UUPSProxy { /// @dev Storage slot with the address of the current implementation. /// This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is @@ -19,9 +20,9 @@ contract UUPSProxy { // * Constructor * // // ************************************* // - /// @dev Initializes the upgradeable proxy with an initial implementation specified by `_implementation`. - /// If `_data` is nonempty, it's used as data in a delegate call to `_implementation`. This will typically be an encoded - /// function call, and allows initializing the storage of the proxy like a Solidity constructor. + /// @notice Initializes the upgradeable proxy with an initial implementation specified by `_implementation`. + /// @dev If `_data` is nonempty, it's used as data in a delegate call to `_implementation`. + /// This will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity constructor. constructor(address _implementation, bytes memory _data) { assembly { sstore(IMPLEMENTATION_SLOT, _implementation) diff --git a/contracts/src/rng/BlockhashRNG.sol b/contracts/src/rng/BlockhashRNG.sol index d104780f4..f6cfc3f90 100644 --- a/contracts/src/rng/BlockhashRNG.sol +++ b/contracts/src/rng/BlockhashRNG.sol @@ -5,11 +5,11 @@ pragma solidity ^0.8.24; import "./IRNG.sol"; /// @title Random Number Generator using blockhash with fallback. -/// @dev -/// Random Number Generator returning the blockhash with a fallback behaviour. -/// On L2 like Arbitrum block production is sporadic so block timestamp is more reliable than block number. -/// Returns 0 when no random number is available. -/// Allows saving the random number for use in the future. It allows the contract to retrieve the blockhash even after the time window. +/// @notice Random Number Generator returning the blockhash with a fallback behaviour. +/// +/// @dev On L2 like Arbitrum block production is sporadic so block timestamp is more reliable than block number. +/// Returns 0 when no random number is available. +/// Allows saving the random number for use in the future. It allows the contract to retrieve the blockhash even after the time window. contract BlockHashRNG is IRNG { // ************************************* // // * Storage * // @@ -39,7 +39,7 @@ contract BlockHashRNG is IRNG { // * Constructor * // // ************************************* // - /// @dev Constructor. + /// @notice Constructor. /// @param _owner The Owner of the contract. /// @param _consumer The address that can request random numbers. /// @param _lookaheadTime The time lookahead in seconds for the random number. @@ -53,13 +53,13 @@ contract BlockHashRNG is IRNG { // * Governance * // // ************************************* // - /// @dev Changes the owner of the contract. + /// @notice Changes the owner of the contract. /// @param _owner The new owner. function changeOwner(address _owner) external onlyByOwner { owner = _owner; } - /// @dev Changes the consumer of the RNG. + /// @notice Changes the consumer of the RNG. /// @param _consumer The new consumer. function changeConsumer(address _consumer) external onlyByOwner { consumer = _consumer; @@ -69,12 +69,13 @@ contract BlockHashRNG is IRNG { // * State Modifiers * // // ************************************* // - /// @dev Request a random number. + /// @notice Request a random number. function requestRandomness() external override onlyByConsumer { requestTimestamp = block.timestamp; } - /// @dev Return the random number. If it has not been saved and is still computable compute it. + /// @notice Return the random number. + /// @dev If it has not been saved and is still computable compute it. /// @return randomNumber The random number or 0 if it is not ready or has not been requested. function receiveRandomness() external override onlyByConsumer returns (uint256 randomNumber) { if (requestTimestamp == 0) return 0; // No requests were made yet. @@ -104,14 +105,14 @@ contract BlockHashRNG is IRNG { // * View Functions * // // ************************************* // - /// @dev Check if randomness is ready to be received. + /// @notice Check if randomness is ready to be received. /// @return ready True if randomness can be received. function isRandomnessReady() external view returns (bool ready) { if (requestTimestamp == 0) return false; // No requests were made yet. return block.timestamp >= requestTimestamp + lookaheadTime; } - /// @dev Get the timestamp when randomness will be ready. + /// @notice Get the timestamp when randomness will be ready. /// @return readyTimestamp The timestamp when randomness will be available. function getRandomnessReadyTimestamp() external view returns (uint256 readyTimestamp) { if (requestTimestamp == 0) return 0; // No requests were made yet. diff --git a/contracts/src/rng/ChainlinkRNG.sol b/contracts/src/rng/ChainlinkRNG.sol index 744bc5248..b493bfc0d 100644 --- a/contracts/src/rng/ChainlinkRNG.sol +++ b/contracts/src/rng/ChainlinkRNG.sol @@ -8,7 +8,7 @@ import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/V import "./IRNG.sol"; /// @title Random Number Generator that uses Chainlink VRF v2.5 -/// https://blog.chain.link/introducing-vrf-v2-5/ +/// @dev https://blog.chain.link/introducing-vrf-v2-5/ contract ChainlinkRNG is IRNG, VRFConsumerBaseV2Plus { // ************************************* // // * Storage * // @@ -27,7 +27,7 @@ contract ChainlinkRNG is IRNG, VRFConsumerBaseV2Plus { // * Events * // // ************************************* // - /// @dev Emitted when a request is sent to the VRF Coordinator + /// @notice Emitted when a request is sent to the VRF Coordinator /// @param _requestId The ID of the request event RequestSent(uint256 indexed _requestId); @@ -54,7 +54,7 @@ contract ChainlinkRNG is IRNG, VRFConsumerBaseV2Plus { // * Constructor * // // ************************************* // - /// @dev Constructor, initializing the implementation to reduce attack surface. + /// @notice Constructor, initializing the implementation to reduce attack surface. /// @param _owner The owner of the contract. /// @param _consumer The address that can request random numbers. /// @param _vrfCoordinator The address of the VRFCoordinator contract. @@ -83,44 +83,44 @@ contract ChainlinkRNG is IRNG, VRFConsumerBaseV2Plus { // * Governance * // // ************************************* // - /// @dev Changes the owner of the contract. + /// @notice Changes the owner of the contract. /// @param _owner The new owner. function changeOwner(address _owner) external onlyByOwner { owner = _owner; } - /// @dev Changes the consumer of the RNG. + /// @notice Changes the consumer of the RNG. /// @param _consumer The new consumer. function changeConsumer(address _consumer) external onlyByOwner { consumer = _consumer; } - /// @dev Changes the VRF Coordinator of the contract. + /// @notice Changes the VRF Coordinator of the contract. /// @param _vrfCoordinator The new VRF Coordinator. function changeVrfCoordinator(address _vrfCoordinator) external onlyByOwner { s_vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator); emit CoordinatorSet(_vrfCoordinator); } - /// @dev Changes the key hash of the contract. + /// @notice Changes the key hash of the contract. /// @param _keyHash The new key hash. function changeKeyHash(bytes32 _keyHash) external onlyByOwner { keyHash = _keyHash; } - /// @dev Changes the subscription ID of the contract. + /// @notice Changes the subscription ID of the contract. /// @param _subscriptionId The new subscription ID. function changeSubscriptionId(uint256 _subscriptionId) external onlyByOwner { subscriptionId = _subscriptionId; } - /// @dev Changes the request confirmations of the contract. + /// @notice Changes the request confirmations of the contract. /// @param _requestConfirmations The new request confirmations. function changeRequestConfirmations(uint16 _requestConfirmations) external onlyByOwner { requestConfirmations = _requestConfirmations; } - /// @dev Changes the callback gas limit of the contract. + /// @notice Changes the callback gas limit of the contract. /// @param _callbackGasLimit The new callback gas limit. function changeCallbackGasLimit(uint32 _callbackGasLimit) external onlyByOwner { callbackGasLimit = _callbackGasLimit; @@ -130,7 +130,8 @@ contract ChainlinkRNG is IRNG, VRFConsumerBaseV2Plus { // * State Modifiers * // // ************************************* // - /// @dev Request a random number. Consumer only. + /// @notice Request a random number. + /// @dev Consumer only. function requestRandomness() external override onlyByConsumer { // Will revert if subscription is not set and funded. uint256 requestId = s_vrfCoordinator.requestRandomWords( @@ -150,7 +151,7 @@ contract ChainlinkRNG is IRNG, VRFConsumerBaseV2Plus { emit RequestSent(requestId); } - /// @dev Callback function called by the VRF Coordinator when the random value is generated. + /// @notice Callback function called by the VRF Coordinator when the random value is generated. /// @param _requestId The ID of the request. /// @param _randomWords The random values answering the request. function fulfillRandomWords(uint256 _requestId, uint256[] calldata _randomWords) internal override { @@ -163,7 +164,7 @@ contract ChainlinkRNG is IRNG, VRFConsumerBaseV2Plus { // * Public Views * // // ************************************* // - /// @dev Return the random number. + /// @notice Return the random number. /// @return randomNumber The random number or 0 if it is not ready or has not been requested. function receiveRandomness() external view override returns (uint256 randomNumber) { randomNumber = randomNumbers[lastRequestId]; diff --git a/contracts/src/rng/IRNG.sol b/contracts/src/rng/IRNG.sol index c407b763f..4945a80af 100644 --- a/contracts/src/rng/IRNG.sol +++ b/contracts/src/rng/IRNG.sol @@ -4,13 +4,21 @@ pragma solidity >=0.8.0 <0.9.0; /// @title Random Number Generator interface interface IRNG { - /// @dev Request a random number. + // ************************************* // + // * State Modifiers * // + // ************************************* // + + /// @notice Request a random number. function requestRandomness() external; - /// @dev Receive the random number. + /// @notice Receive the random number. /// @return randomNumber Random number or 0 if not available function receiveRandomness() external returns (uint256 randomNumber); + // ************************************* // + // * Errors * // + // ************************************* // + error OwnerOnly(); error ConsumerOnly(); } diff --git a/contracts/src/rng/IRandomizer.sol b/contracts/src/rng/IRandomizer.sol index 6a6296f82..875d5b574 100644 --- a/contracts/src/rng/IRandomizer.sol +++ b/contracts/src/rng/IRandomizer.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0 <0.9.0; -// Randomizer protocol interface +// @title Randomizer protocol interface interface IRandomizer { function request(uint256 callbackGasLimit) external returns (uint256); diff --git a/contracts/src/rng/IncrementalNG.sol b/contracts/src/rng/IncrementalNG.sol index 542090e71..7f6170244 100644 --- a/contracts/src/rng/IncrementalNG.sol +++ b/contracts/src/rng/IncrementalNG.sol @@ -13,12 +13,12 @@ contract IncrementalNG is IRNG { number = _start; } - /// @dev Request a random number. + /// @notice Request a random number. function requestRandomness() external override { // nop } - /// @dev Get the "random number" (which is always the same). + /// @notice Get the "random number" (which is always the same). /// @return randomNumber The random number or 0 if it is not ready or has not been requested. function receiveRandomness() external override returns (uint256 randomNumber) { unchecked { diff --git a/contracts/src/rng/RNGWithFallback.sol b/contracts/src/rng/RNGWithFallback.sol index 8501d4db9..30c84ea4c 100644 --- a/contracts/src/rng/RNGWithFallback.sol +++ b/contracts/src/rng/RNGWithFallback.sol @@ -27,6 +27,7 @@ contract RNGWithFallback is IRNG { // * Constructor * // // ************************************* // + /// @notice Constructor /// @param _owner Owner address /// @param _consumer Consumer address /// @param _fallbackTimeoutSeconds Time in seconds to wait before falling back to next RNG @@ -58,19 +59,19 @@ contract RNGWithFallback is IRNG { // * Governance Functions * // // ************************************* // - /// @dev Change the owner + /// @notice Change the owner /// @param _newOwner Address of the new owner function changeOwner(address _newOwner) external onlyByOwner { owner = _newOwner; } - /// @dev Change the consumer + /// @notice Change the consumer /// @param _consumer Address of the new consumer function changeConsumer(address _consumer) external onlyByOwner { consumer = _consumer; } - /// @dev Change the fallback timeout + /// @notice Change the fallback timeout /// @param _fallbackTimeoutSeconds New timeout in seconds function changeFallbackTimeout(uint256 _fallbackTimeoutSeconds) external onlyByOwner { fallbackTimeoutSeconds = _fallbackTimeoutSeconds; @@ -81,14 +82,14 @@ contract RNGWithFallback is IRNG { // * State Modifiers * // // ************************************* // - /// @dev Request a random number from the primary RNG + /// @notice Request a random number from the primary RNG /// @dev The consumer is trusted not to make concurrent requests. function requestRandomness() external override onlyByConsumer { requestTimestamp = block.timestamp; rng.requestRandomness(); } - /// @dev Receive the random number from the primary RNG with fallback to the blockhash RNG if the primary RNG does not respond passed a timeout. + /// @notice Receive the random number from the primary RNG with fallback to the blockhash RNG if the primary RNG does not respond passed a timeout. /// @return randomNumber Random number or 0 if not available function receiveRandomness() external override onlyByConsumer returns (uint256 randomNumber) { randomNumber = rng.receiveRandomness(); diff --git a/contracts/src/rng/RandomizerRNG.sol b/contracts/src/rng/RandomizerRNG.sol index 648ce5c3d..2107702bd 100644 --- a/contracts/src/rng/RandomizerRNG.sol +++ b/contracts/src/rng/RandomizerRNG.sol @@ -23,7 +23,7 @@ contract RandomizerRNG is IRNG { // * Events * // // ************************************* // - /// @dev Emitted when a request is sent to the VRF Coordinator + /// @notice Emitted when a request is sent to the VRF Coordinator /// @param requestId The ID of the request event RequestSent(uint256 indexed requestId); @@ -50,7 +50,7 @@ contract RandomizerRNG is IRNG { // * Constructor * // // ************************************* // - /// @dev Constructor + /// @notice Constructor /// @param _owner The Owner of the contract. /// @param _consumer The address that can request random numbers. /// @param _randomizer The Randomizer.ai oracle contract. @@ -65,31 +65,31 @@ contract RandomizerRNG is IRNG { // * Governance * // // ************************ // - /// @dev Changes the owner of the contract. + /// @notice Changes the owner of the contract. /// @param _owner The new owner. function changeOwner(address _owner) external onlyByOwner { owner = _owner; } - /// @dev Changes the consumer of the RNG. + /// @notice Changes the consumer of the RNG. /// @param _consumer The new consumer. function changeConsumer(address _consumer) external onlyByOwner { consumer = _consumer; } - /// @dev Change the Randomizer callback gas limit. + /// @notice Change the Randomizer callback gas limit. /// @param _callbackGasLimit the new limit. function setCallbackGasLimit(uint256 _callbackGasLimit) external onlyByOwner { callbackGasLimit = _callbackGasLimit; } - /// @dev Change the Randomizer address. + /// @notice Change the Randomizer address. /// @param _randomizer the new Randomizer address. function setRandomizer(address _randomizer) external onlyByOwner { randomizer = IRandomizer(_randomizer); } - /// @dev Allows the owner to withdraw randomizer funds. + /// @notice Allows the owner to withdraw randomizer funds. /// @param _amount Amount to withdraw in wei. function randomizerWithdraw(uint256 _amount) external onlyByOwner { randomizer.clientWithdrawTo(msg.sender, _amount); @@ -99,14 +99,15 @@ contract RandomizerRNG is IRNG { // * State Modifiers * // // ************************************* // - /// @dev Request a random number. Consumer only. + /// @notice Request a random number. + /// @dev Consumer only. function requestRandomness() external override onlyByConsumer { uint256 requestId = randomizer.request(callbackGasLimit); lastRequestId = requestId; emit RequestSent(requestId); } - /// @dev Callback function called by the randomizer contract when the random value is generated. + /// @notice Callback function called by the randomizer contract when the random value is generated. /// @param _id The ID of the request. /// @param _value The random value answering the request. function randomizerCallback(uint256 _id, bytes32 _value) external { @@ -119,7 +120,7 @@ contract RandomizerRNG is IRNG { // * Public Views * // // ************************************* // - /// @dev Return the random number. + /// @notice Return the random number. /// @return randomNumber The random number or 0 if it is not ready or has not been requested. function receiveRandomness() external view override returns (uint256 randomNumber) { randomNumber = randomNumbers[lastRequestId]; diff --git a/contracts/src/token/Faucet.sol b/contracts/src/token/Faucet.sol index b64a7e2ab..2790bc20e 100644 --- a/contracts/src/token/Faucet.sol +++ b/contracts/src/token/Faucet.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.24; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +/// forge-lint: disable-next-item(erc20-unchecked-transfer) contract Faucet { // ************************************* // // * Storage * // diff --git a/contracts/src/utils/TransactionBatcher.sol b/contracts/src/utils/TransactionBatcher.sol index 1bbff78a2..bb6d25b23 100644 --- a/contracts/src/utils/TransactionBatcher.sol +++ b/contracts/src/utils/TransactionBatcher.sol @@ -3,7 +3,12 @@ pragma solidity ^0.8.0; // Adapted from https://github.com/daostack/web3-transaction-batcher/blob/1b88d2ea062f8f2d9fdfdf9bbe85d2bbef780151/contracts/Batcher.sol +/// @title Transaction Batcher contract TransactionBatcher { + /// @notice Batch send transactions, all the calls must succeed. + /// @param targets The targets of the calls. + /// @param values The values of the calls. + /// @param datas The datas of the calls. function batchSend(address[] memory targets, uint256[] memory values, bytes[] memory datas) public payable { for (uint256 i = 0; i < targets.length; i++) { (bool success, ) = targets[i].call{value: values[i]}(datas[i]); @@ -11,12 +16,17 @@ contract TransactionBatcher { } } + /// @notice Batch send transactions WITHOUT reverting on call failure. + /// @param targets The targets of the calls. + /// @param values The values of the calls. + /// @param datas The datas of the calls. function batchSendUnchecked( address[] memory targets, uint256[] memory values, bytes[] memory datas ) public payable { for (uint256 i = 0; i < targets.length; i++) { + /// forge-lint: disable-next-line(unchecked-call) targets[i].call{value: values[i]}(datas[i]); // Intentionally ignoring return value. } } diff --git a/contracts/test/arbitration/draw.ts b/contracts/test/arbitration/draw.ts index 4992501f1..3c0b9fac4 100644 --- a/contracts/test/arbitration/draw.ts +++ b/contracts/test/arbitration/draw.ts @@ -154,7 +154,7 @@ describe("Draw Benchmark", async () => { // Relayer tx await homeGateway .connect(await ethers.getSigner(relayer)) - ["relayCreateDispute((bytes32,uint256,address,uint256,uint256,uint256,string,uint256,bytes))"]( + ["relayCreateDispute((bytes32,uint256,address,uint256,uint256,uint256,uint256,bytes))"]( { foreignBlockHash: lastBlock?.hash, foreignChainID: 31337, @@ -162,7 +162,6 @@ describe("Draw Benchmark", async () => { foreignDisputeID: disputeId, externalDisputeID: ethers.keccak256(ethers.toUtf8Bytes("future of france")), templateId: 0, - templateUri: "", choices: 2, extraData: `0x000000000000000000000000000000000000000000000000000000000000000${createDisputeCourtId}0000000000000000000000000000000000000000000000000000000000000003`, }, diff --git a/contracts/test/arbitration/ruler.ts b/contracts/test/arbitration/ruler.ts index dafdb59da..fef56ac8f 100644 --- a/contracts/test/arbitration/ruler.ts +++ b/contracts/test/arbitration/ruler.ts @@ -95,9 +95,9 @@ describe("KlerosCoreRuler", async () => { .and.to.emit(core, "Ruling") .withArgs(resolver.target, disputeID, anyValue) .and.to.emit(core, "TokenAndETHShift") - .withArgs(dev.address, disputeID, 0, 1, 0, anyValue, ZeroAddress) + .withArgs(dev.address, disputeID, 0, 10000, 10000, 0, anyValue, ZeroAddress) .and.to.emit(resolver, "DisputeRequest") - .withArgs(core.target, disputeID, localDisputeID, templateId, "") + .withArgs(core.target, disputeID, localDisputeID, templateId) .and.to.emit(resolver, "Ruling") .withArgs(core.target, disputeID, anyValue); }); @@ -119,9 +119,9 @@ describe("KlerosCoreRuler", async () => { .and.to.emit(core, "Ruling") .withArgs(resolver.target, disputeID, 2) .and.to.emit(core, "TokenAndETHShift") - .withArgs(dev.address, disputeID, 0, 1, 0, anyValue, ZeroAddress) + .withArgs(dev.address, disputeID, 0, 10000, 10000, 0, anyValue, ZeroAddress) .and.to.emit(resolver, "DisputeRequest") - .withArgs(core.target, disputeID, localDisputeID, templateId, "") + .withArgs(core.target, disputeID, localDisputeID, templateId) .and.to.emit(resolver, "Ruling") .withArgs(core.target, disputeID, 2); }); @@ -139,7 +139,7 @@ describe("KlerosCoreRuler", async () => { .to.emit(core, "DisputeCreation") .withArgs(disputeID, resolver.target) .and.to.emit(resolver, "DisputeRequest") - .withArgs(core.target, disputeID, localDisputeID, templateId, ""); + .withArgs(core.target, disputeID, localDisputeID, templateId); await expect(core.connect(deployer).executeRuling(disputeID, 3, true, true)).revertedWithCustomError( core, @@ -154,7 +154,7 @@ describe("KlerosCoreRuler", async () => { await expect(core.execute(disputeID, 0)) .and.to.emit(core, "TokenAndETHShift") - .withArgs(dev.address, disputeID, 0, 1, 0, anyValue, ZeroAddress); + .withArgs(dev.address, disputeID, 0, 10000, 10000, 0, anyValue, ZeroAddress); }); }); diff --git a/contracts/test/arbitration/staking-neo.ts b/contracts/test/arbitration/staking-neo.ts index 9cd83efeb..3e34fb8cb 100644 --- a/contracts/test/arbitration/staking-neo.ts +++ b/contracts/test/arbitration/staking-neo.ts @@ -469,7 +469,6 @@ describe("Staking", async () => { expect(await sortition.delayedStakeWriteIndex()).to.be.equal(0); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); await pnk.approve(core.target, PNK(1000)); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(3000))) .to.emit(sortition, "StakeDelayed") .withArgs(deployer, 2, PNK(3000)); @@ -481,10 +480,9 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000)]); }); }); @@ -507,9 +505,8 @@ describe("Staking", async () => { ]); // stake unchanged, delayed expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(2); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 1st delayed stake got deleted - expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 2nd delayed stake got deleted - expect(await sortition.latestDelayedStakeIndex(deployer, 1)).to.be.equal(0); // Deprecated. Always 0 + expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 1st delayed stake got deleted + expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 2nd delayed stake got deleted }); it("Should transfer PNK after delayed stake execution", async () => { @@ -535,7 +532,6 @@ describe("Staking", async () => { it("Should delay the stake decrease", async () => { expect(await sortition.delayedStakeWriteIndex()).to.be.equal(0); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(1000))) .to.emit(sortition, "StakeDelayed") .withArgs(deployer, 2, PNK(1000)); @@ -547,10 +543,9 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000)]); }); }); @@ -572,9 +567,8 @@ describe("Staking", async () => { ]); // stake unchanged, delayed expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(2); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 1st delayed stake got deleted - expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 2nd delayed stake got deleted - expect(await sortition.latestDelayedStakeIndex(deployer, 1)).to.be.equal(0); // Deprecated. Always 0 + expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 1st delayed stake got deleted + expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 2nd delayed stake got deleted }); it("Should withdraw some PNK", async () => { @@ -600,7 +594,6 @@ describe("Staking", async () => { it("Should delay the stake decrease", async () => { expect(await sortition.delayedStakeWriteIndex()).to.be.equal(0); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(1000))) .to.emit(sortition, "StakeDelayed") .withArgs(deployer, 2, PNK(1000)); @@ -612,17 +605,15 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000)]); }); }); describe("When stake is increased back to the previous amount", () => { it("Should delay the stake increase", async () => { balanceBefore = await pnk.balanceOf(deployer); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(2000))) .to.emit(sortition, "StakeDelayed") .withArgs(deployer, 2, PNK(2000)); @@ -634,11 +625,10 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(2); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000), false]); - expect(await sortition.delayedStakes(2)).to.be.deep.equal([deployer, 2, PNK(2000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000)]); + expect(await sortition.delayedStakes(2)).to.be.deep.equal([deployer, 2, PNK(2000)]); }); }); @@ -663,9 +653,8 @@ describe("Staking", async () => { ]); // stake unchanged, delayed expect(await sortition.delayedStakeWriteIndex()).to.be.equal(2); expect(await sortition.delayedStakeReadIndex()).to.be.equal(3); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 1st delayed stake got deleted - expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 2nd delayed stake got deleted - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 + expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 1st delayed stake got deleted + expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 2nd delayed stake got deleted }); it("Should not transfer any PNK", async () => { @@ -692,7 +681,6 @@ describe("Staking", async () => { expect(await sortition.delayedStakeWriteIndex()).to.be.equal(0); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); await pnk.approve(core.target, PNK(1000)); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(3000))) .to.emit(sortition, "StakeDelayed") .withArgs(deployer, 2, PNK(3000)); @@ -704,17 +692,15 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000)]); }); }); describe("When stake is decreased back to the previous amount", () => { it("Should cancel out the stake decrease back", async () => { balanceBefore = await pnk.balanceOf(deployer); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(2000))) .to.emit(sortition, "StakeDelayed") .withArgs(deployer, 2, PNK(2000)); @@ -726,11 +712,10 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(2); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000), false]); - expect(await sortition.delayedStakes(2)).to.be.deep.equal([deployer, 2, PNK(2000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000)]); + expect(await sortition.delayedStakes(2)).to.be.deep.equal([deployer, 2, PNK(2000)]); }); }); @@ -753,9 +738,8 @@ describe("Staking", async () => { ]); // stake unchanged, delayed expect(await sortition.delayedStakeWriteIndex()).to.be.equal(2); expect(await sortition.delayedStakeReadIndex()).to.be.equal(3); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 1st delayed stake got deleted - expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 2nd delayed stake got deleted - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 + expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 1st delayed stake got deleted + expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 2nd delayed stake got deleted }); it("Should not transfer any PNK", async () => { diff --git a/contracts/test/arbitration/staking.ts b/contracts/test/arbitration/staking.ts index d27a5f10e..7e5344c39 100644 --- a/contracts/test/arbitration/staking.ts +++ b/contracts/test/arbitration/staking.ts @@ -82,7 +82,6 @@ describe("Staking", async () => { expect(await sortition.delayedStakeWriteIndex()).to.be.equal(0); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); await pnk.approve(core.target, PNK(1000)); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(3000))) .to.emit(sortition, "StakeDelayed") .withArgs(deployer, 2, PNK(3000)); @@ -94,10 +93,9 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000)]); }); }); @@ -120,9 +118,8 @@ describe("Staking", async () => { ]); // stake unchanged, delayed expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(2); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 1st delayed stake got deleted - expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 2nd delayed stake got deleted - expect(await sortition.latestDelayedStakeIndex(deployer, 1)).to.be.equal(0); // Deprecated. Always 0 + expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 1st delayed stake got deleted + expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 2nd delayed stake got deleted }); it("Should transfer PNK after delayed stake execution", async () => { @@ -146,7 +143,6 @@ describe("Staking", async () => { it("Should delay the stake decrease", async () => { expect(await sortition.delayedStakeWriteIndex()).to.be.equal(0); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(1000))) .to.emit(sortition, "StakeDelayed") .withArgs(deployer, 2, PNK(1000)); @@ -158,10 +154,9 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000)]); }); }); @@ -183,9 +178,8 @@ describe("Staking", async () => { ]); // stake unchanged, delayed expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(2); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 1st delayed stake got deleted - expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 2nd delayed stake got deleted - expect(await sortition.latestDelayedStakeIndex(deployer, 1)).to.be.equal(0); // Deprecated. Always 0 + expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 1st delayed stake got deleted + expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 2nd delayed stake got deleted }); it("Should withdraw some PNK", async () => { @@ -209,7 +203,6 @@ describe("Staking", async () => { it("Should delay the stake decrease", async () => { expect(await sortition.delayedStakeWriteIndex()).to.be.equal(0); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(1000))) .to.emit(sortition, "StakeDelayed") .withArgs(deployer, 2, PNK(1000)); @@ -221,17 +214,15 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000)]); }); }); describe("When stake is increased back to the previous amount", () => { it("Should delay the stake increase", async () => { balanceBefore = await pnk.balanceOf(deployer); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(2000))).to.emit(sortition, "StakeDelayed"); expect(await sortition.getJurorBalance(deployer, 2)).to.be.deep.equal([PNK(4000), 0, PNK(2000), 2]); // stake unchanged, delayed }); @@ -241,11 +232,10 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(2); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000), false]); - expect(await sortition.delayedStakes(2)).to.be.deep.equal([deployer, 2, PNK(2000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(1000)]); + expect(await sortition.delayedStakes(2)).to.be.deep.equal([deployer, 2, PNK(2000)]); }); }); @@ -270,9 +260,8 @@ describe("Staking", async () => { ]); // stake unchanged, delayed expect(await sortition.delayedStakeWriteIndex()).to.be.equal(2); expect(await sortition.delayedStakeReadIndex()).to.be.equal(3); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 1st delayed stake got deleted - expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 2nd delayed stake got deleted - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 + expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 1st delayed stake got deleted + expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 2nd delayed stake got deleted }); it("Should not transfer any PNK", async () => { @@ -297,7 +286,6 @@ describe("Staking", async () => { expect(await sortition.delayedStakeWriteIndex()).to.be.equal(0); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); await pnk.approve(core.target, PNK(1000)); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(3000))) .to.emit(sortition, "StakeDelayed") .withArgs(deployer, 2, PNK(3000)); @@ -309,17 +297,15 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(1); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000)]); }); }); describe("When stake is decreased back to the previous amount", () => { it("Should cancel out the stake decrease back", async () => { balanceBefore = await pnk.balanceOf(deployer); - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 await expect(core.setStake(2, PNK(2000))) .to.emit(sortition, "StakeDelayed") .withArgs(deployer, 2, PNK(2000)); @@ -331,11 +317,10 @@ describe("Staking", async () => { }); it("Should store the delayed stake for later", async () => { - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 expect(await sortition.delayedStakeWriteIndex()).to.be.equal(2); expect(await sortition.delayedStakeReadIndex()).to.be.equal(1); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000), false]); - expect(await sortition.delayedStakes(2)).to.be.deep.equal([deployer, 2, PNK(2000), false]); + expect(await sortition.delayedStakes(1)).to.be.deep.equal([deployer, 2, PNK(3000)]); + expect(await sortition.delayedStakes(2)).to.be.deep.equal([deployer, 2, PNK(2000)]); }); }); @@ -358,9 +343,8 @@ describe("Staking", async () => { ]); // stake unchanged, delayed expect(await sortition.delayedStakeWriteIndex()).to.be.equal(2); expect(await sortition.delayedStakeReadIndex()).to.be.equal(3); - expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 1st delayed stake got deleted - expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0, false]); // the 2nd delayed stake got deleted - expect(await sortition.latestDelayedStakeIndex(deployer, 2)).to.be.equal(0); // Deprecated. Always 0 + expect(await sortition.delayedStakes(1)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 1st delayed stake got deleted + expect(await sortition.delayedStakes(2)).to.be.deep.equal([ethers.ZeroAddress, 0, 0]); // the 2nd delayed stake got deleted }); it("Should not transfer any PNK", async () => { diff --git a/contracts/test/foundry/KlerosCore_Disputes.t.sol b/contracts/test/foundry/KlerosCore_Disputes.t.sol index f9840014d..5b61c93cb 100644 --- a/contracts/test/foundry/KlerosCore_Disputes.t.sol +++ b/contracts/test/foundry/KlerosCore_Disputes.t.sol @@ -10,6 +10,7 @@ import "../../src/libraries/Constants.sol"; /// @title KlerosCore_DisputesTest /// @dev Tests for KlerosCore dispute creation and management +/// forge-lint: disable-next-item(erc20-unchecked-transfer) contract KlerosCore_DisputesTest is KlerosCore_TestBase { function test_createDispute_eth() public { // Create a new court and DK to test non-standard extra data diff --git a/contracts/test/foundry/KlerosCore_Execution.t.sol b/contracts/test/foundry/KlerosCore_Execution.t.sol index 01efd6409..7681eff29 100644 --- a/contracts/test/foundry/KlerosCore_Execution.t.sol +++ b/contracts/test/foundry/KlerosCore_Execution.t.sol @@ -11,6 +11,7 @@ import "../../src/libraries/Constants.sol"; /// @title KlerosCore_ExecutionTest /// @dev Tests for KlerosCore execution, rewards, and ruling finalization +/// forge-lint: disable-next-item(erc20-unchecked-transfer) contract KlerosCore_ExecutionTest is KlerosCore_TestBase { function test_execute() public { uint256 disputeID = 0; @@ -98,16 +99,16 @@ contract KlerosCore_ExecutionTest is KlerosCore_TestBase { vm.expectEmit(true, true, true, true); emit SortitionModule.StakeLocked(staker1, 1000, true); vm.expectEmit(true, true, true, true); - emit KlerosCore.TokenAndETHShift(staker1, disputeID, 0, 0, -int256(1000), 0, IERC20(address(0))); + emit KlerosCore.TokenAndETHShift(staker1, disputeID, 0, 0, 0, -int256(1000), 0, IERC20(address(0))); // penalties // Check iterations for the winning staker to see the shifts vm.expectEmit(true, true, true, true); emit SortitionModule.StakeLocked(staker2, 0, true); vm.expectEmit(true, true, true, true); - emit KlerosCore.TokenAndETHShift(staker2, disputeID, 0, 10000, 0, 0, IERC20(address(0))); + emit KlerosCore.TokenAndETHShift(staker2, disputeID, 0, 10000, 0, 0, 0, IERC20(address(0))); // penalties but amounts are 0 vm.expectEmit(true, true, true, true); emit SortitionModule.StakeLocked(staker2, 0, true); vm.expectEmit(true, true, true, true); - emit KlerosCore.TokenAndETHShift(staker2, disputeID, 0, 10000, 0, 0, IERC20(address(0))); + emit KlerosCore.TokenAndETHShift(staker2, disputeID, 0, 10000, 0, 0, 0, IERC20(address(0))); // penalties but amounts are 0 core.execute(disputeID, 0, 3); // Do 3 iterations to check penalties first (uint256 totalStaked, uint256 totalLocked, , ) = sortitionModule.getJurorBalance(staker1, GENERAL_COURT); @@ -123,16 +124,16 @@ contract KlerosCore_ExecutionTest is KlerosCore_TestBase { vm.expectEmit(true, true, true, true); emit SortitionModule.StakeLocked(staker1, 0, true); vm.expectEmit(true, true, true, true); - emit KlerosCore.TokenAndETHShift(staker1, disputeID, 0, 0, 0, 0, IERC20(address(0))); + emit KlerosCore.TokenAndETHShift(staker1, disputeID, 0, 0, 0, 0, 0, IERC20(address(0))); // rewards but amounts are 0 // Check iterations for the winning staker to see the shifts vm.expectEmit(true, true, true, true); emit SortitionModule.StakeLocked(staker2, 1000, true); vm.expectEmit(true, true, true, true); - emit KlerosCore.TokenAndETHShift(staker2, disputeID, 0, 10000, 500, 0.045 ether, IERC20(address(0))); + emit KlerosCore.TokenAndETHShift(staker2, disputeID, 0, 10000, 10000, 500, 0.045 ether, IERC20(address(0))); // rewards vm.expectEmit(true, true, true, true); emit SortitionModule.StakeLocked(staker2, 1000, true); vm.expectEmit(true, true, true, true); - emit KlerosCore.TokenAndETHShift(staker2, disputeID, 0, 10000, 500, 0.045 ether, IERC20(address(0))); + emit KlerosCore.TokenAndETHShift(staker2, disputeID, 0, 10000, 10000, 500, 0.045 ether, IERC20(address(0))); // rewards core.execute(disputeID, 0, 10); // Finish the iterations. We need only 3 but check that it corrects the count. (, totalLocked, , ) = sortitionModule.getJurorBalance(staker2, GENERAL_COURT); @@ -537,9 +538,9 @@ contract KlerosCore_ExecutionTest is KlerosCore_TestBase { // Check only once per penalty and per reward vm.expectEmit(true, true, true, true); - emit KlerosCore.TokenAndETHShift(staker1, disputeID, 0, 10000, 0, 0, feeToken); + emit KlerosCore.TokenAndETHShift(staker1, disputeID, 0, 10000, 0, 0, 0, feeToken); // penalties but amounts are 0 vm.expectEmit(true, true, true, true); - emit KlerosCore.TokenAndETHShift(staker1, disputeID, 0, 10000, 0, 0.06 ether, feeToken); + emit KlerosCore.TokenAndETHShift(staker1, disputeID, 0, 10000, 10000, 0, 0.06 ether, feeToken); // rewards core.execute(disputeID, 0, 6); KlerosCore.Round memory round = core.getRoundInfo(disputeID, 0); diff --git a/contracts/test/foundry/KlerosCore_Governance.t.sol b/contracts/test/foundry/KlerosCore_Governance.t.sol index 53d438c4d..c95ba123f 100644 --- a/contracts/test/foundry/KlerosCore_Governance.t.sol +++ b/contracts/test/foundry/KlerosCore_Governance.t.sol @@ -266,7 +266,7 @@ contract KlerosCore_GovernanceTest is KlerosCore_TestBase { supportedDK ); - _assertCourtParameters(2, GENERAL_COURT, true, 2000, 20000, 0.04 ether, 50, false); + _assertCourtParameters(2, GENERAL_COURT, true, 2000, 20000, 0.04 ether, 50); uint256[] memory children = core.getCourtChildren(2); assertEq(children.length, 0, "No children"); @@ -356,7 +356,7 @@ contract KlerosCore_GovernanceTest is KlerosCore_TestBase { [uint256(10), uint256(20), uint256(30), uint256(40)] // Times per period ); - _assertCourtParameters(GENERAL_COURT, FORKING_COURT, true, 2000, 20000, 0.04 ether, 50, false); + _assertCourtParameters(GENERAL_COURT, FORKING_COURT, true, 2000, 20000, 0.04 ether, 50); _assertTimesPerPeriod(GENERAL_COURT, [uint256(10), uint256(20), uint256(30), uint256(40)]); } diff --git a/contracts/test/foundry/KlerosCore_Initialization.t.sol b/contracts/test/foundry/KlerosCore_Initialization.t.sol index 7e521063d..3aa316c0d 100644 --- a/contracts/test/foundry/KlerosCore_Initialization.t.sol +++ b/contracts/test/foundry/KlerosCore_Initialization.t.sol @@ -14,8 +14,9 @@ import "../../src/libraries/Constants.sol"; /// @title KlerosCore_InitializationTest /// @dev Tests for KlerosCore initialization and basic configuration +/// forge-lint: disable-next-item(erc20-unchecked-transfer) contract KlerosCore_InitializationTest is KlerosCore_TestBase { - function test_initialize() public { + function test_initialize() public view { assertEq(core.owner(), msg.sender, "Wrong owner"); assertEq(core.guardian(), guardian, "Wrong guardian"); assertEq(address(core.pinakion()), address(pinakion), "Wrong pinakion address"); @@ -23,8 +24,8 @@ contract KlerosCore_InitializationTest is KlerosCore_TestBase { assertEq(address(core.sortitionModule()), address(sortitionModule), "Wrong sortitionModule address"); assertEq(core.getDisputeKitsLength(), 2, "Wrong DK array length"); - _assertCourtParameters(FORKING_COURT, FORKING_COURT, false, 0, 0, 0, 0, false); - _assertCourtParameters(GENERAL_COURT, FORKING_COURT, false, 1000, 10000, 0.03 ether, 511, false); + _assertCourtParameters(FORKING_COURT, FORKING_COURT, false, 0, 0, 0, 0); + _assertCourtParameters(GENERAL_COURT, FORKING_COURT, false, 1000, 10000, 0.03 ether, 511); uint256[] memory children = core.getCourtChildren(GENERAL_COURT); assertEq(children.length, 0, "No children"); @@ -96,7 +97,6 @@ contract KlerosCore_InitializationTest is KlerosCore_TestBase { address newOwner = msg.sender; address newGuardian = vm.addr(1); address newStaker1 = vm.addr(2); - address newOther = vm.addr(9); address newJurorProsecutionModule = vm.addr(8); uint256 newMinStake = 1000; uint256 newAlpha = 10000; diff --git a/contracts/test/foundry/KlerosCore_Staking.t.sol b/contracts/test/foundry/KlerosCore_Staking.t.sol index 070a38716..a7cb96bef 100644 --- a/contracts/test/foundry/KlerosCore_Staking.t.sol +++ b/contracts/test/foundry/KlerosCore_Staking.t.sol @@ -10,6 +10,7 @@ import "../../src/libraries/Constants.sol"; /// @title KlerosCore_StakingTest /// @dev Tests for KlerosCore staking mechanics and stake management +/// forge-lint: disable-next-item(erc20-unchecked-transfer) contract KlerosCore_StakingTest is KlerosCore_TestBase { function test_setStake_increase() public { vm.prank(owner); @@ -194,13 +195,10 @@ contract KlerosCore_StakingTest is KlerosCore_TestBase { uint256 delayedStakeId = sortitionModule.delayedStakeWriteIndex(); assertEq(delayedStakeId, 1, "Wrong delayedStakeWriteIndex"); assertEq(sortitionModule.delayedStakeReadIndex(), 1, "Wrong delayedStakeReadIndex"); - (address account, uint96 courtID, uint256 stake, bool alreadyTransferred) = sortitionModule.delayedStakes( - delayedStakeId - ); + (address account, uint96 courtID, uint256 stake) = sortitionModule.delayedStakes(delayedStakeId); assertEq(account, staker1, "Wrong staker account"); assertEq(courtID, GENERAL_COURT, "Wrong court id"); assertEq(stake, 1500, "Wrong amount staked in court"); - assertEq(alreadyTransferred, false, "Should be flagged as transferred"); (uint256 totalStaked, uint256 totalLocked, uint256 stakedInCourt, uint256 nbCourts) = sortitionModule .getJurorBalance(staker1, GENERAL_COURT); @@ -342,25 +340,22 @@ contract KlerosCore_StakingTest is KlerosCore_TestBase { assertEq(sortitionModule.delayedStakeWriteIndex(), 3, "Wrong delayedStakeWriteIndex"); assertEq(sortitionModule.delayedStakeReadIndex(), 1, "Wrong delayedStakeReadIndex"); - (address account, uint96 courtID, uint256 stake, bool alreadyTransferred) = sortitionModule.delayedStakes(1); + (address account, uint96 courtID, uint256 stake) = sortitionModule.delayedStakes(1); // Check each delayed stake assertEq(account, staker1, "Wrong staker account for the first delayed stake"); assertEq(courtID, GENERAL_COURT, "Wrong court ID"); assertEq(stake, 1500, "Wrong staking amount"); - assertEq(alreadyTransferred, false, "Should be false"); - (account, courtID, stake, alreadyTransferred) = sortitionModule.delayedStakes(2); + (account, courtID, stake) = sortitionModule.delayedStakes(2); assertEq(account, staker2, "Wrong staker2 account"); assertEq(courtID, GENERAL_COURT, "Wrong court id for staker2"); assertEq(stake, 0, "Wrong amount for delayed stake of staker2"); - assertEq(alreadyTransferred, false, "Should be false"); - (account, courtID, stake, alreadyTransferred) = sortitionModule.delayedStakes(3); + (account, courtID, stake) = sortitionModule.delayedStakes(3); assertEq(account, staker1, "Wrong staker1 account"); assertEq(courtID, GENERAL_COURT, "Wrong court id for staker1"); assertEq(stake, 1800, "Wrong amount for delayed stake of staker1"); - assertEq(alreadyTransferred, false, "Should be false"); // So far the only amount transferred was 10000 by staker2. Staker 1 has two delayed stakes, for 1500 and 1800 pnk. assertEq(pinakion.balanceOf(address(core)), 10000, "Wrong token balance of the core"); @@ -394,12 +389,11 @@ contract KlerosCore_StakingTest is KlerosCore_TestBase { // Check that delayed stakes are nullified for (uint i = 2; i <= sortitionModule.delayedStakeWriteIndex(); i++) { - (account, courtID, stake, alreadyTransferred) = sortitionModule.delayedStakes(i); + (account, courtID, stake) = sortitionModule.delayedStakes(i); assertEq(account, address(0), "Wrong staker account after delayed stake deletion"); assertEq(courtID, 0, "Court id should be nullified"); assertEq(stake, 0, "No amount to stake"); - assertEq(alreadyTransferred, false, "Should be false"); } assertEq(pinakion.balanceOf(staker1), 999999999999998200, "Wrong token balance of staker1"); diff --git a/contracts/test/foundry/KlerosCore_TestBase.sol b/contracts/test/foundry/KlerosCore_TestBase.sol index 9418f2444..991cd7469 100644 --- a/contracts/test/foundry/KlerosCore_TestBase.sol +++ b/contracts/test/foundry/KlerosCore_TestBase.sol @@ -23,6 +23,7 @@ import {IKlerosCore, KlerosCoreSnapshotProxy} from "../../src/arbitration/view/K /// @title KlerosCore_TestBase /// @dev Abstract base contract for KlerosCore tests containing shared setup and utilities +/// forge-lint: disable-next-item(erc20-unchecked-transfer) abstract contract KlerosCore_TestBase is Test { event Initialized(uint64 version); @@ -223,17 +224,15 @@ abstract contract KlerosCore_TestBase is Test { uint256 expectedMinStake, uint256 expectedAlpha, uint256 expectedFeeForJuror, - uint256 expectedJurorsForJump, - bool expectedDisabled - ) internal { + uint256 expectedJurorsForJump + ) internal view { ( uint96 courtParent, bool courtHiddenVotes, uint256 courtMinStake, uint256 courtAlpha, uint256 courtFeeForJuror, - uint256 courtJurorsForCourtJump, - bool courtDisabled + uint256 courtJurorsForCourtJump ) = core.courts(courtId); assertEq(courtParent, expectedParent, "Wrong court parent"); @@ -242,11 +241,10 @@ abstract contract KlerosCore_TestBase is Test { assertEq(courtAlpha, expectedAlpha, "Wrong alpha value"); assertEq(courtFeeForJuror, expectedFeeForJuror, "Wrong feeForJuror value"); assertEq(courtJurorsForCourtJump, expectedJurorsForJump, "Wrong jurorsForCourtJump value"); - assertEq(courtDisabled, expectedDisabled, "Wrong disabled state"); } /// @dev Helper function to check times per period - function _assertTimesPerPeriod(uint96 courtId, uint256[4] memory expectedTimes) internal { + function _assertTimesPerPeriod(uint96 courtId, uint256[4] memory expectedTimes) internal view { uint256[4] memory courtTimesPerPeriod = core.getTimesPerPeriod(courtId); for (uint256 i = 0; i < 4; i++) { assertEq(courtTimesPerPeriod[i], expectedTimes[i], "Wrong times per period"); diff --git a/contracts/test/integration/index.ts b/contracts/test/integration/index.ts index f3c97b2a1..c8a656357 100644 --- a/contracts/test/integration/index.ts +++ b/contracts/test/integration/index.ts @@ -76,28 +76,28 @@ describe("Integration tests", async () => { await core.setStake(1, ONE_THOUSAND_PNK); await sortitionModule.getJurorBalance(deployer, 1).then((result) => { - expect(result.totalStaked).to.equal(ONE_THOUSAND_PNK); + expect(result.totalStakedPnk).to.equal(ONE_THOUSAND_PNK); expect(result.totalLocked).to.equal(0); logJurorBalance(result); }); await core.setStake(1, ONE_HUNDRED_PNK * 5n); await sortitionModule.getJurorBalance(deployer, 1).then((result) => { - expect(result.totalStaked).to.equal(ONE_HUNDRED_PNK * 5n); + expect(result.totalStakedPnk).to.equal(ONE_HUNDRED_PNK * 5n); expect(result.totalLocked).to.equal(0); logJurorBalance(result); }); await core.setStake(1, 0); await sortitionModule.getJurorBalance(deployer, 1).then((result) => { - expect(result.totalStaked).to.equal(0); + expect(result.totalStakedPnk).to.equal(0); expect(result.totalLocked).to.equal(0); logJurorBalance(result); }); await core.setStake(1, ONE_THOUSAND_PNK * 4n); await sortitionModule.getJurorBalance(deployer, 1).then((result) => { - expect(result.totalStaked).to.equal(ONE_THOUSAND_PNK * 4n); + expect(result.totalStakedPnk).to.equal(ONE_THOUSAND_PNK * 4n); expect(result.totalLocked).to.equal(0); logJurorBalance(result); }); @@ -117,8 +117,7 @@ describe("Integration tests", async () => { foreignGateway.target, 1, 46619385602526556702049273755915206310773794210139929511467397410441395547901n, - 0, - "" + 0 ); if (tx.blockNumber === null) throw new Error("tx.blockNumber is null"); const lastBlock = await ethers.provider.getBlock(tx.blockNumber - 1); @@ -134,7 +133,7 @@ describe("Integration tests", async () => { // Relayer tx const tx2 = await homeGateway .connect(relayer) - ["relayCreateDispute((bytes32,uint256,address,uint256,uint256,uint256,string,uint256,bytes))"]( + ["relayCreateDispute((bytes32,uint256,address,uint256,uint256,uint256,uint256,bytes))"]( { foreignBlockHash: ethers.toBeHex(lastBlock.hash), foreignChainID: 31337, @@ -142,7 +141,6 @@ describe("Integration tests", async () => { foreignDisputeID: disputeId, externalDisputeID: ethers.keccak256(ethers.toUtf8Bytes("future of france")), templateId: 0, - templateUri: "", choices: 2, extraData: "0x00", }, @@ -203,6 +201,10 @@ describe("Integration tests", async () => { }; }); -const logJurorBalance = async (result: { totalStaked: bigint; totalLocked: bigint }) => { - console.log("staked=%s, locked=%s", ethers.formatUnits(result.totalStaked), ethers.formatUnits(result.totalLocked)); +const logJurorBalance = async (result: { totalStakedPnk: bigint; totalLocked: bigint }) => { + console.log( + "staked=%s, locked=%s", + ethers.formatUnits(result.totalStakedPnk), + ethers.formatUnits(result.totalLocked) + ); }; diff --git a/foundry.toml b/foundry.toml new file mode 100644 index 000000000..fad3d8b70 --- /dev/null +++ b/foundry.toml @@ -0,0 +1,12 @@ +# For `forge doc` only which requires running from the top-level folder +[profile.default] +solc = "0.8.30" +evm_version = "cancun" +via_ir = true +optimizer = true +optimizer_runs = 10000 + +src = 'contracts/src' +out = 'contracts/out' +libs = ['./node_modules', 'contracts/lib'] + diff --git a/package.json b/package.json index 01e9468a1..6104cc7cc 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,9 @@ "postinstall": "yarn check-prerequisites; husky install", "reinstall": "YARN_CHECKSUM_BEHAVIOR=update yarn install --no-immutable", "build:web:ci": "yarn workspaces focus @kleros/kleros-v2-web && yarn workspaces foreach -Ap --include kleros-app --include contracts run build && yarn workspace @kleros/kleros-v2-web build-netlify", - "build:web-devtools:ci": "yarn workspaces focus @kleros/kleros-v2-web-devtools && yarn workspaces foreach -Ap --include contracts run build && yarn workspace @kleros/kleros-v2-web-devtools build-netlify" + "build:web-devtools:ci": "yarn workspaces focus @kleros/kleros-v2-web-devtools && yarn workspaces foreach -Ap --include contracts run build && yarn workspace @kleros/kleros-v2-web-devtools build-netlify", + "build:contract-docs": "forge doc --build --out contracts/dist", + "start:contract-docs": "forge doc --serve" }, "alias": { "process": "process/browser.js", diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 000000000..c54475ab1 --- /dev/null +++ b/remappings.txt @@ -0,0 +1,9 @@ +@ensdomains/=node_modules/@ensdomains/ +@openzeppelin/=node_modules/@openzeppelin/ +@kleros/=node_modules/@kleros/ +ds-test/=contracts/ +forge-std/=contracts/ +eth-gas-reporter/=node_modules/eth-gas-reporter/ +hardhat-deploy/=node_modules/hardhat-deploy/ +hardhat/=node_modules/hardhat/ +solmate/=contracts/lib/solmate/src/