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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions contracts/deploy/00-home-chain-arbitration-mainnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
const classicDisputeKitID = 1; // Classic DK
const disputeKit = await deployUpgradable(deployments, "DisputeKitClassic", {
from: deployer,
args: [deployer, ZeroAddress, weth.target, classicDisputeKitID],
args: [deployer, ZeroAddress, weth.target],
log: true,
});

Expand Down Expand Up @@ -125,23 +125,23 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
// Extra dispute kits
const disputeKitShutter = await deployUpgradable(deployments, "DisputeKitShutter", {
from: deployer,
args: [deployer, core.target, weth.target, classicDisputeKitID],
args: [deployer, core.target, weth.target],
log: true,
});
await core.addNewDisputeKit(disputeKitShutter.address);
const disputeKitShutterID = (await core.getDisputeKitsLength()) - 1n;

const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", {
from: deployer,
args: [deployer, core.target, weth.target, classicDisputeKitID],
args: [deployer, core.target, weth.target],
log: true,
});
await core.addNewDisputeKit(disputeKitGated.address);
const disputeKitGatedID = (await core.getDisputeKitsLength()) - 1n;

const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", {
from: deployer,
args: [deployer, core.target, weth.target, disputeKitShutterID], // Does not jump to DKClassic
args: [deployer, core.target, weth.target], // TODO: jump to a Shutter DK instead of a Classic one?
log: true,
});
await core.addNewDisputeKit(disputeKitGatedShutter.address);
Expand Down
2 changes: 1 addition & 1 deletion contracts/deploy/00-home-chain-arbitration-university.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
const disputeKit = await deployUpgradable(deployments, "DisputeKitClassicUniversity", {
from: deployer,
contract: "DisputeKitClassic",
args: [deployer, ZeroAddress, weth.target, 1],
args: [deployer, ZeroAddress, weth.target],
log: true,
});

Expand Down
8 changes: 4 additions & 4 deletions contracts/deploy/00-home-chain-arbitration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
const classicDisputeKitID = 1; // Classic DK
const disputeKit = await deployUpgradable(deployments, "DisputeKitClassic", {
from: deployer,
args: [deployer, ZeroAddress, weth.target, classicDisputeKitID],
args: [deployer, ZeroAddress, weth.target],
log: true,
});

Expand Down Expand Up @@ -115,7 +115,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
// Extra dispute kits
const disputeKitShutter = await deployUpgradable(deployments, "DisputeKitShutter", {
from: deployer,
args: [deployer, core.target, weth.target, classicDisputeKitID],
args: [deployer, core.target, weth.target],
log: true,
});
await core.addNewDisputeKit(disputeKitShutter.address);
Expand All @@ -124,7 +124,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)

const disputeKitGated = await deployUpgradable(deployments, "DisputeKitGated", {
from: deployer,
args: [deployer, core.target, weth.target, classicDisputeKitID],
args: [deployer, core.target, weth.target],
log: true,
});
await core.addNewDisputeKit(disputeKitGated.address);
Expand All @@ -133,7 +133,7 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)

