From e4d4bdbb10090ece1c2e54ba231f19553db5b9bb Mon Sep 17 00:00:00 2001 From: unknownunknown1 Date: Wed, 27 Aug 2025 18:29:54 +1000 Subject: [PATCH 1/9] feat(KlerosCore): allow to jump to a non-classic DK --- .../deploy/00-home-chain-arbitration-neo.ts | 9 ++++---- contracts/deploy/00-home-chain-arbitration.ts | 9 ++++---- contracts/src/arbitration/KlerosCoreBase.sol | 7 ++++-- .../dispute-kits/DisputeKitClassic.sol | 10 +++++++-- .../dispute-kits/DisputeKitClassicBase.sol | 19 ++++++++++++++-- .../dispute-kits/DisputeKitGated.sol | 10 +++++++-- .../dispute-kits/DisputeKitGatedShutter.sol | 10 +++++++-- .../dispute-kits/DisputeKitShutter.sol | 10 +++++++-- .../dispute-kits/DisputeKitSybilResistant.sol | 6 +++-- .../arbitration/interfaces/IDisputeKit.sol | 4 ++++ contracts/test/foundry/KlerosCore.t.sol | 22 ++++++++++++------- 11 files changed, 86 insertions(+), 30 deletions(-) diff --git a/contracts/deploy/00-home-chain-arbitration-neo.ts b/contracts/deploy/00-home-chain-arbitration-neo.ts index f5211c4af..a47e9e4b3 100644 --- a/contracts/deploy/00-home-chain-arbitration-neo.ts +++ b/contracts/deploy/00-home-chain-arbitration-neo.ts @@ -28,10 +28,11 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) await deployUpgradable(deployments, "EvidenceModule", { from: deployer, args: [deployer], log: true }); + const jumpDisputeKitID = 1; // Classic DK const disputeKit = await deployUpgradable(deployments, "DisputeKitClassicNeo", { from: deployer, contract: "DisputeKitClassic", - args: [deployer, ZeroAddress, weth.target], + args: [deployer, ZeroAddress, weth.target, jumpDisputeKitID], log: true, }); @@ -124,21 +125,21 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) // Extra dispute kits const disputeKitShutter = await deployUpgradable(deployments, "DisputeKitShutter", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, jumpDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitShutter.address); const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, jumpDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitGated.address); const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, jumpDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitGatedShutter.address); diff --git a/contracts/deploy/00-home-chain-arbitration.ts b/contracts/deploy/00-home-chain-arbitration.ts index 1d590813f..8f8587877 100644 --- a/contracts/deploy/00-home-chain-arbitration.ts +++ b/contracts/deploy/00-home-chain-arbitration.ts @@ -34,9 +34,10 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) log: true, }); + const jumpDisputeKitID = 1; // Classic DK const disputeKit = await deployUpgradable(deployments, "DisputeKitClassic", { from: deployer, - args: [deployer, ZeroAddress, weth.target], + args: [deployer, ZeroAddress, weth.target, jumpDisputeKitID], log: true, }); @@ -105,7 +106,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) // Extra dispute kits const disputeKitShutter = await deployUpgradable(deployments, "DisputeKitShutter", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, jumpDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitShutter.address); @@ -113,7 +114,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, jumpDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitGated.address); @@ -121,7 +122,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", { from: deployer, - args: [deployer, core.target, weth.target], + args: [deployer, core.target, weth.target, jumpDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitGatedShutter.address); diff --git a/contracts/src/arbitration/KlerosCoreBase.sol b/contracts/src/arbitration/KlerosCoreBase.sol index 0a9946bbc..cbec7646c 100644 --- a/contracts/src/arbitration/KlerosCoreBase.sol +++ b/contracts/src/arbitration/KlerosCoreBase.sol @@ -1079,8 +1079,11 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable courtJump = true; if (!courts[newCourtID].supportedDisputeKits[newDisputeKitID]) { - // Switch to classic dispute kit if parent court doesn't support the current one. - newDisputeKitID = DISPUTE_KIT_CLASSIC; + newDisputeKitID = disputeKits[_round.disputeKitID].getJumpDisputeKitID(); + if (!courts[newCourtID].supportedDisputeKits[newDisputeKitID]) { + // Switch to classic dispute kit if parent court doesn't support the jump DK. + newDisputeKitID = DISPUTE_KIT_CLASSIC; + } disputeKitJump = true; } } diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol b/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol index ce5932c92..5f2a86536 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol @@ -26,8 +26,14 @@ contract DisputeKitClassic is DisputeKitClassicBase { /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. - function initialize(address _owner, KlerosCore _core, address _wNative) external reinitializer(1) { - __DisputeKitClassicBase_initialize(_owner, _core, _wNative); + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. + function initialize( + address _owner, + KlerosCore _core, + address _wNative, + uint256 _jumpDisputeKitID + ) external reinitializer(1) { + __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } function reinitialize(address _wNative) external reinitializer(9) { diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol b/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol index f2d7a154c..34ffd5e96 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol @@ -6,7 +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"; +import {ONE_BASIS_POINT, DISPUTE_KIT_CLASSIC} from "../../libraries/Constants.sol"; /// @title DisputeKitClassicBase /// Abstract Dispute kit classic implementation of the Kleros v1 features including: @@ -68,6 +68,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi public alreadyDrawn; // True if the address has already been drawn, false by default. To be added to the Round struct when fully redeploying rather than upgrading. mapping(uint256 coreDisputeID => bool) public coreDisputeIDToActive; // True if this dispute kit is active for this core dispute ID. address public wNative; // The wrapped native token for safeSend(). + uint256 public jumpDisputeKitID; // The ID of the dispute kit in Kleros Core disputeKits array that the dispute should switch to after the court jump, in case the new court doesn't support this dispute kit. // ************************************* // // * Events * // @@ -147,14 +148,17 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. function __DisputeKitClassicBase_initialize( address _owner, KlerosCore _core, - address _wNative + address _wNative, + uint256 _jumpDisputeKitID ) internal onlyInitializing { owner = _owner; core = _core; wNative = _wNative; + jumpDisputeKitID = _jumpDisputeKitID; } // ************************ // @@ -176,6 +180,12 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi owner = _owner; } + /// @dev Changes the dispute kit ID used for the jump. + /// @param _jumpDisputeKitID The new value for the `jumpDisputeKitID` storage variable. + function changeJumpDisputeKitID(uint256 _jumpDisputeKitID) external onlyByOwner { + jumpDisputeKitID = _jumpDisputeKitID; + } + /// @dev Changes the `core` storage variable. /// @param _core The new value for the `core` storage variable. function changeCore(address _core) external onlyByOwner { @@ -639,6 +649,11 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi return (_currentNbVotes * 2) + 1; } + function getJumpDisputeKitID() external view returns (uint256) { + // Fall back to classic DK in case the jump ID is not defined. + return jumpDisputeKitID == 0 ? DISPUTE_KIT_CLASSIC : jumpDisputeKitID; + } + /// @dev Returns true if the specified voter was active in this round. /// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit. /// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit. diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol b/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol index c0e70f741..ae97a5a5e 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol @@ -42,8 +42,14 @@ contract DisputeKitGated is DisputeKitClassicBase { /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. - function initialize(address _owner, KlerosCore _core, address _wNative) external reinitializer(1) { - __DisputeKitClassicBase_initialize(_owner, _core, _wNative); + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. + function initialize( + address _owner, + KlerosCore _core, + address _wNative, + uint256 _jumpDisputeKitID + ) external reinitializer(1) { + __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } function reinitialize(address _wNative) external reinitializer(9) { diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol b/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol index c5de2483b..ee4e9c089 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol @@ -61,8 +61,14 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase { /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. - function initialize(address _owner, KlerosCore _core, address _wNative) external reinitializer(1) { - __DisputeKitClassicBase_initialize(_owner, _core, _wNative); + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. + function initialize( + address _owner, + KlerosCore _core, + address _wNative, + uint256 _jumpDisputeKitID + ) external reinitializer(1) { + __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } function reinitialize(address _wNative) external reinitializer(9) { diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol b/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol index 886f5dbec..d98e20132 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol @@ -45,8 +45,14 @@ contract DisputeKitShutter is DisputeKitClassicBase { /// @param _owner The owner's address. /// @param _core The KlerosCore arbitrator. /// @param _wNative The wrapped native token address, typically wETH. - function initialize(address _owner, KlerosCore _core, address _wNative) external reinitializer(1) { - __DisputeKitClassicBase_initialize(_owner, _core, _wNative); + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. + function initialize( + address _owner, + KlerosCore _core, + address _wNative, + uint256 _jumpDisputeKitID + ) external reinitializer(1) { + __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } function reinitialize(address _wNative) external reinitializer(9) { diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol index e213f6d11..38d728444 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol @@ -40,13 +40,15 @@ contract DisputeKitSybilResistant is DisputeKitClassicBase { /// @param _core The KlerosCore arbitrator. /// @param _poh The Proof of Humanity registry. /// @param _wNative The wrapped native token address, typically wETH. + /// @param _jumpDisputeKitID The ID of the dispute kit to switch to after the court jump. function initialize( address _owner, KlerosCore _core, IProofOfHumanity _poh, - address _wNative + address _wNative, + uint256 _jumpDisputeKitID ) external reinitializer(1) { - __DisputeKitClassicBase_initialize(_owner, _core, _wNative); + __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); poh = _poh; singleDrawPerJuror = true; } diff --git a/contracts/src/arbitration/interfaces/IDisputeKit.sol b/contracts/src/arbitration/interfaces/IDisputeKit.sol index 0aab45b9c..ca3047176 100644 --- a/contracts/src/arbitration/interfaces/IDisputeKit.sol +++ b/contracts/src/arbitration/interfaces/IDisputeKit.sol @@ -127,6 +127,10 @@ interface IDisputeKit { uint256 _currentNbVotes ) external view returns (uint256); // TODO: remove previousDisputeKit + /// @dev Returns the dispute kid ID for Kleros Core, that will be used after court jump. + /// @return The ID of the dispute kit in Kleros Core disputeKits array. + function getJumpDisputeKitID() external view returns (uint256); + /// @dev Returns true if the specified voter was active in this round. /// @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. diff --git a/contracts/test/foundry/KlerosCore.t.sol b/contracts/test/foundry/KlerosCore.t.sol index 30e84abdc..e5b5e3c1a 100644 --- a/contracts/test/foundry/KlerosCore.t.sol +++ b/contracts/test/foundry/KlerosCore.t.sol @@ -102,10 +102,11 @@ contract KlerosCoreTest is Test { UUPSProxy proxyCore = new UUPSProxy(address(coreLogic), ""); bytes memory initDataDk = abi.encodeWithSignature( - "initialize(address,address,address)", + "initialize(address,address,address,uint256)", owner, address(proxyCore), - address(wNative) + address(wNative), + DISPUTE_KIT_CLASSIC ); UUPSProxy proxyDk = new UUPSProxy(address(dkLogic), initDataDk); @@ -235,6 +236,8 @@ contract KlerosCoreTest is Test { assertEq(pinakion.allowance(staker2, address(core)), 1 ether, "Wrong allowance for staker2"); assertEq(disputeKit.owner(), msg.sender, "Wrong DK owner"); + assertEq(disputeKit.getJumpDisputeKitID(), DISPUTE_KIT_CLASSIC, "Wrong jump DK"); + assertEq(disputeKit.jumpDisputeKitID(), DISPUTE_KIT_CLASSIC, "Wrong jump DK storage var"); assertEq(address(disputeKit.core()), address(core), "Wrong core in DK"); assertEq(sortitionModule.owner(), msg.sender, "Wrong SM owner"); @@ -289,10 +292,11 @@ contract KlerosCoreTest is Test { UUPSProxy proxyCore = new UUPSProxy(address(coreLogic), ""); bytes memory initDataDk = abi.encodeWithSignature( - "initialize(address,address,address)", + "initialize(address,address,address,uint256)", owner, address(proxyCore), - address(wNative) + address(wNative), + DISPUTE_KIT_CLASSIC ); UUPSProxy proxyDk = new UUPSProxy(address(dkLogic), initDataDk); @@ -2115,10 +2119,11 @@ contract KlerosCoreTest is Test { DisputeKitClassic dkLogic = new DisputeKitClassic(); // Create a new DK and court to check the switch bytes memory initDataDk = abi.encodeWithSignature( - "initialize(address,address,address)", + "initialize(address,address,address,uint256)", owner, address(core), - address(wNative) + address(wNative), + DISPUTE_KIT_CLASSIC ); UUPSProxy proxyDk = new UUPSProxy(address(dkLogic), initDataDk); @@ -2939,10 +2944,11 @@ contract KlerosCoreTest is Test { DisputeKitClassic dkLogic = new DisputeKitClassic(); // Create a new DK to check castVote. bytes memory initDataDk = abi.encodeWithSignature( - "initialize(address,address,address)", + "initialize(address,address,address,uint256)", owner, address(core), - address(wNative) + address(wNative), + DISPUTE_KIT_CLASSIC ); UUPSProxy proxyDk = new UUPSProxy(address(dkLogic), initDataDk); From 1d37d89b6c35057a679b63cdd7ceb2cd74054b7b Mon Sep 17 00:00:00 2001 From: unknownunknown1 Date: Wed, 27 Aug 2025 21:22:36 +1000 Subject: [PATCH 2/9] feat(DK): update reinitializer --- .../src/arbitration/dispute-kits/DisputeKitClassic.sol | 6 +++--- contracts/src/arbitration/dispute-kits/DisputeKitGated.sol | 6 +++--- .../src/arbitration/dispute-kits/DisputeKitGatedShutter.sol | 6 +++--- .../src/arbitration/dispute-kits/DisputeKitShutter.sol | 6 +++--- .../arbitration/dispute-kits/DisputeKitSybilResistant.sol | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol b/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol index 5f2a86536..af89d2816 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol @@ -11,7 +11,7 @@ import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol"; /// - an incentive system: equal split between coherent votes, /// - an appeal system: fund 2 choices only, vote on any choice. contract DisputeKitClassic is DisputeKitClassicBase { - string public constant override version = "0.12.0"; + string public constant override version = "0.13.0"; // ************************************* // // * Constructor * // @@ -36,8 +36,8 @@ contract DisputeKitClassic is DisputeKitClassicBase { __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } - function reinitialize(address _wNative) external reinitializer(9) { - wNative = _wNative; + function reinitialize(uint256 _jumpDisputeKitID) external reinitializer(10) { + jumpDisputeKitID = _jumpDisputeKitID; } // ************************ // diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol b/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol index ae97a5a5e..9097a8fd3 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitGated.sol @@ -27,7 +27,7 @@ interface IBalanceHolderERC1155 { /// - an incentive system: equal split between coherent votes, /// - an appeal system: fund 2 choices only, vote on any choice. contract DisputeKitGated is DisputeKitClassicBase { - string public constant override version = "0.12.0"; + string public constant override version = "0.13.0"; // ************************************* // // * Constructor * // @@ -52,8 +52,8 @@ contract DisputeKitGated is DisputeKitClassicBase { __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } - function reinitialize(address _wNative) external reinitializer(9) { - wNative = _wNative; + function reinitialize(uint256 _jumpDisputeKitID) external reinitializer(10) { + jumpDisputeKitID = _jumpDisputeKitID; } // ************************ // diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol b/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol index ee4e9c089..158bfc5a6 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitGatedShutter.sol @@ -28,7 +28,7 @@ interface IBalanceHolderERC1155 { /// - an incentive system: equal split between coherent votes, /// - an appeal system: fund 2 choices only, vote on any choice. contract DisputeKitGatedShutter is DisputeKitClassicBase { - string public constant override version = "0.12.0"; + string public constant override version = "0.13.0"; // ************************************* // // * Events * // @@ -71,8 +71,8 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase { __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } - function reinitialize(address _wNative) external reinitializer(9) { - wNative = _wNative; + function reinitialize(uint256 _jumpDisputeKitID) external reinitializer(10) { + jumpDisputeKitID = _jumpDisputeKitID; } // ************************ // diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol b/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol index d98e20132..0dfbf6985 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitShutter.sol @@ -12,7 +12,7 @@ import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol"; /// - an incentive system: equal split between coherent votes, /// - an appeal system: fund 2 choices only, vote on any choice. contract DisputeKitShutter is DisputeKitClassicBase { - string public constant override version = "0.12.0"; + string public constant override version = "0.13.0"; // ************************************* // // * Events * // @@ -55,8 +55,8 @@ contract DisputeKitShutter is DisputeKitClassicBase { __DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID); } - function reinitialize(address _wNative) external reinitializer(9) { - wNative = _wNative; + function reinitialize(uint256 _jumpDisputeKitID) external reinitializer(10) { + jumpDisputeKitID = _jumpDisputeKitID; } // ************************ // diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol index 38d728444..66553f02a 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitSybilResistant.sol @@ -18,7 +18,7 @@ interface IProofOfHumanity { /// - an incentive system: equal split between coherent votes, /// - an appeal system: fund 2 choices only, vote on any choice. contract DisputeKitSybilResistant is DisputeKitClassicBase { - string public constant override version = "0.12.0"; + string public constant override version = "0.13.0"; // ************************************* // // * Storage * // From 3ed7310a7d471ec5e57ba8b9a8a6458f32a79e71 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Fri, 29 Aug 2025 01:30:46 +0100 Subject: [PATCH 3/9] chore: minor comments tweaks --- contracts/src/arbitration/KlerosCoreBase.sol | 3 ++- .../dispute-kits/DisputeKitClassicBase.sol | 16 +++++++++------- .../src/arbitration/interfaces/IDisputeKit.sol | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/contracts/src/arbitration/KlerosCoreBase.sol b/contracts/src/arbitration/KlerosCoreBase.sol index cbec7646c..891d7173f 100644 --- a/contracts/src/arbitration/KlerosCoreBase.sol +++ b/contracts/src/arbitration/KlerosCoreBase.sol @@ -1079,9 +1079,10 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable courtJump = true; if (!courts[newCourtID].supportedDisputeKits[newDisputeKitID]) { + // The current Dispute Kit is not compatible with the new court, jump to another Dispute Kit. newDisputeKitID = disputeKits[_round.disputeKitID].getJumpDisputeKitID(); if (!courts[newCourtID].supportedDisputeKits[newDisputeKitID]) { - // Switch to classic dispute kit if parent court doesn't support the jump DK. + // The new Dispute Kit is still not compatible, fall back to `DisputeKitClassic` which is always supported. newDisputeKitID = DISPUTE_KIT_CLASSIC; } disputeKitJump = true; diff --git a/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol b/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol index 34ffd5e96..9afbfa3e3 100644 --- a/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol +++ b/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol @@ -180,18 +180,18 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi owner = _owner; } - /// @dev Changes the dispute kit ID used for the jump. - /// @param _jumpDisputeKitID The new value for the `jumpDisputeKitID` storage variable. - function changeJumpDisputeKitID(uint256 _jumpDisputeKitID) external onlyByOwner { - jumpDisputeKitID = _jumpDisputeKitID; - } - /// @dev Changes the `core` storage variable. /// @param _core The new value for the `core` storage variable. function changeCore(address _core) external onlyByOwner { core = KlerosCore(_core); } + /// @dev Changes the dispute kit ID used for the jump. + /// @param _jumpDisputeKitID The new value for the `jumpDisputeKitID` storage variable. + function changeJumpDisputeKitID(uint256 _jumpDisputeKitID) external onlyByOwner { + jumpDisputeKitID = _jumpDisputeKitID; + } + // ************************************* // // * State Modifiers * // // ************************************* // @@ -649,7 +649,9 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi return (_currentNbVotes * 2) + 1; } - function getJumpDisputeKitID() external view returns (uint256) { + /// @dev Returns the dispute kid ID be used after court jump by Kleros Core. + /// @return The ID of the dispute kit in Kleros Core disputeKits array. + function getJumpDisputeKitID() external view override returns (uint256) { // Fall back to classic DK in case the jump ID is not defined. return jumpDisputeKitID == 0 ? DISPUTE_KIT_CLASSIC : jumpDisputeKitID; } diff --git a/contracts/src/arbitration/interfaces/IDisputeKit.sol b/contracts/src/arbitration/interfaces/IDisputeKit.sol index ca3047176..6add5c22e 100644 --- a/contracts/src/arbitration/interfaces/IDisputeKit.sol +++ b/contracts/src/arbitration/interfaces/IDisputeKit.sol @@ -127,7 +127,7 @@ interface IDisputeKit { uint256 _currentNbVotes ) external view returns (uint256); // TODO: remove previousDisputeKit - /// @dev Returns the dispute kid ID for Kleros Core, that will be used after court jump. + /// @dev Returns the dispute kid ID be used after court jump by Kleros Core. /// @return The ID of the dispute kit in Kleros Core disputeKits array. function getJumpDisputeKitID() external view returns (uint256); From c9e73347d1892ac2e0b40b21aec912a508033deb Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Fri, 29 Aug 2025 03:05:53 +0100 Subject: [PATCH 4/9] chore: deploy script tweak to make DisputeKitGatedShutter jump to DisputeKitShutter --- .../deploy/00-home-chain-arbitration-neo.ts | 13 ++++++++----- contracts/deploy/00-home-chain-arbitration.ts | 19 +++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/contracts/deploy/00-home-chain-arbitration-neo.ts b/contracts/deploy/00-home-chain-arbitration-neo.ts index a47e9e4b3..22c2ffade 100644 --- a/contracts/deploy/00-home-chain-arbitration-neo.ts +++ b/contracts/deploy/00-home-chain-arbitration-neo.ts @@ -28,11 +28,11 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) await deployUpgradable(deployments, "EvidenceModule", { from: deployer, args: [deployer], log: true }); - const jumpDisputeKitID = 1; // Classic DK + const classicDisputeKitID = 1; // Classic DK const disputeKit = await deployUpgradable(deployments, "DisputeKitClassicNeo", { from: deployer, contract: "DisputeKitClassic", - args: [deployer, ZeroAddress, weth.target, jumpDisputeKitID], + args: [deployer, ZeroAddress, weth.target, classicDisputeKitID], log: true, }); @@ -123,23 +123,26 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) await core.changeArbitrableWhitelist(resolver.address, true); // Extra dispute kits + const disputeKitShutterID = 2; const disputeKitShutter = await deployUpgradable(deployments, "DisputeKitShutter", { from: deployer, - args: [deployer, core.target, weth.target, jumpDisputeKitID], + args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitShutter.address); + const disputeKitGatedID = 3; const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", { from: deployer, - args: [deployer, core.target, weth.target, jumpDisputeKitID], + args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitGated.address); + const disputeKitGatedShutterID = 4; const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", { from: deployer, - args: [deployer, core.target, weth.target, jumpDisputeKitID], + args: [deployer, core.target, weth.target, disputeKitShutterID], // Does not jump to DKClassic log: true, }); await core.addNewDisputeKit(disputeKitGatedShutter.address); diff --git a/contracts/deploy/00-home-chain-arbitration.ts b/contracts/deploy/00-home-chain-arbitration.ts index 8f8587877..8ec753194 100644 --- a/contracts/deploy/00-home-chain-arbitration.ts +++ b/contracts/deploy/00-home-chain-arbitration.ts @@ -34,10 +34,10 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) log: true, }); - const jumpDisputeKitID = 1; // Classic DK + const classicDisputeKitID = 1; // Classic DK const disputeKit = await deployUpgradable(deployments, "DisputeKitClassic", { from: deployer, - args: [deployer, ZeroAddress, weth.target, jumpDisputeKitID], + args: [deployer, ZeroAddress, weth.target, classicDisputeKitID], log: true, }); @@ -104,29 +104,32 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) } // Extra dispute kits + const disputeKitShutterID = 2; const disputeKitShutter = await deployUpgradable(deployments, "DisputeKitShutter", { from: deployer, - args: [deployer, core.target, weth.target, jumpDisputeKitID], + args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitShutter.address); - await core.enableDisputeKits(Courts.GENERAL, [2], true); // enable disputeKitShutter on the General Court + await core.enableDisputeKits(Courts.GENERAL, [disputeKitShutterID], true); // enable disputeKitShutter on the General Court + const disputeKitGatedID = 3; const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", { from: deployer, - args: [deployer, core.target, weth.target, jumpDisputeKitID], + args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitGated.address); - await core.enableDisputeKits(Courts.GENERAL, [3], true); // enable disputeKitGated on the General Court + await core.enableDisputeKits(Courts.GENERAL, [disputeKitGatedID], true); // enable disputeKitGated on the General Court + const disputeKitGatedShutterID = 4; const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", { from: deployer, - args: [deployer, core.target, weth.target, jumpDisputeKitID], + args: [deployer, core.target, weth.target, disputeKitShutterID], // Does not jump to DKClassic log: true, }); await core.addNewDisputeKit(disputeKitGatedShutter.address); - await core.enableDisputeKits(Courts.GENERAL, [4], true); // enable disputeKitGatedShutter on the General Court + await core.enableDisputeKits(Courts.GENERAL, [disputeKitGatedShutterID], true); // enable disputeKitGatedShutter on the General Court // Snapshot proxy await deploy("KlerosCoreSnapshotProxy", { From e6114bab12da4179bee66ab8ada4b6334eae4297 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Fri, 29 Aug 2025 04:09:54 +0100 Subject: [PATCH 5/9] fix: defensive guard added against jumpDisputeKitID not set --- contracts/src/arbitration/KlerosCoreBase.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/src/arbitration/KlerosCoreBase.sol b/contracts/src/arbitration/KlerosCoreBase.sol index 891d7173f..5551d5257 100644 --- a/contracts/src/arbitration/KlerosCoreBase.sol +++ b/contracts/src/arbitration/KlerosCoreBase.sol @@ -1081,8 +1081,8 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable if (!courts[newCourtID].supportedDisputeKits[newDisputeKitID]) { // The current Dispute Kit is not compatible with the new court, jump to another Dispute Kit. newDisputeKitID = disputeKits[_round.disputeKitID].getJumpDisputeKitID(); - if (!courts[newCourtID].supportedDisputeKits[newDisputeKitID]) { - // The new Dispute Kit is still not compatible, fall back to `DisputeKitClassic` which is always supported. + if (newDisputeKitID == NULL_DISPUTE_KIT || !courts[newCourtID].supportedDisputeKits[newDisputeKitID]) { + // The new Dispute Kit is not defined or still not compatible, fall back to `DisputeKitClassic` which is always supported. newDisputeKitID = DISPUTE_KIT_CLASSIC; } disputeKitJump = true; From 9d31252bb310aef00d9c175340fc602545ac26a0 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Fri, 29 Aug 2025 04:28:31 +0100 Subject: [PATCH 6/9] chore: dynamically retrieve the disputeKitID in the deployment script --- contracts/deploy/00-home-chain-arbitration-neo.ts | 6 +++--- contracts/deploy/00-home-chain-arbitration.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/deploy/00-home-chain-arbitration-neo.ts b/contracts/deploy/00-home-chain-arbitration-neo.ts index 22c2ffade..c5e50d6a2 100644 --- a/contracts/deploy/00-home-chain-arbitration-neo.ts +++ b/contracts/deploy/00-home-chain-arbitration-neo.ts @@ -123,29 +123,29 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) await core.changeArbitrableWhitelist(resolver.address, true); // Extra dispute kits - const disputeKitShutterID = 2; const disputeKitShutter = await deployUpgradable(deployments, "DisputeKitShutter", { from: deployer, args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitShutter.address); + const disputeKitShutterID = Number(await core.getDisputeKitsLength()); - const disputeKitGatedID = 3; const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", { from: deployer, args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitGated.address); + const disputeKitGatedID = Number(await core.getDisputeKitsLength()); - const disputeKitGatedShutterID = 4; const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", { from: deployer, args: [deployer, core.target, weth.target, disputeKitShutterID], // Does not jump to DKClassic log: true, }); await core.addNewDisputeKit(disputeKitGatedShutter.address); + const disputeKitGatedShutterID = Number(await core.getDisputeKitsLength()); // Snapshot proxy await deploy("KlerosCoreSnapshotProxy", { diff --git a/contracts/deploy/00-home-chain-arbitration.ts b/contracts/deploy/00-home-chain-arbitration.ts index 8ec753194..8163c6f66 100644 --- a/contracts/deploy/00-home-chain-arbitration.ts +++ b/contracts/deploy/00-home-chain-arbitration.ts @@ -104,31 +104,31 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) } // Extra dispute kits - const disputeKitShutterID = 2; const disputeKitShutter = await deployUpgradable(deployments, "DisputeKitShutter", { from: deployer, args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitShutter.address); + const disputeKitShutterID = Number(await core.getDisputeKitsLength()); await core.enableDisputeKits(Courts.GENERAL, [disputeKitShutterID], true); // enable disputeKitShutter on the General Court - const disputeKitGatedID = 3; const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", { from: deployer, args: [deployer, core.target, weth.target, classicDisputeKitID], log: true, }); await core.addNewDisputeKit(disputeKitGated.address); + const disputeKitGatedID = Number(await core.getDisputeKitsLength()); await core.enableDisputeKits(Courts.GENERAL, [disputeKitGatedID], true); // enable disputeKitGated on the General Court - const disputeKitGatedShutterID = 4; const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", { from: deployer, args: [deployer, core.target, weth.target, disputeKitShutterID], // Does not jump to DKClassic log: true, }); await core.addNewDisputeKit(disputeKitGatedShutter.address); + const disputeKitGatedShutterID = Number(await core.getDisputeKitsLength()); await core.enableDisputeKits(Courts.GENERAL, [disputeKitGatedShutterID], true); // enable disputeKitGatedShutter on the General Court // Snapshot proxy From 63d3a642d0cd5602e4d20d2ae9035cca3d331eab Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Wed, 3 Sep 2025 00:38:03 +0100 Subject: [PATCH 7/9] test: cover dispute kit jump to non-classic scenario --- .../test/foundry/KlerosCore_Appeals.t.sol | 149 +++++++++++++++++- 1 file changed, 145 insertions(+), 4 deletions(-) diff --git a/contracts/test/foundry/KlerosCore_Appeals.t.sol b/contracts/test/foundry/KlerosCore_Appeals.t.sol index 189e9cb89..6421cb119 100644 --- a/contracts/test/foundry/KlerosCore_Appeals.t.sol +++ b/contracts/test/foundry/KlerosCore_Appeals.t.sol @@ -146,7 +146,7 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { disputeKit.fundAppeal{value: 0.1 ether}(disputeID, 2); } - function test_appeal_fullFundingNoSwitch() public { + function test_appeal_fullFundingNoJump() public { uint256 disputeID = 0; vm.prank(staker1); @@ -208,10 +208,12 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { core.passPeriod(disputeID); } - function test_appeal_fullFundingDKCourtSwitch() public { + function test_appeal_fullFundingCourtJumpAndDKJumpToClassic() public { + // Setup: dk2 supported by court2 with dk2._jumpDisputeKitID == DISPUTE_KIT_CLASSIC + // Ensure that court2 jumps to GENERAL_COURT and dk2 jumps to DISPUTE_KIT_CLASSIC uint256 disputeID = 0; DisputeKitClassic dkLogic = new DisputeKitClassic(); - // Create a new DK and court to check the switch + // Create a new DK and court to check the jump bytes memory initDataDk = abi.encodeWithSignature( "initialize(address,address,address,uint256)", owner, @@ -250,6 +252,7 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { supportedDK = new uint256[](1); supportedDK[0] = newDkID; core.enableDisputeKits(newCourtID, supportedDK, true); + assertEq(core.isSupported(newCourtID, newDkID), true, "New DK should be supported by new court"); vm.prank(staker1); core.setStake(newCourtID, 20000); @@ -279,7 +282,6 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { vm.prank(crowdfunder1); newDisputeKit.fundAppeal{value: 0.63 ether}(disputeID, 1); - vm.prank(crowdfunder2); assertEq(core.isDisputeKitJumping(disputeID), true, "Should be jumping"); @@ -293,6 +295,7 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { emit KlerosCoreBase.AppealDecision(disputeID, arbitrable); vm.expectEmit(true, true, true, true); emit KlerosCoreBase.NewPeriod(disputeID, KlerosCoreBase.Period.evidence); + vm.prank(crowdfunder2); newDisputeKit.fundAppeal{value: 0.42 ether}(disputeID, 2); (, bool jumped, ) = newDisputeKit.disputes(disputeID); @@ -326,6 +329,144 @@ contract KlerosCore_AppealsTest is KlerosCore_TestBase { assertEq(account, staker1, "Wrong drawn account in the classic DK"); } + function test_appeal_fullFundingCourtJumpAndDKJumpToNonClassic() public { + // Setup: + // dk2 supported by GENERAL_COURT, which is a non-DISPUTE_KIT_CLASSIC + // dk3 supported by court2, with dk3._jumpDisputeKitID == dk2 + // Ensure that court2 jumps to GENERAL_COURT and dk3 jumps to dk2 + uint256 disputeID = 0; + uint96 newCourtID = 2; + uint256 dkID2 = 2; + uint256 dkID3 = 3; + + DisputeKitClassic dkLogic = new DisputeKitClassic(); + + bytes memory initDataDk2 = abi.encodeWithSignature( + "initialize(address,address,address,uint256)", + owner, + address(core), + address(wNative), + DISPUTE_KIT_CLASSIC + ); + UUPSProxy proxyDk2 = new UUPSProxy(address(dkLogic), initDataDk2); + DisputeKitClassic disputeKit2 = DisputeKitClassic(address(proxyDk2)); + + bytes memory initDataDk3 = abi.encodeWithSignature( + "initialize(address,address,address,uint256)", + owner, + address(core), + address(wNative), + dkID2 + ); + UUPSProxy proxyDk3 = new UUPSProxy(address(dkLogic), initDataDk3); + DisputeKitClassic disputeKit3 = DisputeKitClassic(address(proxyDk3)); + + vm.prank(owner); + core.addNewDisputeKit(disputeKit2); + vm.prank(owner); + core.addNewDisputeKit(disputeKit3); + + uint256[] memory supportedDK = new uint256[](2); + supportedDK[0] = DISPUTE_KIT_CLASSIC; + supportedDK[1] = dkID3; + vm.prank(owner); + core.createCourt( + GENERAL_COURT, + hiddenVotes, + minStake, + alpha, + feeForJuror, + 3, // jurors for jump. Low number to ensure jump after the first appeal + [uint256(60), uint256(120), uint256(180), uint256(240)], // Times per period + sortitionExtraData, + supportedDK + ); + assertEq(core.isSupported(newCourtID, dkID3), true, "dkID3 should be supported by new court"); + + vm.prank(owner); + supportedDK[0] = DISPUTE_KIT_CLASSIC; + supportedDK[1] = dkID2; + core.enableDisputeKits(GENERAL_COURT, supportedDK, true); + assertEq(core.isSupported(GENERAL_COURT, dkID2), true, "dkID2 should be supported by GENERAL_COURT"); + + bytes memory newExtraData = abi.encodePacked(uint256(newCourtID), DEFAULT_NB_OF_JURORS, dkID3); + arbitrable.changeArbitratorExtraData(newExtraData); + + vm.prank(staker1); + core.setStake(newCourtID, 20000); + vm.prank(disputer); + arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action"); + vm.warp(block.timestamp + minStakingTime); + sortitionModule.passPhase(); // Generating + vm.warp(block.timestamp + rngLookahead); + sortitionModule.passPhase(); // Drawing phase + + KlerosCoreBase.Round memory round = core.getRoundInfo(disputeID, 0); + assertEq(round.disputeKitID, dkID3, "Wrong DK ID"); + + core.draw(disputeID, DEFAULT_NB_OF_JURORS); + vm.warp(block.timestamp + timesPerPeriod[0]); + core.passPeriod(disputeID); // Vote + + uint256[] memory voteIDs = new uint256[](3); + voteIDs[0] = 0; + voteIDs[1] = 1; + voteIDs[2] = 2; + + vm.prank(staker1); + disputeKit3.castVote(disputeID, voteIDs, 2, 0, "XYZ"); + + core.passPeriod(disputeID); // Appeal + + vm.prank(crowdfunder1); + disputeKit3.fundAppeal{value: 0.63 ether}(disputeID, 1); + + assertEq(core.isDisputeKitJumping(disputeID), true, "Should be jumping"); + + vm.expectEmit(true, true, true, true); + emit KlerosCoreBase.CourtJump(disputeID, 1, newCourtID, GENERAL_COURT); + vm.expectEmit(true, true, true, true); + emit KlerosCoreBase.DisputeKitJump(disputeID, 1, dkID3, dkID2); + vm.expectEmit(true, true, true, true); + emit DisputeKitClassicBase.DisputeCreation(disputeID, 2, newExtraData); + vm.expectEmit(true, true, true, true); + emit KlerosCoreBase.AppealDecision(disputeID, arbitrable); + vm.expectEmit(true, true, true, true); + emit KlerosCoreBase.NewPeriod(disputeID, KlerosCoreBase.Period.evidence); + vm.prank(crowdfunder2); + disputeKit3.fundAppeal{value: 0.42 ether}(disputeID, 2); + + (, bool jumped, ) = disputeKit3.disputes(disputeID); + assertEq(jumped, true, "jumped should be true"); + assertEq( + (disputeKit3.getFundedChoices(disputeID)).length, + 2, + "No fresh round created so the number of funded choices should be 2" + ); + + round = core.getRoundInfo(disputeID, 1); + assertEq(round.disputeKitID, dkID2, "Wrong DK ID"); + assertEq(sortitionModule.disputesWithoutJurors(), 1, "Wrong disputesWithoutJurors count"); + (uint96 courtID, , , , ) = core.disputes(disputeID); + assertEq(courtID, GENERAL_COURT, "Wrong court ID"); + + (, jumped, ) = disputeKit2.disputes(disputeID); + assertEq(jumped, false, "jumped should be false in the DK that dispute jumped to"); + + // Check jump modifier + vm.prank(address(core)); + vm.expectRevert(DisputeKitClassicBase.DisputeJumpedToParentDK.selector); + disputeKit3.draw(disputeID, 1); + + // And check that draw in the new round works + vm.expectEmit(true, true, true, true); + emit KlerosCoreBase.Draw(staker1, disputeID, 1, 0); // roundID = 1 VoteID = 0 + core.draw(disputeID, 1); + + (address account, , , ) = disputeKit2.getVoteInfo(disputeID, 1, 0); + assertEq(account, staker1, "Wrong drawn account in the classic DK"); + } + function test_appeal_quickPassPeriod() public { uint256 disputeID = 0; From d79497542273b66d021cb711b99d6acb71656643 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Wed, 3 Sep 2025 23:24:36 +0100 Subject: [PATCH 8/9] chore: changelog --- contracts/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/CHANGELOG.md b/contracts/CHANGELOG.md index 79845b880..d919c236a 100644 --- a/contracts/CHANGELOG.md +++ b/contracts/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Common Changelog](https://common-changelog.org/). - **Breaking:** Rename the interface from `RNG` to `IRNG` ([#2054](https://github.com/kleros/kleros-v2/issues/2054)) - **Breaking:** Remove the `_block` parameter from `IRNG.requestRandomness()` and `IRNG.receiveRandomness()`, not needed for the primary VRF-based RNG ([#2054](https://github.com/kleros/kleros-v2/issues/2054)) - **Breaking:** Rename `governor` to `owner` in order to comply with the lightweight ownership standard [ERC-5313](https://eipsinsight.com/ercs/erc-5313) ([#2112](https://github.com/kleros/kleros-v2/issues/2112)) +- **Breaking:** Add a new state variable `jumpDisputeKitID` to the `DisputeKitClassicBase` contract ([#2114](https://github.com/kleros/kleros-v2/issues/2114)) - 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)) @@ -27,6 +28,7 @@ The format is based on [Common Changelog](https://common-changelog.org/). ### Added - Allow the dispute kits to force an early court jump and to override the number of votes after an appeal (future-proofing) ([#2110](https://github.com/kleros/kleros-v2/issues/2110)) +- Allow the dispute kits to specify which new dispute kit to use when a court jump occurs ([#2114](https://github.com/kleros/kleros-v2/issues/2114)) ### Fixed From 62b0a50ab52b40e4ed70dc53b004b60874e1f805 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Wed, 3 Sep 2025 23:33:39 +0100 Subject: [PATCH 9/9] fix: deploy script --- contracts/deploy/00-home-chain-arbitration-neo.ts | 6 +++--- contracts/deploy/00-home-chain-arbitration.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/deploy/00-home-chain-arbitration-neo.ts b/contracts/deploy/00-home-chain-arbitration-neo.ts index c5e50d6a2..36a895c74 100644 --- a/contracts/deploy/00-home-chain-arbitration-neo.ts +++ b/contracts/deploy/00-home-chain-arbitration-neo.ts @@ -129,7 +129,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) log: true, }); await core.addNewDisputeKit(disputeKitShutter.address); - const disputeKitShutterID = Number(await core.getDisputeKitsLength()); + const disputeKitShutterID = (await core.getDisputeKitsLength()) - 1n; const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", { from: deployer, @@ -137,7 +137,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) log: true, }); await core.addNewDisputeKit(disputeKitGated.address); - const disputeKitGatedID = Number(await core.getDisputeKitsLength()); + const disputeKitGatedID = (await core.getDisputeKitsLength()) - 1n; const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", { from: deployer, @@ -145,7 +145,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) log: true, }); await core.addNewDisputeKit(disputeKitGatedShutter.address); - const disputeKitGatedShutterID = Number(await core.getDisputeKitsLength()); + const disputeKitGatedShutterID = (await core.getDisputeKitsLength()) - 1n; // Snapshot proxy await deploy("KlerosCoreSnapshotProxy", { diff --git a/contracts/deploy/00-home-chain-arbitration.ts b/contracts/deploy/00-home-chain-arbitration.ts index 8163c6f66..25291052d 100644 --- a/contracts/deploy/00-home-chain-arbitration.ts +++ b/contracts/deploy/00-home-chain-arbitration.ts @@ -110,7 +110,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) log: true, }); await core.addNewDisputeKit(disputeKitShutter.address); - const disputeKitShutterID = Number(await core.getDisputeKitsLength()); + const disputeKitShutterID = (await core.getDisputeKitsLength()) - 1n; await core.enableDisputeKits(Courts.GENERAL, [disputeKitShutterID], true); // enable disputeKitShutter on the General Court const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", { @@ -119,7 +119,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) log: true, }); await core.addNewDisputeKit(disputeKitGated.address); - const disputeKitGatedID = Number(await core.getDisputeKitsLength()); + const disputeKitGatedID = (await core.getDisputeKitsLength()) - 1n; await core.enableDisputeKits(Courts.GENERAL, [disputeKitGatedID], true); // enable disputeKitGated on the General Court const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", { @@ -128,7 +128,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) log: true, }); await core.addNewDisputeKit(disputeKitGatedShutter.address); - const disputeKitGatedShutterID = Number(await core.getDisputeKitsLength()); + const disputeKitGatedShutterID = (await core.getDisputeKitsLength()) - 1n; await core.enableDisputeKits(Courts.GENERAL, [disputeKitGatedShutterID], true); // enable disputeKitGatedShutter on the General Court // Snapshot proxy