Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion contracts/src/arbitration/KlerosCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,11 @@ contract KlerosCore is IArbitratorV2, Initializable, UUPSProxiable {
uint256 startIndex = round.drawIterations; // for gas: less storage reads
uint256 i;
while (i < _iterations && round.drawnJurors.length < round.nbVotes) {
(address drawnAddress, uint96 fromSubcourtID) = disputeKit.draw(_disputeID, startIndex + i++);
(address drawnAddress, uint96 fromSubcourtID) = disputeKit.draw(
_disputeID,
startIndex + i++,
round.nbVotes
);
if (drawnAddress == address(0)) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,16 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
/// @inheritdoc IDisputeKit
function draw(
uint256 _coreDisputeID,
uint256 _nonce
) external override onlyByCore isActive(_coreDisputeID) returns (address drawnAddress, uint96 fromSubcourtID) {
uint256 _nonce,
uint256 _roundNbVotes
)
public
virtual
override
onlyByCore
isActive(_coreDisputeID)
returns (address drawnAddress, uint96 fromSubcourtID)
{
uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
Dispute storage dispute = disputes[localDisputeID];
uint256 localRoundID = dispute.rounds.length - 1;
Expand All @@ -266,7 +274,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
return (drawnAddress, fromSubcourtID);
}

if (_postDrawCheck(round, _coreDisputeID, drawnAddress)) {
if (_postDrawCheck(round, _coreDisputeID, drawnAddress, _roundNbVotes)) {
Vote storage vote = round.votes.push();
vote.account = drawnAddress;
round.alreadyDrawn[drawnAddress] = true;
Expand Down Expand Up @@ -778,19 +786,20 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
///
/// @param _coreDisputeID ID of the dispute in the core contract.
/// @param _juror Chosen address.
/// @return result Whether the address passes the check or not.
/// @return Whether the address passes the check or not.
function _postDrawCheck(
Round storage /*_round*/,
uint256 _coreDisputeID,
address _juror
) internal view virtual returns (bool result) {
address _juror,
uint256 /*_roundNbVotes*/
) internal view virtual returns (bool) {
if (singleDrawPerJuror) {
uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
Dispute storage dispute = disputes[localDisputeID];
Round storage round = dispute.rounds[dispute.rounds.length - 1];
result = !round.alreadyDrawn[_juror];
return !round.alreadyDrawn[_juror];
} else {
result = true;
return true;
}
}

Expand Down
5 changes: 3 additions & 2 deletions contracts/src/arbitration/dispute-kits/DisputeKitGated.sol
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,10 @@ contract DisputeKitGated is DisputeKitClassicBase {
function _postDrawCheck(
Round storage _round,
uint256 _coreDisputeID,
address _juror
address _juror,
uint256 _roundNbVotes
) internal view override returns (bool) {
if (!super._postDrawCheck(_round, _coreDisputeID, _juror)) return false;
if (!super._postDrawCheck(_round, _coreDisputeID, _juror, _roundNbVotes)) return false;

// Get the local dispute and extract token info from extraData
uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

import {DisputeKitClassicBase} from "./DisputeKitClassicBase.sol";
import {KlerosCore} from "../KlerosCore.sol";

interface IBalanceHolder {
/// @notice Returns the number of tokens in `owner` account.
/// @dev Compatible with ERC-20 and ERC-721.
/// @param owner The address of the owner.
/// @return balance The number of tokens in `owner` account.
function balanceOf(address owner) external view returns (uint256 balance);
}

/// @title DisputeKitGatedArgentinaConsumerProtection
/// @notice Dispute kit implementation adapted from DisputeKitClassic
/// - a drawing system: proportional to staked PNK among the jurors holding a `accreditedProfessionalToken` or a `accreditedConsumerProtectionLawyerToken`
/// and at least one of the drawn jurors is holding a `accreditedConsumerProtectionLawyerToken`,
/// - a vote aggregation system: plurality,
/// - an incentive system: equal split between coherent votes,
/// - an appeal system: fund 2 choices only, vote on any choice.
contract DisputeKitGatedArgentinaConsumerProtection is DisputeKitClassicBase {
string public constant override version = "2.0.0";

// ************************************* //
// * Storage * //
// ************************************* //

address public accreditedProfessionalToken; // The address of the accredited professional token.
address public accreditedConsumerProtectionLawyerToken; // The address of the accredited consumer protection lawyer token.
mapping(uint256 localDisputeID => mapping(uint256 localRoundID => bool)) public drawnConsumerProtectionLawyer; // Maps the local dispute and round ID to the boolean indicating if the consumer protection lawyer was drawn.

// ************************************* //
// * Constructor * //
// ************************************* //

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

/// @notice Initializer.
/// @param _owner The owner's address.
/// @param _core The KlerosCore arbitrator.
/// @param _wNative The wrapped native token address, typically wETH.
/// @param _accreditedProfessionalToken The address of the accredited professional token.
/// @param _accreditedConsumerProtectionLawyerToken The address of the accredited consumer protection lawyer token.
function initialize(
address _owner,
KlerosCore _core,
address _wNative,
address _accreditedProfessionalToken,
address _accreditedConsumerProtectionLawyerToken
) external initializer {
__DisputeKitClassicBase_initialize(_owner, _core, _wNative);
accreditedProfessionalToken = _accreditedProfessionalToken;
accreditedConsumerProtectionLawyerToken = _accreditedConsumerProtectionLawyerToken;
}

// ************************ //
// * Governance * //
// ************************ //

/// @dev Access Control to perform implementation upgrades (UUPS Proxiable)
/// Only the owner can perform upgrades (`onlyByOwner`)
function _authorizeUpgrade(address) internal view override onlyByOwner {
// NOP
}

/// @notice Changes the accredited professional token.
/// @param _accreditedProfessionalToken The address of the accredited lawyer token.
function changeAccreditedProfessionalToken(address _accreditedProfessionalToken) external onlyByOwner {
accreditedProfessionalToken = _accreditedProfessionalToken;
}

/// @notice Changes the accredited consumer protection lawyer token.
/// @param _accreditedConsumerProtectionLawyerToken The address of the accredited consumer protection lawyer token.
function changeAccreditedConsumerProtectionLawyerToken(
address _accreditedConsumerProtectionLawyerToken
) external onlyByOwner {
accreditedConsumerProtectionLawyerToken = _accreditedConsumerProtectionLawyerToken;
}

// ************************************* //
// * State Modifiers * //
// ************************************* //

/// @inheritdoc DisputeKitClassicBase
function draw(
uint256 _coreDisputeID,
uint256 _nonce,
uint256 _roundNbVotes
) public override onlyByCore isActive(_coreDisputeID) returns (address drawnAddress, uint96 fromSubcourtID) {
(drawnAddress, fromSubcourtID) = super.draw(_coreDisputeID, _nonce, _roundNbVotes);

if (drawnAddress == address(0)) return (drawnAddress, fromSubcourtID);

if (IBalanceHolder(accreditedConsumerProtectionLawyerToken).balanceOf(drawnAddress) > 0) {
// The drawnAddress is a consumer protection lawyer.
uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
uint256 localRoundID = disputes[localDisputeID].rounds.length - 1;
drawnConsumerProtectionLawyer[localDisputeID][localRoundID] = true;
}
return (drawnAddress, fromSubcourtID);
}

// ************************************* //
// * Internal * //
// ************************************* //

/// @inheritdoc DisputeKitClassicBase
function _postDrawCheck(
Round storage _round,
uint256 _coreDisputeID,
address _juror,
uint256 _roundNbVotes
) internal view override returns (bool) {
if (IBalanceHolder(accreditedConsumerProtectionLawyerToken).balanceOf(_juror) == 0) {
// The juror is not a consumer protection lawyer.
uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
Dispute storage dispute = disputes[localDisputeID];
uint256 localRoundID = dispute.rounds.length - 1;
if (
dispute.rounds[localRoundID].votes.length == _roundNbVotes - 1 &&
!drawnConsumerProtectionLawyer[localDisputeID][localRoundID]
) {
// This is the last draw iteration and we still have not drawn a consumer protection lawyer.
// Reject this draw so that another iteration can try again later.
return false;
}
if (IBalanceHolder(accreditedProfessionalToken).balanceOf(_juror) == 0) {
// The juror does not hold either of the tokens.
return false;
}
}
return super._postDrawCheck(_round, _coreDisputeID, _juror, _roundNbVotes);
}

// ************************************* //
// * Errors * //
// ************************************* //

error TokenNotSupported(address tokenGate);
}
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,10 @@ contract DisputeKitGatedShutter is DisputeKitClassicBase {
function _postDrawCheck(
Round storage _round,
uint256 _coreDisputeID,
address _juror
address _juror,
uint256 _roundNbVotes
) internal view override returns (bool) {
if (!super._postDrawCheck(_round, _coreDisputeID, _juror)) return false;
if (!super._postDrawCheck(_round, _coreDisputeID, _juror, _roundNbVotes)) return false;

// Get the local dispute and extract token info from extraData
uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ contract DisputeKitSybilResistant is DisputeKitClassicBase {
function _postDrawCheck(
Round storage _round,
uint256 _coreDisputeID,
address _juror
address _juror,
uint256 _roundNbVotes
) internal view override returns (bool) {
return super._postDrawCheck(_round, _coreDisputeID, _juror) && poh.isRegistered(_juror);
return super._postDrawCheck(_round, _coreDisputeID, _juror, _roundNbVotes) && poh.isRegistered(_juror);
}
}
4 changes: 3 additions & 1 deletion contracts/src/arbitration/interfaces/IDisputeKit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@ interface IDisputeKit {
/// @dev Access restricted to Kleros Core only.
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
/// @param _nonce Nonce.
/// @param _roundNbVotes The number of votes in the round, including already drawn and yet to be drawn.
/// @return drawnAddress The drawn address.
/// @return fromSubcourtID The subcourt ID from which the juror was drawn.
function draw(
uint256 _coreDisputeID,
uint256 _nonce
uint256 _nonce,
uint256 _roundNbVotes
) external returns (address drawnAddress, uint96 fromSubcourtID);

// ************************************* //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
{
IDisputeKit disputeKit = disputeKits[round.disputeKitID];
uint256 iteration = round.drawIterations + 1;
(address drawnAddress, uint96 fromSubcourtID) = disputeKit.draw(_disputeID, iteration);
(address drawnAddress, uint96 fromSubcourtID) = disputeKit.draw(_disputeID, iteration, round.nbVotes);
if (drawnAddress == address(0)) {
revert NoJurorDrawn();
}
Expand Down
Loading
Loading