Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

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 Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// 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 `accreditedLawyerToken` 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 accreditedLawyerToken; // The address of the accredited lawyer 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 _accreditedLawyerToken The address of the accredited lawyer token.
/// @param _accreditedConsumerProtectionLawyerToken The address of the accredited consumer protection lawyer token.
function initialize(
address _owner,
KlerosCore _core,
address _wNative,
address _accreditedLawyerToken,
address _accreditedConsumerProtectionLawyerToken
) external initializer {
__DisputeKitClassicBase_initialize(_owner, _core, _wNative);
accreditedLawyerToken = _accreditedLawyerToken;
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 lawyer token.
/// @param _accreditedLawyerToken The address of the accredited lawyer token.
function changeAccreditedLawyerToken(address _accreditedLawyerToken) external onlyByOwner {
accreditedLawyerToken = _accreditedLawyerToken;
}

/// @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);

uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
Dispute storage dispute = disputes[localDisputeID];
uint256 localRoundID = dispute.rounds.length - 1;
if (IBalanceHolder(accreditedConsumerProtectionLawyerToken).balanceOf(drawnAddress) > 0) {
// The drawnAddress is a consumer protection lawyer.
drawnConsumerProtectionLawyer[localDisputeID][localRoundID] = true;
} else {
// The drawnAddress is not a consumer protection lawyer.
if (
dispute.rounds[localRoundID].votes.length == _roundNbVotes &&
!drawnConsumerProtectionLawyer[localDisputeID][localRoundID]
) {
// This is the last draw iteration and we still have not drawn a consumer protection lawyer.
// Drop the last round.votes pushed by super.draw(), so that another iteration can try again.
drawnAddress = address(0);
dispute.rounds[localRoundID].votes.pop();
// Note that round.alreadyDrawn[drawnAddress] is not cleared because we don't know if it has been drawn more than once.
// It's fine because this DisputeKit does not enable singleDrawPerJuror.
}
}
return (drawnAddress, fromSubcourtID);
}

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

/// @inheritdoc DisputeKitClassicBase
function _postDrawCheck(
Round storage _round,
uint256 _coreDisputeID,
address _juror
) internal view override returns (bool) {
if (!super._postDrawCheck(_round, _coreDisputeID, _juror)) return false;
return
(IBalanceHolder(accreditedLawyerToken).balanceOf(_juror) > 0) ||
(IBalanceHolder(accreditedConsumerProtectionLawyerToken).balanceOf(_juror) > 0);
}

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

error TokenNotSupported(address tokenGate);
}
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