diff --git a/contracts/.solcover.js b/contracts/.solcover.js
index d9911ce98..46bc39e3c 100644
--- a/contracts/.solcover.js
+++ b/contracts/.solcover.js
@@ -7,6 +7,7 @@ const shell = require("shelljs");
module.exports = {
istanbulReporter: ["lcov"],
configureYulOptimizer: true,
+ irMinimum: true,
onCompileComplete: async function (_config) {
await run("typechain");
},
diff --git a/contracts/CHANGELOG.md b/contracts/CHANGELOG.md
index 2e571aa14..07201c2f5 100644
--- a/contracts/CHANGELOG.md
+++ b/contracts/CHANGELOG.md
@@ -4,7 +4,39 @@ All notable changes to this package will be documented in this file.
The format is based on [Common Changelog](https://common-changelog.org/).
-## [0.11.0] - 2025-08-01
+## [0.13.0] - 2025-08-07 (Not published yet)
+
+### Changed
+
+- **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))
+- Make the primary VRF-based RNG fall back to `BlockhashRNG` if the VRF request is not fulfilled within a timeout ([#2054](https://github.com/kleros/kleros-v2/issues/2054))
+- Authenticate the calls to the RNGs to prevent 3rd parties from depleting the Chainlink VRF subscription funds ([#2054](https://github.com/kleros/kleros-v2/issues/2054))
+- Use `block.timestamp` rather than `block.number` for `BlockhashRNG` for better reliability on Arbitrum as block production is sporadic depending on network conditions. ([#2054](https://github.com/kleros/kleros-v2/issues/2054))
+- Set the Hardhat Solidity version to v0.8.30 and enable the IR pipeline ([#2069](https://github.com/kleros/kleros-v2/issues/2069))
+- Set the Foundry Solidity version to v0.8.30 and enable the IR pipeline ([#2073](https://github.com/kleros/kleros-v2/issues/2073))
+- Widen the allowed solc version to any v0.8.x for the interfaces only ([#2083](https://github.com/kleros/kleros-v2/issues/2083))
+- Make `IDisputeKit.getDegreeOfCoherenceReward()` multi-dimensional so different calculations may be applied to PNK rewards, fee rewards and PNK penalties (future-proofing) ([#2090](https://github.com/kleros/kleros-v2/issues/2090))
+- Consolidate the constant `ALPHA_DIVISOR` with `ONE_BASIS_POINTS` ([#2090](https://github.com/kleros/kleros-v2/issues/2090))
+- Bump `hardhat` to v2.26.2 ([#2069](https://github.com/kleros/kleros-v2/issues/2069))
+- Bump `@kleros/vea-contracts` to v0.7.0 ([#2073](https://github.com/kleros/kleros-v2/issues/2073))
+
+### 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))
+
+## [0.12.0] - 2025-08-05
+
+### Changed
+
+- **Breaking:** Make `viem` a peer dependency, it should be provided by the consuming package ([`4594536`](https://github.com/kleros/kleros-v2/commit/4594536c))
+
+### Added
+
+- Add helper function `getDisputeKitsViem` to retrieve a deployment's available dispute kit infos including their capabilities (`isShutter`, `isGated`) ([`5a81f9e`](https://github.com/kleros/kleros-v2/commit/5a81f9ec))
+
+## [0.11.0] - 2025-08-02
### Changed
@@ -107,6 +139,8 @@ The format is based on [Common Changelog](https://common-changelog.org/).
## [0.8.1] - 2025-04-10
+[0.13.0]: https://github.com/kleros/kleros-v2/releases/tag/@kleros%2Fkleros-v2-contracts@0.13.0
+[0.12.0]: https://github.com/kleros/kleros-v2/releases/tag/@kleros%2Fkleros-v2-contracts@0.12.0
[0.11.0]: https://github.com/kleros/kleros-v2/releases/tag/@kleros%2Fkleros-v2-contracts@0.11.0
[0.10.0]: https://github.com/kleros/kleros-v2/releases/tag/@kleros%2Fkleros-v2-contracts@0.10.0
[0.9.4]: https://github.com/kleros/kleros-v2/releases/tag/@kleros%2Fkleros-v2-contracts@0.9.4
diff --git a/contracts/audit/METRICS.html b/contracts/audit/METRICS.html
new file mode 100644
index 000000000..df48b7b11
--- /dev/null
+++ b/contracts/audit/METRICS.html
@@ -0,0 +1,209524 @@
+
+
+
+
+
+
+
+
+ Solidity Metrics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Rendering Report...
Note: This window will update automatically. In case it is not, close the window and try again (vscode
+ bug) :/
+
+
+
+
diff --git a/contracts/audit/METRICS.md b/contracts/audit/METRICS.md
new file mode 100644
index 000000000..1a1a40ea3
--- /dev/null
+++ b/contracts/audit/METRICS.md
@@ -0,0 +1,681 @@
+[
](https://consensys.io/diligence)
+
+[[ 🌐 ](https://consensys.io/diligence) [ 📩 ](mailto:diligence@consensys.net) [ 🔥 ](https://consensys.io/diligence/tools/)]
+
+
+# Solidity Metrics for 'CLI'
+
+## Table of contents
+
+- [Scope](#t-scope)
+ - [Source Units in Scope](#t-source-Units-in-Scope)
+ - [Deployable Logic Contracts](#t-deployable-contracts)
+ - [Out of Scope](#t-out-of-scope)
+ - [Excluded Source Units](#t-out-of-scope-excluded-source-units)
+ - [Duplicate Source Units](#t-out-of-scope-duplicate-source-units)
+ - [Doppelganger Contracts](#t-out-of-scope-doppelganger-contracts)
+- [Report Overview](#t-report)
+ - [Risk Summary](#t-risk)
+ - [Source Lines](#t-source-lines)
+ - [Inline Documentation](#t-inline-documentation)
+ - [Components](#t-components)
+ - [Exposed Functions](#t-exposed-functions)
+ - [StateVariables](#t-statevariables)
+ - [Capabilities](#t-capabilities)
+ - [Dependencies](#t-package-imports)
+ - [Totals](#t-totals)
+
+## Scope
+
+This section lists files that are in scope for the metrics report.
+
+- **Project:** `'CLI'`
+- **Included Files:**
+ - ``
+- **Excluded Paths:**
+ - ``
+- **File Limit:** `undefined`
+
+ - **Exclude File list Limit:** `undefined`
+
+- **Workspace Repository:** `unknown` (`undefined`@`undefined`)
+
+### Source Units in Scope
+
+Source Units Analyzed: **`31`**
+Source Units in Scope: **`31`** (**100%**)
+
+| Type | File | Logic Contracts | Interfaces | Lines | nLines | nSLOC | Comment Lines | Complex. Score | Capabilities |
+| -------- | --------------------------------------------------------- | --------------- | ---------- | -------- | -------- | -------- | ------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| 📝 | src/arbitration/KlerosCore.sol | 1 | \*\*\*\* | 75 | 63 | 28 | 26 | 13 | \*\*\*\* |
+| 🎨 | src/arbitration/KlerosCoreBase.sol | 1 | \*\*\*\* | 1211 | 1162 | 809 | 286 | 449 | **🖥💰** |
+| 📝 | src/arbitration/KlerosCoreNeo.sol | 1 | \*\*\*\* | 142 | 124 | 55 | 51 | 36 | \*\*\*\* |
+| 📝 | src/arbitration/PolicyRegistry.sol | 1 | \*\*\*\* | 88 | 88 | 30 | 41 | 24 | \*\*\*\* |
+| 📝 | src/arbitration/SortitionModule.sol | 1 | \*\*\*\* | 50 | 44 | 15 | 20 | 13 | \*\*\*\* |
+| 🎨 | src/arbitration/SortitionModuleBase.sol | 1 | \*\*\*\* | 689 | 645 | 393 | 205 | 300 | **🖥🧮** |
+| 📝 | src/arbitration/SortitionModuleNeo.sol | 1 | \*\*\*\* | 103 | 91 | 48 | 28 | 31 | \*\*\*\* |
+| 📝 | src/arbitration/arbitrables/DisputeResolver.sol | 1 | \*\*\*\* | 149 | 134 | 72 | 50 | 43 | **💰** |
+| 📝 | src/arbitration/DisputeTemplateRegistry.sol | 1 | \*\*\*\* | 83 | 79 | 30 | 33 | 25 | \*\*\*\* |
+| 📝 | src/arbitration/dispute-kits/DisputeKitClassic.sol | 1 | \*\*\*\* | 46 | 46 | 16 | 21 | 13 | \*\*\*\* |
+| 🎨 | src/arbitration/dispute-kits/DisputeKitClassicBase.sol | 1 | \*\*\*\* | 713 | 632 | 365 | 225 | 192 | **💰🧮** |
+| 📝🔍 | src/arbitration/dispute-kits/DisputeKitGated.sol | 1 | 2 | 117 | 99 | 39 | 51 | 57 | **🖥🔆** |
+| 📝🔍 | src/arbitration/dispute-kits/DisputeKitGatedShutter.sol | 1 | 2 | 194 | 159 | 59 | 84 | 69 | **🖥🧮🔆** |
+| 📝 | src/arbitration/dispute-kits/DisputeKitShutter.sol | 1 | \*\*\*\* | 123 | 107 | 36 | 54 | 27 | **🧮** |
+| 📝🔍 | src/arbitration/dispute-kits/DisputeKitSybilResistant.sol | 1 | 1 | 76 | 58 | 20 | 33 | 16 | **🔆** |
+| 📝 | src/arbitration/evidence/EvidenceModule.sol | 1 | \*\*\*\* | 70 | 70 | 26 | 30 | 21 | \*\*\*\* |
+| 🔍 | src/arbitration/interfaces/IArbitrableV2.sol | \*\*\*\* | 1 | 40 | 39 | 12 | 22 | 3 | \*\*\*\* |
+| 🔍 | src/arbitration/interfaces/IArbitratorV2.sol | \*\*\*\* | 1 | 83 | 44 | 9 | 50 | 14 | **💰** |
+| 🔍 | src/arbitration/interfaces/IDisputeKit.sol | \*\*\*\* | 1 | 128 | 39 | 11 | 60 | 23 | \*\*\*\* |
+| 🔍 | src/arbitration/interfaces/IDisputeTemplateRegistry.sol | \*\*\*\* | 1 | 25 | 20 | 9 | 8 | 3 | \*\*\*\* |
+| 🔍 | src/arbitration/interfaces/IEvidence.sol | \*\*\*\* | 1 | 12 | 12 | 4 | 6 | 1 | \*\*\*\* |
+| 🔍 | src/arbitration/interfaces/ISortitionModule.sol | \*\*\*\* | 1 | 63 | 16 | 10 | 4 | 33 | \*\*\*\* |
+| | src/libraries/Constants.sol | \*\*\*\* | \*\*\*\* | 39 | 39 | 26 | 13 | 2 | \*\*\*\* |
+| 📚 | src/libraries/SafeERC20.sol | 1 | \*\*\*\* | 47 | 47 | 18 | 24 | 12 | \*\*\*\* |
+| 📚🔍 | src/libraries/SafeSend.sol | 1 | 1 | 24 | 19 | 9 | 7 | 15 | **💰📤** |
+| 📝 | src/rng/RNGWithFallback.sol | 1 | \*\*\*\* | 103 | 103 | 48 | 41 | 35 | \*\*\*\* |
+| 📝 | src/rng/ChainlinkRNG.sol | 1 | \*\*\*\* | 173 | 173 | 85 | 70 | 50 | \*\*\*\* |
+| 🔍 | src/rng/IRNG.sol | \*\*\*\* | 1 | 13 | 8 | 3 | 5 | 5 | \*\*\*\* |
+| 🎨 | src/proxy/UUPSProxiable.sol | 1 | \*\*\*\* | 140 | 122 | 44 | 71 | 46 | **🖥💰👥♻️** |
+| 📝 | src/proxy/UUPSProxy.sol | 1 | \*\*\*\* | 90 | 90 | 38 | 37 | 65 | **🖥💰👥** |
+| 🎨 | src/proxy/Initializable.sol | 1 | \*\*\*\* | 215 | 215 | 70 | 128 | 31 | **🖥** |
+| 📝📚🔍🎨 | **Totals** | **23** | **13** | **5124** | **4587** | **2437** | **1784** | **1667** | **🖥💰📤👥🧮🔆♻️** |
+
+
+Legend: [➕]
+
+
+
+- Lines: total lines of the source unit
+- nLines: normalized lines of the source unit (e.g. normalizes functions spanning multiple lines)
+- nSLOC: normalized source lines of code (only source-code lines; no comments, no blank lines)
+- Comment Lines: lines containing single or block comments
+- Complexity Score: a custom complexity score derived from code statements that are known to introduce code complexity (branches, loops, calls, external interfaces, ...)
+
+
+
+
+
+##### Deployable Logic Contracts
+
+Total: 16
+
+- 📝 `KlerosCore`
+- 📝 `KlerosCoreNeo`
+- 📝 `PolicyRegistry`
+- 📝 `SortitionModule`
+- 📝 `SortitionModuleNeo`
+- [➕]
+
+
+- 📝
DisputeResolver
+- 📝
DisputeTemplateRegistry
+- 📝
DisputeKitClassic
+- 📝
DisputeKitGated
+- 📝
DisputeKitGatedShutter
+- 📝
DisputeKitShutter
+- 📝
DisputeKitSybilResistant
+- 📝
EvidenceModule
+- 📝
RNGWithFallback
+- 📝
ChainlinkRNG
+- 📝
UUPSProxy
+
+
+
+#### Out of Scope
+
+##### Excluded Source Units
+
+Source Units Excluded: **`0`**
+
+[➕]
+
+
+| File |
+| ------ |
+| None |
+
+
+
+##### Duplicate Source Units
+
+Duplicate Source Units Excluded: **`0`**
+
+[➕]
+
+
+| File |
+| ------ |
+| None |
+
+
+
+##### Doppelganger Contracts
+
+Doppelganger Contracts: **`3`**
+
+[➕]
+
+
+| File | Contract | Doppelganger |
+| ------ | -------- | ------------ |
+| src/arbitration/dispute-kits/DisputeKitGated.sol | IBalanceHolderERC1155 | (fuzzy) [0](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v2.5.0/contracts/introspection/IERC1820Implementer.sol), [1](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.2.0/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [2](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.2.2-solc-0.7/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [3](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.3.0/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [4](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.3.0-solc-0.7/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [5](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.4.0/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [6](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.4.0-solc-0.7/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [7](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.0.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [8](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.0.0-beta.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [9](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.0.0-rc.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [10](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.1.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [11](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.1.0-rc.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [12](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.2.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [13](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0/contracts/introspection/IERC1820Implementer.sol), [14](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.0/contracts/drafts/IERC1820Implementer.sol), [15](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.1/contracts/drafts/IERC1820Implementer.sol), [16](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.2/contracts/introspection/IERC1820Implementer.sol), [17](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.3/contracts/introspection/IERC1820Implementer.sol), [18](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/introspection/IERC1820Implementer.sol), [19](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0-beta.0/contracts/introspection/IERC1820Implementer.sol), [20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0-beta.1/contracts/introspection/IERC1820Implementer.sol), [21](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0-beta.2/contracts/introspection/IERC1820Implementer.sol), [22](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/introspection/IERC1820Implementer.sol), [23](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0-rc.0/contracts/introspection/IERC1820Implementer.sol), [24](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/introspection/IERC1820Implementer.sol), [25](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/introspection/IERC1820Implementer.sol), [26](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0-beta.0/contracts/introspection/IERC1820Implementer.sol), [27](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0-rc.0/contracts/introspection/IERC1820Implementer.sol), [28](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0-rc.1/contracts/introspection/IERC1820Implementer.sol), [29](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.1/contracts/introspection/IERC1820Implementer.sol), [30](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.2/contracts/introspection/IERC1820Implementer.sol), [31](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0/contracts/introspection/IERC1820Implementer.sol), [32](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0-rc.0/contracts/introspection/IERC1820Implementer.sol), [33](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.0/contracts/introspection/IERC1820Implementer.sol), [34](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.0-rc.0/contracts/introspection/IERC1820Implementer.sol), [35](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.1-solc-0.7/contracts/introspection/IERC1820Implementer.sol), [36](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.2-solc-0.7/contracts/introspection/IERC1820Implementer.sol) |
+| src/arbitration/dispute-kits/DisputeKitGatedShutter.sol | IBalanceHolderERC1155 | (fuzzy) [0](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v2.5.0/contracts/introspection/IERC1820Implementer.sol), [1](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.2.0/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [2](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.2.2-solc-0.7/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [3](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.3.0/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [4](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.3.0-solc-0.7/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [5](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.4.0/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [6](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.4.0-solc-0.7/contracts/introspection/IERC1820ImplementerUpgradeable.sol), [7](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.0.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [8](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.0.0-beta.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [9](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.0.0-rc.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [10](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.1.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [11](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.1.0-rc.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [12](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.2.0/contracts/utils/introspection/IERC1820ImplementerUpgradeable.sol), [13](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0/contracts/introspection/IERC1820Implementer.sol), [14](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.0/contracts/drafts/IERC1820Implementer.sol), [15](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.1/contracts/drafts/IERC1820Implementer.sol), [16](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.2/contracts/introspection/IERC1820Implementer.sol), [17](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.3/contracts/introspection/IERC1820Implementer.sol), [18](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/introspection/IERC1820Implementer.sol), [19](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0-beta.0/contracts/introspection/IERC1820Implementer.sol), [20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0-beta.1/contracts/introspection/IERC1820Implementer.sol), [21](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0-beta.2/contracts/introspection/IERC1820Implementer.sol), [22](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/introspection/IERC1820Implementer.sol), [23](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0-rc.0/contracts/introspection/IERC1820Implementer.sol), [24](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/introspection/IERC1820Implementer.sol), [25](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/introspection/IERC1820Implementer.sol), [26](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0-beta.0/contracts/introspection/IERC1820Implementer.sol), [27](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0-rc.0/contracts/introspection/IERC1820Implementer.sol), [28](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0-rc.1/contracts/introspection/IERC1820Implementer.sol), [29](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.1/contracts/introspection/IERC1820Implementer.sol), [30](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.2/contracts/introspection/IERC1820Implementer.sol), [31](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0/contracts/introspection/IERC1820Implementer.sol), [32](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0-rc.0/contracts/introspection/IERC1820Implementer.sol), [33](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.0/contracts/introspection/IERC1820Implementer.sol), [34](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.0-rc.0/contracts/introspection/IERC1820Implementer.sol), [35](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.1-solc-0.7/contracts/introspection/IERC1820Implementer.sol), [36](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.2-solc-0.7/contracts/introspection/IERC1820Implementer.sol) |
+| src/arbitration/dispute-kits/DisputeKitSybilResistant.sol | IProofOfHumanity | (fuzzy) [0](https://github.com/smartcontractkit/chainlink/blob/explorer-v0.8.5/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [1](https://github.com/smartcontractkit/chainlink/blob/upgrade/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [2](https://github.com/smartcontractkit/chainlink/blob/v.0.8.12/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [3](https://github.com/smartcontractkit/chainlink/blob/v0.8.12/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [4](https://github.com/smartcontractkit/chainlink/blob/v0.8.13/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [5](https://github.com/smartcontractkit/chainlink/blob/v0.8.14/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [6](https://github.com/smartcontractkit/chainlink/blob/v0.8.15/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [7](https://github.com/smartcontractkit/chainlink/blob/v0.8.16/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [8](https://github.com/smartcontractkit/chainlink/blob/v0.8.17/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [9](https://github.com/smartcontractkit/chainlink/blob/v0.8.18/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [10](https://github.com/smartcontractkit/chainlink/blob/v0.9.0/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [11](https://github.com/smartcontractkit/chainlink/blob/v0.9.2/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [12](https://github.com/smartcontractkit/chainlink/blob/v0.9.3/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [13](https://github.com/smartcontractkit/chainlink/blob/v0.9.4/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [14](https://github.com/smartcontractkit/chainlink/blob/v0.9.5/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [15](https://github.com/smartcontractkit/chainlink/blob/v0.9.6/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [16](https://github.com/smartcontractkit/chainlink/blob/v0.9.7/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [17](https://github.com/smartcontractkit/chainlink/blob/v0.9.8/evm-contracts/src/v0.6/interfaces/BlockHashStoreInterface.sol), [18](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v2.0.0/contracts/introspection/IERC165.sol), [19](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v2.0.1/contracts/introspection/IERC165.sol), [20](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v2.0.2/contracts/introspection/IERC165.sol), [21](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v2.1.2/contracts/introspection/IERC165.sol), [22](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v2.1.3/contracts/introspection/IERC165.sol), [23](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v2.2.1/contracts/introspection/IERC165.sol), [24](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v2.2.2/contracts/introspection/IERC165.sol), [25](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v2.2.3/contracts/introspection/IERC165.sol), [26](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v2.5.0/contracts/introspection/IERC165.sol), [27](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.2.0/contracts/introspection/IERC165Upgradeable.sol), [28](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.2.2-solc-0.7/contracts/introspection/IERC165Upgradeable.sol), [29](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.3.0/contracts/introspection/IERC165Upgradeable.sol), [30](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.3.0-solc-0.7/contracts/introspection/IERC165Upgradeable.sol), [31](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.4.0/contracts/introspection/IERC165Upgradeable.sol), [32](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v3.4.0-solc-0.7/contracts/introspection/IERC165Upgradeable.sol), [33](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.0.0/contracts/utils/introspection/IERC165Upgradeable.sol), [34](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.0.0-beta.0/contracts/utils/introspection/IERC165Upgradeable.sol), [35](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.0.0-rc.0/contracts/utils/introspection/IERC165Upgradeable.sol), [36](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.1.0/contracts/utils/introspection/IERC165Upgradeable.sol), [37](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.1.0-rc.0/contracts/utils/introspection/IERC165Upgradeable.sol), [38](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.2.0/contracts/utils/introspection/IERC165Upgradeable.sol), [39](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.0.0/contracts/introspection/IERC165.sol), [40](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.0.0-rc.1/contracts/introspection/IERC165.sol), [41](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.0.0-rc.2/contracts/introspection/IERC165.sol), [42](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.0.0-rc.3/contracts/introspection/IERC165.sol), [43](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.0.0-rc.4/contracts/introspection/IERC165.sol), [44](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.0.1/contracts/introspection/IERC165.sol), [45](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.1.0-rc.1/contracts/introspection/IERC165.sol), [46](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.1.0-rc.2/contracts/introspection/IERC165.sol), [47](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.1.1/contracts/introspection/IERC165.sol), [48](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.1.2/contracts/introspection/IERC165.sol), [49](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.1.3/contracts/introspection/IERC165.sol), [50](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.2.0/contracts/introspection/IERC165.sol), [51](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.2.0-rc.1/contracts/introspection/IERC165.sol), [52](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0/contracts/introspection/IERC165.sol), [53](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.0/contracts/introspection/IERC165.sol), [54](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.1/contracts/introspection/IERC165.sol), [55](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.2/contracts/introspection/IERC165.sol), [56](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0-rc.3/contracts/introspection/IERC165.sol), [57](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0/contracts/introspection/IERC165.sol), [58](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0-beta.0/contracts/introspection/IERC165.sol), [59](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0-beta.1/contracts/introspection/IERC165.sol), [60](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.4.0-beta.2/contracts/introspection/IERC165.sol), [61](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0/contracts/introspection/IERC165.sol), [62](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.0-rc.0/contracts/introspection/IERC165.sol), [63](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/introspection/IERC165.sol), [64](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/introspection/IERC165.sol), [65](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0-beta.0/contracts/introspection/IERC165.sol), [66](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0-rc.0/contracts/introspection/IERC165.sol), [67](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0-rc.1/contracts/introspection/IERC165.sol), [68](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.1/contracts/introspection/IERC165.sol), [69](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.2/contracts/introspection/IERC165.sol), [70](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0/contracts/introspection/IERC165.sol), [71](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.1.0-rc.0/contracts/introspection/IERC165.sol), [72](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.0/contracts/introspection/IERC165.sol), [73](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.0-rc.0/contracts/introspection/IERC165.sol), [74](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.1-solc-0.7/contracts/introspection/IERC165.sol), [75](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.2-solc-0.7/contracts/introspection/IERC165.sol) |
+
+
+
+## Report
+
+### Overview
+
+The analysis finished with **`0`** errors and **`0`** duplicate files.
+
+#### Risk
+
+
+
+
+
+#### Source Lines (sloc vs. nsloc)
+
+
+
+
+
+#### Inline Documentation
+
+- **Comment-to-Source Ratio:** On average there are`1.59` code lines per comment (lower=better).
+- **ToDo's:** `2`
+
+#### Components
+
+| 📝Contracts | 📚Libraries | 🔍Interfaces | 🎨Abstract |
+| ----------- | ----------- | ------------ | ---------- |
+| 16 | 2 | 13 | 5 |
+
+#### Exposed Functions
+
+This section lists functions that are explicitly declared public or payable. Please note that getter methods for public stateVars are not included.
+
+| 🌐Public | 💰Payable |
+| -------- | --------- |
+| 192 | 10 |
+
+| External | Internal | Private | Pure | View |
+| -------- | -------- | ------- | ---- | ---- |
+| 175 | 217 | 2 | 13 | 80 |
+
+#### StateVariables
+
+| Total | 🌐Public |
+| ----- | -------- |
+| 87 | 80 |
+
+#### Capabilities
+
+| Solidity Versions observed | 🧪 Experimental Features | 💰 Can Receive Funds | 🖥 Uses Assembly | 💣 Has Destroyable Contracts |
+| ------------------------------ | ------------------------ | -------------------- | -------------------------- | ---------------------------- |
+| `^0.8.24`
`>=0.8.0 <0.9.0` | | `yes` | `yes`
(12 asm blocks) | \*\*\*\* |
+
+| 📤 Transfers ETH | ⚡ Low-Level Calls | 👥 DelegateCall | 🧮 Uses Hash Functions | 🔖 ECRecover | 🌀 New/Create/Create2 |
+| ---------------- | ------------------ | --------------- | ---------------------- | ------------ | --------------------- |
+| `yes` | \*\*\*\* | `yes` | `yes` | \*\*\*\* | \*\*\*\* |
+
+| ♻️ TryCatch | Σ Unchecked |
+| ----------- | ----------- |
+| `yes` | \*\*\*\* |
+
+#### Dependencies / External Imports
+
+| Dependency / Import Path | Count |
+| ------------------------------------------------------------------- | ----- |
+| @chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol | 1 |
+| @chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol | 1 |
+| @openzeppelin/contracts/token/ERC20/IERC20.sol | 3 |
+| @openzeppelin/contracts/token/ERC721/IERC721.sol | 1 |
+
+#### Totals
+
+##### Summary
+
+
+
+
+
+##### AST Node Statistics
+
+###### Function Calls
+
+
+
+
+
+###### Assembly Calls
+
+
+
+
+
+###### AST Total
+
+
+
+
+
+##### Inheritance Graph
+
+[➕]
+
+
+
+##### CallGraph
+
+[➕]
+
+
+
+###### Contract Summary
+
+[➕]
+
+
+
+Files Description Table
+
+| File Name | SHA-1 Hash |
+| --------------------------------------------------------- | ---------------------------------------- |
+| src/arbitration/KlerosCore.sol | 5362842ab3a88870e8b3b42cd7cf7a2058240483 |
+| src/arbitration/KlerosCoreBase.sol | fe829c80ad434e185151a8b5d299a585f576c619 |
+| src/arbitration/KlerosCoreNeo.sol | 0260ef7a9309d5c73c4c9b1a3f5c7653904f79c7 |
+| src/arbitration/PolicyRegistry.sol | c36299120ac7318dd29e4ab86f50b0447d87af96 |
+| src/arbitration/SortitionModule.sol | c1c1b2dd061590cd6c7eee41767e5c3fb898d506 |
+| src/arbitration/SortitionModuleBase.sol | 7b1037fcdfffc58bf3522f70ed128cad4915e032 |
+| src/arbitration/SortitionModuleNeo.sol | 8570ebeff10d723ce33b75e077dffb6fcf6e2f08 |
+| src/arbitration/arbitrables/DisputeResolver.sol | 7dad21f0219e0a1b698859205bbf31b07abfdc7c |
+| src/arbitration/DisputeTemplateRegistry.sol | c31e000957b979dc513d8b123253adb9edce50c7 |
+| src/arbitration/dispute-kits/DisputeKitClassic.sol | 32bcb287571a3c4a861659be31a1ee2dc6ea6784 |
+| src/arbitration/dispute-kits/DisputeKitClassicBase.sol | 7c0b04f3e73c0a10a29abfa78f78220e4e43f983 |
+| src/arbitration/dispute-kits/DisputeKitGated.sol | e5f7f2ecd65f5e279c7b55e9edbe6f4cfc5fd632 |
+| src/arbitration/dispute-kits/DisputeKitGatedShutter.sol | abb7f1ac20fa60db95d97fea6e9396b108d40924 |
+| src/arbitration/dispute-kits/DisputeKitShutter.sol | 3720b60db303b866eacbb3dee529a638eaf6782e |
+| src/arbitration/dispute-kits/DisputeKitSybilResistant.sol | 69158bf07e78b3cb9892a5fd6cfc768181989a7f |
+| src/arbitration/evidence/EvidenceModule.sol | 45845f3d5ad5548a30698105061bc0e64fd7718f |
+| src/arbitration/interfaces/IArbitrableV2.sol | ce54b7140a6109b2d919cea38ff94ac2ad775398 |
+| src/arbitration/interfaces/IArbitratorV2.sol | 4e0baa0ff6155737540105f9fdb5b54ba2905d2c |
+| src/arbitration/interfaces/IDisputeKit.sol | f21b52ed28a905b3073adaf68961938200cfd644 |
+| src/arbitration/interfaces/IDisputeTemplateRegistry.sol | 8d0351ebeee1ebd8603af9cf50c2ab62f6e8fecb |
+| src/arbitration/interfaces/IEvidence.sol | 12ee130f67f5d38afbcc810129b030cf7f58f058 |
+| src/arbitration/interfaces/ISortitionModule.sol | 2c6d74752298263f7c649e3feecfb9a03c87a16a |
+| src/libraries/Constants.sol | 4d1d44dbeeda03f47a646e9552873d26acf4254d |
+| src/libraries/SafeERC20.sol | 68e49603280eab2b68eabc554dc310ff3313a64c |
+| src/libraries/SafeSend.sol | 9d282cf6456950471debec28103fcb99638fa83d |
+| src/rng/RNGWithFallback.sol | 9729dbce3e1dd5981073df38ac31fe356aae0027 |
+| src/rng/ChainlinkRNG.sol | ce0d145807bc7b5b59b0e29ba468d9b0c0dc0314 |
+| src/rng/IRNG.sol | d701581ef4910cdde9dbc3006dd0229cc3ea6012 |
+| src/proxy/UUPSProxiable.sol | 80d46ace20ea4c9c6a0cbc1093d6e88eb19aeb12 |
+| src/proxy/UUPSProxy.sol | 18a2a570a8ef2ec5f02d59bd1c03a176203e8ccf |
+| src/proxy/Initializable.sol | 7079462341810d12b0c0ea93845d053bdcbdabff |
+
+Contracts Description Table
+
+| Contract | Type | Bases | | |
+| :--------------------------: | :-------------------------------------: | :----------------------------------------------------: | :------------: | :------------------------------------: |
+| └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** |
+| | | | | |
+| **KlerosCore** | Implementation | KlerosCoreBase | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | reinitialize | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| | | | | |
+| **KlerosCoreBase** | Implementation | IArbitratorV2, Initializable, UUPSProxiable | | |
+| └ | \_\_KlerosCoreBase_initialize | Internal 🔒 | 🛑 | onlyInitializing |
+| └ | pause | External ❗️ | 🛑 | onlyByGuardianOrGovernor whenNotPaused |
+| └ | unpause | External ❗️ | 🛑 | onlyByGovernor whenPaused |
+| └ | executeGovernorProposal | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeGovernor | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeGuardian | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changePinakion | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeJurorProsecutionModule | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeSortitionModule | External ❗️ | 🛑 | onlyByGovernor |
+| └ | addNewDisputeKit | External ❗️ | 🛑 | onlyByGovernor |
+| └ | createCourt | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeCourtParameters | External ❗️ | 🛑 | onlyByGovernor |
+| └ | enableDisputeKits | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeAcceptedFeeTokens | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeCurrencyRates | External ❗️ | 🛑 | onlyByGovernor |
+| └ | setStake | External ❗️ | 🛑 | whenNotPaused |
+| └ | setStakeBySortitionModule | External ❗️ | 🛑 | NO❗️ |
+| └ | transferBySortitionModule | External ❗️ | 🛑 | NO❗️ |
+| └ | createDispute | External ❗️ | 💵 | NO❗️ |
+| └ | createDispute | External ❗️ | 🛑 | NO❗️ |
+| └ | \_createDispute | Internal 🔒 | 🛑 | |
+| └ | passPeriod | External ❗️ | 🛑 | NO❗️ |
+| └ | draw | External ❗️ | 🛑 | NO❗️ |
+| └ | appeal | External ❗️ | 💵 | NO❗️ |
+| └ | execute | External ❗️ | 🛑 | whenNotPaused |
+| └ | \_executePenalties | Internal 🔒 | 🛑 | |
+| └ | \_executeRewards | Internal 🔒 | 🛑 | |
+| └ | executeRuling | External ❗️ | 🛑 | NO❗️ |
+| └ | arbitrationCost | Public ❗️ | | NO❗️ |
+| └ | arbitrationCost | Public ❗️ | | NO❗️ |
+| └ | appealCost | Public ❗️ | | NO❗️ |
+| └ | appealPeriod | External ❗️ | | NO❗️ |
+| └ | currentRuling | Public ❗️ | | NO❗️ |
+| └ | getRoundInfo | External ❗️ | | NO❗️ |
+| └ | getPnkAtStakePerJuror | External ❗️ | | NO❗️ |
+| └ | getNumberOfRounds | External ❗️ | | NO❗️ |
+| └ | isSupported | External ❗️ | | NO❗️ |
+| └ | getTimesPerPeriod | External ❗️ | | NO❗️ |
+| └ | getNumberOfVotes | External ❗️ | | NO❗️ |
+| └ | isDisputeKitJumping | External ❗️ | | NO❗️ |
+| └ | getDisputeKitsLength | External ❗️ | | NO❗️ |
+| └ | convertEthToTokenAmount | Public ❗️ | | NO❗️ |
+| └ | \_transferFeeToken | Internal 🔒 | 🛑 | |
+| └ | \_applyCoherence | Internal 🔒 | | |
+| └ | \_calculatePnkAtStake | Internal 🔒 | | |
+| └ | \_enableDisputeKit | Internal 🔒 | 🛑 | |
+| └ | \_setStake | Internal 🔒 | 🛑 | |
+| └ | \_stakingFailed | Internal 🔒 | | |
+| └ | \_extraDataToCourtIDMinJurorsDisputeKit | Internal 🔒 | | |
+| | | | | |
+| **KlerosCoreNeo** | Implementation | KlerosCoreBase | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | reinitialize | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| └ | changeJurorNft | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeArbitrableWhitelist | External ❗️ | 🛑 | onlyByGovernor |
+| └ | setStake | External ❗️ | 🛑 | whenNotPaused |
+| └ | \_createDispute | Internal 🔒 | 🛑 | |
+| └ | \_stakingFailed | Internal 🔒 | | |
+| | | | | |
+| **PolicyRegistry** | Implementation | UUPSProxiable, Initializable | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | initialize2 | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| └ | changeGovernor | External ❗️ | 🛑 | onlyByGovernor |
+| └ | setPolicy | External ❗️ | 🛑 | onlyByGovernor |
+| | | | | |
+| **SortitionModule** | Implementation | SortitionModuleBase | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | initialize4 | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| | | | | |
+| **SortitionModuleBase** | Implementation | ISortitionModule, Initializable, UUPSProxiable | | |
+| └ | \_\_SortitionModuleBase_initialize | Internal 🔒 | 🛑 | onlyInitializing |
+| └ | changeGovernor | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeMinStakingTime | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeMaxDrawingTime | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeRandomNumberGenerator | External ❗️ | 🛑 | onlyByGovernor |
+| └ | passPhase | External ❗️ | 🛑 | NO❗️ |
+| └ | createTree | External ❗️ | 🛑 | onlyByCore |
+| └ | executeDelayedStakes | External ❗️ | 🛑 | NO❗️ |
+| └ | createDisputeHook | External ❗️ | 🛑 | onlyByCore |
+| └ | postDrawHook | External ❗️ | 🛑 | onlyByCore |
+| └ | notifyRandomNumber | Public ❗️ | 🛑 | NO❗️ |
+| └ | validateStake | External ❗️ | 🛑 | onlyByCore |
+| └ | \_validateStake | Internal 🔒 | 🛑 | |
+| └ | setStake | External ❗️ | 🛑 | onlyByCore |
+| └ | \_setStake | Internal 🔒 | 🛑 | |
+| └ | lockStake | External ❗️ | 🛑 | onlyByCore |
+| └ | unlockStake | External ❗️ | 🛑 | onlyByCore |
+| └ | penalizeStake | External ❗️ | 🛑 | onlyByCore |
+| └ | setJurorInactive | External ❗️ | 🛑 | onlyByCore |
+| └ | withdrawLeftoverPNK | External ❗️ | 🛑 | NO❗️ |
+| └ | draw | Public ❗️ | | NO❗️ |
+| └ | stakeOf | Public ❗️ | | NO❗️ |
+| └ | stakeOf | Public ❗️ | | NO❗️ |
+| └ | getJurorBalance | External ❗️ | | NO❗️ |
+| └ | getJurorCourtIDs | Public ❗️ | | NO❗️ |
+| └ | isJurorStaked | External ❗️ | | NO❗️ |
+| └ | getJurorLeftoverPNK | Public ❗️ | | NO❗️ |
+| └ | \_updateParents | Private 🔐 | 🛑 | |
+| └ | \_stakePathIDToAccount | Internal 🔒 | | |
+| └ | \_extraDataToTreeK | Internal 🔒 | | |
+| └ | \_set | Internal 🔒 | 🛑 | |
+| └ | \_accountAndCourtIDToStakePathID | Internal 🔒 | | |
+| | | | | |
+| **SortitionModuleNeo** | Implementation | SortitionModuleBase | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | initialize4 | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| └ | changeMaxStakePerJuror | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeMaxTotalStaked | External ❗️ | 🛑 | onlyByGovernor |
+| └ | \_validateStake | Internal 🔒 | 🛑 | onlyByCore |
+| | | | | |
+| **DisputeResolver** | Implementation | IArbitrableV2 | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | changeGovernor | External ❗️ | 🛑 | NO❗️ |
+| └ | changeArbitrator | External ❗️ | 🛑 | NO❗️ |
+| └ | changeTemplateRegistry | External ❗️ | 🛑 | NO❗️ |
+| └ | createDisputeForTemplate | External ❗️ | 💵 | NO❗️ |
+| └ | createDisputeForTemplateUri | External ❗️ | 💵 | NO❗️ |
+| └ | rule | External ❗️ | 🛑 | NO❗️ |
+| └ | \_createDispute | Internal 🔒 | 🛑 | |
+| | | | | |
+| **DisputeTemplateRegistry** | Implementation | IDisputeTemplateRegistry, UUPSProxiable, Initializable | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | initialize2 | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| └ | changeGovernor | External ❗️ | 🛑 | onlyByGovernor |
+| └ | setDisputeTemplate | External ❗️ | 🛑 | NO❗️ |
+| | | | | |
+| **DisputeKitClassic** | Implementation | DisputeKitClassicBase | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | reinitialize | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| | | | | |
+| **DisputeKitClassicBase** | Implementation | IDisputeKit, Initializable, UUPSProxiable | | |
+| └ | \_\_DisputeKitClassicBase_initialize | Internal 🔒 | 🛑 | onlyInitializing |
+| └ | executeGovernorProposal | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeGovernor | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeCore | External ❗️ | 🛑 | onlyByGovernor |
+| └ | createDispute | External ❗️ | 🛑 | onlyByCore |
+| └ | draw | External ❗️ | 🛑 | onlyByCore notJumped |
+| └ | castCommit | External ❗️ | 🛑 | NO❗️ |
+| └ | \_castCommit | Internal 🔒 | 🛑 | notJumped |
+| └ | castVote | External ❗️ | 🛑 | NO❗️ |
+| └ | \_castVote | Internal 🔒 | 🛑 | notJumped |
+| └ | fundAppeal | External ❗️ | 💵 | notJumped |
+| └ | withdrawFeesAndRewards | External ❗️ | 🛑 | NO❗️ |
+| └ | hashVote | Public ❗️ | | NO❗️ |
+| └ | getFundedChoices | Public ❗️ | | NO❗️ |
+| └ | currentRuling | External ❗️ | | NO❗️ |
+| └ | getDegreeOfCoherence | External ❗️ | | NO❗️ |
+| └ | getCoherentCount | External ❗️ | | NO❗️ |
+| └ | areCommitsAllCast | External ❗️ | | NO❗️ |
+| └ | areVotesAllCast | External ❗️ | | NO❗️ |
+| └ | isAppealFunded | External ❗️ | | NO❗️ |
+| └ | isVoteActive | External ❗️ | | NO❗️ |
+| └ | getRoundInfo | External ❗️ | | NO❗️ |
+| └ | getNumberOfRounds | External ❗️ | | NO❗️ |
+| └ | getLocalDisputeRoundID | External ❗️ | | NO❗️ |
+| └ | getVoteInfo | External ❗️ | | NO❗️ |
+| └ | \_postDrawCheck | Internal 🔒 | | |
+| | | | | |
+| **IBalanceHolder** | Interface | | | |
+| └ | balanceOf | External ❗️ | | NO❗️ |
+| | | | | |
+| **IBalanceHolderERC1155** | Interface | | | |
+| └ | balanceOf | External ❗️ | | NO❗️ |
+| | | | | |
+| **DisputeKitGated** | Implementation | DisputeKitClassicBase | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | reinitialize | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| └ | extraDataToTokenInfo | Public ❗️ | | NO❗️ |
+| └ | \_postDrawCheck | Internal 🔒 | | |
+| | | | | |
+| **IBalanceHolder** | Interface | | | |
+| └ | balanceOf | External ❗️ | | NO❗️ |
+| | | | | |
+| **IBalanceHolderERC1155** | Interface | | | |
+| └ | balanceOf | External ❗️ | | NO❗️ |
+| | | | | |
+| **DisputeKitGatedShutter** | Implementation | DisputeKitClassicBase | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | reinitialize | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| └ | castCommitShutter | External ❗️ | 🛑 | notJumped |
+| └ | castVoteShutter | External ❗️ | 🛑 | NO❗️ |
+| └ | hashVote | Public ❗️ | | NO❗️ |
+| └ | \_extraDataToTokenInfo | Internal 🔒 | | |
+| └ | \_postDrawCheck | Internal 🔒 | | |
+| | | | | |
+| **DisputeKitShutter** | Implementation | DisputeKitClassicBase | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | reinitialize | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| └ | castCommitShutter | External ❗️ | 🛑 | notJumped |
+| └ | castVoteShutter | External ❗️ | 🛑 | NO❗️ |
+| └ | hashVote | Public ❗️ | | NO❗️ |
+| | | | | |
+| **IProofOfHumanity** | Interface | | | |
+| └ | isRegistered | External ❗️ | | NO❗️ |
+| | | | | |
+| **DisputeKitSybilResistant** | Implementation | DisputeKitClassicBase | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| └ | \_postDrawCheck | Internal 🔒 | | |
+| | | | | |
+| **EvidenceModule** | Implementation | IEvidence, Initializable, UUPSProxiable | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | initialize | External ❗️ | 🛑 | reinitializer |
+| └ | initialize2 | External ❗️ | 🛑 | reinitializer |
+| └ | \_authorizeUpgrade | Internal 🔒 | | onlyByGovernor |
+| └ | submitEvidence | External ❗️ | 🛑 | NO❗️ |
+| | | | | |
+| **IArbitrableV2** | Interface | | | |
+| └ | rule | External ❗️ | 🛑 | NO❗️ |
+| | | | | |
+| **IArbitratorV2** | Interface | | | |
+| └ | createDispute | External ❗️ | 💵 | NO❗️ |
+| └ | createDispute | External ❗️ | 🛑 | NO❗️ |
+| └ | arbitrationCost | External ❗️ | | NO❗️ |
+| └ | arbitrationCost | External ❗️ | | NO❗️ |
+| └ | currentRuling | External ❗️ | | NO❗️ |
+| | | | | |
+| **IDisputeKit** | Interface | | | |
+| └ | createDispute | External ❗️ | 🛑 | NO❗️ |
+| └ | draw | External ❗️ | 🛑 | NO❗️ |
+| └ | currentRuling | External ❗️ | | NO❗️ |
+| └ | getDegreeOfCoherence | External ❗️ | | NO❗️ |
+| └ | getCoherentCount | External ❗️ | | NO❗️ |
+| └ | areCommitsAllCast | External ❗️ | | NO❗️ |
+| └ | areVotesAllCast | External ❗️ | | NO❗️ |
+| └ | isAppealFunded | External ❗️ | | NO❗️ |
+| └ | isVoteActive | External ❗️ | | NO❗️ |
+| └ | getRoundInfo | External ❗️ | | NO❗️ |
+| └ | getVoteInfo | External ❗️ | | NO❗️ |
+| | | | | |
+| **IDisputeTemplateRegistry** | Interface | | | |
+| └ | setDisputeTemplate | External ❗️ | 🛑 | NO❗️ |
+| | | | | |
+| **IEvidence** | Interface | | | |
+| | | | | |
+| **ISortitionModule** | Interface | | | |
+| └ | createTree | External ❗️ | 🛑 | NO❗️ |
+| └ | validateStake | External ❗️ | 🛑 | NO❗️ |
+| └ | setStake | External ❗️ | 🛑 | NO❗️ |
+| └ | setJurorInactive | External ❗️ | 🛑 | NO❗️ |
+| └ | lockStake | External ❗️ | 🛑 | NO❗️ |
+| └ | unlockStake | External ❗️ | 🛑 | NO❗️ |
+| └ | penalizeStake | External ❗️ | 🛑 | NO❗️ |
+| └ | notifyRandomNumber | External ❗️ | 🛑 | NO❗️ |
+| └ | draw | External ❗️ | | NO❗️ |
+| └ | getJurorBalance | External ❗️ | | NO❗️ |
+| └ | getJurorCourtIDs | External ❗️ | | NO❗️ |
+| └ | isJurorStaked | External ❗️ | | NO❗️ |
+| └ | getJurorLeftoverPNK | External ❗️ | | NO❗️ |
+| └ | createDisputeHook | External ❗️ | 🛑 | NO❗️ |
+| └ | postDrawHook | External ❗️ | 🛑 | NO❗️ |
+| └ | withdrawLeftoverPNK | External ❗️ | 🛑 | NO❗️ |
+| | | | | |
+| **SafeERC20** | Library | | | |
+| └ | increaseAllowance | Internal 🔒 | 🛑 | |
+| └ | safeTransfer | Internal 🔒 | 🛑 | |
+| └ | safeTransferFrom | Internal 🔒 | 🛑 | |
+| | | | | |
+| **WethLike** | Interface | | | |
+| └ | deposit | External ❗️ | 💵 | NO❗️ |
+| └ | transfer | External ❗️ | 🛑 | NO❗️ |
+| | | | | |
+| **SafeSend** | Library | | | |
+| └ | safeSend | Internal 🔒 | 🛑 | |
+| | | | | |
+| **RNGWithFallback** | Implementation | IRNG | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | changeGovernor | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeConsumer | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeFallbackTimeout | External ❗️ | 🛑 | onlyByGovernor |
+| └ | requestRandomness | External ❗️ | 🛑 | onlyByConsumer |
+| └ | receiveRandomness | External ❗️ | 🛑 | onlyByConsumer |
+| | | | | |
+| **ChainlinkRNG** | Implementation | IRNG, VRFConsumerBaseV2Plus | | |
+| └ | | Public ❗️ | 🛑 | VRFConsumerBaseV2Plus |
+| └ | changeGovernor | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeConsumer | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeVrfCoordinator | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeKeyHash | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeSubscriptionId | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeRequestConfirmations | External ❗️ | 🛑 | onlyByGovernor |
+| └ | changeCallbackGasLimit | External ❗️ | 🛑 | onlyByGovernor |
+| └ | requestRandomness | External ❗️ | 🛑 | onlyByConsumer |
+| └ | fulfillRandomWords | Internal 🔒 | 🛑 | |
+| └ | receiveRandomness | External ❗️ | | NO❗️ |
+| | | | | |
+| **IRNG** | Interface | | | |
+| └ | requestRandomness | External ❗️ | 🛑 | NO❗️ |
+| └ | receiveRandomness | External ❗️ | 🛑 | NO❗️ |
+| | | | | |
+| **UUPSProxiable** | Implementation | | | |
+| └ | \_authorizeUpgrade | Internal 🔒 | 🛑 | |
+| └ | upgradeToAndCall | Public ❗️ | 💵 | NO❗️ |
+| └ | proxiableUUID | External ❗️ | | NO❗️ |
+| └ | version | External ❗️ | | NO❗️ |
+| └ | \_getImplementation | Internal 🔒 | | |
+| | | | | |
+| **UUPSProxy** | Implementation | | | |
+| └ | | Public ❗️ | 🛑 | NO❗️ |
+| └ | \_delegate | Internal 🔒 | 🛑 | |
+| └ | \_getImplementation | Internal 🔒 | | |
+| └ | | External ❗️ | 💵 | NO❗️ |
+| └ | | External ❗️ | 💵 | NO❗️ |
+| | | | | |
+| **Initializable** | Implementation | | | |
+| └ | \_checkInitializing | Internal 🔒 | | |
+| └ | \_disableInitializers | Internal 🔒 | 🛑 | |
+| └ | \_getInitializedVersion | Internal 🔒 | | |
+| └ | \_isInitializing | Internal 🔒 | | |
+| └ | \_getInitializableStorage | Private 🔐 | | |
+
+Legend
+
+| Symbol | Meaning |
+| :----: | ------------------------- |
+| 🛑 | Function can modify state |
+| 💵 | Function is payable |
+
+
+____
+
+Thinking about smart contract security? We can provide training, ongoing advice, and smart contract auditing. [Contact us](https://consensys.io/diligence/contact/).
+
diff --git a/contracts/deploy/00-ethereum-pnk.ts b/contracts/deploy/00-ethereum-pnk.ts
deleted file mode 100644
index 85c674fb7..000000000
--- a/contracts/deploy/00-ethereum-pnk.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { HardhatRuntimeEnvironment } from "hardhat/types";
-import { DeployFunction } from "hardhat-deploy/types";
-import { ForeignChains, HardhatChain, isSkipped } from "./utils";
-
-enum Chains {
- SEPOLIA = ForeignChains.ETHEREUM_SEPOLIA,
- HARDHAT = HardhatChain.HARDHAT,
-}
-
-const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
- const { deployments, getNamedAccounts, getChainId } = hre;
- const { deploy } = deployments;
-
- // fallback to hardhat node signers on local network
- const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
- const chainId = Number(await getChainId());
- console.log("deploying to %s with deployer %s", Chains[chainId], deployer);
-
- await deploy("PinakionV2", {
- from: deployer,
- args: [],
- log: true,
- });
-};
-
-deployArbitration.tags = ["Pinakion"];
-deployArbitration.skip = async ({ network }) => {
- return isSkipped(network, !Chains[network.config.chainId ?? 0]);
-};
-
-export default deployArbitration;
diff --git a/contracts/deploy/00-home-chain-arbitration-neo.ts b/contracts/deploy/00-home-chain-arbitration-neo.ts
index 45a6a7d15..f5211c4af 100644
--- a/contracts/deploy/00-home-chain-arbitration-neo.ts
+++ b/contracts/deploy/00-home-chain-arbitration-neo.ts
@@ -6,13 +6,12 @@ import { changeCurrencyRate } from "./utils/klerosCoreHelper";
import { HomeChains, isSkipped, isDevnet, PNK, ETH } from "./utils";
import { getContractOrDeploy, getContractOrDeployUpgradable } from "./utils/getContractOrDeploy";
import { deployERC20AndFaucet, deployERC721 } from "./utils/deployTokens";
-import { ChainlinkRNG, DisputeKitClassic, KlerosCoreNeo } from "../typechain-types";
+import { ChainlinkRNG, DisputeKitClassic, KlerosCoreNeo, RNGWithFallback } from "../typechain-types";
const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const { ethers, deployments, getNamedAccounts, getChainId } = hre;
const { deploy } = deployments;
const { ZeroAddress } = hre.ethers;
- const RNG_LOOKAHEAD = 20;
// fallback to hardhat node signers on local network
const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
@@ -45,7 +44,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
const devnet = isDevnet(hre.network);
const minStakingTime = devnet ? 180 : 1800;
const maxFreezingTime = devnet ? 600 : 1800;
- const rng = (await ethers.getContract("ChainlinkRNG")) as ChainlinkRNG;
+ const rngWithFallback = await ethers.getContract("RNGWithFallback");
const maxStakePerJuror = PNK(2_000);
const maxTotalStaked = PNK(2_000_000);
const sortitionModule = await deployUpgradable(deployments, "SortitionModuleNeo", {
@@ -55,8 +54,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
klerosCoreAddress,
minStakingTime,
maxFreezingTime,
- rng.target,
- RNG_LOOKAHEAD,
+ rngWithFallback.target,
maxStakePerJuror,
maxTotalStaked,
],
@@ -87,21 +85,21 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
}); // nonce+2 (implementation), nonce+3 (proxy)
// disputeKit.changeCore() only if necessary
- const disputeKitContract = (await hre.ethers.getContract("DisputeKitClassicNeo")) as DisputeKitClassic;
+ const disputeKitContract = await hre.ethers.getContract("DisputeKitClassicNeo");
const currentCore = await disputeKitContract.core();
if (currentCore !== klerosCore.address) {
console.log(`disputeKit.changeCore(${klerosCore.address})`);
await disputeKitContract.changeCore(klerosCore.address);
}
- // rng.changeSortitionModule() only if necessary
- const rngSortitionModule = await rng.sortitionModule();
- if (rngSortitionModule !== sortitionModule.address) {
- console.log(`rng.changeSortitionModule(${sortitionModule.address})`);
- await rng.changeSortitionModule(sortitionModule.address);
+ // rngWithFallback.changeConsumer() only if necessary
+ const rngConsumer = await rngWithFallback.consumer();
+ if (rngConsumer !== sortitionModule.address) {
+ console.log(`rngWithFallback.changeConsumer(${sortitionModule.address})`);
+ await rngWithFallback.changeConsumer(sortitionModule.address);
}
- const core = (await hre.ethers.getContract("KlerosCoreNeo")) as KlerosCoreNeo;
+ const core = await hre.ethers.getContract("KlerosCoreNeo");
try {
await changeCurrencyRate(core, await weth.getAddress(), true, 1, 1);
} catch (e) {
diff --git a/contracts/deploy/00-home-chain-arbitration-ruler.ts b/contracts/deploy/00-home-chain-arbitration-ruler.ts
index d49431c46..5e6e5bdc7 100644
--- a/contracts/deploy/00-home-chain-arbitration-ruler.ts
+++ b/contracts/deploy/00-home-chain-arbitration-ruler.ts
@@ -35,7 +35,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
],
log: true,
});
- const core = (await hre.ethers.getContract("KlerosCoreRuler")) as KlerosCoreRuler;
+ const core = await hre.ethers.getContract("KlerosCoreRuler");
try {
await changeCurrencyRate(core, await pnk.getAddress(), true, 12225583, 12);
diff --git a/contracts/deploy/00-home-chain-arbitration-university.ts b/contracts/deploy/00-home-chain-arbitration-university.ts
index 81267ca91..e3fce871e 100644
--- a/contracts/deploy/00-home-chain-arbitration-university.ts
+++ b/contracts/deploy/00-home-chain-arbitration-university.ts
@@ -64,14 +64,14 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
}); // nonce+2 (implementation), nonce+3 (proxy)
// disputeKit.changeCore() only if necessary
- const disputeKitContract = (await ethers.getContract("DisputeKitClassicUniversity")) as DisputeKitClassic;
+ const disputeKitContract = await ethers.getContract("DisputeKitClassicUniversity");
const currentCore = await disputeKitContract.core();
if (currentCore !== klerosCore.address) {
console.log(`disputeKit.changeCore(${klerosCore.address})`);
await disputeKitContract.changeCore(klerosCore.address);
}
- const core = (await hre.ethers.getContract("KlerosCoreUniversity")) as KlerosCoreUniversity;
+ const core = await hre.ethers.getContract("KlerosCoreUniversity");
try {
await changeCurrencyRate(core, await pnk.getAddress(), true, 12225583, 12);
await changeCurrencyRate(core, await dai.getAddress(), true, 60327783, 11);
diff --git a/contracts/deploy/00-home-chain-arbitration.ts b/contracts/deploy/00-home-chain-arbitration.ts
index c22a1b960..1d590813f 100644
--- a/contracts/deploy/00-home-chain-arbitration.ts
+++ b/contracts/deploy/00-home-chain-arbitration.ts
@@ -6,13 +6,12 @@ import { changeCurrencyRate } from "./utils/klerosCoreHelper";
import { HomeChains, isSkipped, isDevnet, PNK, ETH, Courts } from "./utils";
import { getContractOrDeploy, getContractOrDeployUpgradable } from "./utils/getContractOrDeploy";
import { deployERC20AndFaucet } from "./utils/deployTokens";
-import { ChainlinkRNG, DisputeKitClassic, KlerosCore } from "../typechain-types";
+import { ChainlinkRNG, DisputeKitClassic, KlerosCore, RNGWithFallback } from "../typechain-types";
const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const { ethers, deployments, getNamedAccounts, getChainId } = hre;
const { deploy } = deployments;
const { ZeroAddress } = hre.ethers;
- const RNG_LOOKAHEAD = 20;
// fallback to hardhat node signers on local network
const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
@@ -50,10 +49,10 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
const devnet = isDevnet(hre.network);
const minStakingTime = devnet ? 180 : 1800;
const maxFreezingTime = devnet ? 600 : 1800;
- const rng = (await ethers.getContract("ChainlinkRNG")) as ChainlinkRNG;
+ const rngWithFallback = await ethers.getContract("RNGWithFallback");
const sortitionModule = await deployUpgradable(deployments, "SortitionModule", {
from: deployer,
- args: [deployer, klerosCoreAddress, minStakingTime, maxFreezingTime, rng.target, RNG_LOOKAHEAD],
+ args: [deployer, klerosCoreAddress, minStakingTime, maxFreezingTime, rngWithFallback.target],
log: true,
}); // nonce (implementation), nonce+1 (proxy)
@@ -80,21 +79,21 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
}); // nonce+2 (implementation), nonce+3 (proxy)
// disputeKit.changeCore() only if necessary
- const disputeKitContract = (await ethers.getContract("DisputeKitClassic")) as DisputeKitClassic;
+ const disputeKitContract = await ethers.getContract("DisputeKitClassic");
const currentCore = await disputeKitContract.core();
if (currentCore !== klerosCore.address) {
console.log(`disputeKit.changeCore(${klerosCore.address})`);
await disputeKitContract.changeCore(klerosCore.address);
}
- // rng.changeSortitionModule() only if necessary
- const rngSortitionModule = await rng.sortitionModule();
- if (rngSortitionModule !== sortitionModule.address) {
- console.log(`rng.changeSortitionModule(${sortitionModule.address})`);
- await rng.changeSortitionModule(sortitionModule.address);
+ // rngWithFallback.changeConsumer() only if necessary
+ const rngConsumer = await rngWithFallback.consumer();
+ if (rngConsumer !== sortitionModule.address) {
+ console.log(`rngWithFallback.changeConsumer(${sortitionModule.address})`);
+ await rngWithFallback.changeConsumer(sortitionModule.address);
}
- const core = (await hre.ethers.getContract("KlerosCore")) as KlerosCore;
+ const core = await hre.ethers.getContract("KlerosCore");
try {
await changeCurrencyRate(core, await pnk.getAddress(), true, 12225583, 12);
await changeCurrencyRate(core, await dai.getAddress(), true, 60327783, 11);
diff --git a/contracts/deploy/00-home-chain-pnk-faucet.ts b/contracts/deploy/00-home-chain-pnk-faucet.ts
deleted file mode 100644
index e10eb93bf..000000000
--- a/contracts/deploy/00-home-chain-pnk-faucet.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { HardhatRuntimeEnvironment } from "hardhat/types";
-import { DeployFunction } from "hardhat-deploy/types";
-import { HomeChains, isSkipped } from "./utils";
-
-const pnkByChain = new Map([
- [HomeChains.ARBITRUM_ONE, "0x330bD769382cFc6d50175903434CCC8D206DCAE5"],
- [HomeChains.ARBITRUM_SEPOLIA, "INSERT ARBITRUM SEPOLIA PNK TOKEN ADDRESS HERE"],
-]);
-
-const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
- const { deployments, getNamedAccounts, getChainId } = hre;
- const { deploy, execute } = deployments;
-
- // fallback to hardhat node signers on local network
- const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
- const chainId = Number(await getChainId());
- console.log("deploying to %s with deployer %s", HomeChains[chainId], deployer);
-
- const pnkAddress = pnkByChain.get(chainId);
- if (pnkAddress) {
- await deploy("PNKFaucet", {
- from: deployer,
- contract: "Faucet",
- args: [pnkAddress],
- log: true,
- });
- await execute("PNKFaucet", { from: deployer, log: true }, "changeAmount", hre.ethers.parseUnits("10000", "ether"));
- }
-};
-
-deployArbitration.tags = ["PnkFaucet"];
-deployArbitration.skip = async ({ network }) => {
- return isSkipped(network, !HomeChains[network.config.chainId ?? 0]);
-};
-
-export default deployArbitration;
diff --git a/contracts/deploy/00-home-chain-resolver.ts b/contracts/deploy/00-home-chain-resolver.ts
index d7d2186ef..64d3431f6 100644
--- a/contracts/deploy/00-home-chain-resolver.ts
+++ b/contracts/deploy/00-home-chain-resolver.ts
@@ -1,7 +1,7 @@
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { HomeChains, isSkipped } from "./utils";
-import { deployUpgradable } from "./utils/deployUpgradable";
+import { getContractOrDeploy } from "./utils/getContractOrDeploy";
const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const { deployments, getNamedAccounts, getChainId } = hre;
@@ -15,7 +15,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
const klerosCore = await deployments.get("KlerosCore");
const disputeTemplateRegistry = await deployments.get("DisputeTemplateRegistry");
- await deploy("DisputeResolver", {
+ await getContractOrDeploy(hre, "DisputeResolver", {
from: deployer,
args: [klerosCore.address, disputeTemplateRegistry.address],
log: true,
diff --git a/contracts/deploy/00-chainlink-rng.ts b/contracts/deploy/00-rng-chainlink.ts
similarity index 76%
rename from contracts/deploy/00-chainlink-rng.ts
rename to contracts/deploy/00-rng-chainlink.ts
index 1062fe936..78a1c5e87 100644
--- a/contracts/deploy/00-chainlink-rng.ts
+++ b/contracts/deploy/00-rng-chainlink.ts
@@ -2,10 +2,10 @@ import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { HomeChains, isSkipped } from "./utils";
import { getContractOrDeploy } from "./utils/getContractOrDeploy";
+import { RNGWithFallback } from "../typechain-types";
const deployRng: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
- const { deployments, getNamedAccounts, getChainId } = hre;
- const { deploy } = deployments;
+ const { getNamedAccounts, getChainId, ethers } = hre;
// fallback to hardhat node signers on local network
const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
@@ -57,11 +57,16 @@ const deployRng: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const requestConfirmations = 200; // between 1 and 200 L2 blocks
const callbackGasLimit = 100000;
- await deploy("ChainlinkRNG", {
+ const oldRng = await ethers.getContractOrNull("ChainlinkRNG");
+ if (!oldRng) {
+ console.log("Register this Chainlink consumer here: http://vrf.chain.link/");
+ }
+
+ const rng = await getContractOrDeploy(hre, "ChainlinkRNG", {
from: deployer,
args: [
deployer,
- deployer, // The consumer is configured as the SortitionModule later
+ deployer, // The consumer is configured as the RNGWithFallback later
ChainlinkVRFCoordinator.target,
keyHash,
subscriptionId,
@@ -70,6 +75,26 @@ const deployRng: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
],
log: true,
});
+
+ const fallbackTimeoutSeconds = 30 * 60; // 30 minutes
+ await getContractOrDeploy(hre, "RNGWithFallback", {
+ from: deployer,
+ args: [
+ deployer,
+ deployer, // The consumer is configured as the SortitionModule later
+ fallbackTimeoutSeconds,
+ rng.target,
+ ],
+ log: true,
+ });
+
+ // rng.changeConsumer() only if necessary
+ const rngWithFallback = await ethers.getContract("RNGWithFallback");
+ const rngConsumer = await rng.consumer();
+ if (rngConsumer !== rngWithFallback.target) {
+ console.log(`rng.changeConsumer(${rngWithFallback.target})`);
+ await rng.changeConsumer(rngWithFallback.target);
+ }
};
deployRng.tags = ["ChainlinkRNG"];
diff --git a/contracts/deploy/00-randomizer-rng.ts b/contracts/deploy/00-rng-randomizer.ts
similarity index 54%
rename from contracts/deploy/00-randomizer-rng.ts
rename to contracts/deploy/00-rng-randomizer.ts
index c28dc3cda..8413b39f6 100644
--- a/contracts/deploy/00-randomizer-rng.ts
+++ b/contracts/deploy/00-rng-randomizer.ts
@@ -2,10 +2,10 @@ import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { HomeChains, isSkipped } from "./utils";
import { getContractOrDeploy } from "./utils/getContractOrDeploy";
+import { RNGWithFallback } from "../typechain-types";
const deployRng: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
- const { deployments, getNamedAccounts, getChainId } = hre;
- const { deploy } = deployments;
+ const { getNamedAccounts, getChainId, ethers } = hre;
// fallback to hardhat node signers on local network
const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
@@ -20,11 +20,35 @@ const deployRng: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
log: true,
});
- await getContractOrDeploy(hre, "RandomizerRNG", {
+ const rng = await getContractOrDeploy(hre, "RandomizerRNG", {
from: deployer,
- args: [deployer, deployer, randomizerOracle.target], // The consumer is configured as the SortitionModule later
+ args: [
+ deployer,
+ deployer, // The consumer is configured as the RNGWithFallback later
+ randomizerOracle.target,
+ ],
log: true,
});
+
+ const fallbackTimeoutSeconds = 30 * 60; // 30 minutes
+ await getContractOrDeploy(hre, "RNGWithFallback", {
+ from: deployer,
+ args: [
+ deployer,
+ deployer, // The consumer is configured as the SortitionModule later
+ fallbackTimeoutSeconds,
+ rng.target,
+ ],
+ log: true,
+ });
+
+ // rng.changeConsumer() only if necessary
+ const rngWithFallback = await ethers.getContract("RNGWithFallback");
+ const rngConsumer = await rng.consumer();
+ if (rngConsumer !== rngWithFallback.target) {
+ console.log(`rng.changeConsumer(${rngWithFallback.target})`);
+ await rng.changeConsumer(rngWithFallback.target);
+ }
};
deployRng.tags = ["RandomizerRNG"];
diff --git a/contracts/deploy/00-rng.ts b/contracts/deploy/00-rng.ts
deleted file mode 100644
index 2489406c1..000000000
--- a/contracts/deploy/00-rng.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { HardhatRuntimeEnvironment } from "hardhat/types";
-import { DeployFunction } from "hardhat-deploy/types";
-import { SortitionModule } from "../typechain-types";
-import { HomeChains, isMainnet, isSkipped } from "./utils";
-import { deployUpgradable } from "./utils/deployUpgradable";
-import { getContractOrDeploy } from "./utils/getContractOrDeploy";
-
-const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
- const { deployments, getNamedAccounts, getChainId, ethers } = hre;
- const { deploy } = deployments;
- const RNG_LOOKAHEAD = 20;
-
- // fallback to hardhat node signers on local network
- const deployer = (await getNamedAccounts()).deployer ?? (await hre.ethers.getSigners())[0].address;
- const chainId = Number(await getChainId());
- console.log("deploying to %s with deployer %s", HomeChains[chainId], deployer);
-
- const sortitionModule = (await ethers.getContract("SortitionModuleNeo")) as SortitionModule;
-
- const randomizerOracle = await getContractOrDeploy(hre, "RandomizerOracle", {
- from: deployer,
- contract: "RandomizerMock",
- args: [],
- log: true,
- });
-
- const rng1 = await deploy("RandomizerRNG", {
- from: deployer,
- args: [deployer, sortitionModule.target, randomizerOracle.address],
- log: true,
- });
-
- const rng2 = await deploy("BlockHashRNG", {
- from: deployer,
- args: [],
- log: true,
- });
-
- await sortitionModule.changeRandomNumberGenerator(rng2.address, RNG_LOOKAHEAD);
-};
-
-deployArbitration.tags = ["RNG"];
-deployArbitration.skip = async ({ network }) => {
- return isSkipped(network, isMainnet(network));
-};
-
-export default deployArbitration;
diff --git a/contracts/deploy/05-arbitrable-dispute-template.ts b/contracts/deploy/change-arbitrable-dispute-template.ts
similarity index 94%
rename from contracts/deploy/05-arbitrable-dispute-template.ts
rename to contracts/deploy/change-arbitrable-dispute-template.ts
index 0ac04fba8..8b41ce2d2 100644
--- a/contracts/deploy/05-arbitrable-dispute-template.ts
+++ b/contracts/deploy/change-arbitrable-dispute-template.ts
@@ -31,7 +31,7 @@ const deployResolver: DeployFunction = async (hre: HardhatRuntimeEnvironment) =>
"specification": "KIP88"
}`;
- const arbitrable = (await ethers.getContract("ArbitrableExample")) as ArbitrableExample;
+ const arbitrable = await ethers.getContract("ArbitrableExample");
let tx = await (await arbitrable.changeDisputeTemplate(template, "disputeTemplateMapping: TODO")).wait();
tx?.logs?.forEach((event) => {
if (event instanceof EventLog) console.log("event: %O", event.args);
diff --git a/contracts/deploy/change-sortition-module-rng.ts b/contracts/deploy/change-sortition-module-rng.ts
index a9573e6be..2b5e72435 100644
--- a/contracts/deploy/change-sortition-module-rng.ts
+++ b/contracts/deploy/change-sortition-module-rng.ts
@@ -23,11 +23,11 @@ const task: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
sortitionModule = await ethers.getContract("SortitionModule");
}
- console.log(`chainlinkRng.changeSortitionModule(${sortitionModule.target})`);
- await chainlinkRng.changeSortitionModule(sortitionModule.target);
+ console.log(`chainlinkRng.changeConsumer(${sortitionModule.target})`);
+ await chainlinkRng.changeConsumer(sortitionModule.target);
- console.log(`sortitionModule.changeRandomNumberGenerator(${chainlinkRng.target}, 0)`);
- await sortitionModule.changeRandomNumberGenerator(chainlinkRng.target, 0);
+ console.log(`sortitionModule.changeRandomNumberGenerator(${chainlinkRng.target})`);
+ await sortitionModule.changeRandomNumberGenerator(chainlinkRng.target);
};
task.tags = ["ChangeSortitionModuleRNG"];
diff --git a/contracts/deployments/disputeKitsViem.ts b/contracts/deployments/disputeKitsViem.ts
new file mode 100644
index 000000000..45ae23998
--- /dev/null
+++ b/contracts/deployments/disputeKitsViem.ts
@@ -0,0 +1,85 @@
+import { getContracts } from "./contractsViem";
+import { Abi, AbiEvent, getAbiItem, PublicClient } from "viem";
+import { DeploymentName } from "./utils";
+
+export type DisputeKitContracts = ReturnType;
+export type DisputeKit =
+ | NonNullable
+ | NonNullable
+ | NonNullable
+ | NonNullable
+ | null;
+export type DisputeKitInfos = {
+ address: `0x${string}`;
+ contract: DisputeKit;
+ isGated: boolean;
+ isShutter: boolean;
+};
+export type DisputeKitByIds = Record;
+
+const fetchDisputeKits = async (client: PublicClient, klerosCoreAddress: `0x${string}`, klerosCoreAbi: Abi) => {
+ const DisputeKitCreated = getAbiItem({
+ abi: klerosCoreAbi,
+ name: "DisputeKitCreated",
+ }) as AbiEvent;
+ const logs = await client.getLogs({
+ address: klerosCoreAddress,
+ event: DisputeKitCreated,
+ fromBlock: 0n,
+ toBlock: "latest",
+ });
+ return Object.fromEntries(
+ logs
+ .filter((log) => {
+ const args = log.args as Record;
+ return "_disputeKitID" in args && "_disputeKitAddress" in args;
+ })
+ .map((log) => {
+ const { _disputeKitID, _disputeKitAddress } = log.args as {
+ _disputeKitID: bigint;
+ _disputeKitAddress: string;
+ };
+ return {
+ disputeKitID: _disputeKitID,
+ disputeKitAddress: _disputeKitAddress,
+ };
+ })
+ .map(({ disputeKitID, disputeKitAddress }) => [disputeKitID!.toString(), disputeKitAddress as `0x${string}`])
+ );
+};
+
+export const getDisputeKits = async (client: PublicClient, deployment: DeploymentName): Promise => {
+ const { klerosCore, disputeKitClassic, disputeKitShutter, disputeKitGated, disputeKitGatedShutter } = getContracts({
+ publicClient: client,
+ deployment: deployment,
+ });
+
+ const isDefined = (kit: T): kit is NonNullable => kit != null;
+ const disputeKitContracts = [disputeKitClassic, disputeKitShutter, disputeKitGated, disputeKitGatedShutter].filter(
+ isDefined
+ );
+ const shutterEnabled = [disputeKitShutter, disputeKitGatedShutter].filter(isDefined);
+ const gatedEnabled = [disputeKitGated, disputeKitGatedShutter].filter(isDefined);
+
+ const disputeKitMap = await fetchDisputeKits(client, klerosCore.address, klerosCore.abi);
+
+ return Object.fromEntries(
+ Object.entries(disputeKitMap).map(([disputeKitID, address]) => {
+ const contract =
+ disputeKitContracts.find((contract) => contract.address.toLowerCase() === address.toLowerCase()) ?? null;
+ return [
+ disputeKitID,
+ {
+ address,
+ contract: contract satisfies DisputeKit,
+ isGated: contract
+ ? gatedEnabled.some((gated) => contract.address.toLowerCase() === gated.address.toLowerCase())
+ : false,
+ isShutter: contract
+ ? shutterEnabled.some((shutter) => contract.address.toLowerCase() === shutter.address.toLowerCase())
+ : false,
+ },
+ ];
+ })
+ );
+};
diff --git a/contracts/deployments/index.ts b/contracts/deployments/index.ts
index 3479c5edf..c94968751 100644
--- a/contracts/deployments/index.ts
+++ b/contracts/deployments/index.ts
@@ -17,3 +17,6 @@ export * from "./utils";
// Contracts getters
export { getContracts as getContractsEthers } from "./contractsEthers";
export { getContracts as getContractsViem } from "./contractsViem";
+
+// Dispute kits getters
+export { getDisputeKits as getDisputeKitsViem, type DisputeKitByIds, type DisputeKitInfos } from "./disputeKitsViem";
diff --git a/contracts/foundry.toml b/contracts/foundry.toml
index a8c6351ec..00b0c68b8 100644
--- a/contracts/foundry.toml
+++ b/contracts/foundry.toml
@@ -1,15 +1,24 @@
[profile.default]
+solc = "0.8.30"
+via_ir = true
+optimizer = true
+optimizer_runs = 500
+optimizer_details = { yulDetails = { stackAllocation = true } }
+additional_compiler_profiles = [
+ { name = "tests", via_ir = false }
+]
+compilation_restrictions = [
+ { paths = "test/foundry/KlerosCore.t.sol", via_ir = false },
+]
src = 'src'
out = 'out'
libs = ['../node_modules', 'lib']
[rpc_endpoints]
arbitrumSepolia = "https://sepolia-rollup.arbitrum.io/rpc"
-arbitrumGoerli = "https://goerli-rollup.arbitrum.io/rpc"
arbitrum = "https://arb1.arbitrum.io/rpc"
sepolia = "https://sepolia.infura.io/v3/${INFURA_API_KEY}"
-goerli = "https://goerli.infura.io/v3/${INFURA_API_KEY}"
mainnet = "https://mainnet.infura.io/v3/${INFURA_API_KEY}"
chiado = "https://rpc.chiado.gnosis.gateway.fm"
gnosischain = "https://rpc.gnosis.gateway.fm"
diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts
index 75379d0b9..dc1cce1c6 100644
--- a/contracts/hardhat.config.ts
+++ b/contracts/hardhat.config.ts
@@ -26,28 +26,12 @@ const config: HardhatUserConfig = {
solidity: {
compilers: [
{
- version: "0.8.28",
+ version: "0.8.30",
settings: {
- // viaIR: true,
+ viaIR: process.env.VIA_IR !== "false", // Defaults to true
optimizer: {
enabled: true,
- runs: 100,
- },
- outputSelection: {
- "*": {
- "*": ["storageLayout"],
- },
- },
- },
- },
- {
- // For Vea
- version: "0.8.24",
- settings: {
- // viaIR: true,
- optimizer: {
- enabled: true,
- runs: 100,
+ runs: 10000,
},
outputSelection: {
"*": {
diff --git a/contracts/package.json b/contracts/package.json
index fc719eb7f..0bca4e700 100644
--- a/contracts/package.json
+++ b/contracts/package.json
@@ -1,6 +1,6 @@
{
"name": "@kleros/kleros-v2-contracts",
- "version": "0.11.0",
+ "version": "0.12.0",
"description": "Smart contracts for Kleros version 2",
"main": "./cjs/deployments/index.js",
"module": "./esm/deployments/index.js",
@@ -121,7 +121,7 @@
"@types/mocha": "^10.0.10",
"@types/node": "^20.17.6",
"@types/sinon": "^17.0.4",
- "@wagmi/cli": "^2.2.0",
+ "@wagmi/cli": "^2.3.2",
"abitype": "^0.10.3",
"chai": "^4.5.0",
"dotenv": "^16.6.1",
@@ -131,7 +131,7 @@
"gluegun": "^5.2.0",
"graphql": "^16.9.0",
"graphql-request": "^7.1.2",
- "hardhat": "2.25.0",
+ "hardhat": "2.26.2",
"hardhat-contract-sizer": "^2.10.0",
"hardhat-deploy": "^1.0.4",
"hardhat-deploy-ethers": "^0.4.2",
@@ -154,10 +154,17 @@
},
"dependencies": {
"@chainlink/contracts": "^1.4.0",
- "@kleros/vea-contracts": "^0.6.0",
+ "@kleros/vea-contracts": "^0.7.0",
"@openzeppelin/contracts": "^5.4.0",
"@shutter-network/shutter-sdk": "0.0.2",
- "isomorphic-fetch": "^3.0.0",
+ "isomorphic-fetch": "^3.0.0"
+ },
+ "peerDependencies": {
"viem": "^2.24.1"
+ },
+ "peerDependenciesMeta": {
+ "viem": {
+ "optional": false
+ }
}
}
diff --git a/contracts/scripts/coverage.sh b/contracts/scripts/coverage.sh
index c228fbae5..9a1fc24eb 100755
--- a/contracts/scripts/coverage.sh
+++ b/contracts/scripts/coverage.sh
@@ -21,6 +21,7 @@ fi
# Generate the Hardhat coverage report
yarn clean
echo "Building contracts with Hardhat..."
+export VIA_IR=false
yarn build
echo "Running Hardhat coverage..."
yarn hardhat coverage --solcoverjs ./.solcover.js --temp artifacts --show-stack-traces --testfiles "test/**/*.ts"
diff --git a/contracts/scripts/disputeRelayerBot.ts b/contracts/scripts/disputeRelayerBot.ts
index 9437bdf52..87848993a 100644
--- a/contracts/scripts/disputeRelayerBot.ts
+++ b/contracts/scripts/disputeRelayerBot.ts
@@ -33,9 +33,9 @@ export default async function main(
homeGatewayArtifact: string,
feeTokenArtifact?: string
) {
- const core = (await ethers.getContract("KlerosCore")) as KlerosCore;
- const homeGateway = (await ethers.getContract(homeGatewayArtifact)) as HomeGateway;
- const feeToken = feeTokenArtifact ? ((await ethers.getContract(feeTokenArtifact)) as TestERC20) : undefined;
+ const core = await ethers.getContract("KlerosCore");
+ const homeGateway = await ethers.getContract(homeGatewayArtifact);
+ const feeToken = feeTokenArtifact ? await ethers.getContract(feeTokenArtifact) : undefined;
const foreignChainProvider = new ethers.providers.JsonRpcProvider(foreignNetwork.url);
const foreignGatewayDeployment = await foreignDeployments.get(foreignGatewayArtifact);
diff --git a/contracts/scripts/generateMetrics.sh b/contracts/scripts/generateMetrics.sh
new file mode 100755
index 000000000..4e7bea4be
--- /dev/null
+++ b/contracts/scripts/generateMetrics.sh
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+SOURCE_DIR="src"
+
+yarn dlx solidity-code-metrics \
+ "$SOURCE_DIR"/arbitration/KlerosCore* \
+ "$SOURCE_DIR"/arbitration/PolicyRegistry.sol \
+ "$SOURCE_DIR"/arbitration/SortitionModule* \
+ "$SOURCE_DIR"/arbitration/arbitrables/DisputeResolver.sol \
+ "$SOURCE_DIR"/arbitration/DisputeTemplateRegistry.sol \
+ "$SOURCE_DIR"/arbitration/dispute-kits/* \
+ "$SOURCE_DIR"/arbitration/evidence/EvidenceModule.sol \
+ "$SOURCE_DIR"/arbitration/interfaces/* \
+ "$SOURCE_DIR"/libraries/Constants.sol \
+ "$SOURCE_DIR"/libraries/Safe* \
+ "$SOURCE_DIR"/rng/RNGWithFallback.sol \
+ "$SOURCE_DIR"/rng/ChainlinkRNG.sol \
+ "$SOURCE_DIR"/rng/IRNG.sol \
+ "$SOURCE_DIR"/proxy/UUPSProx* \
+ "$SOURCE_DIR"/proxy/Initializable.sol \
+>METRICS.md
diff --git a/contracts/scripts/getDisputeKits.ts b/contracts/scripts/getDisputeKits.ts
new file mode 100644
index 000000000..32f2b18eb
--- /dev/null
+++ b/contracts/scripts/getDisputeKits.ts
@@ -0,0 +1,33 @@
+import { getDisputeKits } from "../deployments/disputeKitsViem";
+import { createPublicClient, http } from "viem";
+import { arbitrumSepolia } from "viem/chains";
+
+const rpc = process.env.ARBITRUM_SEPOLIA_RPC;
+if (!rpc) {
+ throw new Error("ARBITRUM_SEPOLIA_RPC is not set");
+}
+
+const client = createPublicClient({
+ chain: arbitrumSepolia,
+ transport: http(rpc),
+});
+
+async function main() {
+ try {
+ console.log("Fetching DisputeKitCreated events...");
+ const disputeKitResult = await getDisputeKits(client, "devnet");
+ console.log(disputeKitResult);
+ } catch (error) {
+ console.error("Error fetching events:", error);
+ throw error;
+ }
+}
+
+if (require.main === module) {
+ main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
+}
diff --git a/contracts/src/arbitration/DisputeTemplateRegistry.sol b/contracts/src/arbitration/DisputeTemplateRegistry.sol
index 2ea0a71f6..e63d7c297 100644
--- a/contracts/src/arbitration/DisputeTemplateRegistry.sol
+++ b/contracts/src/arbitration/DisputeTemplateRegistry.sol
@@ -25,7 +25,7 @@ contract DisputeTemplateRegistry is IDisputeTemplateRegistry, UUPSProxiable, Ini
// ************************************* //
modifier onlyByGovernor() {
- require(governor == msg.sender, "Governor only");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
@@ -80,4 +80,10 @@ contract DisputeTemplateRegistry is IDisputeTemplateRegistry, UUPSProxiable, Ini
templateId = templates++;
emit DisputeTemplate(templateId, _templateTag, _templateData, _templateDataMappings);
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error GovernorOnly();
}
diff --git a/contracts/src/arbitration/KlerosCoreBase.sol b/contracts/src/arbitration/KlerosCoreBase.sol
index 2b9998bda..3164e7245 100644
--- a/contracts/src/arbitration/KlerosCoreBase.sol
+++ b/contracts/src/arbitration/KlerosCoreBase.sol
@@ -88,7 +88,6 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
// * Storage * //
// ************************************* //
- uint256 private constant ALPHA_DIVISOR = 1e4; // The number to divide `Court.alpha` by.
uint256 private constant NON_PAYABLE_AMOUNT = (2 ** 256 - 2) / 2; // An amount higher than the supply of ETH.
address public governor; // The governor of the contract.
@@ -566,10 +565,8 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
if (round.drawnJurors.length != round.nbVotes) revert DisputeStillDrawing();
dispute.period = court.hiddenVotes ? Period.commit : Period.vote;
} else if (dispute.period == Period.commit) {
- if (
- block.timestamp - dispute.lastPeriodChange < court.timesPerPeriod[uint256(dispute.period)] &&
- !disputeKits[round.disputeKitID].areCommitsAllCast(_disputeID)
- ) {
+ // Note that we do not want to pass to Voting period if all the commits are cast because it breaks the Shutter auto-reveal currently.
+ if (block.timestamp - dispute.lastPeriodChange < court.timesPerPeriod[uint256(dispute.period)]) {
revert CommitPeriodNotPassed();
}
dispute.period = Period.vote;
@@ -768,20 +765,21 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
IDisputeKit disputeKit = disputeKits[round.disputeKitID];
// [0, 1] value that determines how coherent the juror was in this round, in basis points.
- uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence(
+ uint256 coherence = disputeKit.getDegreeOfCoherencePenalty(
_params.disputeID,
_params.round,
_params.repartition,
_params.feePerJurorInRound,
_params.pnkAtStakePerJurorInRound
);
- if (degreeOfCoherence > ALPHA_DIVISOR) {
- // Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
- degreeOfCoherence = ALPHA_DIVISOR;
+
+ // Guard against degree exceeding 1, though it should be ensured by the dispute kit.
+ if (coherence > ONE_BASIS_POINT) {
+ coherence = ONE_BASIS_POINT;
}
// Fully coherent jurors won't be penalized.
- uint256 penalty = (round.pnkAtStakePerJuror * (ALPHA_DIVISOR - degreeOfCoherence)) / ALPHA_DIVISOR;
+ uint256 penalty = (round.pnkAtStakePerJuror * (ONE_BASIS_POINT - coherence)) / ONE_BASIS_POINT;
// Unlock the PNKs affected by the penalty
address account = round.drawnJurors[_params.repartition];
@@ -794,7 +792,7 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
account,
_params.disputeID,
_params.round,
- degreeOfCoherence,
+ coherence,
-int256(availablePenalty),
0,
round.feeToken
@@ -826,7 +824,7 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
IDisputeKit disputeKit = disputeKits[round.disputeKitID];
// [0, 1] value that determines how coherent the juror was in this round, in basis points.
- uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence(
+ (uint256 pnkCoherence, uint256 feeCoherence) = disputeKit.getDegreeOfCoherenceReward(
_params.disputeID,
_params.round,
_params.repartition % _params.numberOfVotesInRound,
@@ -834,21 +832,24 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
_params.pnkAtStakePerJurorInRound
);
- // Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
- if (degreeOfCoherence > ALPHA_DIVISOR) {
- degreeOfCoherence = ALPHA_DIVISOR;
+ // Guard against degree exceeding 1, though it should be ensured by the dispute kit.
+ if (pnkCoherence > ONE_BASIS_POINT) {
+ pnkCoherence = ONE_BASIS_POINT;
+ }
+ if (feeCoherence > ONE_BASIS_POINT) {
+ feeCoherence = ONE_BASIS_POINT;
}
address account = round.drawnJurors[_params.repartition % _params.numberOfVotesInRound];
- uint256 pnkLocked = _applyCoherence(round.pnkAtStakePerJuror, degreeOfCoherence);
+ uint256 pnkLocked = _applyCoherence(round.pnkAtStakePerJuror, pnkCoherence);
// Release the rest of the PNKs of the juror for this round.
sortitionModule.unlockStake(account, pnkLocked);
// Transfer the rewards
- uint256 pnkReward = _applyCoherence(_params.pnkPenaltiesInRound / _params.coherentCount, degreeOfCoherence);
+ uint256 pnkReward = _applyCoherence(_params.pnkPenaltiesInRound / _params.coherentCount, pnkCoherence);
round.sumPnkRewardPaid += pnkReward;
- uint256 feeReward = _applyCoherence(round.totalFeesForJurors / _params.coherentCount, degreeOfCoherence);
+ uint256 feeReward = _applyCoherence(round.totalFeesForJurors / _params.coherentCount, feeCoherence);
round.sumFeeRewardPaid += feeReward;
pinakion.safeTransfer(account, pnkReward);
_transferFeeToken(round.feeToken, payable(account), feeReward);
@@ -856,7 +857,7 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
account,
_params.disputeID,
_params.round,
- degreeOfCoherence,
+ pnkCoherence,
int256(pnkReward),
int256(feeReward),
round.feeToken
@@ -1059,10 +1060,10 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
/// @dev Applies degree of coherence to an amount
/// @param _amount The base amount to apply coherence to.
- /// @param _degreeOfCoherence The degree of coherence in basis points.
+ /// @param _coherence The degree of coherence in basis points.
/// @return The amount after applying the degree of coherence.
- function _applyCoherence(uint256 _amount, uint256 _degreeOfCoherence) internal pure returns (uint256) {
- return (_amount * _degreeOfCoherence) / ALPHA_DIVISOR;
+ function _applyCoherence(uint256 _amount, uint256 _coherence) internal pure returns (uint256) {
+ return (_amount * _coherence) / ONE_BASIS_POINT;
}
/// @dev Calculates PNK at stake per juror based on court parameters
@@ -1070,7 +1071,7 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
/// @param _alpha The alpha parameter for the court in basis points.
/// @return The amount of PNK at stake per juror.
function _calculatePnkAtStake(uint256 _minStake, uint256 _alpha) internal pure returns (uint256) {
- return (_minStake * _alpha) / ALPHA_DIVISOR;
+ return (_minStake * _alpha) / ONE_BASIS_POINT;
}
/// @dev Toggles the dispute kit support for a given court.
diff --git a/contracts/src/arbitration/KlerosGovernor.sol b/contracts/src/arbitration/KlerosGovernor.sol
index 7e9415a7b..a0a23061e 100644
--- a/contracts/src/arbitration/KlerosGovernor.sol
+++ b/contracts/src/arbitration/KlerosGovernor.sol
@@ -70,18 +70,18 @@ contract KlerosGovernor is IArbitrableV2 {
modifier duringSubmissionPeriod() {
uint256 offset = sessions[sessions.length - 1].durationOffset;
- require(block.timestamp - lastApprovalTime <= submissionTimeout + offset, "Submission time has ended.");
+ if (block.timestamp - lastApprovalTime > submissionTimeout + offset) revert SubmissionTimeHasEnded();
_;
}
modifier duringApprovalPeriod() {
uint256 offset = sessions[sessions.length - 1].durationOffset;
- require(block.timestamp - lastApprovalTime > submissionTimeout + offset, "Approval time not started yet.");
+ if (block.timestamp - lastApprovalTime <= submissionTimeout + offset) revert ApprovalTimeNotStarted();
_;
}
modifier onlyByGovernor() {
- require(address(this) == msg.sender, "Only the governor allowed.");
+ if (address(this) != msg.sender) revert GovernorOnly();
_;
}
@@ -208,14 +208,14 @@ contract KlerosGovernor is IArbitrableV2 {
uint256[] memory _dataSize,
string memory _description
) external payable duringSubmissionPeriod {
- require(_target.length == _value.length, "Wrong input: target and value");
- require(_target.length == _dataSize.length, "Wrong input: target and datasize");
+ if (_target.length != _value.length) revert WrongInputTargetAndValue();
+ if (_target.length != _dataSize.length) revert WrongInputTargetAndDatasize();
Session storage session = sessions[sessions.length - 1];
Submission storage submission = submissions.push();
submission.submitter = payable(msg.sender);
// Do the assignment first to avoid creating a new variable and bypass a 'stack too deep' error.
submission.deposit = submissionBaseDeposit + arbitrator.arbitrationCost(arbitratorExtraData);
- require(msg.value >= submission.deposit, "Not enough ETH to cover deposit");
+ if (msg.value < submission.deposit) revert InsufficientDeposit();
bytes32 listHash;
bytes32 currentTxHash;
@@ -233,7 +233,7 @@ contract KlerosGovernor is IArbitrableV2 {
currentTxHash = keccak256(abi.encodePacked(transaction.target, transaction.value, transaction.data));
listHash = keccak256(abi.encodePacked(currentTxHash, listHash));
}
- require(!session.alreadySubmitted[listHash], "List already submitted");
+ if (session.alreadySubmitted[listHash]) revert ListAlreadySubmitted();
session.alreadySubmitted[listHash] = true;
submission.listHash = listHash;
submission.submissionTime = block.timestamp;
@@ -256,11 +256,11 @@ contract KlerosGovernor is IArbitrableV2 {
function withdrawTransactionList(uint256 _submissionID, bytes32 _listHash) external {
Session storage session = sessions[sessions.length - 1];
Submission storage submission = submissions[session.submittedLists[_submissionID]];
- require(block.timestamp - lastApprovalTime <= submissionTimeout / 2, "Should be in first half");
- // This require statement is an extra check to prevent _submissionID linking to the wrong list because of index swap during withdrawal.
- require(submission.listHash == _listHash, "Wrong list hash");
- require(submission.submitter == msg.sender, "Only submitter can withdraw");
- require(block.timestamp - submission.submissionTime <= withdrawTimeout, "Withdrawing time has passed.");
+ if (block.timestamp - lastApprovalTime > submissionTimeout / 2) revert ShouldOnlyWithdrawInFirstHalf();
+ // This is an extra check to prevent _submissionID linking to the wrong list because of index swap during withdrawal.
+ if (submission.listHash != _listHash) revert WrongListHash();
+ if (submission.submitter != msg.sender) revert OnlySubmitterCanWithdraw();
+ if (block.timestamp - submission.submissionTime > withdrawTimeout) revert WithdrawingTimeHasPassed();
session.submittedLists[_submissionID] = session.submittedLists[session.submittedLists.length - 1];
session.alreadySubmitted[_listHash] = false;
session.submittedLists.pop();
@@ -273,7 +273,7 @@ contract KlerosGovernor is IArbitrableV2 {
/// If nothing was submitted changes session.
function executeSubmissions() external duringApprovalPeriod {
Session storage session = sessions[sessions.length - 1];
- require(session.status == Status.NoDispute, "Already disputed");
+ if (session.status != Status.NoDispute) revert AlreadyDisputed();
if (session.submittedLists.length == 0) {
lastApprovalTime = block.timestamp;
session.status = Status.Resolved;
@@ -310,9 +310,9 @@ contract KlerosGovernor is IArbitrableV2 {
/// Note If the final ruling is "0" nothing is approved and deposits will stay locked in the contract.
function rule(uint256 _disputeID, uint256 _ruling) external override {
Session storage session = sessions[sessions.length - 1];
- require(msg.sender == address(arbitrator), "Only arbitrator allowed");
- require(session.status == Status.DisputeCreated, "Wrong status");
- require(_ruling <= session.submittedLists.length, "Ruling is out of bounds.");
+ if (msg.sender != address(arbitrator)) revert OnlyArbitratorAllowed();
+ if (session.status != Status.DisputeCreated) revert NotDisputed();
+ if (_ruling > session.submittedLists.length) revert RulingOutOfBounds();
if (_ruling != 0) {
Submission storage submission = submissions[session.submittedLists[_ruling - 1]];
@@ -338,8 +338,8 @@ contract KlerosGovernor is IArbitrableV2 {
/// @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.
function executeTransactionList(uint256 _listID, uint256 _cursor, uint256 _count) external {
Submission storage submission = submissions[_listID];
- require(submission.approved, "Should be approved");
- require(block.timestamp - submission.approvalTime <= executionTimeout, "Time to execute has passed");
+ if (!submission.approved) revert SubmissionNotApproved();
+ if (block.timestamp - submission.approvalTime > executionTimeout) revert TimeToExecuteHasPassed();
for (uint256 i = _cursor; i < submission.txs.length && (_count == 0 || i < _cursor + _count); i++) {
Transaction storage transaction = submission.txs[i];
uint256 expendableFunds = getExpendableFunds();
@@ -347,7 +347,7 @@ contract KlerosGovernor is IArbitrableV2 {
(bool callResult, ) = transaction.target.call{value: transaction.value}(transaction.data);
// An extra check to prevent re-entrancy through target call.
if (callResult == true) {
- require(!transaction.executed, "Already executed");
+ if (transaction.executed) revert AlreadyExecuted();
transaction.executed = true;
}
}
@@ -407,4 +407,27 @@ contract KlerosGovernor is IArbitrableV2 {
function getCurrentSessionNumber() external view returns (uint256) {
return sessions.length - 1;
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error SubmissionTimeHasEnded();
+ error ApprovalTimeNotStarted();
+ error GovernorOnly();
+ error WrongInputTargetAndValue();
+ error WrongInputTargetAndDatasize();
+ error InsufficientDeposit();
+ error ListAlreadySubmitted();
+ error ShouldOnlyWithdrawInFirstHalf();
+ error WrongListHash();
+ error OnlySubmitterCanWithdraw();
+ error WithdrawingTimeHasPassed();
+ error AlreadyDisputed();
+ error OnlyArbitratorAllowed();
+ error NotDisputed();
+ error RulingOutOfBounds();
+ error SubmissionNotApproved();
+ error TimeToExecuteHasPassed();
+ error AlreadyExecuted();
}
diff --git a/contracts/src/arbitration/PolicyRegistry.sol b/contracts/src/arbitration/PolicyRegistry.sol
index eb32476d6..f4ed36322 100644
--- a/contracts/src/arbitration/PolicyRegistry.sol
+++ b/contracts/src/arbitration/PolicyRegistry.sol
@@ -32,7 +32,7 @@ contract PolicyRegistry is UUPSProxiable, Initializable {
/// @dev Requires that the sender is the governor.
modifier onlyByGovernor() {
- require(governor == msg.sender, "No allowed: governor only");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
@@ -85,4 +85,10 @@ contract PolicyRegistry is UUPSProxiable, Initializable {
policies[_courtID] = _policy;
emit PolicyUpdate(_courtID, _courtName, policies[_courtID]);
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error GovernorOnly();
}
diff --git a/contracts/src/arbitration/SortitionModule.sol b/contracts/src/arbitration/SortitionModule.sol
index cb4f14c58..7e881264b 100644
--- a/contracts/src/arbitration/SortitionModule.sol
+++ b/contracts/src/arbitration/SortitionModule.sol
@@ -2,7 +2,7 @@
pragma solidity ^0.8.24;
-import {SortitionModuleBase, KlerosCore, RNG} from "./SortitionModuleBase.sol";
+import {SortitionModuleBase, KlerosCore, IRNG} from "./SortitionModuleBase.sol";
/// @title SortitionModule
/// @dev A factory of trees that keeps track of staked values for sortition.
@@ -24,16 +24,14 @@ contract SortitionModule is SortitionModuleBase {
/// @param _minStakingTime Minimal time to stake
/// @param _maxDrawingTime Time after which the drawing phase can be switched
/// @param _rng The random number generator.
- /// @param _rngLookahead Lookahead value for rng.
function initialize(
address _governor,
KlerosCore _core,
uint256 _minStakingTime,
uint256 _maxDrawingTime,
- RNG _rng,
- uint256 _rngLookahead
+ IRNG _rng
) external reinitializer(1) {
- __SortitionModuleBase_initialize(_governor, _core, _minStakingTime, _maxDrawingTime, _rng, _rngLookahead);
+ __SortitionModuleBase_initialize(_governor, _core, _minStakingTime, _maxDrawingTime, _rng);
}
function initialize4() external reinitializer(4) {
diff --git a/contracts/src/arbitration/SortitionModuleBase.sol b/contracts/src/arbitration/SortitionModuleBase.sol
index 577d9fd22..2ad7b89b2 100644
--- a/contracts/src/arbitration/SortitionModuleBase.sol
+++ b/contracts/src/arbitration/SortitionModuleBase.sol
@@ -7,7 +7,7 @@ import {ISortitionModule} from "./interfaces/ISortitionModule.sol";
import {IDisputeKit} from "./interfaces/IDisputeKit.sol";
import {Initializable} from "../proxy/Initializable.sol";
import {UUPSProxiable} from "../proxy/UUPSProxiable.sol";
-import {RNG} from "../rng/RNG.sol";
+import {IRNG} from "../rng/IRNG.sol";
import "../libraries/Constants.sol";
/// @title SortitionModuleBase
@@ -50,11 +50,11 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
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; // Number of the block when RNG request was made.
+ uint256 public randomNumberRequestBlock; // DEPRECATED: to be removed in the next redeploy
uint256 public disputesWithoutJurors; // The number of disputes that have not finished drawing jurors.
- RNG public rng; // The random number generator.
+ IRNG public rng; // The random number generator.
uint256 public randomNumber; // Random number returned by RNG.
- uint256 public rngLookahead; // Minimal block distance between requesting and obtaining a random number.
+ 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(bytes32 treeHash => SortitionSumTree) sortitionSumTrees; // The mapping trees by keys.
@@ -104,8 +104,7 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
KlerosCore _core,
uint256 _minStakingTime,
uint256 _maxDrawingTime,
- RNG _rng,
- uint256 _rngLookahead
+ IRNG _rng
) internal onlyInitializing {
governor = _governor;
core = _core;
@@ -113,7 +112,6 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
maxDrawingTime = _maxDrawingTime;
lastPhaseChange = block.timestamp;
rng = _rng;
- rngLookahead = _rngLookahead;
delayedStakeReadIndex = 1;
}
@@ -122,12 +120,12 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
// ************************************* //
modifier onlyByGovernor() {
- require(address(governor) == msg.sender, "Access not allowed: Governor only.");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
modifier onlyByCore() {
- require(address(core) == msg.sender, "Access not allowed: KlerosCore only.");
+ if (address(core) != msg.sender) revert KlerosCoreOnly();
_;
}
@@ -153,15 +151,12 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
maxDrawingTime = _maxDrawingTime;
}
- /// @dev Changes the `_rng` and `_rngLookahead` storage variables.
- /// @param _rng The new value for the `RNGenerator` storage variable.
- /// @param _rngLookahead The new value for the `rngLookahead` storage variable.
- function changeRandomNumberGenerator(RNG _rng, uint256 _rngLookahead) external onlyByGovernor {
+ /// @dev Changes the `rng` storage variable.
+ /// @param _rng The new random number generator.
+ function changeRandomNumberGenerator(IRNG _rng) external onlyByGovernor {
rng = _rng;
- rngLookahead = _rngLookahead;
if (phase == Phase.generating) {
- rng.requestRandomness(block.number + rngLookahead);
- randomNumberRequestBlock = block.number;
+ rng.requestRandomness();
}
}
@@ -171,23 +166,18 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
function passPhase() external {
if (phase == Phase.staking) {
- require(
- block.timestamp - lastPhaseChange >= minStakingTime,
- "The minimum staking time has not passed yet."
- );
- require(disputesWithoutJurors > 0, "There are no disputes that need jurors.");
- rng.requestRandomness(block.number + rngLookahead);
- randomNumberRequestBlock = block.number;
+ if (block.timestamp - lastPhaseChange < minStakingTime) revert MinStakingTimeNotPassed();
+ if (disputesWithoutJurors == 0) revert NoDisputesThatNeedJurors();
+ rng.requestRandomness();
phase = Phase.generating;
} else if (phase == Phase.generating) {
- randomNumber = rng.receiveRandomness(randomNumberRequestBlock + rngLookahead);
- require(randomNumber != 0, "Random number is not ready yet");
+ randomNumber = rng.receiveRandomness();
+ if (randomNumber == 0) revert RandomNumberNotReady();
phase = Phase.drawing;
} else if (phase == Phase.drawing) {
- require(
- disputesWithoutJurors == 0 || block.timestamp - lastPhaseChange >= maxDrawingTime,
- "There are still disputes without jurors and the maximum drawing time has not passed yet."
- );
+ if (disputesWithoutJurors > 0 && block.timestamp - lastPhaseChange < maxDrawingTime) {
+ revert DisputesWithoutJurorsAndMaxDrawingTimeNotPassed();
+ }
phase = Phase.staking;
}
@@ -201,8 +191,8 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
function createTree(bytes32 _key, bytes memory _extraData) external override onlyByCore {
SortitionSumTree storage tree = sortitionSumTrees[_key];
uint256 K = _extraDataToTreeK(_extraData);
- require(tree.K == 0, "Tree already exists.");
- require(K > 1, "K must be greater than one.");
+ if (tree.K != 0) revert TreeAlreadyExists();
+ if (K <= 1) revert KMustBeGreaterThanOne();
tree.K = K;
tree.nodes.push(0);
}
@@ -210,8 +200,8 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
/// @dev Executes the next delayed stakes.
/// @param _iterations The number of delayed stakes to execute.
function executeDelayedStakes(uint256 _iterations) external {
- require(phase == Phase.staking, "Should be in Staking phase.");
- require(delayedStakeWriteIndex >= delayedStakeReadIndex, "No delayed stake to execute.");
+ if (phase != Phase.staking) revert NotStakingPhase();
+ if (delayedStakeWriteIndex < delayedStakeReadIndex) revert NoDelayedStakeToExecute();
uint256 actualIterations = (delayedStakeReadIndex + _iterations) - 1 > delayedStakeWriteIndex
? (delayedStakeWriteIndex - delayedStakeReadIndex) + 1
@@ -348,14 +338,14 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
// Update the sortition sum tree.
bytes32 stakePathID = _accountAndCourtIDToStakePathID(_account, _courtID);
bool finished = false;
- uint96 currenCourtID = _courtID;
+ uint96 currentCourtID = _courtID;
while (!finished) {
// Tokens are also implicitly staked in parent courts through sortition module to increase the chance of being drawn.
- _set(bytes32(uint256(currenCourtID)), _newStake, stakePathID);
- if (currenCourtID == GENERAL_COURT) {
+ _set(bytes32(uint256(currentCourtID)), _newStake, stakePathID);
+ if (currentCourtID == GENERAL_COURT) {
finished = true;
} else {
- (currenCourtID, , , , , , ) = core.courts(currenCourtID); // Get the parent court.
+ (currentCourtID, , , , , , ) = core.courts(currentCourtID); // Get the parent court.
}
}
emit StakeSet(_account, _courtID, _newStake, juror.stakedPnk);
@@ -420,7 +410,7 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
// 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.
uint256 amount = getJurorLeftoverPNK(_account);
- require(amount > 0, "Not eligible for withdrawal.");
+ if (amount == 0) revert NotEligibleForWithdrawal();
jurors[_account].stakedPnk = 0;
core.transferBySortitionModule(_account, amount);
emit LeftoverPNKWithdrawn(_account, amount);
@@ -444,7 +434,7 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
uint256 _coreDisputeID,
uint256 _nonce
) public view override returns (address drawnAddress) {
- require(phase == Phase.drawing, "Wrong phase.");
+ if (phase != Phase.drawing) revert NotDrawingPhase();
SortitionSumTree storage tree = sortitionSumTrees[_key];
if (tree.nodes[0] == 0) {
@@ -692,4 +682,21 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
stakePathID := mload(ptr)
}
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error GovernorOnly();
+ error KlerosCoreOnly();
+ error MinStakingTimeNotPassed();
+ error NoDisputesThatNeedJurors();
+ error RandomNumberNotReady();
+ error DisputesWithoutJurorsAndMaxDrawingTimeNotPassed();
+ error TreeAlreadyExists();
+ error KMustBeGreaterThanOne();
+ error NotStakingPhase();
+ error NoDelayedStakeToExecute();
+ error NotEligibleForWithdrawal();
+ error NotDrawingPhase();
}
diff --git a/contracts/src/arbitration/SortitionModuleNeo.sol b/contracts/src/arbitration/SortitionModuleNeo.sol
index b966c9379..9758882fe 100644
--- a/contracts/src/arbitration/SortitionModuleNeo.sol
+++ b/contracts/src/arbitration/SortitionModuleNeo.sol
@@ -2,7 +2,7 @@
pragma solidity ^0.8.24;
-import {SortitionModuleBase, KlerosCore, RNG, StakingResult} from "./SortitionModuleBase.sol";
+import {SortitionModuleBase, KlerosCore, IRNG, StakingResult} from "./SortitionModuleBase.sol";
/// @title SortitionModuleNeo
/// @dev A factory of trees that keeps track of staked values for sortition.
@@ -32,7 +32,6 @@ contract SortitionModuleNeo is SortitionModuleBase {
/// @param _minStakingTime Minimal time to stake
/// @param _maxDrawingTime Time after which the drawing phase can be switched
/// @param _rng The random number generator.
- /// @param _rngLookahead Lookahead value for rng.
/// @param _maxStakePerJuror The maximum amount of PNK a juror can stake in a court.
/// @param _maxTotalStaked The maximum amount of PNK that can be staked in all courts.
function initialize(
@@ -40,12 +39,11 @@ contract SortitionModuleNeo is SortitionModuleBase {
KlerosCore _core,
uint256 _minStakingTime,
uint256 _maxDrawingTime,
- RNG _rng,
- uint256 _rngLookahead,
+ IRNG _rng,
uint256 _maxStakePerJuror,
uint256 _maxTotalStaked
) external reinitializer(2) {
- __SortitionModuleBase_initialize(_governor, _core, _minStakingTime, _maxDrawingTime, _rng, _rngLookahead);
+ __SortitionModuleBase_initialize(_governor, _core, _minStakingTime, _maxDrawingTime, _rng);
maxStakePerJuror = _maxStakePerJuror;
maxTotalStaked = _maxTotalStaked;
}
diff --git a/contracts/src/arbitration/arbitrables/ArbitrableExample.sol b/contracts/src/arbitration/arbitrables/ArbitrableExample.sol
index 810688788..b7596566e 100644
--- a/contracts/src/arbitration/arbitrables/ArbitrableExample.sol
+++ b/contracts/src/arbitration/arbitrables/ArbitrableExample.sol
@@ -37,7 +37,7 @@ contract ArbitrableExample is IArbitrableV2 {
// ************************************* //
modifier onlyByGovernor() {
- require(msg.sender == governor, "Only the governor allowed.");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
@@ -126,8 +126,8 @@ contract ArbitrableExample is IArbitrableV2 {
uint256 localDisputeID = disputes.length;
disputes.push(DisputeStruct({isRuled: false, ruling: 0, numberOfRulingOptions: numberOfRulingOptions}));
- require(weth.safeTransferFrom(msg.sender, address(this), _feeInWeth), "Transfer failed");
- require(weth.increaseAllowance(address(arbitrator), _feeInWeth), "Allowance increase failed");
+ if (!weth.safeTransferFrom(msg.sender, address(this), _feeInWeth)) revert TransferFailed();
+ if (!weth.increaseAllowance(address(arbitrator), _feeInWeth)) revert AllowanceIncreaseFailed();
disputeID = arbitrator.createDispute(numberOfRulingOptions, arbitratorExtraData, weth, _feeInWeth);
externalIDtoLocalID[disputeID] = localDisputeID;
@@ -142,13 +142,24 @@ contract ArbitrableExample is IArbitrableV2 {
function rule(uint256 _arbitratorDisputeID, uint256 _ruling) external override {
uint256 localDisputeID = externalIDtoLocalID[_arbitratorDisputeID];
DisputeStruct storage dispute = disputes[localDisputeID];
- require(msg.sender == address(arbitrator), "Only the arbitrator can execute this.");
- require(_ruling <= dispute.numberOfRulingOptions, "Invalid ruling.");
- require(dispute.isRuled == false, "This dispute has been ruled already.");
+ if (msg.sender != address(arbitrator)) revert ArbitratorOnly();
+ if (_ruling > dispute.numberOfRulingOptions) revert RulingOutOfBounds();
+ if (dispute.isRuled) revert DisputeAlreadyRuled();
dispute.isRuled = true;
dispute.ruling = _ruling;
emit Ruling(IArbitratorV2(msg.sender), _arbitratorDisputeID, dispute.ruling);
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error GovernorOnly();
+ error TransferFailed();
+ error AllowanceIncreaseFailed();
+ error ArbitratorOnly();
+ error RulingOutOfBounds();
+ error DisputeAlreadyRuled();
}
diff --git a/contracts/src/arbitration/arbitrables/DisputeResolver.sol b/contracts/src/arbitration/arbitrables/DisputeResolver.sol
index 8fa1da02f..7248356cd 100644
--- a/contracts/src/arbitration/arbitrables/DisputeResolver.sol
+++ b/contracts/src/arbitration/arbitrables/DisputeResolver.sol
@@ -48,17 +48,17 @@ contract DisputeResolver is IArbitrableV2 {
/// @dev Changes the governor.
/// @param _governor The address of the new governor.
function changeGovernor(address _governor) external {
- require(governor == msg.sender, "Access not allowed: Governor only.");
+ if (governor != msg.sender) revert GovernorOnly();
governor = _governor;
}
function changeArbitrator(IArbitratorV2 _arbitrator) external {
- require(governor == msg.sender, "Access not allowed: Governor only.");
+ if (governor != msg.sender) revert GovernorOnly();
arbitrator = _arbitrator;
}
function changeTemplateRegistry(IDisputeTemplateRegistry _templateRegistry) external {
- require(governor == msg.sender, "Access not allowed: Governor only.");
+ if (governor != msg.sender) revert GovernorOnly();
templateRegistry = _templateRegistry;
}
@@ -109,9 +109,9 @@ contract DisputeResolver is IArbitrableV2 {
function rule(uint256 _arbitratorDisputeID, uint256 _ruling) external override {
uint256 localDisputeID = arbitratorDisputeIDToLocalID[_arbitratorDisputeID];
DisputeStruct storage dispute = disputes[localDisputeID];
- require(msg.sender == address(arbitrator), "Only the arbitrator can execute this.");
- require(_ruling <= dispute.numberOfRulingOptions, "Invalid ruling.");
- require(!dispute.isRuled, "This dispute has been ruled already.");
+ if (msg.sender != address(arbitrator)) revert ArbitratorOnly();
+ if (_ruling > dispute.numberOfRulingOptions) revert RulingOutOfBounds();
+ if (dispute.isRuled) revert DisputeAlreadyRuled();
dispute.isRuled = true;
dispute.ruling = _ruling;
@@ -130,7 +130,7 @@ contract DisputeResolver is IArbitrableV2 {
string memory _disputeTemplateUri,
uint256 _numberOfRulingOptions
) internal virtual returns (uint256 arbitratorDisputeID) {
- require(_numberOfRulingOptions > 1, "Should be at least 2 ruling options.");
+ if (_numberOfRulingOptions <= 1) revert ShouldBeAtLeastTwoRulingOptions();
arbitratorDisputeID = arbitrator.createDispute{value: msg.value}(_numberOfRulingOptions, _arbitratorExtraData);
uint256 localDisputeID = disputes.length;
@@ -146,4 +146,14 @@ contract DisputeResolver is IArbitrableV2 {
uint256 templateId = templateRegistry.setDisputeTemplate("", _disputeTemplate, _disputeTemplateDataMappings);
emit DisputeRequest(arbitrator, arbitratorDisputeID, localDisputeID, templateId, _disputeTemplateUri);
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error GovernorOnly();
+ error ArbitratorOnly();
+ error RulingOutOfBounds();
+ error DisputeAlreadyRuled();
+ error ShouldBeAtLeastTwoRulingOptions();
}
diff --git a/contracts/src/arbitration/devtools/DisputeResolverRuler.sol b/contracts/src/arbitration/devtools/DisputeResolverRuler.sol
index 85bede8bf..05c033c38 100644
--- a/contracts/src/arbitration/devtools/DisputeResolverRuler.sol
+++ b/contracts/src/arbitration/devtools/DisputeResolverRuler.sol
@@ -36,7 +36,7 @@ contract DisputeResolverRuler is DisputeResolver {
string memory _disputeTemplateUri,
uint256 _numberOfRulingOptions
) internal override returns (uint256 arbitratorDisputeID) {
- require(_numberOfRulingOptions > 1, "Should be at least 2 ruling options.");
+ if (_numberOfRulingOptions <= 1) revert ShouldBeAtLeastTwoRulingOptions();
uint256 localDisputeID = disputes.length;
DisputeStruct storage dispute = disputes.push();
diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol b/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol
index 9b1968400..0922f047b 100644
--- a/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol
+++ b/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol
@@ -6,6 +6,7 @@ import {KlerosCore, KlerosCoreBase, IDisputeKit, ISortitionModule} from "../Kler
import {Initializable} from "../../proxy/Initializable.sol";
import {UUPSProxiable} from "../../proxy/UUPSProxiable.sol";
import {SafeSend} from "../../libraries/SafeSend.sol";
+import {ONE_BASIS_POINT} from "../../libraries/Constants.sol";
/// @title DisputeKitClassicBase
/// Abstract Dispute kit classic implementation of the Kleros v1 features including:
@@ -57,7 +58,6 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
uint256 public constant WINNER_STAKE_MULTIPLIER = 10000; // Multiplier of the appeal cost that the winner has to pay as fee stake for a round in basis points. Default is 1x of appeal fee.
uint256 public constant LOSER_STAKE_MULTIPLIER = 20000; // Multiplier of the appeal cost that the loser has to pay as fee stake for a round in basis points. Default is 2x of appeal fee.
uint256 public constant LOSER_APPEAL_PERIOD_MULTIPLIER = 5000; // Multiplier of the appeal period for the choice that wasn't voted for in the previous round, in basis points. Default is 1/2 of original appeal period.
- uint256 public constant ONE_BASIS_POINT = 10000; // One basis point, for scaling.
address public governor; // The governor of the contract.
KlerosCore public core; // The Kleros Core arbitrator
@@ -125,17 +125,17 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
// ************************************* //
modifier onlyByGovernor() {
- require(governor == msg.sender, "Access not allowed: Governor only.");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
modifier onlyByCore() {
- require(address(core) == msg.sender, "Access not allowed: KlerosCore only.");
+ if (address(core) != msg.sender) revert KlerosCoreOnly();
_;
}
modifier notJumped(uint256 _coreDisputeID) {
- require(!disputes[coreDisputeIDToLocal[_coreDisputeID]].jumped, "Dispute jumped to a parent DK!");
+ if (disputes[coreDisputeIDToLocal[_coreDisputeID]].jumped) revert DisputeJumpedToParentDK();
_;
}
@@ -171,7 +171,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
bytes memory _data
) external onlyByGovernor {
(bool success, ) = _destination.call{value: _amount}(_data);
- require(success, "Unsuccessful call");
+ if (!success) revert UnsuccessfulCall();
}
/// @dev Changes the `governor` storage variable.
@@ -269,14 +269,14 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
bytes32 _commit
) internal notJumped(_coreDisputeID) {
(, , KlerosCore.Period period, , ) = core.disputes(_coreDisputeID);
- require(period == KlerosCoreBase.Period.commit, "The dispute should be in Commit period.");
- require(_commit != bytes32(0), "Empty commit.");
- require(coreDisputeIDToActive[_coreDisputeID], "Not active for core dispute ID");
+ if (period != KlerosCoreBase.Period.commit) revert NotCommitPeriod();
+ if (_commit == bytes32(0)) revert EmptyCommit();
+ if (!coreDisputeIDToActive[_coreDisputeID]) revert NotActiveForCoreDisputeID();
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
Round storage round = dispute.rounds[dispute.rounds.length - 1];
for (uint256 i = 0; i < _voteIDs.length; i++) {
- require(round.votes[_voteIDs[i]].account == msg.sender, "The caller has to own the vote.");
+ if (round.votes[_voteIDs[i]].account != msg.sender) revert JurorHasToOwnTheVote();
round.votes[_voteIDs[i]].commit = _commit;
}
round.totalCommitted += _voteIDs.length;
@@ -310,12 +310,12 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
address _juror
) internal notJumped(_coreDisputeID) {
(, , KlerosCore.Period period, , ) = core.disputes(_coreDisputeID);
- require(period == KlerosCoreBase.Period.vote, "The dispute should be in Vote period.");
- require(_voteIDs.length > 0, "No voteID provided");
- require(coreDisputeIDToActive[_coreDisputeID], "Not active for core dispute ID");
+ if (period != KlerosCoreBase.Period.vote) revert NotVotePeriod();
+ if (_voteIDs.length == 0) revert EmptyVoteIDs();
+ if (!coreDisputeIDToActive[_coreDisputeID]) revert NotActiveForCoreDisputeID();
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
- require(_choice <= dispute.numberOfChoices, "Choice out of bounds");
+ if (_choice > dispute.numberOfChoices) revert ChoiceOutOfBounds();
Round storage round = dispute.rounds[dispute.rounds.length - 1];
{
@@ -325,12 +325,10 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
// Save the votes.
for (uint256 i = 0; i < _voteIDs.length; i++) {
- require(round.votes[_voteIDs[i]].account == _juror, "The juror has to own the vote.");
- require(
- !hiddenVotes || round.votes[_voteIDs[i]].commit == voteHash,
- "The vote hash must match the commitment in courts with hidden votes."
- );
- require(!round.votes[_voteIDs[i]].voted, "Vote already cast.");
+ if (round.votes[_voteIDs[i]].account != _juror) revert JurorHasToOwnTheVote();
+ if (hiddenVotes && round.votes[_voteIDs[i]].commit != voteHash)
+ revert HashDoesNotMatchHiddenVoteCommitment();
+ if (round.votes[_voteIDs[i]].voted) revert VoteAlreadyCast();
round.votes[_voteIDs[i]].choice = _choice;
round.votes[_voteIDs[i]].voted = true;
}
@@ -361,29 +359,30 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
/// @param _choice A choice that receives funding.
function fundAppeal(uint256 _coreDisputeID, uint256 _choice) external payable notJumped(_coreDisputeID) {
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
- require(_choice <= dispute.numberOfChoices, "There is no such ruling to fund.");
- require(coreDisputeIDToActive[_coreDisputeID], "Not active for core dispute ID");
+ if (_choice > dispute.numberOfChoices) revert ChoiceOutOfBounds();
+ if (!coreDisputeIDToActive[_coreDisputeID]) revert NotActiveForCoreDisputeID();
(uint256 appealPeriodStart, uint256 appealPeriodEnd) = core.appealPeriod(_coreDisputeID);
- require(block.timestamp >= appealPeriodStart && block.timestamp < appealPeriodEnd, "Appeal period is over.");
+ if (block.timestamp < appealPeriodStart || block.timestamp >= appealPeriodEnd) revert AppealPeriodIsOver();
uint256 multiplier;
(uint256 ruling, , ) = this.currentRuling(_coreDisputeID);
if (ruling == _choice) {
multiplier = WINNER_STAKE_MULTIPLIER;
} else {
- require(
- block.timestamp - appealPeriodStart <
- ((appealPeriodEnd - appealPeriodStart) * LOSER_APPEAL_PERIOD_MULTIPLIER) / ONE_BASIS_POINT,
- "Appeal period is over for loser"
- );
+ if (
+ block.timestamp - appealPeriodStart >=
+ ((appealPeriodEnd - appealPeriodStart) * LOSER_APPEAL_PERIOD_MULTIPLIER) / ONE_BASIS_POINT
+ ) {
+ revert AppealPeriodIsOverForLoser();
+ }
multiplier = LOSER_STAKE_MULTIPLIER;
}
Round storage round = dispute.rounds[dispute.rounds.length - 1];
uint256 coreRoundID = core.getNumberOfRounds(_coreDisputeID) - 1;
- require(!round.hasPaid[_choice], "Appeal fee is already paid.");
+ if (round.hasPaid[_choice]) revert AppealFeeIsAlreadyPaid();
uint256 appealCost = core.appealCost(_coreDisputeID);
uint256 totalCost = appealCost + (appealCost * multiplier) / ONE_BASIS_POINT;
@@ -440,9 +439,9 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
uint256 _choice
) external returns (uint256 amount) {
(, , , bool isRuled, ) = core.disputes(_coreDisputeID);
- require(isRuled, "Dispute should be resolved.");
- require(!core.paused(), "Core is paused");
- require(coreDisputeIDToActive[_coreDisputeID], "Not active for core dispute ID");
+ if (!isRuled) revert DisputeNotResolved();
+ if (core.paused()) revert CoreIsPaused();
+ if (!coreDisputeIDToActive[_coreDisputeID]) revert NotActiveForCoreDisputeID();
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
Round storage round = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]];
@@ -527,14 +526,39 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
/// @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 The degree of coherence in basis points.
- function getDegreeOfCoherence(
+ /// @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.
+ function getDegreeOfCoherenceReward(
+ uint256 _coreDisputeID,
+ uint256 _coreRoundID,
+ uint256 _voteID,
+ uint256 /* _feePerJuror */,
+ uint256 /* _pnkAtStakePerJuror */
+ ) external view override returns (uint256 pnkCoherence, uint256 feeCoherence) {
+ uint256 coherence = _getDegreeOfCoherence(_coreDisputeID, _coreRoundID, _voteID);
+ 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.
+ function getDegreeOfCoherencePenalty(
uint256 _coreDisputeID,
uint256 _coreRoundID,
uint256 _voteID,
uint256 /* _feePerJuror */,
uint256 /* _pnkAtStakePerJuror */
- ) external view override returns (uint256) {
+ ) external view override returns (uint256 pnkCoherence) {
+ return _getDegreeOfCoherence(_coreDisputeID, _coreRoundID, _voteID);
+ }
+
+ function _getDegreeOfCoherence(
+ uint256 _coreDisputeID,
+ uint256 _coreRoundID,
+ uint256 _voteID
+ ) internal view returns (uint256 coherence) {
// In this contract this degree can be either 0 or 1, but in other dispute kits this value can be something in between.
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
Vote storage vote = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]].votes[_voteID];
@@ -710,4 +734,27 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
result = true;
}
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error GovernorOnly();
+ error KlerosCoreOnly();
+ error DisputeJumpedToParentDK();
+ error UnsuccessfulCall();
+ error NotCommitPeriod();
+ error EmptyCommit();
+ error NotActiveForCoreDisputeID();
+ error JurorHasToOwnTheVote();
+ error NotVotePeriod();
+ error EmptyVoteIDs();
+ error ChoiceOutOfBounds();
+ error HashDoesNotMatchHiddenVoteCommitment();
+ error VoteAlreadyCast();
+ error AppealPeriodIsOver();
+ error AppealPeriodIsOverForLoser();
+ error AppealFeeIsAlreadyPaid();
+ error DisputeNotResolved();
+ error CoreIsPaused();
}
diff --git a/contracts/src/arbitration/evidence/EvidenceModule.sol b/contracts/src/arbitration/evidence/EvidenceModule.sol
index fe55122b9..4967597ab 100644
--- a/contracts/src/arbitration/evidence/EvidenceModule.sol
+++ b/contracts/src/arbitration/evidence/EvidenceModule.sol
@@ -22,7 +22,7 @@ contract EvidenceModule is IEvidence, Initializable, UUPSProxiable {
// ************************************* //
modifier onlyByGovernor() {
- require(governor == msg.sender, "Access not allowed: Governor only.");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
@@ -67,4 +67,10 @@ contract EvidenceModule is IEvidence, Initializable, UUPSProxiable {
function submitEvidence(uint256 _externalDisputeID, string calldata _evidence) external {
emit Evidence(_externalDisputeID, msg.sender, _evidence);
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error GovernorOnly();
}
diff --git a/contracts/src/arbitration/evidence/ModeratedEvidenceModule.sol b/contracts/src/arbitration/evidence/ModeratedEvidenceModule.sol
index dbfbd8adf..1c612265f 100644
--- a/contracts/src/arbitration/evidence/ModeratedEvidenceModule.sol
+++ b/contracts/src/arbitration/evidence/ModeratedEvidenceModule.sol
@@ -5,12 +5,9 @@ pragma solidity ^0.8.24;
// TODO: standard interfaces should be placed in a separated repo (?)
import {IArbitrableV2, IArbitratorV2} from "../interfaces/IArbitrableV2.sol";
import "../interfaces/IDisputeTemplateRegistry.sol";
-import "../../libraries/CappedMath.sol";
/// @title Implementation of the Evidence Standard with Moderated Submissions
contract ModeratedEvidenceModule is IArbitrableV2 {
- using CappedMath for uint256;
-
// ************************************* //
// * Enums / Structs * //
// ************************************* //
@@ -205,8 +202,8 @@ contract ModeratedEvidenceModule is IArbitrableV2 {
ArbitratorData storage arbitratorData = arbitratorDataList[arbitratorDataList.length - 1];
uint256 arbitrationCost = arbitrator.arbitrationCost(arbitratorData.arbitratorExtraData);
- uint256 totalCost = arbitrationCost.mulCap(totalCostMultiplier) / MULTIPLIER_DIVISOR;
- uint256 depositRequired = totalCost.mulCap(initialDepositMultiplier) / MULTIPLIER_DIVISOR;
+ uint256 totalCost = (arbitrationCost * totalCostMultiplier) / MULTIPLIER_DIVISOR;
+ uint256 depositRequired = (totalCost * initialDepositMultiplier) / MULTIPLIER_DIVISOR;
Moderation storage moderation = evidenceData.moderations.push();
// Overpaying is allowed.
@@ -245,12 +242,12 @@ contract ModeratedEvidenceModule is IArbitrableV2 {
ArbitratorData storage arbitratorData = arbitratorDataList[moderation.arbitratorDataID];
uint256 arbitrationCost = arbitrator.arbitrationCost(arbitratorData.arbitratorExtraData);
- uint256 totalCost = arbitrationCost.mulCap(totalCostMultiplier) / MULTIPLIER_DIVISOR;
+ uint256 totalCost = (arbitrationCost * totalCostMultiplier) / MULTIPLIER_DIVISOR;
uint256 opposition = 3 - uint256(_side);
uint256 depositRequired = moderation.paidFees[opposition] * 2;
if (depositRequired == 0) {
- depositRequired = totalCost.mulCap(initialDepositMultiplier) / MULTIPLIER_DIVISOR;
+ depositRequired = (totalCost * initialDepositMultiplier) / MULTIPLIER_DIVISOR;
} else if (depositRequired > totalCost) {
depositRequired = totalCost;
}
@@ -317,7 +314,9 @@ contract ModeratedEvidenceModule is IArbitrableV2 {
) internal returns (uint256) {
uint256 contribution;
uint256 remainingETH;
- uint256 requiredAmount = _totalRequired.subCap(_moderation.paidFees[uint256(_side)]);
+ uint256 requiredAmount = _moderation.paidFees[uint256(_side)] >= _totalRequired
+ ? 0
+ : _totalRequired - _moderation.paidFees[uint256(_side)];
(contribution, remainingETH) = calculateContribution(_amount, requiredAmount);
_moderation.contributions[_contributor][uint256(_side)] += contribution;
_moderation.paidFees[uint256(_side)] += contribution;
diff --git a/contracts/src/arbitration/interfaces/IArbitrableV2.sol b/contracts/src/arbitration/interfaces/IArbitrableV2.sol
index cba10115e..22dac6e4a 100644
--- a/contracts/src/arbitration/interfaces/IArbitrableV2.sol
+++ b/contracts/src/arbitration/interfaces/IArbitrableV2.sol
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.24;
+pragma solidity >=0.8.0 <0.9.0;
import "./IArbitratorV2.sol";
diff --git a/contracts/src/arbitration/interfaces/IArbitratorV2.sol b/contracts/src/arbitration/interfaces/IArbitratorV2.sol
index e2e76badb..9559c81b3 100644
--- a/contracts/src/arbitration/interfaces/IArbitratorV2.sol
+++ b/contracts/src/arbitration/interfaces/IArbitratorV2.sol
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.24;
+pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IArbitrableV2.sol";
diff --git a/contracts/src/arbitration/interfaces/IDisputeKit.sol b/contracts/src/arbitration/interfaces/IDisputeKit.sol
index 4c8f458cc..6a72b35e4 100644
--- a/contracts/src/arbitration/interfaces/IDisputeKit.sol
+++ b/contracts/src/arbitration/interfaces/IDisputeKit.sol
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.24;
+pragma solidity >=0.8.0 <0.9.0;
import "./IArbitratorV2.sol";
@@ -67,14 +67,30 @@ interface IDisputeKit {
/// @param _voteID The ID of the vote.
/// @param _feePerJuror The fee per juror.
/// @param _pnkAtStakePerJuror The PNK at stake per juror.
- /// @return The degree of coherence in basis points.
- function getDegreeOfCoherence(
+ /// @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.
+ function getDegreeOfCoherenceReward(
uint256 _coreDisputeID,
uint256 _coreRoundID,
uint256 _voteID,
uint256 _feePerJuror,
uint256 _pnkAtStakePerJuror
- ) external view returns (uint256);
+ ) 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.
+ /// @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.
+ /// @param _feePerJuror The fee per juror.
+ /// @param _pnkAtStakePerJuror The PNK at stake per juror.
+ /// @return pnkCoherence The degree of coherence in basis points for the dispute PNK reward.
+ function getDegreeOfCoherencePenalty(
+ uint256 _coreDisputeID,
+ uint256 _coreRoundID,
+ uint256 _voteID,
+ uint256 _feePerJuror,
+ uint256 _pnkAtStakePerJuror
+ ) external view returns (uint256 pnkCoherence);
/// @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.
diff --git a/contracts/src/arbitration/interfaces/IDisputeTemplateRegistry.sol b/contracts/src/arbitration/interfaces/IDisputeTemplateRegistry.sol
index 3ce9d522d..23e09f8d5 100644
--- a/contracts/src/arbitration/interfaces/IDisputeTemplateRegistry.sol
+++ b/contracts/src/arbitration/interfaces/IDisputeTemplateRegistry.sol
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.24;
+pragma solidity >=0.8.0 <0.9.0;
/// @title IDisputeTemplate
/// @notice Dispute Template interface.
diff --git a/contracts/src/arbitration/interfaces/IEvidence.sol b/contracts/src/arbitration/interfaces/IEvidence.sol
index 0be6082b5..a7683186a 100644
--- a/contracts/src/arbitration/interfaces/IEvidence.sol
+++ b/contracts/src/arbitration/interfaces/IEvidence.sol
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.24;
+pragma solidity >=0.8.0 <0.9.0;
/// @title IEvidence
interface IEvidence {
diff --git a/contracts/src/arbitration/interfaces/ISortitionModule.sol b/contracts/src/arbitration/interfaces/ISortitionModule.sol
index 183fc331a..5cf10e6ae 100644
--- a/contracts/src/arbitration/interfaces/ISortitionModule.sol
+++ b/contracts/src/arbitration/interfaces/ISortitionModule.sol
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.24;
+
+pragma solidity >=0.8.0 <0.9.0;
import "../../libraries/Constants.sol";
diff --git a/contracts/src/arbitration/university/KlerosCoreUniversity.sol b/contracts/src/arbitration/university/KlerosCoreUniversity.sol
index 35fd5262c..5744088b0 100644
--- a/contracts/src/arbitration/university/KlerosCoreUniversity.sol
+++ b/contracts/src/arbitration/university/KlerosCoreUniversity.sol
@@ -6,9 +6,9 @@ import {IArbitrableV2, IArbitratorV2} from "../interfaces/IArbitratorV2.sol";
import {IDisputeKit} from "../interfaces/IDisputeKit.sol";
import {ISortitionModuleUniversity} from "./ISortitionModuleUniversity.sol";
import {SafeERC20, IERC20} from "../../libraries/SafeERC20.sol";
-import "../../libraries/Constants.sol";
import {UUPSProxiable} from "../../proxy/UUPSProxiable.sol";
import {Initializable} from "../../proxy/Initializable.sol";
+import "../../libraries/Constants.sol";
/// @title KlerosCoreUniversity
/// Core arbitrator contract for educational purposes.
@@ -87,7 +87,6 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
// * Storage * //
// ************************************* //
- uint256 private constant ALPHA_DIVISOR = 1e4; // The number to divide `Court.alpha` by.
uint256 private constant NON_PAYABLE_AMOUNT = (2 ** 256 - 2) / 2; // An amount higher than the supply of ETH.
address public governor; // The governor of the contract.
@@ -526,7 +525,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
: convertEthToTokenAmount(_feeToken, court.feeForJuror);
round.nbVotes = _feeAmount / feeForJuror;
round.disputeKitID = disputeKitID;
- round.pnkAtStakePerJuror = (court.minStake * court.alpha) / ALPHA_DIVISOR;
+ round.pnkAtStakePerJuror = (court.minStake * court.alpha) / ONE_BASIS_POINT;
round.totalFeesForJurors = _feeAmount;
round.feeToken = IERC20(_feeToken);
@@ -655,7 +654,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
Court storage court = courts[newCourtID];
extraRound.nbVotes = msg.value / court.feeForJuror; // As many votes that can be afforded by the provided funds.
- extraRound.pnkAtStakePerJuror = (court.minStake * court.alpha) / ALPHA_DIVISOR;
+ extraRound.pnkAtStakePerJuror = (court.minStake * court.alpha) / ONE_BASIS_POINT;
extraRound.totalFeesForJurors = msg.value;
extraRound.disputeKitID = newDisputeKitID;
@@ -754,20 +753,21 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
IDisputeKit disputeKit = disputeKits[round.disputeKitID];
// [0, 1] value that determines how coherent the juror was in this round, in basis points.
- uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence(
+ uint256 coherence = disputeKit.getDegreeOfCoherencePenalty(
_params.disputeID,
_params.round,
_params.repartition,
_params.feePerJurorInRound,
_params.pnkAtStakePerJurorInRound
);
- if (degreeOfCoherence > ALPHA_DIVISOR) {
- // Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
- degreeOfCoherence = ALPHA_DIVISOR;
+
+ // Guard against degree exceeding 1, though it should be ensured by the dispute kit.
+ if (coherence > ONE_BASIS_POINT) {
+ coherence = ONE_BASIS_POINT;
}
// Fully coherent jurors won't be penalized.
- uint256 penalty = (round.pnkAtStakePerJuror * (ALPHA_DIVISOR - degreeOfCoherence)) / ALPHA_DIVISOR;
+ uint256 penalty = (round.pnkAtStakePerJuror * (ONE_BASIS_POINT - coherence)) / ONE_BASIS_POINT;
// Unlock the PNKs affected by the penalty
address account = round.drawnJurors[_params.repartition];
@@ -780,7 +780,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
account,
_params.disputeID,
_params.round,
- degreeOfCoherence,
+ coherence,
-int256(availablePenalty),
0,
round.feeToken
@@ -818,7 +818,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
IDisputeKit disputeKit = disputeKits[round.disputeKitID];
// [0, 1] value that determines how coherent the juror was in this round, in basis points.
- uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence(
+ (uint256 pnkCoherence, uint256 feeCoherence) = disputeKit.getDegreeOfCoherenceReward(
_params.disputeID,
_params.round,
_params.repartition % _params.numberOfVotesInRound,
@@ -826,21 +826,24 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
_params.pnkAtStakePerJurorInRound
);
- // Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
- if (degreeOfCoherence > ALPHA_DIVISOR) {
- degreeOfCoherence = ALPHA_DIVISOR;
+ // Guard against degree exceeding 1, though it should be ensured by the dispute kit.
+ if (pnkCoherence > ONE_BASIS_POINT) {
+ pnkCoherence = ONE_BASIS_POINT;
+ }
+ if (feeCoherence > ONE_BASIS_POINT) {
+ feeCoherence = ONE_BASIS_POINT;
}
address account = round.drawnJurors[_params.repartition % _params.numberOfVotesInRound];
- uint256 pnkLocked = (round.pnkAtStakePerJuror * degreeOfCoherence) / ALPHA_DIVISOR;
+ uint256 pnkLocked = (round.pnkAtStakePerJuror * pnkCoherence) / ONE_BASIS_POINT;
// Release the rest of the PNKs of the juror for this round.
sortitionModule.unlockStake(account, pnkLocked);
// Transfer the rewards
- uint256 pnkReward = ((_params.pnkPenaltiesInRound / _params.coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
+ uint256 pnkReward = ((_params.pnkPenaltiesInRound / _params.coherentCount) * pnkCoherence) / ONE_BASIS_POINT;
round.sumPnkRewardPaid += pnkReward;
- uint256 feeReward = ((round.totalFeesForJurors / _params.coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
+ uint256 feeReward = ((round.totalFeesForJurors / _params.coherentCount) * feeCoherence) / ONE_BASIS_POINT;
round.sumFeeRewardPaid += feeReward;
pinakion.safeTransfer(account, pnkReward);
if (round.feeToken == NATIVE_CURRENCY) {
@@ -854,7 +857,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
account,
_params.disputeID,
_params.round,
- degreeOfCoherence,
+ pnkCoherence,
int256(pnkReward),
int256(feeReward),
round.feeToken
diff --git a/contracts/src/arbitration/university/SortitionModuleUniversity.sol b/contracts/src/arbitration/university/SortitionModuleUniversity.sol
index 619a0b934..db61958fd 100644
--- a/contracts/src/arbitration/university/SortitionModuleUniversity.sol
+++ b/contracts/src/arbitration/university/SortitionModuleUniversity.sol
@@ -67,12 +67,12 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable,
// ************************************* //
modifier onlyByGovernor() {
- require(address(governor) == msg.sender, "Access not allowed: Governor only.");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
modifier onlyByCore() {
- require(address(core) == msg.sender, "Access not allowed: KlerosCore only.");
+ if (address(core) != msg.sender) revert KlerosCoreOnly();
_;
}
@@ -280,7 +280,7 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable,
// 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.
uint256 amount = getJurorLeftoverPNK(_account);
- require(amount > 0, "Not eligible for withdrawal.");
+ if (amount == 0) revert NotEligibleForWithdrawal();
jurors[_account].stakedPnk = 0;
core.transferBySortitionModule(_account, amount);
emit LeftoverPNKWithdrawn(_account, amount);
@@ -364,4 +364,12 @@ contract SortitionModuleUniversity is ISortitionModuleUniversity, UUPSProxiable,
}
}
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error GovernorOnly();
+ error KlerosCoreOnly();
+ error NotEligibleForWithdrawal();
}
diff --git a/contracts/src/arbitration/view/KlerosCoreSnapshotProxy.sol b/contracts/src/arbitration/view/KlerosCoreSnapshotProxy.sol
index e94eccbd9..74cdb84ce 100644
--- a/contracts/src/arbitration/view/KlerosCoreSnapshotProxy.sol
+++ b/contracts/src/arbitration/view/KlerosCoreSnapshotProxy.sol
@@ -26,7 +26,7 @@ contract KlerosCoreSnapshotProxy {
// ************************************* //
modifier onlyByGovernor() {
- require(governor == msg.sender, "Access not allowed: Governor only.");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
@@ -69,4 +69,10 @@ contract KlerosCoreSnapshotProxy {
function balanceOf(address _account) external view returns (uint256 totalStaked) {
(totalStaked, , , ) = core.sortitionModule().getJurorBalance(_account, 0);
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error GovernorOnly();
}
diff --git a/contracts/src/gateway/ForeignGateway.sol b/contracts/src/gateway/ForeignGateway.sol
index 5938da773..1e615936f 100644
--- a/contracts/src/gateway/ForeignGateway.sol
+++ b/contracts/src/gateway/ForeignGateway.sol
@@ -49,17 +49,16 @@ contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable {
// ************************************* //
modifier onlyFromVea(address _messageSender) {
- require(
- veaOutbox == msg.sender ||
- (block.timestamp < deprecatedVeaOutboxExpiration && deprecatedVeaOutbox == msg.sender),
- "Access not allowed: Vea Outbox only."
- );
- require(_messageSender == homeGateway, "Access not allowed: HomeGateway only.");
+ if (
+ veaOutbox != msg.sender &&
+ (block.timestamp >= deprecatedVeaOutboxExpiration || deprecatedVeaOutbox != msg.sender)
+ ) revert VeaOutboxOnly();
+ if (_messageSender != homeGateway) revert HomeGatewayMessageSenderOnly();
_;
}
modifier onlyByGovernor() {
- require(governor == msg.sender, "Access not allowed: Governor only.");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
@@ -105,7 +104,7 @@ contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable {
/// @dev Changes the governor.
/// @param _governor The address of the new governor.
function changeGovernor(address _governor) external {
- require(governor == msg.sender, "Access not allowed: Governor only.");
+ if (governor != msg.sender) revert GovernorOnly();
governor = _governor;
}
@@ -122,7 +121,7 @@ contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable {
/// @dev Changes the home gateway.
/// @param _homeGateway The address of the new home gateway.
function changeHomeGateway(address _homeGateway) external {
- require(governor == msg.sender, "Access not allowed: Governor only.");
+ if (governor != msg.sender) revert GovernorOnly();
homeGateway = _homeGateway;
}
@@ -143,7 +142,7 @@ contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable {
uint256 _choices,
bytes calldata _extraData
) external payable override returns (uint256 disputeID) {
- require(msg.value >= arbitrationCost(_extraData), "Not paid enough for arbitration");
+ if (msg.value < arbitrationCost(_extraData)) revert ArbitrationFeesNotEnough();
disputeID = localDisputeID++;
uint256 chainID;
@@ -206,8 +205,8 @@ contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable {
) external override onlyFromVea(_messageSender) {
DisputeData storage dispute = disputeHashtoDisputeData[_disputeHash];
- require(dispute.id != 0, "Dispute does not exist");
- require(!dispute.ruled, "Cannot rule twice");
+ if (dispute.id == 0) revert DisputeDoesNotExist();
+ if (dispute.ruled) revert CannotRuleTwice();
dispute.ruled = true;
dispute.relayer = _relayer;
@@ -219,8 +218,8 @@ contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable {
/// @inheritdoc IForeignGateway
function withdrawFees(bytes32 _disputeHash) external override {
DisputeData storage dispute = disputeHashtoDisputeData[_disputeHash];
- require(dispute.id != 0, "Dispute does not exist");
- require(dispute.ruled, "Not ruled yet");
+ if (dispute.id == 0) revert DisputeDoesNotExist();
+ if (!dispute.ruled) revert NotRuledYet();
uint256 amount = dispute.paid;
dispute.paid = 0;
@@ -247,9 +246,9 @@ contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable {
revert("Not supported");
}
- // ************************ //
- // * Internal * //
- // ************************ //
+ // ************************************* //
+ // * Internal * //
+ // ************************************* //
function extraDataToCourtIDMinJurors(
bytes memory _extraData
@@ -268,4 +267,16 @@ contract ForeignGateway is IForeignGateway, UUPSProxiable, Initializable {
minJurors = DEFAULT_NB_OF_JURORS;
}
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error GovernorOnly();
+ error HomeGatewayMessageSenderOnly();
+ error VeaOutboxOnly();
+ error ArbitrationFeesNotEnough();
+ error DisputeDoesNotExist();
+ error CannotRuleTwice();
+ error NotRuledYet();
}
diff --git a/contracts/src/gateway/HomeGateway.sol b/contracts/src/gateway/HomeGateway.sol
index 40a767790..2ef8e606f 100644
--- a/contracts/src/gateway/HomeGateway.sol
+++ b/contracts/src/gateway/HomeGateway.sol
@@ -45,7 +45,7 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable {
/// @dev Requires that the sender is the governor.
modifier onlyByGovernor() {
- require(governor == msg.sender, "No allowed: governor only");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
@@ -129,8 +129,8 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable {
/// @inheritdoc IHomeGateway
function relayCreateDispute(RelayCreateDisputeParams memory _params) external payable override {
- require(feeToken == NATIVE_CURRENCY, "Fees paid in ERC20 only");
- require(_params.foreignChainID == foreignChainID, "Foreign chain ID not supported");
+ if (feeToken != NATIVE_CURRENCY) revert FeesPaidInNativeCurrencyOnly();
+ if (_params.foreignChainID != foreignChainID) revert ForeignChainIDNotSupported();
bytes32 disputeHash = keccak256(
abi.encodePacked(
@@ -144,7 +144,7 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable {
)
);
RelayedData storage relayedData = disputeHashtoRelayedData[disputeHash];
- require(relayedData.relayer == address(0), "Dispute already relayed");
+ if (relayedData.relayer != address(0)) revert DisputeAlreadyRelayed();
uint256 disputeID = arbitrator.createDispute{value: msg.value}(_params.choices, _params.extraData);
disputeIDtoHash[disputeID] = disputeHash;
@@ -167,8 +167,8 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable {
/// @inheritdoc IHomeGateway
function relayCreateDispute(RelayCreateDisputeParams memory _params, uint256 _feeAmount) external {
- require(feeToken != NATIVE_CURRENCY, "Fees paid in native currency only");
- require(_params.foreignChainID == foreignChainID, "Foreign chain ID not supported");
+ if (feeToken == NATIVE_CURRENCY) revert FeesPaidInERC20Only();
+ if (_params.foreignChainID != foreignChainID) revert ForeignChainIDNotSupported();
bytes32 disputeHash = keccak256(
abi.encodePacked(
@@ -182,10 +182,10 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable {
)
);
RelayedData storage relayedData = disputeHashtoRelayedData[disputeHash];
- require(relayedData.relayer == address(0), "Dispute already relayed");
+ if (relayedData.relayer != address(0)) revert DisputeAlreadyRelayed();
- require(feeToken.safeTransferFrom(msg.sender, address(this), _feeAmount), "Transfer failed");
- require(feeToken.increaseAllowance(address(arbitrator), _feeAmount), "Allowance increase failed");
+ if (!feeToken.safeTransferFrom(msg.sender, address(this), _feeAmount)) revert TransferFailed();
+ if (!feeToken.increaseAllowance(address(arbitrator), _feeAmount)) revert AllowanceIncreaseFailed();
uint256 disputeID = arbitrator.createDispute(_params.choices, _params.extraData, feeToken, _feeAmount);
disputeIDtoHash[disputeID] = disputeHash;
@@ -209,7 +209,7 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable {
/// @inheritdoc IArbitrableV2
function rule(uint256 _disputeID, uint256 _ruling) external override {
- require(msg.sender == address(arbitrator), "Only Arbitrator");
+ if (msg.sender != address(arbitrator)) revert ArbitratorOnly();
bytes32 disputeHash = disputeIDtoHash[_disputeID];
RelayedData memory relayedData = disputeHashtoRelayedData[disputeHash];
@@ -234,4 +234,17 @@ contract HomeGateway is IHomeGateway, UUPSProxiable, Initializable {
function receiverGateway() external view override returns (address) {
return foreignGateway;
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error GovernorOnly();
+ error ArbitratorOnly();
+ error FeesPaidInERC20Only();
+ error FeesPaidInNativeCurrencyOnly();
+ error ForeignChainIDNotSupported();
+ error DisputeAlreadyRelayed();
+ error TransferFailed();
+ error AllowanceIncreaseFailed();
}
diff --git a/contracts/src/gateway/interfaces/IForeignGateway.sol b/contracts/src/gateway/interfaces/IForeignGateway.sol
index 7bdbe0f13..49f51e5de 100644
--- a/contracts/src/gateway/interfaces/IForeignGateway.sol
+++ b/contracts/src/gateway/interfaces/IForeignGateway.sol
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.24;
+pragma solidity >=0.8.0 <0.9.0;
import "../../arbitration/interfaces/IArbitratorV2.sol";
import "@kleros/vea-contracts/src/interfaces/gateways/IReceiverGateway.sol";
diff --git a/contracts/src/gateway/interfaces/IHomeGateway.sol b/contracts/src/gateway/interfaces/IHomeGateway.sol
index 2ccc00ac7..b80f194d6 100644
--- a/contracts/src/gateway/interfaces/IHomeGateway.sol
+++ b/contracts/src/gateway/interfaces/IHomeGateway.sol
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.24;
+pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@kleros/vea-contracts/src/interfaces/gateways/ISenderGateway.sol";
diff --git a/contracts/src/kleros-v1/kleros-liquid-xdai/xKlerosLiquidV2.sol b/contracts/src/kleros-v1/kleros-liquid-xdai/xKlerosLiquidV2.sol
index 0d7d97adb..9a25909b7 100644
--- a/contracts/src/kleros-v1/kleros-liquid-xdai/xKlerosLiquidV2.sol
+++ b/contracts/src/kleros-v1/kleros-liquid-xdai/xKlerosLiquidV2.sol
@@ -9,7 +9,7 @@ import {ITokenController} from "../interfaces/ITokenController.sol";
import {WrappedPinakion} from "./WrappedPinakion.sol";
import {IRandomAuRa} from "./interfaces/IRandomAuRa.sol";
-import {SortitionSumTreeFactory} from "../../libraries/SortitionSumTreeFactory.sol";
+import {SortitionSumTreeFactory} from "../libraries/SortitionSumTreeFactory.sol";
import "../../gateway/interfaces/IForeignGateway.sol";
/// @title xKlerosLiquidV2
@@ -141,7 +141,6 @@ contract xKlerosLiquidV2 is Initializable, ITokenController, IArbitratorV2 {
uint256 public constant MAX_STAKE_PATHS = 4; // The maximum number of stake paths a juror can have.
uint256 public constant DEFAULT_NB_OF_JURORS = 3; // The default number of jurors in a dispute.
uint256 public constant NON_PAYABLE_AMOUNT = (2 ** 256 - 2) / 2; // An amount higher than the supply of ETH.
- uint256 public constant ALPHA_DIVISOR = 1e4; // The number to divide `Court.alpha` by.
// General Contracts
address public governor; // The governor of the contract.
WrappedPinakion public pinakion; // The Pinakion token contract.
diff --git a/contracts/src/libraries/SortitionSumTreeFactory.sol b/contracts/src/kleros-v1/libraries/SortitionSumTreeFactory.sol
similarity index 100%
rename from contracts/src/libraries/SortitionSumTreeFactory.sol
rename to contracts/src/kleros-v1/libraries/SortitionSumTreeFactory.sol
diff --git a/contracts/src/libraries/CappedMath.sol b/contracts/src/libraries/CappedMath.sol
deleted file mode 100644
index ce0cdf55c..000000000
--- a/contracts/src/libraries/CappedMath.sol
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.24;
-
-/// @title CappedMath
-/// @dev Math operations with caps for under and overflow.
-library CappedMath {
- uint256 private constant UINT_MAX = type(uint256).max;
-
- /// @dev Adds two unsigned integers, returns 2^256 - 1 on overflow.
- function addCap(uint256 _a, uint256 _b) internal pure returns (uint256) {
- unchecked {
- uint256 c = _a + _b;
- return c >= _a ? c : UINT_MAX;
- }
- }
-
- /// @dev Subtracts two integers, returns 0 on underflow.
- function subCap(uint256 _a, uint256 _b) internal pure returns (uint256) {
- if (_b > _a) return 0;
- else return _a - _b;
- }
-
- /// @dev Multiplies two unsigned integers, returns 2^256 - 1 on overflow.
- function mulCap(uint256 _a, uint256 _b) internal pure returns (uint256) {
- // Gas optimization: this is cheaper than requiring '_a' not being zero, but the
- // benefit is lost if '_b' is also tested.
- // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
- if (_a == 0) return 0;
-
- unchecked {
- uint256 c = _a * _b;
- return c / _a == _b ? c : UINT_MAX;
- }
- }
-}
diff --git a/contracts/src/libraries/Constants.sol b/contracts/src/libraries/Constants.sol
index bed573fa6..10c42d8a9 100644
--- a/contracts/src/libraries/Constants.sol
+++ b/contracts/src/libraries/Constants.sol
@@ -20,6 +20,9 @@ uint256 constant DEFAULT_K = 6; // Default number of children per node.
uint256 constant DEFAULT_NB_OF_JURORS = 3; // The default number of jurors in a dispute.
IERC20 constant NATIVE_CURRENCY = IERC20(address(0)); // The native currency, such as ETH on Arbitrum, Optimism and Ethereum L1.
+// Units
+uint256 constant ONE_BASIS_POINT = 10000;
+
enum OnError {
Revert,
Return
diff --git a/contracts/src/rng/BlockhashRNG.sol b/contracts/src/rng/BlockhashRNG.sol
index 4421b4301..a36501e6e 100644
--- a/contracts/src/rng/BlockhashRNG.sol
+++ b/contracts/src/rng/BlockhashRNG.sol
@@ -2,43 +2,119 @@
pragma solidity ^0.8.24;
-import "./RNG.sol";
+import "./IRNG.sol";
/// @title Random Number Generator using blockhash with fallback.
-/// @author Clément Lesaege -
/// @dev
/// Random Number Generator returning the blockhash with a fallback behaviour.
-/// In case no one called it within the 256 blocks, it returns the previous blockhash.
-/// This contract must be used when returning 0 is a worse failure mode than returning another blockhash.
-/// Allows saving the random number for use in the future. It allows the contract to still access the blockhash even after 256 blocks.
-contract BlockHashRNG is RNG {
- mapping(uint256 block => uint256 number) public randomNumbers; // randomNumbers[block] is the random number for this block, 0 otherwise.
+/// 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 * //
+ // ************************************* //
+
+ address public governor; // The address that can withdraw funds.
+ address public consumer; // The address that can request random numbers.
+ uint256 public immutable lookaheadTime; // Minimal time in seconds between requesting and obtaining a random number.
+ uint256 public requestTimestamp; // Timestamp of the current request
+ mapping(uint256 timestamp => uint256 number) public randomNumbers; // randomNumbers[timestamp] is the random number for this timestamp, 0 otherwise.
+
+ // ************************************* //
+ // * Function Modifiers * //
+ // ************************************* //
+
+ modifier onlyByGovernor() {
+ if (governor != msg.sender) revert GovernorOnly();
+ _;
+ }
+
+ modifier onlyByConsumer() {
+ if (consumer != msg.sender) revert ConsumerOnly();
+ _;
+ }
+
+ // ************************************* //
+ // * Constructor * //
+ // ************************************* //
+
+ /// @dev Constructor.
+ /// @param _governor The Governor of the contract.
+ /// @param _consumer The address that can request random numbers.
+ /// @param _lookaheadTime The time lookahead in seconds for the random number.
+ constructor(address _governor, address _consumer, uint256 _lookaheadTime) {
+ governor = _governor;
+ consumer = _consumer;
+ lookaheadTime = _lookaheadTime;
+ }
+
+ // ************************************* //
+ // * Governance * //
+ // ************************************* //
+
+ /// @dev Changes the governor of the contract.
+ /// @param _governor The new governor.
+ function changeGovernor(address _governor) external onlyByGovernor {
+ governor = _governor;
+ }
+
+ /// @dev Changes the consumer of the RNG.
+ /// @param _consumer The new consumer.
+ function changeConsumer(address _consumer) external onlyByGovernor {
+ consumer = _consumer;
+ }
+
+ // ************************************* //
+ // * State Modifiers * //
+ // ************************************* //
/// @dev Request a random number.
- /// @param _block Block the random number is linked to.
- function requestRandomness(uint256 _block) external override {
- // nop
+ 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.
- /// @param _block Block the random number is linked to.
/// @return randomNumber The random number or 0 if it is not ready or has not been requested.
- function receiveRandomness(uint256 _block) external override returns (uint256 randomNumber) {
- randomNumber = randomNumbers[_block];
+ function receiveRandomness() external override onlyByConsumer returns (uint256 randomNumber) {
+ if (requestTimestamp == 0) return 0; // No requests were made yet.
+
+ uint256 expectedTimestamp = requestTimestamp + lookaheadTime;
+
+ // Check if enough time has passed
+ if (block.timestamp < expectedTimestamp) {
+ return 0; // Not ready yet
+ }
+
+ // Check if we already have a saved random number for this timestamp window
+ randomNumber = randomNumbers[expectedTimestamp];
if (randomNumber != 0) {
return randomNumber;
}
- if (_block < block.number) {
- // The random number is not already set and can be.
- if (blockhash(_block) != 0x0) {
- // Normal case.
- randomNumber = uint256(blockhash(_block));
- } else {
- // The contract was not called in time. Fallback to returning previous blockhash.
- randomNumber = uint256(blockhash(block.number - 1));
- }
+ // Use last block hash for randomness
+ randomNumber = uint256(blockhash(block.number - 1));
+ if (randomNumber != 0) {
+ randomNumbers[expectedTimestamp] = randomNumber;
}
- randomNumbers[_block] = randomNumber;
+ return randomNumber;
+ }
+
+ // ************************************* //
+ // * View Functions * //
+ // ************************************* //
+
+ /// @dev 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.
+ /// @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.
+ return requestTimestamp + lookaheadTime;
}
}
diff --git a/contracts/src/rng/ChainlinkRNG.sol b/contracts/src/rng/ChainlinkRNG.sol
index b829177c3..a5bff291f 100644
--- a/contracts/src/rng/ChainlinkRNG.sol
+++ b/contracts/src/rng/ChainlinkRNG.sol
@@ -5,17 +5,17 @@ pragma solidity ^0.8.24;
import {VRFConsumerBaseV2Plus, IVRFCoordinatorV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
-import "./RNG.sol";
+import "./IRNG.sol";
/// @title Random Number Generator that uses Chainlink VRF v2.5
/// https://blog.chain.link/introducing-vrf-v2-5/
-contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
+contract ChainlinkRNG is IRNG, VRFConsumerBaseV2Plus {
// ************************************* //
// * Storage * //
// ************************************* //
address public governor; // The address that can withdraw funds.
- address public sortitionModule; // The address of the SortitionModule.
+ address public consumer; // The address that can request random numbers.
bytes32 public keyHash; // The gas lane key hash value - Defines the maximum gas price you are willing to pay for a request in wei (ID of the off-chain VRF job).
uint256 public subscriptionId; // The unique identifier of the subscription used for funding requests.
uint16 public requestConfirmations; // How many confirmations the Chainlink node should wait before responding.
@@ -29,25 +29,25 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
// ************************************* //
/// @dev Emitted when a request is sent to the VRF Coordinator
- /// @param requestId The ID of the request
- event RequestSent(uint256 indexed requestId);
+ /// @param _requestId The ID of the request
+ event RequestSent(uint256 indexed _requestId);
/// Emitted when a request has been fulfilled.
- /// @param requestId The ID of the request
- /// @param randomWord The random value answering the request.
- event RequestFulfilled(uint256 indexed requestId, uint256 randomWord);
+ /// @param _requestId The ID of the request
+ /// @param _randomWord The random value answering the request.
+ event RequestFulfilled(uint256 indexed _requestId, uint256 _randomWord);
// ************************************* //
// * Function Modifiers * //
// ************************************* //
modifier onlyByGovernor() {
- require(governor == msg.sender, "Governor only");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
- modifier onlyBySortitionModule() {
- require(sortitionModule == msg.sender, "SortitionModule only");
+ modifier onlyByConsumer() {
+ if (consumer != msg.sender) revert ConsumerOnly();
_;
}
@@ -57,7 +57,7 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
/// @dev Constructor, initializing the implementation to reduce attack surface.
/// @param _governor The Governor of the contract.
- /// @param _sortitionModule The address of the SortitionModule contract.
+ /// @param _consumer The address that can request random numbers.
/// @param _vrfCoordinator The address of the VRFCoordinator contract.
/// @param _keyHash The gas lane key hash value - Defines the maximum gas price you are willing to pay for a request in wei (ID of the off-chain VRF job).
/// @param _subscriptionId The unique identifier of the subscription used for funding requests.
@@ -66,7 +66,7 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
/// @dev https://docs.chain.link/vrf/v2-5/subscription/get-a-random-number
constructor(
address _governor,
- address _sortitionModule,
+ address _consumer,
address _vrfCoordinator,
bytes32 _keyHash,
uint256 _subscriptionId,
@@ -74,7 +74,7 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
uint32 _callbackGasLimit
) VRFConsumerBaseV2Plus(_vrfCoordinator) {
governor = _governor;
- sortitionModule = _sortitionModule;
+ consumer = _consumer;
keyHash = _keyHash;
subscriptionId = _subscriptionId;
requestConfirmations = _requestConfirmations;
@@ -91,10 +91,10 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
governor = _governor;
}
- /// @dev Changes the sortition module of the contract.
- /// @param _sortitionModule The new sortition module.
- function changeSortitionModule(address _sortitionModule) external onlyByGovernor {
- sortitionModule = _sortitionModule;
+ /// @dev Changes the consumer of the RNG.
+ /// @param _consumer The new consumer.
+ function changeConsumer(address _consumer) external onlyByGovernor {
+ consumer = _consumer;
}
/// @dev Changes the VRF Coordinator of the contract.
@@ -132,8 +132,8 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
// * State Modifiers * //
// ************************************* //
- /// @dev Request a random number. SortitionModule only.
- function requestRandomness(uint256 /*_block*/) external override onlyBySortitionModule {
+ /// @dev Request a random number. Consumer only.
+ function requestRandomness() external override onlyByConsumer {
// Will revert if subscription is not set and funded.
uint256 requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
@@ -167,7 +167,7 @@ contract ChainlinkRNG is RNG, VRFConsumerBaseV2Plus {
/// @dev Return the random number.
/// @return randomNumber The random number or 0 if it is not ready or has not been requested.
- function receiveRandomness(uint256 /*_block*/) external view override returns (uint256 randomNumber) {
+ 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
new file mode 100644
index 000000000..1a767fee0
--- /dev/null
+++ b/contracts/src/rng/IRNG.sol
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity >=0.8.0 <0.9.0;
+
+/// @title Random Number Generator interface
+interface IRNG {
+ /// @dev Request a random number.
+ function requestRandomness() external;
+
+ /// @dev Receive the random number.
+ /// @return randomNumber Random number or 0 if not available
+ function receiveRandomness() external returns (uint256 randomNumber);
+
+ error GovernorOnly();
+ error ConsumerOnly();
+}
diff --git a/contracts/src/rng/IRandomizer.sol b/contracts/src/rng/IRandomizer.sol
index ea549ffa4..6a6296f82 100644
--- a/contracts/src/rng/IRandomizer.sol
+++ b/contracts/src/rng/IRandomizer.sol
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.24;
+pragma solidity >=0.8.0 <0.9.0;
// Randomizer protocol interface
interface IRandomizer {
diff --git a/contracts/src/rng/IncrementalNG.sol b/contracts/src/rng/IncrementalNG.sol
index aa4b7d840..542090e71 100644
--- a/contracts/src/rng/IncrementalNG.sol
+++ b/contracts/src/rng/IncrementalNG.sol
@@ -1,13 +1,12 @@
// SPDX-License-Identifier: MIT
-/// @title Incremental Number Generator
-/// @author JayBuidl
-/// @dev A Random Number Generator which returns a number incremented by 1 each time. Useful as a fallback method.
-
pragma solidity ^0.8.24;
-import "./RNG.sol";
+import "./IRNG.sol";
-contract IncrementalNG is RNG {
+/// @title Incremental Number Generator
+/// @dev A Random Number Generator which returns a number incremented by 1 each time.
+/// For testing purposes.
+contract IncrementalNG is IRNG {
uint256 public number;
constructor(uint256 _start) {
@@ -15,15 +14,13 @@ contract IncrementalNG is RNG {
}
/// @dev Request a random number.
- /// @param _block Block the random number is linked to.
- function requestRandomness(uint256 _block) external override {
+ function requestRandomness() external override {
// nop
}
/// @dev Get the "random number" (which is always the same).
- /// @param _block Block the random number is linked to.
/// @return randomNumber The random number or 0 if it is not ready or has not been requested.
- function receiveRandomness(uint256 _block) external override returns (uint256 randomNumber) {
+ function receiveRandomness() external override returns (uint256 randomNumber) {
unchecked {
return number++;
}
diff --git a/contracts/src/rng/RNG.sol b/contracts/src/rng/RNG.sol
deleted file mode 100644
index 5142aa0e5..000000000
--- a/contracts/src/rng/RNG.sol
+++ /dev/null
@@ -1,14 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.24;
-
-interface RNG {
- /// @dev Request a random number.
- /// @param _block Block linked to the request.
- function requestRandomness(uint256 _block) external;
-
- /// @dev Receive the random number.
- /// @param _block Block the random number is linked to.
- /// @return randomNumber Random Number. If the number is not ready or has not been required 0 instead.
- function receiveRandomness(uint256 _block) external returns (uint256 randomNumber);
-}
diff --git a/contracts/src/rng/RNGWithFallback.sol b/contracts/src/rng/RNGWithFallback.sol
new file mode 100644
index 000000000..c47f9eb53
--- /dev/null
+++ b/contracts/src/rng/RNGWithFallback.sol
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.24;
+
+import "./IRNG.sol";
+
+/// @title RNG with fallback mechanism
+/// @notice Uses a primary RNG implementation with automatic fallback to a Blockhash RNG if the primary RNG does not respond passed a timeout.
+contract RNGWithFallback is IRNG {
+ // ************************************* //
+ // * Storage * //
+ // ************************************* //
+
+ IRNG public immutable rng; // RNG address.
+ address public governor; // Governor address
+ address public consumer; // Consumer address
+ uint256 public fallbackTimeoutSeconds; // Time in seconds to wait before falling back to next RNG
+ uint256 public requestTimestamp; // Timestamp of the current request
+
+ // ************************************* //
+ // * Events * //
+ // ************************************* //
+
+ event RNGFallback();
+ event FallbackTimeoutChanged(uint256 _newTimeout);
+
+ // ************************************* //
+ // * Constructor * //
+ // ************************************* //
+
+ /// @param _governor Governor address
+ /// @param _consumer Consumer address
+ /// @param _fallbackTimeoutSeconds Time in seconds to wait before falling back to next RNG
+ /// @param _rng The RNG address (e.g. Chainlink)
+ constructor(address _governor, address _consumer, uint256 _fallbackTimeoutSeconds, IRNG _rng) {
+ if (address(_rng) == address(0)) revert InvalidDefaultRNG();
+
+ governor = _governor;
+ consumer = _consumer;
+ fallbackTimeoutSeconds = _fallbackTimeoutSeconds;
+ rng = _rng;
+ }
+
+ // ************************************* //
+ // * Function Modifiers * //
+ // ************************************* //
+
+ modifier onlyByGovernor() {
+ if (governor != msg.sender) revert GovernorOnly();
+ _;
+ }
+
+ modifier onlyByConsumer() {
+ if (consumer != msg.sender) revert ConsumerOnly();
+ _;
+ }
+
+ // ************************************* //
+ // * Governance Functions * //
+ // ************************************* //
+
+ /// @dev Change the governor
+ /// @param _newGovernor Address of the new governor
+ function changeGovernor(address _newGovernor) external onlyByGovernor {
+ governor = _newGovernor;
+ }
+
+ /// @dev Change the consumer
+ /// @param _consumer Address of the new consumer
+ function changeConsumer(address _consumer) external onlyByGovernor {
+ consumer = _consumer;
+ }
+
+ /// @dev Change the fallback timeout
+ /// @param _fallbackTimeoutSeconds New timeout in seconds
+ function changeFallbackTimeout(uint256 _fallbackTimeoutSeconds) external onlyByGovernor {
+ fallbackTimeoutSeconds = _fallbackTimeoutSeconds;
+ emit FallbackTimeoutChanged(_fallbackTimeoutSeconds);
+ }
+
+ // ************************************* //
+ // * State Modifiers * //
+ // ************************************* //
+
+ /// @dev 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.
+ /// @return randomNumber Random number or 0 if not available
+ function receiveRandomness() external override onlyByConsumer returns (uint256 randomNumber) {
+ randomNumber = rng.receiveRandomness();
+
+ // If we didn't get a random number and the timeout is exceeded, try the fallback
+ if (randomNumber == 0 && block.timestamp > requestTimestamp + fallbackTimeoutSeconds) {
+ randomNumber = uint256(blockhash(block.number - 1));
+ emit RNGFallback();
+ }
+ return randomNumber;
+ }
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error InvalidDefaultRNG();
+}
diff --git a/contracts/src/rng/RandomizerRNG.sol b/contracts/src/rng/RandomizerRNG.sol
index 940c2cf5d..96cbe6321 100644
--- a/contracts/src/rng/RandomizerRNG.sol
+++ b/contracts/src/rng/RandomizerRNG.sol
@@ -2,18 +2,18 @@
pragma solidity ^0.8.24;
-import "./RNG.sol";
+import "./IRNG.sol";
import "./IRandomizer.sol";
/// @title Random Number Generator that uses Randomizer.ai
/// https://randomizer.ai/
-contract RandomizerRNG is RNG {
+contract RandomizerRNG is IRNG {
// ************************************* //
// * Storage * //
// ************************************* //
address public governor; // The address that can withdraw funds.
- address public sortitionModule; // The address of the SortitionModule.
+ address public consumer; // The address that can request random numbers.
IRandomizer public randomizer; // Randomizer address.
uint256 public callbackGasLimit; // Gas limit for the Randomizer.ai callback.
uint256 public lastRequestId; // The last request ID.
@@ -37,12 +37,12 @@ contract RandomizerRNG is RNG {
// ************************************* //
modifier onlyByGovernor() {
- require(governor == msg.sender, "Governor only");
+ if (governor != msg.sender) revert GovernorOnly();
_;
}
- modifier onlyBySortitionModule() {
- require(sortitionModule == msg.sender, "SortitionModule only");
+ modifier onlyByConsumer() {
+ if (consumer != msg.sender) revert ConsumerOnly();
_;
}
@@ -51,11 +51,12 @@ contract RandomizerRNG is RNG {
// ************************************* //
/// @dev Constructor
- /// @param _randomizer Randomizer contract.
- /// @param _governor Governor of the contract.
- constructor(address _governor, address _sortitionModule, IRandomizer _randomizer) {
+ /// @param _governor The Governor of the contract.
+ /// @param _consumer The address that can request random numbers.
+ /// @param _randomizer The Randomizer.ai oracle contract.
+ constructor(address _governor, address _consumer, IRandomizer _randomizer) {
governor = _governor;
- sortitionModule = _sortitionModule;
+ consumer = _consumer;
randomizer = _randomizer;
callbackGasLimit = 50000;
}
@@ -70,10 +71,10 @@ contract RandomizerRNG is RNG {
governor = _governor;
}
- /// @dev Changes the sortition module of the contract.
- /// @param _sortitionModule The new sortition module.
- function changeSortitionModule(address _sortitionModule) external onlyByGovernor {
- sortitionModule = _sortitionModule;
+ /// @dev Changes the consumer of the RNG.
+ /// @param _consumer The new consumer.
+ function changeConsumer(address _consumer) external onlyByGovernor {
+ consumer = _consumer;
}
/// @dev Change the Randomizer callback gas limit.
@@ -98,8 +99,8 @@ contract RandomizerRNG is RNG {
// * State Modifiers * //
// ************************************* //
- /// @dev Request a random number. SortitionModule only.
- function requestRandomness(uint256 /*_block*/) external override onlyBySortitionModule {
+ /// @dev Request a random number. Consumer only.
+ function requestRandomness() external override onlyByConsumer {
uint256 requestId = randomizer.request(callbackGasLimit);
lastRequestId = requestId;
emit RequestSent(requestId);
@@ -109,7 +110,7 @@ contract RandomizerRNG is RNG {
/// @param _id The ID of the request.
/// @param _value The random value answering the request.
function randomizerCallback(uint256 _id, bytes32 _value) external {
- require(msg.sender == address(randomizer), "Randomizer only");
+ if (msg.sender != address(randomizer)) revert RandomizerOnly();
randomNumbers[_id] = uint256(_value);
emit RequestFulfilled(_id, uint256(_value));
}
@@ -120,7 +121,13 @@ contract RandomizerRNG is RNG {
/// @dev Return the random number.
/// @return randomNumber The random number or 0 if it is not ready or has not been requested.
- function receiveRandomness(uint256 /*_block*/) external view override returns (uint256 randomNumber) {
+ function receiveRandomness() external view override returns (uint256 randomNumber) {
randomNumber = randomNumbers[lastRequestId];
}
+
+ // ************************************* //
+ // * Errors * //
+ // ************************************* //
+
+ error RandomizerOnly();
}
diff --git a/contracts/src/test/RNGMock.sol b/contracts/src/test/RNGMock.sol
new file mode 100644
index 000000000..df372265d
--- /dev/null
+++ b/contracts/src/test/RNGMock.sol
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.24;
+
+import "../rng/IRNG.sol";
+
+/// @title Simple mock rng to check fallback
+contract RNGMock is IRNG {
+ uint256 public randomNumber; // The number to return;
+
+ function setRN(uint256 _rn) external {
+ randomNumber = _rn;
+ }
+
+ function requestRandomness() external override {}
+
+ function receiveRandomness() external view override returns (uint256) {
+ return randomNumber;
+ }
+}
diff --git a/contracts/test/arbitration/dispute-kit-gated.ts b/contracts/test/arbitration/dispute-kit-gated.ts
index daa78ce0c..4c3d26052 100644
--- a/contracts/test/arbitration/dispute-kit-gated.ts
+++ b/contracts/test/arbitration/dispute-kit-gated.ts
@@ -47,11 +47,11 @@ describe("DisputeKitGated", async () => {
fallbackToGlobal: true,
keepExistingDeployments: false,
});
- disputeKitGated = (await ethers.getContract("DisputeKitGated")) as DisputeKitGated;
- pnk = (await ethers.getContract("PNK")) as PNK;
- dai = (await ethers.getContract("DAI")) as TestERC20;
- core = (await ethers.getContract("KlerosCore")) as KlerosCore;
- sortitionModule = (await ethers.getContract("SortitionModule")) as SortitionModule;
+ disputeKitGated = await ethers.getContract("DisputeKitGated");
+ pnk = await ethers.getContract("PNK");
+ dai = await ethers.getContract("DAI");
+ core = await ethers.getContract("KlerosCore");
+ sortitionModule = await ethers.getContract("SortitionModule");
// Make the tests more deterministic with this dummy RNG
await deployments.deploy("IncrementalNG", {
@@ -59,16 +59,16 @@ describe("DisputeKitGated", async () => {
args: [RANDOM],
log: true,
});
- rng = (await ethers.getContract("IncrementalNG")) as IncrementalNG;
+ rng = await ethers.getContract("IncrementalNG");
- await sortitionModule.changeRandomNumberGenerator(rng.target, 20).then((tx) => tx.wait());
+ await sortitionModule.changeRandomNumberGenerator(rng.target).then((tx) => tx.wait());
const hre = require("hardhat");
await deployERC721(hre, deployer, "TestERC721", "Nft721");
- nft721 = (await ethers.getContract("Nft721")) as TestERC721;
+ nft721 = await ethers.getContract("Nft721");
await deployERC1155(hre, deployer, "TestERC1155", "Nft1155");
- nft1155 = (await ethers.getContract("Nft1155")) as TestERC1155;
+ nft1155 = await ethers.getContract("Nft1155");
await nft1155.mint(deployer, TOKEN_ID, 1, "0x00");
});
@@ -141,11 +141,6 @@ describe("DisputeKitGated", async () => {
await network.provider.send("evm_mine");
await sortitionModule.passPhase().then((tx) => tx.wait()); // Staking -> Generating
- const lookahead = await sortitionModule.rngLookahead();
- for (let index = 0; index < lookahead; index++) {
- await network.provider.send("evm_mine");
- }
-
await sortitionModule.passPhase().then((tx) => tx.wait()); // Generating -> Drawing
return core.draw(disputeId, 70, { gasLimit: 10000000 });
};
diff --git a/contracts/test/arbitration/draw.ts b/contracts/test/arbitration/draw.ts
index 12790fdb5..4992501f1 100644
--- a/contracts/test/arbitration/draw.ts
+++ b/contracts/test/arbitration/draw.ts
@@ -62,12 +62,12 @@ describe("Draw Benchmark", async () => {
fallbackToGlobal: true,
keepExistingDeployments: false,
});
- disputeKit = (await ethers.getContract("DisputeKitClassic")) as DisputeKitClassic;
- pnk = (await ethers.getContract("PNK")) as PNK;
- core = (await ethers.getContract("KlerosCore")) as KlerosCore;
- homeGateway = (await ethers.getContract("HomeGatewayToEthereum")) as HomeGateway;
- arbitrable = (await ethers.getContract("ArbitrableExample")) as ArbitrableExample;
- sortitionModule = (await ethers.getContract("SortitionModule")) as SortitionModule;
+ disputeKit = await ethers.getContract("DisputeKitClassic");
+ pnk = await ethers.getContract("PNK");
+ core = await ethers.getContract("KlerosCore");
+ homeGateway = await ethers.getContract("HomeGatewayToEthereum");
+ arbitrable = await ethers.getContract("ArbitrableExample");
+ sortitionModule = await ethers.getContract("SortitionModule");
parentCourtMinStake = await core.courts(Courts.GENERAL).then((court) => court.minStake);
@@ -79,9 +79,9 @@ describe("Draw Benchmark", async () => {
args: [RANDOM],
log: true,
});
- rng = (await ethers.getContract("IncrementalNG")) as IncrementalNG;
+ rng = await ethers.getContract("IncrementalNG");
- await sortitionModule.changeRandomNumberGenerator(rng.target, 20).then((tx) => tx.wait());
+ await sortitionModule.changeRandomNumberGenerator(rng.target).then((tx) => tx.wait());
// CourtId 2 = CHILD_COURT
const minStake = 3n * 10n ** 20n; // 300 PNK
@@ -174,11 +174,6 @@ describe("Draw Benchmark", async () => {
await network.provider.send("evm_mine");
await sortitionModule.passPhase().then((tx) => tx.wait()); // Staking -> Generating
- const lookahead = await sortitionModule.rngLookahead();
- for (let index = 0; index < lookahead; index++) {
- await network.provider.send("evm_mine");
- }
-
await sortitionModule.passPhase().then((tx) => tx.wait()); // Generating -> Drawing
await expectFromDraw(core.draw(0, 20, { gasLimit: 1000000 }));
diff --git a/contracts/test/arbitration/index.ts b/contracts/test/arbitration/index.ts
index d8e7ef089..127fa2500 100644
--- a/contracts/test/arbitration/index.ts
+++ b/contracts/test/arbitration/index.ts
@@ -73,9 +73,9 @@ describe("DisputeKitClassic", async () => {
});
it("Should create a dispute", async () => {
- await expect(disputeKit.connect(deployer).createDispute(0, 0, ethers.toBeHex(3), "0x00")).to.be.revertedWith(
- "Access not allowed: KlerosCore only."
- );
+ await expect(
+ disputeKit.connect(deployer).createDispute(0, 0, ethers.toBeHex(3), "0x00")
+ ).to.be.revertedWithCustomError(disputeKit, "KlerosCoreOnly");
const tx = await core
.connect(deployer)
@@ -100,10 +100,10 @@ async function deployContracts(): Promise<
fallbackToGlobal: true,
keepExistingDeployments: false,
});
- const disputeKit = (await ethers.getContract("DisputeKitClassic")) as DisputeKitClassic;
- const disputeKitShutter = (await ethers.getContract("DisputeKitShutter")) as DisputeKitShutter;
- const disputeKitGated = (await ethers.getContract("DisputeKitGated")) as DisputeKitGated;
- const disputeKitGatedShutter = (await ethers.getContract("DisputeKitGatedShutter")) as DisputeKitGatedShutter;
- const core = (await ethers.getContract("KlerosCore")) as KlerosCore;
+ const disputeKit = await ethers.getContract("DisputeKitClassic");
+ const disputeKitShutter = await ethers.getContract("DisputeKitShutter");
+ const disputeKitGated = await ethers.getContract("DisputeKitGated");
+ const disputeKitGatedShutter = await ethers.getContract("DisputeKitGatedShutter");
+ const core = await ethers.getContract("KlerosCore");
return [core, disputeKit, disputeKitShutter, disputeKitGated, disputeKitGatedShutter];
}
diff --git a/contracts/test/arbitration/ruler.ts b/contracts/test/arbitration/ruler.ts
index bf5754295..dafdb59da 100644
--- a/contracts/test/arbitration/ruler.ts
+++ b/contracts/test/arbitration/ruler.ts
@@ -163,7 +163,7 @@ async function deployContracts(): Promise<[KlerosCoreRuler, DisputeResolver]> {
fallbackToGlobal: true,
keepExistingDeployments: false,
});
- const resolver = (await ethers.getContract("DisputeResolverRuler")) as DisputeResolver;
- const core = (await ethers.getContract("KlerosCoreRuler")) as KlerosCoreRuler;
+ const resolver = await ethers.getContract("DisputeResolverRuler");
+ const core = await ethers.getContract("KlerosCoreRuler");
return [core, resolver];
}
diff --git a/contracts/test/arbitration/staking-neo.ts b/contracts/test/arbitration/staking-neo.ts
index 85b0dc884..696935b40 100644
--- a/contracts/test/arbitration/staking-neo.ts
+++ b/contracts/test/arbitration/staking-neo.ts
@@ -56,13 +56,13 @@ describe("Staking", async () => {
fallbackToGlobal: true,
keepExistingDeployments: false,
});
- pnk = (await ethers.getContract("PNK")) as PNK;
- core = (await ethers.getContract("KlerosCoreNeo")) as KlerosCoreNeo;
- sortition = (await ethers.getContract("SortitionModuleNeo")) as SortitionModuleNeo;
- rng = (await ethers.getContract("ChainlinkRNG")) as ChainlinkRNG;
- vrfCoordinator = (await ethers.getContract("ChainlinkVRFCoordinator")) as ChainlinkVRFCoordinatorV2Mock;
- resolver = (await ethers.getContract("DisputeResolverNeo")) as DisputeResolver;
- nft = (await ethers.getContract("KlerosV2NeoEarlyUser")) as TestERC721;
+ pnk = await ethers.getContract("PNK");
+ core = await ethers.getContract("KlerosCoreNeo");
+ sortition = await ethers.getContract("SortitionModuleNeo");
+ rng = await ethers.getContract("ChainlinkRNG");
+ vrfCoordinator = await ethers.getContract("ChainlinkVRFCoordinator");
+ resolver = await ethers.getContract("DisputeResolverNeo");
+ nft = await ethers.getContract("KlerosV2NeoEarlyUser");
// Juror signer setup and funding
const { firstWallet } = await getNamedAccounts();
@@ -105,10 +105,7 @@ describe("Staking", async () => {
const drawFromGeneratingPhase = async () => {
expect(await sortition.phase()).to.be.equal(1); // Generating
- const lookahead = await sortition.rngLookahead();
- for (let index = 0; index < lookahead; index++) {
- await network.provider.send("evm_mine");
- }
+ await network.provider.send("evm_mine");
await vrfCoordinator.fulfillRandomWords(1, rng.target, []);
await sortition.passPhase(); // Generating -> Drawing
@@ -263,7 +260,10 @@ describe("Staking", async () => {
);
expect(await sortition.totalStaked()).to.be.equal(PNK(0));
await drawAndReachStakingPhaseFromGenerating();
- await expect(sortition.executeDelayedStakes(10)).to.revertedWith("No delayed stake to execute.");
+ await expect(sortition.executeDelayedStakes(10)).to.revertedWithCustomError(
+ sortition,
+ "NoDelayedStakeToExecute"
+ );
expect(await sortition.totalStaked()).to.be.equal(PNK(0));
});
@@ -327,7 +327,10 @@ describe("Staking", async () => {
);
expect(await sortition.totalStaked()).to.be.equal(PNK(2000));
await drawAndReachStakingPhaseFromGenerating();
- await expect(sortition.executeDelayedStakes(10)).to.revertedWith("No delayed stake to execute.");
+ await expect(sortition.executeDelayedStakes(10)).to.revertedWithCustomError(
+ sortition,
+ "NoDelayedStakeToExecute"
+ );
expect(await sortition.totalStaked()).to.be.equal(PNK(2000));
});
diff --git a/contracts/test/arbitration/staking.ts b/contracts/test/arbitration/staking.ts
index 4d0262c22..d27a5f10e 100644
--- a/contracts/test/arbitration/staking.ts
+++ b/contracts/test/arbitration/staking.ts
@@ -27,11 +27,11 @@ describe("Staking", async () => {
fallbackToGlobal: true,
keepExistingDeployments: false,
});
- pnk = (await ethers.getContract("PNK")) as PNK;
- core = (await ethers.getContract("KlerosCore")) as KlerosCore;
- sortition = (await ethers.getContract("SortitionModule")) as SortitionModule;
- rng = (await ethers.getContract("ChainlinkRNG")) as ChainlinkRNG;
- vrfCoordinator = (await ethers.getContract("ChainlinkVRFCoordinator")) as ChainlinkVRFCoordinatorV2Mock;
+ pnk = await ethers.getContract("PNK");
+ core = await ethers.getContract("KlerosCore");
+ sortition = await ethers.getContract("SortitionModule");
+ rng = await ethers.getContract("ChainlinkRNG");
+ vrfCoordinator = await ethers.getContract("ChainlinkVRFCoordinator");
};
describe("When outside the Staking phase", async () => {
@@ -53,11 +53,8 @@ describe("Staking", async () => {
await network.provider.send("evm_increaseTime", [2000]); // Wait for minStakingTime
await network.provider.send("evm_mine");
- const lookahead = await sortition.rngLookahead();
await sortition.passPhase(); // Staking -> Generating
- for (let index = 0; index < lookahead; index++) {
- await network.provider.send("evm_mine");
- }
+ await network.provider.send("evm_mine");
balanceBefore = await pnk.balanceOf(deployer);
};
@@ -393,11 +390,9 @@ describe("Staking", async () => {
await network.provider.send("evm_increaseTime", [2000]); // Wait for minStakingTime
await network.provider.send("evm_mine");
- const lookahead = await sortition.rngLookahead();
await sortition.passPhase(); // Staking -> Generating
- for (let index = 0; index < lookahead; index++) {
- await network.provider.send("evm_mine");
- }
+ await network.provider.send("evm_mine");
+
await vrfCoordinator.fulfillRandomWords(1, rng.target, []);
await sortition.passPhase(); // Generating -> Drawing
diff --git a/contracts/test/evidence/index.ts b/contracts/test/evidence/index.ts
index 30d79ab28..bc3f4ff3b 100644
--- a/contracts/test/evidence/index.ts
+++ b/contracts/test/evidence/index.ts
@@ -19,7 +19,6 @@ function getEmittedEvent(eventName: any, receipt: ContractTransactionReceipt): E
describe("Home Evidence contract", async () => {
const arbitrationFee = 1000n;
- const appealFee = arbitrationFee;
const arbitratorExtraData = ethers.AbiCoder.defaultAbiCoder().encode(
["uint256", "uint256"],
[1, 1] // courtId 1, minJurors 1
@@ -51,8 +50,8 @@ describe("Home Evidence contract", async () => {
fallbackToGlobal: true,
keepExistingDeployments: false,
});
- arbitrator = (await ethers.getContract("KlerosCore")) as KlerosCore;
- disputeTemplateRegistry = (await ethers.getContract("DisputeTemplateRegistry")) as DisputeTemplateRegistry;
+ arbitrator = await ethers.getContract("KlerosCore");
+ disputeTemplateRegistry = await ethers.getContract("DisputeTemplateRegistry");
const court = await arbitrator.courts(1);
await arbitrator.changeCourtParameters(
diff --git a/contracts/test/foundry/KlerosCore.t.sol b/contracts/test/foundry/KlerosCore.t.sol
index 20f8555a7..d0c3c190b 100644
--- a/contracts/test/foundry/KlerosCore.t.sol
+++ b/contracts/test/foundry/KlerosCore.t.sol
@@ -12,6 +12,8 @@ import {ISortitionModule} from "../../src/arbitration/interfaces/ISortitionModul
import {SortitionModuleMock, SortitionModuleBase} from "../../src/test/SortitionModuleMock.sol";
import {UUPSProxy} from "../../src/proxy/UUPSProxy.sol";
import {BlockHashRNG} from "../../src/rng/BlockHashRNG.sol";
+import {RNGWithFallback, IRNG} from "../../src/rng/RNGWithFallback.sol";
+import {RNGMock} from "../../src/test/RNGMock.sol";
import {PNK} from "../../src/token/PNK.sol";
import {TestERC20} from "../../src/token/TestERC20.sol";
import {ArbitrableExample, IArbitrableV2} from "../../src/arbitration/arbitrables/ArbitrableExample.sol";
@@ -53,7 +55,7 @@ contract KlerosCoreTest is Test {
uint256 minStakingTime;
uint256 maxDrawingTime;
- uint256 rngLookahead;
+ uint256 rngLookahead; // Time in seconds
string templateData;
string templateDataMappings;
@@ -63,7 +65,6 @@ contract KlerosCoreTest is Test {
SortitionModuleMock smLogic = new SortitionModuleMock();
DisputeKitClassic dkLogic = new DisputeKitClassic();
DisputeTemplateRegistry registryLogic = new DisputeTemplateRegistry();
- rng = new BlockHashRNG();
pinakion = new PNK();
feeToken = new TestERC20("Test", "TST");
wNative = new TestERC20("wrapped ETH", "wETH");
@@ -93,9 +94,11 @@ contract KlerosCoreTest is Test {
sortitionExtraData = abi.encode(uint256(5));
minStakingTime = 18;
maxDrawingTime = 24;
- rngLookahead = 20;
hiddenVotes = false;
+ rngLookahead = 30;
+ rng = new BlockHashRNG(msg.sender, address(sortitionModule), rngLookahead);
+
UUPSProxy proxyCore = new UUPSProxy(address(coreLogic), "");
bytes memory initDataDk = abi.encodeWithSignature(
@@ -109,17 +112,18 @@ contract KlerosCoreTest is Test {
disputeKit = DisputeKitClassic(address(proxyDk));
bytes memory initDataSm = abi.encodeWithSignature(
- "initialize(address,address,uint256,uint256,address,uint256)",
+ "initialize(address,address,uint256,uint256,address)",
governor,
address(proxyCore),
minStakingTime,
maxDrawingTime,
- rng,
- rngLookahead
+ rng
);
UUPSProxy proxySm = new UUPSProxy(address(smLogic), initDataSm);
sortitionModule = SortitionModuleMock(address(proxySm));
+ vm.prank(governor);
+ rng.changeConsumer(address(sortitionModule));
core = KlerosCoreMock(address(proxyCore));
core.initialize(
@@ -239,11 +243,9 @@ contract KlerosCoreTest is Test {
assertEq(sortitionModule.minStakingTime(), 18, "Wrong minStakingTime");
assertEq(sortitionModule.maxDrawingTime(), 24, "Wrong maxDrawingTime");
assertEq(sortitionModule.lastPhaseChange(), block.timestamp, "Wrong lastPhaseChange");
- assertEq(sortitionModule.randomNumberRequestBlock(), 0, "randomNumberRequestBlock should be 0");
assertEq(sortitionModule.disputesWithoutJurors(), 0, "disputesWithoutJurors should be 0");
assertEq(address(sortitionModule.rng()), address(rng), "Wrong RNG address");
assertEq(sortitionModule.randomNumber(), 0, "randomNumber should be 0");
- assertEq(sortitionModule.rngLookahead(), 20, "Wrong rngLookahead");
assertEq(sortitionModule.delayedStakeWriteIndex(), 0, "delayedStakeWriteIndex should be 0");
assertEq(sortitionModule.delayedStakeReadIndex(), 1, "Wrong delayedStakeReadIndex");
@@ -260,7 +262,6 @@ contract KlerosCoreTest is Test {
KlerosCoreMock coreLogic = new KlerosCoreMock();
SortitionModuleMock smLogic = new SortitionModuleMock();
DisputeKitClassic dkLogic = new DisputeKitClassic();
- rng = new BlockHashRNG();
pinakion = new PNK();
governor = msg.sender;
@@ -280,9 +281,11 @@ contract KlerosCoreTest is Test {
sortitionExtraData = abi.encode(uint256(5));
minStakingTime = 18;
maxDrawingTime = 24;
- rngLookahead = 20;
hiddenVotes = false;
+ rngLookahead = 20;
+ rng = new BlockHashRNG(msg.sender, address(sortitionModule), rngLookahead);
+
UUPSProxy proxyCore = new UUPSProxy(address(coreLogic), "");
bytes memory initDataDk = abi.encodeWithSignature(
@@ -296,17 +299,18 @@ contract KlerosCoreTest is Test {
disputeKit = DisputeKitClassic(address(proxyDk));
bytes memory initDataSm = abi.encodeWithSignature(
- "initialize(address,address,uint256,uint256,address,uint256)",
+ "initialize(address,address,uint256,uint256,address)",
governor,
address(proxyCore),
minStakingTime,
maxDrawingTime,
- rng,
- rngLookahead
+ rng
);
UUPSProxy proxySm = new UUPSProxy(address(smLogic), initDataSm);
sortitionModule = SortitionModuleMock(address(proxySm));
+ vm.prank(governor);
+ rng.changeConsumer(address(sortitionModule));
core = KlerosCoreMock(address(proxyCore));
vm.expectEmit(true, true, true, true);
@@ -1015,7 +1019,7 @@ contract KlerosCoreTest is Test {
assertEq(sortitionModule.disputesWithoutJurors(), 1, "Wrong disputesWithoutJurors count");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
assertEq(pinakion.balanceOf(address(core)), 1000, "Wrong token balance of the core");
@@ -1062,7 +1066,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
assertEq(pinakion.balanceOf(address(core)), 2000, "Wrong token balance of the core");
@@ -1089,7 +1093,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
uint256 disputeID = 0;
@@ -1137,7 +1141,7 @@ contract KlerosCoreTest is Test {
vm.prank(staker2);
core.setStake(GENERAL_COURT, 10000);
- vm.expectRevert(bytes("No delayed stake to execute."));
+ vm.expectRevert(SortitionModuleBase.NoDelayedStakeToExecute.selector);
sortitionModule.executeDelayedStakes(5);
// Set the stake and create a dispute to advance the phase
@@ -1145,12 +1149,12 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
uint256 disputeID = 0;
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
- vm.expectRevert(bytes("Should be in Staking phase."));
+ vm.expectRevert(SortitionModuleBase.NotStakingPhase.selector);
sortitionModule.executeDelayedStakes(5);
// Create delayed stake
@@ -1270,14 +1274,14 @@ contract KlerosCoreTest is Test {
assertEq(snapshotProxy.balanceOf(staker1), 12346, "Wrong stPNK balance");
vm.prank(other);
- vm.expectRevert(bytes("Access not allowed: Governor only."));
+ vm.expectRevert(KlerosCoreSnapshotProxy.GovernorOnly.selector);
snapshotProxy.changeCore(IKlerosCore(other));
vm.prank(governor);
snapshotProxy.changeCore(IKlerosCore(other));
assertEq(address(snapshotProxy.core()), other, "Wrong core in snapshot proxy after change");
vm.prank(other);
- vm.expectRevert(bytes("Access not allowed: Governor only."));
+ vm.expectRevert(KlerosCoreSnapshotProxy.GovernorOnly.selector);
snapshotProxy.changeGovernor(other);
vm.prank(governor);
snapshotProxy.changeGovernor(other);
@@ -1433,7 +1437,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
vm.expectEmit(true, true, true, true);
@@ -1469,7 +1473,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS); // No one is staked so check that the empty addresses are not drawn.
@@ -1512,7 +1516,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action"); // Dispute uses general court by default
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
(uint96 courtID, , , , ) = core.disputes(disputeID);
@@ -1555,7 +1559,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -1565,7 +1569,7 @@ contract KlerosCoreTest is Test {
voteIDs[0] = 0;
bytes32 commit;
vm.prank(staker1);
- vm.expectRevert(bytes("The dispute should be in Commit period."));
+ vm.expectRevert(DisputeKitClassicBase.NotCommitPeriod.selector);
disputeKit.castCommit(disputeID, voteIDs, commit);
vm.expectRevert(KlerosCoreBase.EvidenceNotPassedAndNotAppeal.selector);
@@ -1582,13 +1586,13 @@ contract KlerosCoreTest is Test {
assertEq(lastPeriodChange, block.timestamp, "Wrong lastPeriodChange");
vm.prank(staker1);
- vm.expectRevert(bytes("Empty commit."));
+ vm.expectRevert(DisputeKitClassicBase.EmptyCommit.selector);
disputeKit.castCommit(disputeID, voteIDs, commit);
commit = keccak256(abi.encodePacked(YES, salt));
vm.prank(other);
- vm.expectRevert(bytes("The caller has to own the vote."));
+ vm.expectRevert(DisputeKitClassicBase.JurorHasToOwnTheVote.selector);
disputeKit.castCommit(disputeID, voteIDs, commit);
vm.prank(staker1);
@@ -1622,15 +1626,16 @@ contract KlerosCoreTest is Test {
}
// Check reveal in the next period
+ vm.warp(block.timestamp + timesPerPeriod[1]);
core.passPeriod(disputeID);
// Check the require with the wrong choice and then with the wrong salt
vm.prank(staker1);
- vm.expectRevert(bytes("The vote hash must match the commitment in courts with hidden votes."));
+ vm.expectRevert(DisputeKitClassicBase.HashDoesNotMatchHiddenVoteCommitment.selector);
disputeKit.castVote(disputeID, voteIDs, 2, salt, "XYZ");
vm.prank(staker1);
- vm.expectRevert(bytes("The vote hash must match the commitment in courts with hidden votes."));
+ vm.expectRevert(DisputeKitClassicBase.HashDoesNotMatchHiddenVoteCommitment.selector);
disputeKit.castVote(disputeID, voteIDs, YES, salt - 1, "XYZ");
vm.prank(staker1);
@@ -1665,7 +1670,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -1690,7 +1695,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS - 1); // Draw less to check the require later
@@ -1698,7 +1703,7 @@ contract KlerosCoreTest is Test {
uint256[] memory voteIDs = new uint256[](0);
vm.prank(staker1);
- vm.expectRevert(bytes("The dispute should be in Vote period."));
+ vm.expectRevert(DisputeKitClassicBase.NotVotePeriod.selector);
disputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ"); // Leave salt empty as not needed
vm.expectRevert(KlerosCoreBase.DisputeStillDrawing.selector);
@@ -1716,17 +1721,17 @@ contract KlerosCoreTest is Test {
assertEq(lastPeriodChange, block.timestamp, "Wrong lastPeriodChange");
vm.prank(staker1);
- vm.expectRevert(bytes("No voteID provided"));
+ vm.expectRevert(DisputeKitClassicBase.EmptyVoteIDs.selector);
disputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ");
voteIDs = new uint256[](1);
voteIDs[0] = 0; // Split vote IDs to see how the winner changes
vm.prank(staker1);
- vm.expectRevert(bytes("Choice out of bounds"));
+ vm.expectRevert(DisputeKitClassicBase.ChoiceOutOfBounds.selector);
disputeKit.castVote(disputeID, voteIDs, 2 + 1, 0, "XYZ");
vm.prank(other);
- vm.expectRevert(bytes("The juror has to own the vote."));
+ vm.expectRevert(DisputeKitClassicBase.JurorHasToOwnTheVote.selector);
disputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ");
vm.prank(staker1);
@@ -1735,7 +1740,7 @@ contract KlerosCoreTest is Test {
disputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ");
vm.prank(staker1);
- vm.expectRevert(bytes("Vote already cast."));
+ vm.expectRevert(DisputeKitClassicBase.VoteAlreadyCast.selector);
disputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ");
(
@@ -1801,7 +1806,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -1828,7 +1833,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -1869,7 +1874,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -1918,7 +1923,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -1970,7 +1975,7 @@ contract KlerosCoreTest is Test {
core.appeal{value: 0.21 ether}(disputeID, 2, arbitratorExtraData);
vm.prank(crowdfunder1);
- vm.expectRevert(bytes("There is no such ruling to fund."));
+ vm.expectRevert(DisputeKitClassicBase.ChoiceOutOfBounds.selector);
disputeKit.fundAppeal(disputeID, 3);
vm.prank(crowdfunder1);
@@ -1995,7 +2000,7 @@ contract KlerosCoreTest is Test {
assertEq((disputeKit.getFundedChoices(disputeID))[0], 1, "Incorrect funded choice");
vm.prank(crowdfunder1);
- vm.expectRevert(bytes("Appeal fee is already paid."));
+ vm.expectRevert(DisputeKitClassicBase.AppealFeeIsAlreadyPaid.selector);
disputeKit.fundAppeal(disputeID, 1);
}
@@ -2008,7 +2013,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2024,7 +2029,7 @@ contract KlerosCoreTest is Test {
disputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ");
vm.prank(crowdfunder1);
- vm.expectRevert(bytes("Appeal period is over.")); // Appeal period not started yet
+ vm.expectRevert(DisputeKitClassicBase.AppealPeriodIsOver.selector);
disputeKit.fundAppeal{value: 0.1 ether}(disputeID, 1);
core.passPeriod(disputeID);
@@ -2032,14 +2037,14 @@ contract KlerosCoreTest is Test {
vm.prank(crowdfunder1);
vm.warp(block.timestamp + ((end - start) / 2 + 1));
- vm.expectRevert(bytes("Appeal period is over for loser"));
+ vm.expectRevert(DisputeKitClassicBase.AppealPeriodIsOverForLoser.selector);
disputeKit.fundAppeal{value: 0.1 ether}(disputeID, 1); // Losing choice
disputeKit.fundAppeal(disputeID, 2); // Winning choice funding should not revert yet
vm.prank(crowdfunder1);
vm.warp(block.timestamp + (end - start) / 2); // Warp one more to cover the whole period
- vm.expectRevert(bytes("Appeal period is over."));
+ vm.expectRevert(DisputeKitClassicBase.AppealPeriodIsOver.selector);
disputeKit.fundAppeal{value: 0.1 ether}(disputeID, 2);
}
@@ -2052,7 +2057,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2153,7 +2158,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
KlerosCoreBase.Round memory round = core.getRoundInfo(disputeID, 0);
@@ -2210,7 +2215,7 @@ contract KlerosCoreTest is Test {
// Check jump modifier
vm.prank(address(core));
- vm.expectRevert(bytes("Dispute jumped to a parent DK!"));
+ vm.expectRevert(DisputeKitClassicBase.DisputeJumpedToParentDK.selector);
newDisputeKit.draw(disputeID, 1);
// And check that draw in the new round works
@@ -2231,7 +2236,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2265,7 +2270,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
// Split the stakers' votes. The first staker will get VoteID 0 and the second will take the rest.
@@ -2277,7 +2282,7 @@ contract KlerosCoreTest is Test {
core.setStake(GENERAL_COURT, 20000);
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, 2); // Assign leftover votes to staker2
@@ -2311,10 +2316,33 @@ contract KlerosCoreTest is Test {
core.unpause();
assertEq(disputeKit.getCoherentCount(disputeID, 0), 2, "Wrong coherent count");
+
+ uint256 pnkCoherence;
+ uint256 feeCoherence;
// dispute, round, voteID, feeForJuror (not used in classic DK), pnkPerJuror (not used in classic DK)
- assertEq(disputeKit.getDegreeOfCoherence(disputeID, 0, 0, 0, 0), 0, "Wrong degree of coherence 0 vote ID");
- assertEq(disputeKit.getDegreeOfCoherence(disputeID, 0, 1, 0, 0), 10000, "Wrong degree of coherence 1 vote ID");
- assertEq(disputeKit.getDegreeOfCoherence(disputeID, 0, 2, 0, 0), 10000, "Wrong degree of coherence 2 vote ID");
+ (pnkCoherence, feeCoherence) = disputeKit.getDegreeOfCoherenceReward(disputeID, 0, 0, 0, 0);
+ assertEq(pnkCoherence, 0, "Wrong reward pnk coherence 0 vote ID");
+ assertEq(feeCoherence, 0, "Wrong reward fee coherence 0 vote ID");
+
+ (pnkCoherence, feeCoherence) = disputeKit.getDegreeOfCoherenceReward(disputeID, 0, 1, 0, 0);
+ assertEq(pnkCoherence, 10000, "Wrong reward pnk coherence 1 vote ID");
+ assertEq(feeCoherence, 10000, "Wrong reward fee coherence 1 vote ID");
+
+ (pnkCoherence, feeCoherence) = disputeKit.getDegreeOfCoherenceReward(disputeID, 0, 2, 0, 0);
+ assertEq(pnkCoherence, 10000, "Wrong reward pnk coherence 2 vote ID");
+ assertEq(feeCoherence, 10000, "Wrong reward fee coherence 2 vote ID");
+
+ assertEq(disputeKit.getDegreeOfCoherencePenalty(disputeID, 0, 0, 0, 0), 0, "Wrong penalty coherence 0 vote ID");
+ assertEq(
+ disputeKit.getDegreeOfCoherencePenalty(disputeID, 0, 1, 0, 0),
+ 10000,
+ "Wrong penalty coherence 1 vote ID"
+ );
+ assertEq(
+ disputeKit.getDegreeOfCoherencePenalty(disputeID, 0, 2, 0, 0),
+ 10000,
+ "Wrong penalty coherence 2 vote ID"
+ );
vm.expectEmit(true, true, true, true);
emit SortitionModuleBase.StakeLocked(staker1, 1000, true);
@@ -2383,7 +2411,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2398,10 +2426,25 @@ contract KlerosCoreTest is Test {
core.passPeriod(disputeID); // Execution
assertEq(disputeKit.getCoherentCount(disputeID, 0), 0, "Wrong coherent count");
+
+ uint256 pnkCoherence;
+ uint256 feeCoherence;
// dispute, round, voteID, feeForJuror (not used in classic DK), pnkPerJuror (not used in classic DK)
- assertEq(disputeKit.getDegreeOfCoherence(disputeID, 0, 0, 0, 0), 0, "Wrong degree of coherence 0 vote ID");
- assertEq(disputeKit.getDegreeOfCoherence(disputeID, 0, 1, 0, 0), 0, "Wrong degree of coherence 1 vote ID");
- assertEq(disputeKit.getDegreeOfCoherence(disputeID, 0, 2, 0, 0), 0, "Wrong degree of coherence 2 vote ID");
+ (pnkCoherence, feeCoherence) = disputeKit.getDegreeOfCoherenceReward(disputeID, 0, 0, 0, 0);
+ assertEq(pnkCoherence, 0, "Wrong reward pnk coherence 0 vote ID");
+ assertEq(feeCoherence, 0, "Wrong reward fee coherence 0 vote ID");
+
+ (pnkCoherence, feeCoherence) = disputeKit.getDegreeOfCoherenceReward(disputeID, 0, 1, 0, 0);
+ assertEq(pnkCoherence, 0, "Wrong reward pnk coherence 1 vote ID");
+ assertEq(feeCoherence, 0, "Wrong reward fee coherence 1 vote ID");
+
+ (pnkCoherence, feeCoherence) = disputeKit.getDegreeOfCoherenceReward(disputeID, 0, 2, 0, 0);
+ assertEq(pnkCoherence, 0, "Wrong reward pnk coherence 2 vote ID");
+ assertEq(feeCoherence, 0, "Wrong reward fee coherence 2 vote ID");
+
+ assertEq(disputeKit.getDegreeOfCoherencePenalty(disputeID, 0, 0, 0, 0), 0, "Wrong penalty coherence 0 vote ID");
+ assertEq(disputeKit.getDegreeOfCoherencePenalty(disputeID, 0, 1, 0, 0), 0, "Wrong penalty coherence 1 vote ID");
+ assertEq(disputeKit.getDegreeOfCoherencePenalty(disputeID, 0, 2, 0, 0), 0, "Wrong penalty coherence 2 vote ID");
uint256 governorBalance = governor.balance;
uint256 governorTokenBalance = pinakion.balanceOf(governor);
@@ -2458,7 +2501,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2504,7 +2547,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2559,7 +2602,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2594,7 +2637,7 @@ contract KlerosCoreTest is Test {
assertEq(pinakion.balanceOf(address(core)), 1000, "Wrong token balance of the core");
assertEq(pinakion.balanceOf(staker1), 999999999999999000, "Wrong token balance of staker1");
- vm.expectRevert(bytes("Not eligible for withdrawal."));
+ vm.expectRevert(SortitionModuleBase.NotEligibleForWithdrawal.selector);
sortitionModule.withdrawLeftoverPNK(staker1);
vm.expectEmit(true, true, true, true);
@@ -2649,7 +2692,7 @@ contract KlerosCoreTest is Test {
core.setStake(GENERAL_COURT, 20000);
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2703,7 +2746,7 @@ contract KlerosCoreTest is Test {
core.setStake(GENERAL_COURT, 20000);
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2742,7 +2785,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2796,7 +2839,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2839,7 +2882,7 @@ contract KlerosCoreTest is Test {
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
core.draw(disputeID, DEFAULT_NB_OF_JURORS);
@@ -2863,14 +2906,14 @@ contract KlerosCoreTest is Test {
vm.warp(block.timestamp + timesPerPeriod[3]);
core.passPeriod(disputeID); // Execution
- vm.expectRevert(bytes("Dispute should be resolved."));
+ vm.expectRevert(DisputeKitClassicBase.DisputeNotResolved.selector);
disputeKit.withdrawFeesAndRewards(disputeID, payable(staker1), 0, 1);
core.executeRuling(disputeID);
vm.prank(governor);
core.pause();
- vm.expectRevert(bytes("Core is paused"));
+ vm.expectRevert(DisputeKitClassicBase.CoreIsPaused.selector);
disputeKit.withdrawFeesAndRewards(disputeID, payable(staker1), 0, 1);
vm.prank(governor);
core.unpause();
@@ -2938,7 +2981,7 @@ contract KlerosCoreTest is Test {
vm.warp(block.timestamp + minStakingTime);
sortitionModule.passPhase(); // Generating
- vm.roll(block.number + rngLookahead + 1);
+ vm.warp(block.timestamp + rngLookahead);
sortitionModule.passPhase(); // Drawing phase
KlerosCoreBase.Round memory round = core.getRoundInfo(disputeID, 0);
@@ -2969,7 +3012,7 @@ contract KlerosCoreTest is Test {
// Deliberately cast votes using the old DK to see if the exception will be caught.
vm.prank(staker1);
- vm.expectRevert(bytes("Not active for core dispute ID"));
+ vm.expectRevert(DisputeKitClassicBase.NotActiveForCoreDisputeID.selector);
disputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ");
// And check the new DK.
@@ -2990,4 +3033,113 @@ contract KlerosCoreTest is Test {
assertEq(totalCommited, 0, "totalCommited should be 0");
assertEq(choiceCount, 3, "choiceCount should be 3");
}
+
+ function test_RNGFallback() public {
+ RNGWithFallback rngFallback;
+ uint256 fallbackTimeout = 100;
+ RNGMock rngMock = new RNGMock();
+ rngFallback = new RNGWithFallback(msg.sender, address(sortitionModule), fallbackTimeout, rngMock);
+ assertEq(rngFallback.governor(), msg.sender, "Wrong governor");
+ assertEq(rngFallback.consumer(), address(sortitionModule), "Wrong sortition module address");
+ assertEq(address(rngFallback.rng()), address(rngMock), "Wrong RNG in fallback contract");
+ assertEq(rngFallback.fallbackTimeoutSeconds(), fallbackTimeout, "Wrong fallback timeout");
+ assertEq(rngFallback.requestTimestamp(), 0, "Request timestamp should be 0");
+
+ vm.prank(governor);
+ sortitionModule.changeRandomNumberGenerator(rngFallback);
+ assertEq(address(sortitionModule.rng()), address(rngFallback), "Wrong RNG address");
+
+ vm.prank(staker1);
+ core.setStake(GENERAL_COURT, 20000);
+ vm.prank(disputer);
+ arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
+ vm.warp(block.timestamp + minStakingTime);
+
+ sortitionModule.passPhase(); // Generating
+ assertEq(rngFallback.requestTimestamp(), block.timestamp, "Wrong request timestamp");
+
+ vm.expectRevert(SortitionModuleBase.RandomNumberNotReady.selector);
+ sortitionModule.passPhase();
+
+ vm.warp(block.timestamp + fallbackTimeout + 1);
+
+ // Pass several blocks too to see that correct block.number is still picked up.
+ vm.roll(block.number + 5);
+
+ vm.expectEmit(true, true, true, true);
+ emit RNGWithFallback.RNGFallback();
+ sortitionModule.passPhase(); // Drawing phase
+
+ assertEq(sortitionModule.randomNumber(), uint256(blockhash(block.number - 1)), "Wrong random number");
+ }
+
+ function test_RNGFallback_happyPath() public {
+ RNGWithFallback rngFallback;
+ uint256 fallbackTimeout = 100;
+ RNGMock rngMock = new RNGMock();
+ rngFallback = new RNGWithFallback(msg.sender, address(sortitionModule), fallbackTimeout, rngMock);
+
+ vm.prank(governor);
+ sortitionModule.changeRandomNumberGenerator(rngFallback);
+ assertEq(address(sortitionModule.rng()), address(rngFallback), "Wrong RNG address");
+
+ vm.prank(staker1);
+ core.setStake(GENERAL_COURT, 20000);
+ vm.prank(disputer);
+ arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action");
+ vm.warp(block.timestamp + minStakingTime);
+
+ assertEq(rngFallback.requestTimestamp(), 0, "Request timestamp should be 0");
+
+ sortitionModule.passPhase(); // Generating
+ assertEq(rngFallback.requestTimestamp(), block.timestamp, "Wrong request timestamp");
+
+ rngMock.setRN(123);
+
+ sortitionModule.passPhase(); // Drawing phase
+ assertEq(sortitionModule.randomNumber(), 123, "Wrong random number");
+ }
+
+ function test_RNGFallback_sanityChecks() public {
+ RNGWithFallback rngFallback;
+ uint256 fallbackTimeout = 100;
+ RNGMock rngMock = new RNGMock();
+ rngFallback = new RNGWithFallback(msg.sender, address(sortitionModule), fallbackTimeout, rngMock);
+
+ vm.expectRevert(IRNG.ConsumerOnly.selector);
+ vm.prank(governor);
+ rngFallback.requestRandomness();
+
+ vm.expectRevert(IRNG.ConsumerOnly.selector);
+ vm.prank(governor);
+ rngFallback.receiveRandomness();
+
+ vm.expectRevert(IRNG.GovernorOnly.selector);
+ vm.prank(other);
+ rngFallback.changeGovernor(other);
+ vm.prank(governor);
+ rngFallback.changeGovernor(other);
+ assertEq(rngFallback.governor(), other, "Wrong governor");
+
+ // Change governor back for convenience
+ vm.prank(other);
+ rngFallback.changeGovernor(governor);
+
+ vm.expectRevert(IRNG.GovernorOnly.selector);
+ vm.prank(other);
+ rngFallback.changeConsumer(other);
+ vm.prank(governor);
+ rngFallback.changeConsumer(other);
+ assertEq(rngFallback.consumer(), other, "Wrong consumer");
+
+ vm.expectRevert(IRNG.GovernorOnly.selector);
+ vm.prank(other);
+ rngFallback.changeFallbackTimeout(5);
+
+ vm.prank(governor);
+ vm.expectEmit(true, true, true, true);
+ emit RNGWithFallback.FallbackTimeoutChanged(5);
+ rngFallback.changeFallbackTimeout(5);
+ assertEq(rngFallback.fallbackTimeoutSeconds(), 5, "Wrong fallback timeout");
+ }
}
diff --git a/contracts/test/integration/index.ts b/contracts/test/integration/index.ts
index 395b9ed8e..f3c97b2a1 100644
--- a/contracts/test/integration/index.ts
+++ b/contracts/test/integration/index.ts
@@ -9,8 +9,6 @@ import {
HomeGateway,
VeaMock,
DisputeKitClassic,
- RandomizerRNG,
- RandomizerMock,
SortitionModule,
ChainlinkRNG,
ChainlinkVRFCoordinatorV2Mock,
@@ -58,16 +56,16 @@ describe("Integration tests", async () => {
fallbackToGlobal: true,
keepExistingDeployments: false,
});
- rng = (await ethers.getContract("ChainlinkRNG")) as ChainlinkRNG;
- vrfCoordinator = (await ethers.getContract("ChainlinkVRFCoordinator")) as ChainlinkVRFCoordinatorV2Mock;
- disputeKit = (await ethers.getContract("DisputeKitClassic")) as DisputeKitClassic;
- pnk = (await ethers.getContract("PNK")) as PNK;
- core = (await ethers.getContract("KlerosCore")) as KlerosCore;
- vea = (await ethers.getContract("VeaMock")) as VeaMock;
- foreignGateway = (await ethers.getContract("ForeignGatewayOnEthereum")) as ForeignGateway;
- arbitrable = (await ethers.getContract("ArbitrableExample")) as ArbitrableExample;
- homeGateway = (await ethers.getContract("HomeGatewayToEthereum")) as HomeGateway;
- sortitionModule = (await ethers.getContract("SortitionModule")) as SortitionModule;
+ rng = await ethers.getContract("ChainlinkRNG");
+ vrfCoordinator = await ethers.getContract("ChainlinkVRFCoordinator");
+ disputeKit = await ethers.getContract("DisputeKitClassic");
+ pnk = await ethers.getContract("PNK");
+ core = await ethers.getContract("KlerosCore");
+ vea = await ethers.getContract("VeaMock");
+ foreignGateway = await ethers.getContract("ForeignGatewayOnEthereum");
+ arbitrable = await ethers.getContract("ArbitrableExample");
+ homeGateway = await ethers.getContract("HomeGatewayToEthereum");
+ sortitionModule = await ethers.getContract("SortitionModule");
});
it("Resolves a dispute on the home chain with no appeal", async () => {
@@ -161,7 +159,6 @@ describe("Integration tests", async () => {
console.log("KC phase: %d", await sortitionModule.phase());
await sortitionModule.passPhase(); // Staking -> Generating
- await mineBlocks(ethers.getNumber(await sortitionModule.rngLookahead())); // Wait for finality
expect(await sortitionModule.phase()).to.equal(Phase.generating);
console.log("KC phase: %d", await sortitionModule.phase());
await vrfCoordinator.fulfillRandomWords(1, rng.target, []);
@@ -206,6 +203,6 @@ describe("Integration tests", async () => {
};
});
-const logJurorBalance = async (result) => {
+const logJurorBalance = async (result: { totalStaked: bigint; totalLocked: bigint }) => {
console.log("staked=%s, locked=%s", ethers.formatUnits(result.totalStaked), ethers.formatUnits(result.totalLocked));
};
diff --git a/contracts/test/proxy/index.ts b/contracts/test/proxy/index.ts
index 6b66a27fb..12ba6313c 100644
--- a/contracts/test/proxy/index.ts
+++ b/contracts/test/proxy/index.ts
@@ -4,7 +4,7 @@ import { DeployResult } from "hardhat-deploy/types";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { deployUpgradable } from "../../deploy/utils/deployUpgradable";
import { UpgradedByInheritanceV1, UpgradedByInheritanceV2 } from "../../typechain-types";
-import { UpgradedByRewrite as UpgradedByRewriteV1 } from "../../typechain-types/src/proxy/mock/by-rewrite";
+import { UpgradedByRewrite as UpgradedByRewriteV1 } from "../../typechain-types/src/proxy/mock/by-rewrite/UpgradedByRewrite.sol";
import { UpgradedByRewrite as UpgradedByRewriteV2 } from "../../typechain-types/src/proxy/mock/by-rewrite/UpgradedByRewriteV2.sol";
let deployer: HardhatEthersSigner;
@@ -130,9 +130,9 @@ describe("Upgradability", async () => {
});
it("Initializes v1", async () => {
- proxy = (await ethers.getContract("UpgradedByRewrite")) as UpgradedByRewriteV1;
+ proxy = await ethers.getContract("UpgradedByRewrite");
- implementation = (await ethers.getContract("UpgradedByRewrite_Implementation")) as UpgradedByRewriteV1;
+ implementation = await ethers.getContract("UpgradedByRewrite_Implementation");
expect(await proxy.governor()).to.equal(deployer.address);
@@ -156,7 +156,7 @@ describe("Upgradability", async () => {
if (!proxyDeployment.implementation) {
throw new Error("No implementation address");
}
- proxy = (await ethers.getContract("UpgradedByRewrite")) as UpgradedByRewriteV2;
+ proxy = await ethers.getContract("UpgradedByRewrite");
expect(await proxy.governor()).to.equal(deployer.address);
expect(await proxy.counter()).to.equal(3);
@@ -184,9 +184,9 @@ describe("Upgradability", async () => {
});
it("Initializes v1", async () => {
- proxy = (await ethers.getContract("UpgradedByInheritanceV1")) as UpgradedByInheritanceV1;
+ proxy = await ethers.getContract("UpgradedByInheritanceV1");
- implementation = (await ethers.getContract("UpgradedByInheritanceV1_Implementation")) as UpgradedByInheritanceV1;
+ implementation = await ethers.getContract("UpgradedByInheritanceV1_Implementation");
expect(await proxy.governor()).to.equal(deployer.address);
@@ -209,7 +209,7 @@ describe("Upgradability", async () => {
log: true,
});
- proxy = (await ethers.getContract("UpgradedByInheritanceV1")) as UpgradedByInheritanceV2;
+ proxy = await ethers.getContract("UpgradedByInheritanceV1");
expect(await proxy.governor()).to.equal(deployer.address);
diff --git a/contracts/test/rng/index.ts b/contracts/test/rng/index.ts
index 3d4906721..351a2d460 100644
--- a/contracts/test/rng/index.ts
+++ b/contracts/test/rng/index.ts
@@ -1,5 +1,5 @@
import { expect } from "chai";
-import { deployments, ethers, network } from "hardhat";
+import { deployments, ethers, getNamedAccounts, network } from "hardhat";
import {
IncrementalNG,
BlockHashRNG,
@@ -11,6 +11,7 @@ import {
const initialNg = 424242;
const abiCoder = ethers.AbiCoder.defaultAbiCoder();
+let deployer: string;
describe("IncrementalNG", async () => {
let rng: IncrementalNG;
@@ -21,13 +22,13 @@ describe("IncrementalNG", async () => {
});
it("Should return a number incrementing each time", async () => {
- expect(await rng.receiveRandomness.staticCall(689376)).to.equal(initialNg);
- await rng.receiveRandomness(29543).then((tx) => tx.wait());
- expect(await rng.receiveRandomness.staticCall(5894382)).to.equal(initialNg + 1);
- await rng.receiveRandomness(0).then((tx) => tx.wait());
- expect(await rng.receiveRandomness.staticCall(3465)).to.equal(initialNg + 2);
- await rng.receiveRandomness(2n ** 255n).then((tx) => tx.wait());
- expect(await rng.receiveRandomness.staticCall(0)).to.equal(initialNg + 3);
+ expect(await rng.receiveRandomness.staticCall()).to.equal(initialNg);
+ await rng.receiveRandomness().then((tx) => tx.wait());
+ expect(await rng.receiveRandomness.staticCall()).to.equal(initialNg + 1);
+ await rng.receiveRandomness().then((tx) => tx.wait());
+ expect(await rng.receiveRandomness.staticCall()).to.equal(initialNg + 2);
+ await rng.receiveRandomness().then((tx) => tx.wait());
+ expect(await rng.receiveRandomness.staticCall()).to.equal(initialNg + 3);
});
});
@@ -35,26 +36,48 @@ describe("BlockHashRNG", async () => {
let rng: BlockHashRNG;
beforeEach("Setup", async () => {
- const rngFactory = await ethers.getContractFactory("BlockHashRNG");
- rng = (await rngFactory.deploy()) as BlockHashRNG;
+ const [deployer] = await ethers.getSigners();
+ await deployments.delete("BlockHashRNG");
+ await deployments.deploy("BlockHashRNG", {
+ from: deployer.address,
+ args: [deployer.address, deployer.address, 10], // governor, consumer, lookaheadTime (seconds)
+ });
+ rng = await ethers.getContract("BlockHashRNG");
});
- it("Should return a non-zero number for a block number in the past", async () => {
- const tx = await rng.receiveRandomness(5);
- const trace = await network.provider.send("debug_traceTransaction", [tx.hash]);
- await tx.wait();
- const [rn] = abiCoder.decode(["uint"], ethers.getBytes(`${trace.returnValue}`));
- expect(rn).to.not.equal(0);
- await tx.wait();
+ it("Should return a non-zero number after requesting and waiting", async () => {
+ // First request randomness
+ await rng.requestRandomness();
+
+ // Check that it's not ready yet
+ expect(await rng.isRandomnessReady()).to.be.false;
+
+ // Advance time by 10 seconds (the lookahead time)
+ await network.provider.send("evm_increaseTime", [10]);
+ await network.provider.send("evm_mine");
+
+ // Now it should be ready
+ expect(await rng.isRandomnessReady()).to.be.true;
+
+ // Get the random number
+ const randomNumber = await rng.receiveRandomness.staticCall();
+ expect(randomNumber).to.not.equal(0);
});
- it("Should return zero for a block number in the future", async () => {
- const tx = await rng.receiveRandomness(9876543210);
- const trace = await network.provider.send("debug_traceTransaction", [tx.hash]);
- await tx.wait();
- const [rn] = abiCoder.decode(["uint"], ethers.getBytes(`${trace.returnValue}`));
- expect(rn).to.equal(0);
- await tx.wait();
+ it("Should return 0 if randomness not requested", async () => {
+ const randomNumber = await rng.receiveRandomness.staticCall();
+ expect(randomNumber).to.equal(0);
+ });
+
+ it("Should return 0 if not enough time has passed", async () => {
+ await rng.requestRandomness();
+
+ // Don't advance time enough
+ await network.provider.send("evm_increaseTime", [5]); // Only 5 seconds
+ await network.provider.send("evm_mine");
+
+ const randomNumber = await rng.receiveRandomness.staticCall();
+ expect(randomNumber).to.equal(0);
});
});
@@ -63,49 +86,47 @@ describe("ChainlinkRNG", async () => {
let vrfCoordinator: ChainlinkVRFCoordinatorV2Mock;
beforeEach("Setup", async () => {
+ ({ deployer } = await getNamedAccounts());
+
await deployments.fixture(["ChainlinkRNG"], {
fallbackToGlobal: true,
keepExistingDeployments: false,
});
- rng = (await ethers.getContract("ChainlinkRNG")) as ChainlinkRNG;
- vrfCoordinator = (await ethers.getContract("ChainlinkVRFCoordinator")) as ChainlinkVRFCoordinatorV2Mock;
+ rng = await ethers.getContract("ChainlinkRNG");
+ vrfCoordinator = await ethers.getContract("ChainlinkVRFCoordinator");
+
+ await rng.changeConsumer(deployer);
});
it("Should return a non-zero random number", async () => {
const requestId = 1;
- const expectedRn = BigInt(
- ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(["uint256", "uint256"], [requestId, 0]))
- );
+ const expectedRn = BigInt(ethers.keccak256(abiCoder.encode(["uint256", "uint256"], [requestId, 0])));
- let tx = await rng.requestRandomness(0);
+ let tx = await rng.requestRandomness();
await expect(tx).to.emit(rng, "RequestSent").withArgs(requestId);
tx = await vrfCoordinator.fulfillRandomWords(requestId, rng.target, []);
await expect(tx).to.emit(rng, "RequestFulfilled").withArgs(requestId, expectedRn);
- const rn = await rng.receiveRandomness(0);
+ const rn = await rng.receiveRandomness();
expect(rn).to.equal(expectedRn);
await tx.wait();
});
it("Should return only the last random number when multiple requests are made", async () => {
// First request
- let tx = await rng.requestRandomness(0);
+ let tx = await rng.requestRandomness();
const requestId1 = 1;
await expect(tx).to.emit(rng, "RequestSent").withArgs(requestId1);
// Second request
- tx = await rng.requestRandomness(0);
+ tx = await rng.requestRandomness();
const requestId2 = 2;
await expect(tx).to.emit(rng, "RequestSent").withArgs(requestId2);
// Generate expected random numbers
- const expectedRn1 = BigInt(
- ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(["uint256", "uint256"], [requestId1, 0]))
- );
- const expectedRn2 = BigInt(
- ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(["uint256", "uint256"], [requestId2, 0]))
- );
+ const expectedRn1 = BigInt(ethers.keccak256(abiCoder.encode(["uint256", "uint256"], [requestId1, 0])));
+ const expectedRn2 = BigInt(ethers.keccak256(abiCoder.encode(["uint256", "uint256"], [requestId2, 0])));
expect(expectedRn1).to.not.equal(expectedRn2, "Random numbers should be different");
// Fulfill first request
@@ -117,7 +138,7 @@ describe("ChainlinkRNG", async () => {
await expect(tx).to.emit(rng, "RequestFulfilled").withArgs(requestId2, expectedRn2);
// Should return only the last random number
- const rn = await rng.receiveRandomness(0);
+ const rn = await rng.receiveRandomness();
expect(rn).to.equal(expectedRn2);
await tx.wait();
});
@@ -128,12 +149,16 @@ describe("RandomizerRNG", async () => {
let randomizer: RandomizerMock;
beforeEach("Setup", async () => {
+ ({ deployer } = await getNamedAccounts());
+
await deployments.fixture(["RandomizerRNG"], {
fallbackToGlobal: true,
keepExistingDeployments: false,
});
- rng = (await ethers.getContract("RandomizerRNG")) as RandomizerRNG;
- randomizer = (await ethers.getContract("RandomizerOracle")) as RandomizerMock;
+ rng = await ethers.getContract("RandomizerRNG");
+ randomizer = await ethers.getContract("RandomizerOracle");
+
+ await rng.changeConsumer(deployer);
});
it("Should return a non-zero random number", async () => {
@@ -141,25 +166,25 @@ describe("RandomizerRNG", async () => {
const expectedRn = BigInt(ethers.hexlify(randomBytes));
const requestId = 1;
- let tx = await rng.requestRandomness(0);
+ let tx = await rng.requestRandomness();
await expect(tx).to.emit(rng, "RequestSent").withArgs(requestId);
tx = await randomizer.relay(rng.target, requestId, randomBytes);
await expect(tx).to.emit(rng, "RequestFulfilled").withArgs(requestId, expectedRn);
- const rn = await rng.receiveRandomness(0);
+ const rn = await rng.receiveRandomness();
expect(rn).to.equal(expectedRn);
await tx.wait();
});
it("Should return only the last random number when multiple requests are made", async () => {
// First request
- let tx = await rng.requestRandomness(0);
+ let tx = await rng.requestRandomness();
const requestId1 = 1;
await expect(tx).to.emit(rng, "RequestSent").withArgs(requestId1);
// Second request
- tx = await rng.requestRandomness(0);
+ tx = await rng.requestRandomness();
const requestId2 = 2;
await expect(tx).to.emit(rng, "RequestSent").withArgs(requestId2);
@@ -180,7 +205,7 @@ describe("RandomizerRNG", async () => {
await expect(tx).to.emit(rng, "RequestFulfilled").withArgs(requestId2, expectedRn2);
// Should return only the last random number
- const rn = await rng.receiveRandomness(0);
+ const rn = await rng.receiveRandomness();
expect(rn).to.equal(expectedRn2);
await tx.wait();
});
diff --git a/cspell.json b/cspell.json
index 0ab3e541c..0fa6dc52c 100644
--- a/cspell.json
+++ b/cspell.json
@@ -35,6 +35,7 @@
"IERC",
"Initializable",
"ipfs",
+ "IRNG",
"kleros",
"linguo",
"Numberish",
diff --git a/package.json b/package.json
index 6c9deb2d4..01e9468a1 100644
--- a/package.json
+++ b/package.json
@@ -77,7 +77,8 @@
"elliptic@npm:6.5.4": "npm:6.6.1",
"word-wrap@npm:~1.2.3": "npm:1.2.5",
"@codemirror/state": "npm:6.5.2",
- "undici@npm:7.3.0": "npm:7.5.0"
+ "undici@npm:7.3.0": "npm:7.5.0",
+ "viem@npm:2.x": "npm:^2.23.2"
},
"scripts": {
"check-prerequisites": "scripts/check-prerequisites.sh",
diff --git a/web/netlify.toml b/web/netlify.toml
index 8cf55aefd..3f6682c22 100644
--- a/web/netlify.toml
+++ b/web/netlify.toml
@@ -9,3 +9,8 @@ YARN_ENABLE_GLOBAL_CACHE = "true"
[functions]
directory = "web/netlify/functions/"
+
+[[headers]]
+ for = "/*"
+ [headers.values]
+ X-Robots-Tag = "llms-txt"
\ No newline at end of file
diff --git a/web/src/components/DisputeView/CardLabels/index.tsx b/web/src/components/DisputeView/CardLabels/index.tsx
index d6ca94a56..6a37e8a8e 100644
--- a/web/src/components/DisputeView/CardLabels/index.tsx
+++ b/web/src/components/DisputeView/CardLabels/index.tsx
@@ -61,13 +61,13 @@ interface ICardLabels {
}
const LabelArgs: Record>; color: IColors }> = {
- EvidenceTime: { text: "Evidence Time", icon: EvidenceIcon, color: "blue" },
- NotDrawn: { text: "Not Drawn", icon: NotDrawnIcon, color: "grey" },
- CanVote: { text: "Time to vote", icon: CanVoteIcon, color: "blue" },
- Voted: { text: "I voted", icon: VotedIcon, color: "purple" },
- DidNotVote: { text: "Didn't cast a vote", icon: ForgotToVoteIcon, color: "purple" },
+ EvidenceTime: { text: "Evidence time", icon: EvidenceIcon, color: "blue" },
+ NotDrawn: { text: "You were not drawn", icon: NotDrawnIcon, color: "grey" },
+ CanVote: { text: "You can vote now", icon: CanVoteIcon, color: "blue" },
+ Voted: { text: "You voted", icon: VotedIcon, color: "purple" },
+ DidNotVote: { text: "You did not vote", icon: ForgotToVoteIcon, color: "purple" },
CanFund: { text: "Appeal possible", icon: AppealIcon, color: "lightPurple" },
- Funded: { text: "I funded", icon: FundedIcon, color: "lightPurple" },
+ Funded: { text: "You funded an appeal", icon: FundedIcon, color: "lightPurple" },
};
const getFundingRewards = (contributions: ClassicContribution[], closed: boolean) => {
diff --git a/web/src/hooks/useVotingContext.tsx b/web/src/hooks/useVotingContext.tsx
index fa92bf09c..59abeb2f7 100644
--- a/web/src/hooks/useVotingContext.tsx
+++ b/web/src/hooks/useVotingContext.tsx
@@ -3,10 +3,16 @@ import React, { useContext, createContext, useMemo } from "react";
import { useParams } from "react-router-dom";
import { useAccount } from "wagmi";
-import { REFETCH_INTERVAL } from "consts/index";
-import { useReadDisputeKitClassicIsVoteActive } from "hooks/contracts/generated";
+import { REFETCH_INTERVAL, DisputeKits } from "consts/index";
+import {
+ useReadDisputeKitClassicIsVoteActive,
+ useReadDisputeKitShutterIsVoteActive,
+ useReadDisputeKitGatedIsVoteActive,
+ useReadDisputeKitGatedShutterIsVoteActive,
+} from "hooks/contracts/generated";
import { useDisputeDetailsQuery } from "hooks/queries/useDisputeDetailsQuery";
import { useDrawQuery } from "hooks/queries/useDrawQuery";
+import { useDisputeKitAddresses } from "hooks/useDisputeKitAddresses";
import { isUndefined } from "utils/index";
interface IVotingContext {
@@ -35,16 +41,70 @@ export const VotingContextProvider: React.FC<{ children: React.ReactNode }> = ({
const { data: drawData, isLoading } = useDrawQuery(address?.toLowerCase(), id, disputeData?.dispute?.currentRound.id);
const roundId = disputeData?.dispute?.currentRoundIndex;
const voteId = drawData?.draws?.[0]?.voteIDNum;
- const { data: hasVoted } = useReadDisputeKitClassicIsVoteActive({
+
+ const disputeKitAddress = disputeData?.dispute?.currentRound?.disputeKit?.address;
+ const { disputeKitName } = useDisputeKitAddresses({ disputeKitAddress });
+
+ const hookArgs = [BigInt(id ?? 0), roundId, voteId] as const;
+ const isEnabled = !isUndefined(roundId) && !isUndefined(voteId);
+
+ // Add a hook call for each DisputeKit
+ const classicVoteResult = useReadDisputeKitClassicIsVoteActive({
+ query: {
+ enabled: isEnabled && disputeKitName === DisputeKits.Classic,
+ refetchInterval: REFETCH_INTERVAL,
+ },
+ args: hookArgs,
+ });
+
+ const shutterVoteResult = useReadDisputeKitShutterIsVoteActive({
query: {
- enabled: !isUndefined(roundId) && !isUndefined(voteId),
+ enabled: isEnabled && disputeKitName === DisputeKits.Shutter,
refetchInterval: REFETCH_INTERVAL,
},
- args: [BigInt(id ?? 0), roundId, voteId],
+ args: hookArgs,
});
+ const gatedVoteResult = useReadDisputeKitGatedIsVoteActive({
+ query: {
+ enabled: isEnabled && disputeKitName === DisputeKits.Gated,
+ refetchInterval: REFETCH_INTERVAL,
+ },
+ args: hookArgs,
+ });
+
+ const gatedShutterVoteResult = useReadDisputeKitGatedShutterIsVoteActive({
+ query: {
+ enabled: isEnabled && disputeKitName === DisputeKits.GatedShutter,
+ refetchInterval: REFETCH_INTERVAL,
+ },
+ args: hookArgs,
+ });
+
+ // Add a return for each DisputeKit
+ const hasVoted = useMemo(() => {
+ switch (disputeKitName) {
+ case DisputeKits.Classic:
+ return classicVoteResult.data;
+ case DisputeKits.Shutter:
+ return shutterVoteResult.data;
+ case DisputeKits.Gated:
+ return gatedVoteResult.data;
+ case DisputeKits.GatedShutter:
+ return gatedShutterVoteResult.data;
+ default:
+ return undefined;
+ }
+ }, [
+ disputeKitName,
+ classicVoteResult.data,
+ shutterVoteResult.data,
+ gatedVoteResult.data,
+ gatedShutterVoteResult.data,
+ ]);
+
const wasDrawn = useMemo(() => !isUndefined(drawData) && drawData.draws.length > 0, [drawData]);
- const isHiddenVotes = useMemo(() => disputeData?.dispute?.court.hiddenVotes, [disputeData]);
+ const isHiddenVotes = useMemo(() => disputeData?.dispute?.court.hiddenVotes ?? false, [disputeData]);
const isCommitPeriod = useMemo(() => disputeData?.dispute?.period === "commit", [disputeData]);
const isVotingPeriod = useMemo(() => disputeData?.dispute?.period === "vote", [disputeData]);
diff --git a/web/src/pages/Cases/CaseDetails/Timeline.tsx b/web/src/pages/Cases/CaseDetails/Timeline.tsx
index 0dbf8fdfd..3d8af7464 100644
--- a/web/src/pages/Cases/CaseDetails/Timeline.tsx
+++ b/web/src/pages/Cases/CaseDetails/Timeline.tsx
@@ -68,7 +68,7 @@ const Timeline: React.FC<{
currentPeriodIndex: number;
}> = ({ currentPeriodIndex, dispute }) => {
const currentItemIndex = currentPeriodToCurrentItem(currentPeriodIndex, dispute?.court.hiddenVotes);
- const items = useTimeline(dispute, currentItemIndex, currentItemIndex);
+ const items = useTimeline(dispute, currentPeriodIndex);
return (
@@ -103,30 +103,26 @@ const currentPeriodToCurrentItem = (currentPeriodIndex: number, hiddenVotes?: bo
else return currentPeriodIndex - 1;
};
-const useTimeline = (dispute: DisputeDetailsQuery["dispute"], currentItemIndex: number, currentPeriodIndex: number) => {
+const useTimeline = (dispute: DisputeDetailsQuery["dispute"], currentPeriodIndex: number) => {
const isDesktop = useIsDesktop();
- const titles = useMemo(() => {
- const titles = ["Evidence", "Voting", "Appeal", "Executed"];
- if (dispute?.court.hiddenVotes) {
- titles.splice(1, 0, "Commit");
- }
- return titles;
- }, [dispute]);
+ const titles = ["Evidence", "Commit", "Voting", "Appeal", "Executed"];
+
const deadlineCurrentPeriod = getDeadline(
currentPeriodIndex,
dispute?.lastPeriodChange,
dispute?.court.timesPerPeriod
);
+
const countdown = useCountdown(deadlineCurrentPeriod);
const getSubitems = (index: number): string[] | React.ReactNode[] => {
if (typeof countdown !== "undefined" && dispute) {
if (index === titles.length - 1) {
return [];
- } else if (index === currentItemIndex && countdown === 0) {
+ } else if (index === currentPeriodIndex && countdown === 0) {
return ["Time's up!"];
- } else if (index < currentItemIndex) {
+ } else if (index < currentPeriodIndex) {
return [];
- } else if (index === currentItemIndex) {
+ } else if (index === currentPeriodIndex) {
return [secondsToDayHourMinute(countdown)];
} else {
return [secondsToDayHourMinute(dispute?.court.timesPerPeriod[index])];
@@ -134,10 +130,16 @@ const useTimeline = (dispute: DisputeDetailsQuery["dispute"], currentItemIndex:
}
return [];
};
- return titles.map((title, i) => ({
- title: i + 1 < titles.length && isDesktop ? `${title} Period` : title,
- subitems: getSubitems(i),
- }));
+ return titles.flatMap((title, i) => {
+ // if not hidden votes, skip commit index
+ if (!dispute?.court.hiddenVotes && i === Periods.commit) return [];
+ return [
+ {
+ title: i + 1 < titles.length && isDesktop ? `${title} Period` : title,
+ subitems: getSubitems(i),
+ },
+ ];
+ });
};
export const getDeadline = (
diff --git a/web/src/pages/Cases/CaseDetails/Voting/Shutter/Commit.tsx b/web/src/pages/Cases/CaseDetails/Voting/Shutter/Commit.tsx
index db7172f74..28363514f 100644
--- a/web/src/pages/Cases/CaseDetails/Voting/Shutter/Commit.tsx
+++ b/web/src/pages/Cases/CaseDetails/Voting/Shutter/Commit.tsx
@@ -85,6 +85,10 @@ const Commit: React.FC = ({
const handleCommit = useCallback(
async (choice: bigint) => {
+ if (!import.meta.env.REACT_APP_SHUTTER_API || import.meta.env.REACT_APP_SHUTTER_API.trim() === "") {
+ console.error("REACT_APP_SHUTTER_API environment variable is not set or is empty");
+ throw new Error("Cannot commit vote: REACT_APP_SHUTTER_API environment variable is required but not set");
+ }
const message = { message: saltKey };
const rawSalt = !isUndefined(signingAccount)
? await signingAccount.signMessage(message)
diff --git a/web/src/pages/Cases/CaseDetails/Voting/Shutter/index.tsx b/web/src/pages/Cases/CaseDetails/Voting/Shutter/index.tsx
index d2821108e..3000b2872 100644
--- a/web/src/pages/Cases/CaseDetails/Voting/Shutter/index.tsx
+++ b/web/src/pages/Cases/CaseDetails/Voting/Shutter/index.tsx
@@ -26,11 +26,17 @@ const Shutter: React.FC = ({ arbitrable, setIsOpen, dispute, currentPe
const { isCommitPeriod, isVotingPeriod, commited } = useVotingContext();
const voteIDs = useMemo(() => drawData?.draws?.map((draw) => draw.voteIDNum) as string[], [drawData]);
- return id && isCommitPeriod && !commited ? (
-
- ) : id && isVotingPeriod ? (
-
- ) : null;
+ const shouldShowCommit = id && isCommitPeriod && !commited;
+ const shouldShowReveal = id && isVotingPeriod;
+
+ return (
+ <>
+ {shouldShowCommit && (
+
+ )}
+ {shouldShowReveal && }
+ >
+ );
};
export default Shutter;
diff --git a/web/src/public/llms.txt b/web/src/public/llms.txt
new file mode 100644
index 000000000..00c5825f0
--- /dev/null
+++ b/web/src/public/llms.txt
@@ -0,0 +1,9 @@
+# v2.kleros.builders llms.txt
+
+> Facilitates decentralized arbitration by allowing users to create, manage, and resolve dispute cases through crowdsourced juror consensus, rewarding jurors with cryptocurrency for coherent votes on disputes on the blockchain-based Kleros platform.
+
+- [Kleros Dispute Dashboard](https://v2.kleros.builders): Dashboard for managing and viewing decentralized dispute cases, jurors, and court statistics on Kleros platform.
+- [Kleros Dispute Cases](https://v2.kleros.builders/#/cases/display/1/desc/all): Provide a platform for viewing and managing decentralized dispute resolution cases.
+- [Kleros Decentralized Courts](https://v2.kleros.builders/#/courts): Facilitate decentralized dispute resolution by allowing users to stake tokens, participate as jurors, and view court cases.
+- [Kleros Jurors Leaderboard](https://v2.kleros.builders/#/jurors/1/desc/all): Display ranking and statistics of jurors based on coherent voting and rewards in the Kleros decentralized arbitration system.
+- [Get PNK Token](https://v2.kleros.builders/#/get-pnk): Facilitates cross-chain swaps of PNK tokens typically between the Ethereum and Arbitrum networks.
diff --git a/yarn.lock b/yarn.lock
index 166200247..c2cb4f2d5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3065,6 +3065,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/aix-ppc64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/aix-ppc64@npm:0.25.8"
+ conditions: os=aix & cpu=ppc64
+ languageName: node
+ linkType: hard
+
"@esbuild/android-arm64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/android-arm64@npm:0.19.12"
@@ -3079,6 +3086,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/android-arm64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/android-arm64@npm:0.25.8"
+ conditions: os=android & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/android-arm@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/android-arm@npm:0.19.12"
@@ -3093,6 +3107,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/android-arm@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/android-arm@npm:0.25.8"
+ conditions: os=android & cpu=arm
+ languageName: node
+ linkType: hard
+
"@esbuild/android-x64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/android-x64@npm:0.19.12"
@@ -3107,6 +3128,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/android-x64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/android-x64@npm:0.25.8"
+ conditions: os=android & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/darwin-arm64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/darwin-arm64@npm:0.19.12"
@@ -3121,6 +3149,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/darwin-arm64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/darwin-arm64@npm:0.25.8"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/darwin-x64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/darwin-x64@npm:0.19.12"
@@ -3135,6 +3170,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/darwin-x64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/darwin-x64@npm:0.25.8"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/freebsd-arm64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/freebsd-arm64@npm:0.19.12"
@@ -3149,6 +3191,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/freebsd-arm64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/freebsd-arm64@npm:0.25.8"
+ conditions: os=freebsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/freebsd-x64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/freebsd-x64@npm:0.19.12"
@@ -3163,6 +3212,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/freebsd-x64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/freebsd-x64@npm:0.25.8"
+ conditions: os=freebsd & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-arm64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/linux-arm64@npm:0.19.12"
@@ -3177,6 +3233,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-arm64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/linux-arm64@npm:0.25.8"
+ conditions: os=linux & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-arm@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/linux-arm@npm:0.19.12"
@@ -3191,6 +3254,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-arm@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/linux-arm@npm:0.25.8"
+ conditions: os=linux & cpu=arm
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-ia32@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/linux-ia32@npm:0.19.12"
@@ -3205,6 +3275,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-ia32@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/linux-ia32@npm:0.25.8"
+ conditions: os=linux & cpu=ia32
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-loong64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/linux-loong64@npm:0.19.12"
@@ -3219,6 +3296,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-loong64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/linux-loong64@npm:0.25.8"
+ conditions: os=linux & cpu=loong64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-mips64el@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/linux-mips64el@npm:0.19.12"
@@ -3233,6 +3317,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-mips64el@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/linux-mips64el@npm:0.25.8"
+ conditions: os=linux & cpu=mips64el
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-ppc64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/linux-ppc64@npm:0.19.12"
@@ -3247,6 +3338,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-ppc64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/linux-ppc64@npm:0.25.8"
+ conditions: os=linux & cpu=ppc64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-riscv64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/linux-riscv64@npm:0.19.12"
@@ -3261,6 +3359,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-riscv64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/linux-riscv64@npm:0.25.8"
+ conditions: os=linux & cpu=riscv64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-s390x@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/linux-s390x@npm:0.19.12"
@@ -3275,6 +3380,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-s390x@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/linux-s390x@npm:0.25.8"
+ conditions: os=linux & cpu=s390x
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-x64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/linux-x64@npm:0.19.12"
@@ -3289,6 +3401,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-x64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/linux-x64@npm:0.25.8"
+ conditions: os=linux & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/netbsd-arm64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/netbsd-arm64@npm:0.25.8"
+ conditions: os=netbsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/netbsd-x64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/netbsd-x64@npm:0.19.12"
@@ -3303,6 +3429,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/netbsd-x64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/netbsd-x64@npm:0.25.8"
+ conditions: os=netbsd & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/openbsd-arm64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/openbsd-arm64@npm:0.25.8"
+ conditions: os=openbsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/openbsd-x64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/openbsd-x64@npm:0.19.12"
@@ -3317,6 +3457,20 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/openbsd-x64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/openbsd-x64@npm:0.25.8"
+ conditions: os=openbsd & cpu=x64
+ languageName: node
+ linkType: hard
+
+"@esbuild/openharmony-arm64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/openharmony-arm64@npm:0.25.8"
+ conditions: os=openharmony & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/sunos-x64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/sunos-x64@npm:0.19.12"
@@ -3331,6 +3485,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/sunos-x64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/sunos-x64@npm:0.25.8"
+ conditions: os=sunos & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/win32-arm64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/win32-arm64@npm:0.19.12"
@@ -3345,6 +3506,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/win32-arm64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/win32-arm64@npm:0.25.8"
+ conditions: os=win32 & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/win32-ia32@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/win32-ia32@npm:0.19.12"
@@ -3359,6 +3527,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/win32-ia32@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/win32-ia32@npm:0.25.8"
+ conditions: os=win32 & cpu=ia32
+ languageName: node
+ linkType: hard
+
"@esbuild/win32-x64@npm:0.19.12":
version: 0.19.12
resolution: "@esbuild/win32-x64@npm:0.19.12"
@@ -3373,6 +3548,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/win32-x64@npm:0.25.8":
+ version: 0.25.8
+ resolution: "@esbuild/win32-x64@npm:0.25.8"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0":
version: 4.4.0
resolution: "@eslint-community/eslint-utils@npm:4.4.0"
@@ -5941,7 +6123,7 @@ __metadata:
"@kleros/kleros-v2-eslint-config": "workspace:^"
"@kleros/kleros-v2-prettier-config": "workspace:^"
"@kleros/kleros-v2-tsconfig": "workspace:^"
- "@kleros/vea-contracts": "npm:^0.6.0"
+ "@kleros/vea-contracts": "npm:^0.7.0"
"@logtail/pino": "npm:^0.5.0"
"@nomicfoundation/hardhat-chai-matchers": "npm:^2.1.0"
"@nomicfoundation/hardhat-ethers": "npm:^3.1.0"
@@ -5955,7 +6137,7 @@ __metadata:
"@types/mocha": "npm:^10.0.10"
"@types/node": "npm:^20.17.6"
"@types/sinon": "npm:^17.0.4"
- "@wagmi/cli": "npm:^2.2.0"
+ "@wagmi/cli": "npm:^2.3.2"
abitype: "npm:^0.10.3"
chai: "npm:^4.5.0"
dotenv: "npm:^16.6.1"
@@ -5965,7 +6147,7 @@ __metadata:
gluegun: "npm:^5.2.0"
graphql: "npm:^16.9.0"
graphql-request: "npm:^7.1.2"
- hardhat: "npm:2.25.0"
+ hardhat: "npm:2.26.2"
hardhat-contract-sizer: "npm:^2.10.0"
hardhat-deploy: "npm:^1.0.4"
hardhat-deploy-ethers: "npm:^0.4.2"
@@ -5986,7 +6168,11 @@ __metadata:
ts-node: "npm:^10.9.2"
typechain: "npm:^8.3.2"
typescript: "npm:^5.6.3"
- viem: "npm:^2.24.1"
+ peerDependencies:
+ viem: ^2.24.1
+ peerDependenciesMeta:
+ viem:
+ optional: false
languageName: unknown
linkType: soft
@@ -6216,10 +6402,10 @@ __metadata:
languageName: node
linkType: hard
-"@kleros/vea-contracts@npm:^0.6.0":
- version: 0.6.0
- resolution: "@kleros/vea-contracts@npm:0.6.0"
- checksum: 10/1dafd94620d3392c2e00e09e7d1ca923007143f8625b4b584411a7b49404ae5630e870d3e260685964d37ccb9c4c4ab406523b8ec4dd9f89bcf6099a4f5976ec
+"@kleros/vea-contracts@npm:^0.7.0":
+ version: 0.7.0
+ resolution: "@kleros/vea-contracts@npm:0.7.0"
+ checksum: 10/bba12886020cd4bfce39938de56edf2b56472627871ef91b10b721de655e5c20f632a8cb57679927d868375218007898b12033d769b7d33cd3f18447ca093896
languageName: node
linkType: hard
@@ -7469,7 +7655,7 @@ __metadata:
languageName: node
linkType: hard
-"@nomicfoundation/edr@npm:^0.11.1":
+"@nomicfoundation/edr@npm:^0.11.3":
version: 0.11.3
resolution: "@nomicfoundation/edr@npm:0.11.3"
dependencies:
@@ -10091,13 +10277,6 @@ __metadata:
languageName: node
linkType: hard
-"@types/lru-cache@npm:^5.1.0":
- version: 5.1.1
- resolution: "@types/lru-cache@npm:5.1.1"
- checksum: 10/0afadefc983306684a8ef95b6337a0d9e3f687e7e89e1f1f3f2e1ce3fbab5b018bb84cf277d781f871175a2c8f0176762b69e58b6f4296ee1b816cea94d5ef06
- languageName: node
- linkType: hard
-
"@types/mdast@npm:^3.0.0":
version: 3.0.15
resolution: "@types/mdast@npm:3.0.15"
@@ -10999,6 +11178,39 @@ __metadata:
languageName: node
linkType: hard
+"@wagmi/cli@npm:^2.3.2":
+ version: 2.3.2
+ resolution: "@wagmi/cli@npm:2.3.2"
+ dependencies:
+ abitype: "npm:^1.0.4"
+ bundle-require: "npm:^5.1.0"
+ cac: "npm:^6.7.14"
+ change-case: "npm:^5.4.4"
+ chokidar: "npm:4.0.1"
+ dedent: "npm:^0.7.0"
+ dotenv: "npm:^16.3.1"
+ dotenv-expand: "npm:^10.0.0"
+ esbuild: "npm:~0.25.4"
+ escalade: "npm:3.2.0"
+ fdir: "npm:^6.1.1"
+ nanospinner: "npm:1.2.2"
+ pathe: "npm:^1.1.2"
+ picocolors: "npm:^1.0.0"
+ picomatch: "npm:^3.0.0"
+ prettier: "npm:^3.0.3"
+ viem: "npm:2.x"
+ zod: "npm:^3.22.2"
+ peerDependencies:
+ typescript: ">=5.0.4"
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ bin:
+ wagmi: dist/esm/cli.js
+ checksum: 10/85c6b1d4960c6d080d067f7dbac34e6a27d822690d33ef6c131b7714f6be13c5e04b2dd506fce38992ee6183ebe0fb48160e30f4cf901a27ccf34fd1b2dae529
+ languageName: node
+ linkType: hard
+
"@wagmi/connectors@npm:5.7.11, @wagmi/connectors@npm:>=5.7.11, @wagmi/connectors@npm:^5.7.11":
version: 5.7.11
resolution: "@wagmi/connectors@npm:5.7.11"
@@ -13621,6 +13833,17 @@ __metadata:
languageName: node
linkType: hard
+"bundle-require@npm:^5.1.0":
+ version: 5.1.0
+ resolution: "bundle-require@npm:5.1.0"
+ dependencies:
+ load-tsconfig: "npm:^0.2.3"
+ peerDependencies:
+ esbuild: ">=0.18"
+ checksum: 10/735e0220055b9bdac20bea48ec1e10dc3a205232c889ef54767900bebdc721959c4ccb221e4ea434d7ddcd693a8a4445c3d0598e4040ee313ce0ac3aae3e6178
+ languageName: node
+ linkType: hard
+
"busboy@npm:1.6.0, busboy@npm:^1.6.0":
version: 1.6.0
resolution: "busboy@npm:1.6.0"
@@ -17150,6 +17373,95 @@ __metadata:
languageName: node
linkType: hard
+"esbuild@npm:~0.25.4":
+ version: 0.25.8
+ resolution: "esbuild@npm:0.25.8"
+ dependencies:
+ "@esbuild/aix-ppc64": "npm:0.25.8"
+ "@esbuild/android-arm": "npm:0.25.8"
+ "@esbuild/android-arm64": "npm:0.25.8"
+ "@esbuild/android-x64": "npm:0.25.8"
+ "@esbuild/darwin-arm64": "npm:0.25.8"
+ "@esbuild/darwin-x64": "npm:0.25.8"
+ "@esbuild/freebsd-arm64": "npm:0.25.8"
+ "@esbuild/freebsd-x64": "npm:0.25.8"
+ "@esbuild/linux-arm": "npm:0.25.8"
+ "@esbuild/linux-arm64": "npm:0.25.8"
+ "@esbuild/linux-ia32": "npm:0.25.8"
+ "@esbuild/linux-loong64": "npm:0.25.8"
+ "@esbuild/linux-mips64el": "npm:0.25.8"
+ "@esbuild/linux-ppc64": "npm:0.25.8"
+ "@esbuild/linux-riscv64": "npm:0.25.8"
+ "@esbuild/linux-s390x": "npm:0.25.8"
+ "@esbuild/linux-x64": "npm:0.25.8"
+ "@esbuild/netbsd-arm64": "npm:0.25.8"
+ "@esbuild/netbsd-x64": "npm:0.25.8"
+ "@esbuild/openbsd-arm64": "npm:0.25.8"
+ "@esbuild/openbsd-x64": "npm:0.25.8"
+ "@esbuild/openharmony-arm64": "npm:0.25.8"
+ "@esbuild/sunos-x64": "npm:0.25.8"
+ "@esbuild/win32-arm64": "npm:0.25.8"
+ "@esbuild/win32-ia32": "npm:0.25.8"
+ "@esbuild/win32-x64": "npm:0.25.8"
+ dependenciesMeta:
+ "@esbuild/aix-ppc64":
+ optional: true
+ "@esbuild/android-arm":
+ optional: true
+ "@esbuild/android-arm64":
+ optional: true
+ "@esbuild/android-x64":
+ optional: true
+ "@esbuild/darwin-arm64":
+ optional: true
+ "@esbuild/darwin-x64":
+ optional: true
+ "@esbuild/freebsd-arm64":
+ optional: true
+ "@esbuild/freebsd-x64":
+ optional: true
+ "@esbuild/linux-arm":
+ optional: true
+ "@esbuild/linux-arm64":
+ optional: true
+ "@esbuild/linux-ia32":
+ optional: true
+ "@esbuild/linux-loong64":
+ optional: true
+ "@esbuild/linux-mips64el":
+ optional: true
+ "@esbuild/linux-ppc64":
+ optional: true
+ "@esbuild/linux-riscv64":
+ optional: true
+ "@esbuild/linux-s390x":
+ optional: true
+ "@esbuild/linux-x64":
+ optional: true
+ "@esbuild/netbsd-arm64":
+ optional: true
+ "@esbuild/netbsd-x64":
+ optional: true
+ "@esbuild/openbsd-arm64":
+ optional: true
+ "@esbuild/openbsd-x64":
+ optional: true
+ "@esbuild/openharmony-arm64":
+ optional: true
+ "@esbuild/sunos-x64":
+ optional: true
+ "@esbuild/win32-arm64":
+ optional: true
+ "@esbuild/win32-ia32":
+ optional: true
+ "@esbuild/win32-x64":
+ optional: true
+ bin:
+ esbuild: bin/esbuild
+ checksum: 10/9897411732768e652d90fa5dfadae965e8f420d24e5f23fa0604331a1441769e2c7ee4e41ca53e926f1fb51a53af52e01fc9070fdc1a4edf3e9ec9208ee41273
+ languageName: node
+ linkType: hard
+
"escalade@npm:3.2.0":
version: 3.2.0
resolution: "escalade@npm:3.2.0"
@@ -19940,17 +20252,15 @@ __metadata:
languageName: node
linkType: hard
-"hardhat@npm:2.25.0":
- version: 2.25.0
- resolution: "hardhat@npm:2.25.0"
+"hardhat@npm:2.26.2":
+ version: 2.26.2
+ resolution: "hardhat@npm:2.26.2"
dependencies:
"@ethereumjs/util": "npm:^9.1.0"
"@ethersproject/abi": "npm:^5.1.2"
- "@nomicfoundation/edr": "npm:^0.11.1"
+ "@nomicfoundation/edr": "npm:^0.11.3"
"@nomicfoundation/solidity-analyzer": "npm:^0.1.0"
"@sentry/node": "npm:^5.18.1"
- "@types/bn.js": "npm:^5.1.0"
- "@types/lru-cache": "npm:^5.1.0"
adm-zip: "npm:^0.4.16"
aggregate-error: "npm:^3.0.0"
ansi-escapes: "npm:^4.3.0"
@@ -19995,7 +20305,7 @@ __metadata:
optional: true
bin:
hardhat: internal/cli/bootstrap.js
- checksum: 10/b74e83cf8b48e782dd9b7db0d640bcd68fe303c9e269686f9aa4ddcdd7b80e1ca932907003fd42fda005f38d486e4e59726b0f38fd8bf0b981e5810abcc907db
+ checksum: 10/ef9f5f232264ed45a406a7053ccce71e67b0ce084de2de6fa2c24ff0bb1ec0d7b69f61769b3ac94a50445709b25bcf0d8ee135e1509e53331a3fea44d01cbb63
languageName: node
linkType: hard
@@ -33829,28 +34139,6 @@ __metadata:
languageName: node
linkType: hard
-"viem@npm:2.x, viem@npm:^2.1.1":
- version: 2.21.50
- resolution: "viem@npm:2.21.50"
- dependencies:
- "@noble/curves": "npm:1.6.0"
- "@noble/hashes": "npm:1.5.0"
- "@scure/bip32": "npm:1.5.0"
- "@scure/bip39": "npm:1.4.0"
- abitype: "npm:1.0.6"
- isows: "npm:1.0.6"
- ox: "npm:0.1.2"
- webauthn-p256: "npm:0.0.10"
- ws: "npm:8.18.0"
- peerDependencies:
- typescript: ">=5.0.4"
- peerDependenciesMeta:
- typescript:
- optional: true
- checksum: 10/6525c7dfa679d48759d50a31751b1d608f055e4396506c4f48550b81655b75b53978bd2dbe39099ac200f549c7429261d3478810dbd63b36df6a0afd77f69931
- languageName: node
- linkType: hard
-
"viem@npm:>=2.23.11, viem@npm:^2.22.21, viem@npm:^2.23.10, viem@npm:^2.24.1":
version: 2.24.1
resolution: "viem@npm:2.24.1"
@@ -33872,6 +34160,28 @@ __metadata:
languageName: node
linkType: hard
+"viem@npm:^2.1.1":
+ version: 2.21.50
+ resolution: "viem@npm:2.21.50"
+ dependencies:
+ "@noble/curves": "npm:1.6.0"
+ "@noble/hashes": "npm:1.5.0"
+ "@scure/bip32": "npm:1.5.0"
+ "@scure/bip39": "npm:1.4.0"
+ abitype: "npm:1.0.6"
+ isows: "npm:1.0.6"
+ ox: "npm:0.1.2"
+ webauthn-p256: "npm:0.0.10"
+ ws: "npm:8.18.0"
+ peerDependencies:
+ typescript: ">=5.0.4"
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ checksum: 10/6525c7dfa679d48759d50a31751b1d608f055e4396506c4f48550b81655b75b53978bd2dbe39099ac200f549c7429261d3478810dbd63b36df6a0afd77f69931
+ languageName: node
+ linkType: hard
+
"viem@npm:^2.21.59":
version: 2.22.17
resolution: "viem@npm:2.22.17"