const disputeKitGatedShutter = await deployUpgradable(deployments, "DisputeKitGatedShutter", {
from: deployer,
args: [deployer, core.target, weth.target, disputeKitShutterID], // Does not jump to DKClassic
args: [deployer, core.target, weth.target], // TODO: jump to a Shutter DK instead of a Classic one?
log: true,
});
await core.addNewDisputeKit(disputeKitGatedShutter.address);
Expand Down
2 changes: 1 addition & 1 deletion contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const config: HardhatUserConfig = {
viaIR: process.env.VIA_IR !== "false", // Defaults to true
optimizer: {
enabled: true,
runs: 800, // Constrained by the size of the KlerosCore contract
runs: 1000, // Constrained by the size of the KlerosCore contract
},
outputSelection: {
"*": {
Expand Down
108 changes: 60 additions & 48 deletions contracts/src/arbitration/KlerosCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -785,7 +785,7 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
Round storage extraRound = dispute.rounds.push();
uint256 extraRoundID = dispute.rounds.length - 1;

(uint96 newCourtID, uint256 newDisputeKitID, bool courtJump, ) = _getCourtAndDisputeKitJumps(
(uint96 newCourtID, uint256 newDisputeKitID, , bool courtJump, ) = _getCourtAndDisputeKitJumps(
dispute,
round,
courts[dispute.courtID],
Expand Down Expand Up @@ -1086,11 +1086,11 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
Round storage round = dispute.rounds[dispute.rounds.length - 1];
Court storage court = courts[dispute.courtID];

(, uint256 newDisputeKitID, bool courtJump, ) = _getCourtAndDisputeKitJumps(dispute, round, court, _disputeID);

uint256 nbVotesAfterAppeal = disputeKits[newDisputeKitID].getNbVotesAfterAppeal(
disputeKits[round.disputeKitID],
round.nbVotes
(, , uint256 nbVotesAfterAppeal, bool courtJump, ) = _getCourtAndDisputeKitJumps(
dispute,
round,
court,
_disputeID
);

if (courtJump) {
Expand Down Expand Up @@ -1180,20 +1180,36 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
return dispute.rounds[dispute.rounds.length - 1].nbVotes;
}

/// @notice Returns true if the dispute kit will be switched to a parent DK.
/// @param _disputeID The ID of the dispute.
/// @return Whether DK will be switched or not.
function isDisputeKitJumping(uint256 _disputeID) external view returns (bool) {
/// @notice Checks whether a dispute will jump to new court/DK and enforces a compatibility check.
/// @param _disputeID Dispute ID.
/// @return newCourtID Court ID after jump.
/// @return newDisputeKitID Dispute kit ID after jump.
/// @return newRoundNbVotes The number of votes in the new round.
/// @return courtJump Whether the dispute jumps to a new court or not.
/// @return disputeKitJump Whether the dispute jumps to a new dispute kit or not.
function getCourtAndDisputeKitJumps(
uint256 _disputeID
)
external
view
returns (
uint96 newCourtID,
uint256 newDisputeKitID,
uint256 newRoundNbVotes,
bool courtJump,
bool disputeKitJump
)
{
Dispute storage dispute = disputes[_disputeID];
Round storage round = dispute.rounds[dispute.rounds.length - 1];
Court storage court = courts[dispute.courtID];

if (!_isCourtJumping(round, court, _disputeID)) {
return false;
}

// Jump if the parent court doesn't support the current DK.
return !courts[court.parent].supportedDisputeKits[round.disputeKitID];
(newCourtID, newDisputeKitID, newRoundNbVotes, courtJump, disputeKitJump) = _getCourtAndDisputeKitJumps(
dispute,
round,
court,
_disputeID
);
}

/// @notice Returns the length of disputeKits array.
Expand All @@ -1214,51 +1230,47 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
// * Internal * //
// ************************************* //

/// @notice Returns true if the round is jumping to a parent court.
/// @param _round The round to check.
/// @param _court The court to check.
/// @return Whether the round is jumping to a parent court or not.
function _isCourtJumping(
Round storage _round,
Court storage _court,
uint256 _disputeID
) internal view returns (bool) {
return
disputeKits[_round.disputeKitID].earlyCourtJump(_disputeID) || _round.nbVotes >= _court.jurorsForCourtJump;
}

/// @notice Checks whether a dispute will jump to new court/DK, and returns new court and DK.
/// @notice Checks whether a dispute will jump to new court/DK and enforces a compatibility check.
/// @param _dispute Dispute data.
/// @param _round Round ID.
/// @param _court Current court ID.
/// @param _disputeID Dispute ID.
/// @return newCourtID Court ID after jump.
/// @return newDisputeKitID Dispute kit ID after jump.
/// @return newRoundNbVotes The number of votes in the new round.
/// @return courtJump Whether the dispute jumps to a new court or not.
/// @return disputeKitJump Whether the dispute jumps to a new dispute kit or not.
function _getCourtAndDisputeKitJumps(
Dispute storage _dispute,
Round storage _round,
Court storage _court,
uint256 _disputeID
) internal view returns (uint96 newCourtID, uint256 newDisputeKitID, bool courtJump, bool disputeKitJump) {
newCourtID = _dispute.courtID;
newDisputeKitID = _round.disputeKitID;

if (!_isCourtJumping(_round, _court, _disputeID)) return (newCourtID, newDisputeKitID, false, false);

// Jump to parent court.
newCourtID = courts[newCourtID].parent;
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 (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;
)
internal
view
returns (
uint96 newCourtID,
uint256 newDisputeKitID,
uint256 newRoundNbVotes,
bool courtJump,
bool disputeKitJump
)
{
uint256 disputeKitID = _round.disputeKitID;
(newCourtID, newDisputeKitID, newRoundNbVotes, courtJump, disputeKitJump) = disputeKits[disputeKitID]
.getCourtAndDisputeKitJumps(
_disputeID,
_dispute.courtID,
_court.parent,
_court.jurorsForCourtJump,
_round.nbVotes
);

// Ensure compatibility between the next round's court and dispute kit.
if (!courts[newCourtID].supportedDisputeKits[newDisputeKitID] || newDisputeKitID == NULL_DISPUTE_KIT) {
// Fall back to `DisputeKitClassic` which is always supported.
newDisputeKitID = DISPUTE_KIT_CLASSIC;
disputeKitJump = (newDisputeKitID != disputeKitID);
}
}

Expand Down
10 changes: 2 additions & 8 deletions contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,8 @@ contract DisputeKitClassic is DisputeKitClassicBase {
/// @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 initialize(
address _owner,
KlerosCore _core,
address _wNative,
uint256 _jumpDisputeKitID
) external initializer {
__DisputeKitClassicBase_initialize(_owner, _core, _wNative, _jumpDisputeKitID);
function initialize(address _owner, KlerosCore _core, address _wNative) external initializer {
__DisputeKitClassicBase_initialize(_owner, _core, _wNative);
}

// ************************ //
Expand Down
86 changes: 60 additions & 26 deletions contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
bool currentRound; // True if the dispute's current round is active on this Dispute Kit. False if the dispute has jumped to another Dispute Kit.
}

struct NextRoundSettings {
uint256 nbVotes; // The number of votes in the next round.
uint256 jumpDisputeKitID; // The ID of the dispute kit in Kleros Core disputeKits array that the dispute should jump to.
uint96 jumpCourtID; // The ID of the court in Kleros Core courts array that the dispute should jump to.
bool earlyDisputeKitJump; // True if the dispute should jump to a different dispute kit before jumping to an incompatible court, false otherwise.
bool earlyCourtJump; // True if the court should jump to a different court before exceeding the current `court.jurorsForCourtJump` threshold, false otherwise.
bool enabled; // True if the settings are enabled, false otherwise.
}

// ************************************* //
// * Storage * //
// ************************************* //
Expand All @@ -73,7 +82,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
bool public singleDrawPerJuror; // Whether each juror can only draw once per dispute, false by default.
mapping(uint256 coreDisputeID => Active) public coreDisputeIDToActive; // Active status of the dispute and the current round.
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.
mapping(uint96 currentCourtID => NextRoundSettings) public courtIDToNextRoundSettings; // The settings for the next round.

uint256[50] private __gap; // Reserved slots for future upgrades.

Expand Down Expand Up @@ -149,17 +158,14 @@ 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,
uint256 _jumpDisputeKitID
address _wNative
) internal onlyInitializing {
owner = _owner;
core = _core;
wNative = _wNative;
jumpDisputeKitID = _jumpDisputeKitID;
}

// ************************ //
Expand Down Expand Up @@ -187,10 +193,14 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
core = KlerosCore(_core);
}

/// @notice Changes the dispute kit ID used for the jump.
/// @param _jumpDisputeKitID The new value for the `jumpDisputeKitID` storage variable.
function changeJumpDisputeKitID(uint256 _jumpDisputeKitID) external onlyByOwner {
jumpDisputeKitID = _jumpDisputeKitID;
/// @notice Changes the settings for the next round.
/// @param _currentCourtID The ID of the current court.
/// @param _nextRoundSettings The settings for the next round.
function changeNextRoundSettings(
uint96 _currentCourtID,
NextRoundSettings memory _nextRoundSettings
) external onlyByOwner {
courtIDToNextRoundSettings[_currentCourtID] = _nextRoundSettings;
}

// ************************************* //
Expand Down Expand Up @@ -420,7 +430,8 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
// At least two sides are fully funded.
round.feeRewards = round.feeRewards - appealCost;

if (core.isDisputeKitJumping(_coreDisputeID)) {
(, , , , bool isDisputeKitJumping) = core.getCourtAndDisputeKitJumps(_coreDisputeID);
if (isDisputeKitJumping) {
// Don't create a new round in case of a jump, and remove local dispute from the flow.
coreDisputeIDToActive[_coreDisputeID].currentRound = false;
} else {
Expand Down Expand Up @@ -617,22 +628,45 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
}

/// @inheritdoc IDisputeKit
function earlyCourtJump(uint256 /* _coreDisputeID */) external pure override returns (bool) {
return false;
}

/// @inheritdoc IDisputeKit
function getNbVotesAfterAppeal(
IDisputeKit /* _previousDisputeKit */,
uint256 _currentNbVotes
) external pure override returns (uint256) {
return (_currentNbVotes * 2) + 1;
}

/// @inheritdoc IDisputeKit
function getJumpDisputeKitID() external view override returns (uint256) {
// Fall back to classic DK in case the jump ID is not defined.
return jumpDisputeKitID == 0 ? DISPUTE_KIT_CLASSIC : jumpDisputeKitID;
function getCourtAndDisputeKitJumps(
uint256 /* _coreDisputeID */,
uint96 _currentCourtID,
uint96 _parentCourtID,
uint256 _currentCourtJurorsForJump,
uint256 _currentRoundNbVotes
)
public
view
virtual
override
returns (
uint96 newCourtID,
uint256 newDisputeKitID,
uint256 newRoundNbVotes,
bool courtJump,
bool disputeKitJump
)
{
NextRoundSettings storage nextRoundSettings = courtIDToNextRoundSettings[_currentCourtID];
if (nextRoundSettings.enabled) {
newRoundNbVotes = nextRoundSettings.nbVotes;
newCourtID = nextRoundSettings.jumpCourtID;
newDisputeKitID = nextRoundSettings.jumpDisputeKitID;
courtJump = nextRoundSettings.earlyCourtJump;
disputeKitJump = nextRoundSettings.earlyDisputeKitJump;
}
if (nextRoundSettings.nbVotes == 0) {
newRoundNbVotes = (_currentRoundNbVotes * 2) + 1;
}
if (!courtJump) {
courtJump = (newRoundNbVotes >= _currentCourtJurorsForJump);
}
if (newCourtID == 0) {
newCourtID = courtJump ? _parentCourtID : _currentCourtID;
}
if (newDisputeKitID == 0) {
newDisputeKitID = DISPUTE_KIT_CLASSIC;
}
}

/// @inheritdoc IDisputeKit
Expand Down
Loading
Loading