Skip to content

Instantly share code, notes, and snippets.

@unknownunknown1
Last active March 2, 2025 09:15
Show Gist options
  • Save unknownunknown1/77de147e4096486e90dbe7fdb0196dea to your computer and use it in GitHub Desktop.
Save unknownunknown1/77de147e4096486e90dbe7fdb0196dea to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
interface IArbitrableV2 {
/// @dev To be emitted when a dispute is created to link the correct meta-evidence to the disputeID.
/// @param _arbitrator The arbitrator of the contract.
/// @param _arbitratorDisputeID The identifier of the dispute in the Arbitrator contract.
/// @param _externalDisputeID An identifier created outside Kleros by the protocol requesting arbitration.
/// @param _templateId The identifier of the dispute template. Should not be used with _templateUri.
/// @param _templateUri The URI to the dispute template. For example on IPFS: starting with '/ipfs/'. Should not be used with _templateId.
event DisputeRequest(
IArbitratorV2 indexed _arbitrator,
uint256 indexed _arbitratorDisputeID,
uint256 _externalDisputeID,
uint256 _templateId,
string _templateUri
);
/// @dev To be raised when a ruling is given.
/// @param _arbitrator The arbitrator giving the ruling.
/// @param _disputeID The identifier of the dispute in the Arbitrator contract.
/// @param _ruling The ruling which was given.
event Ruling(IArbitratorV2 indexed _arbitrator, uint256 indexed _disputeID, uint256 _ruling);
/// @dev Give a ruling for a dispute.
/// Must be called by the arbitrator.
/// The purpose of this function is to ensure that the address calling it has the right to rule on the contract.
/// @param _disputeID The identifier of the dispute in the Arbitrator contract.
/// @param _ruling Ruling given by the arbitrator.
/// Note that 0 is reserved for "Not able/wanting to make a decision".
function rule(uint256 _disputeID, uint256 _ruling) external;
}
interface IArbitratorV2 {
/// @dev To be emitted when a dispute is created.
/// @param _disputeID The identifier of the dispute in the Arbitrator contract.
/// @param _arbitrable The contract which created the dispute.
event DisputeCreation(uint256 indexed _disputeID, IArbitrableV2 indexed _arbitrable);
/// @dev To be raised when a ruling is given.
/// @param _arbitrable The arbitrable receiving the ruling.
/// @param _disputeID The identifier of the dispute in the Arbitrator contract.
/// @param _ruling The ruling which was given.
event Ruling(IArbitrableV2 indexed _arbitrable, uint256 indexed _disputeID, uint256 _ruling);
/// @dev To be emitted when an ERC20 token is added or removed as a method to pay fees.
/// @param _token The ERC20 token.
/// @param _accepted Whether the token is accepted or not.
event AcceptedFeeToken(IERC20 indexed _token, bool indexed _accepted);
/// @dev To be emitted when the fee for a particular ERC20 token is updated.
/// @param _feeToken The ERC20 token.
/// @param _rateInEth The new rate of the fee token in ETH.
/// @param _rateDecimals The new decimals of the fee token rate.
event NewCurrencyRate(IERC20 indexed _feeToken, uint64 _rateInEth, uint8 _rateDecimals);
/// @dev Create a dispute and pay for the fees in the native currency, typically ETH.
/// Must be called by the arbitrable contract.
/// Must pay at least arbitrationCost(_extraData).
/// @param _numberOfChoices The number of choices the arbitrator can choose from in this dispute.
/// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).
/// @return disputeID The identifier of the dispute created.
function createDispute(
uint256 _numberOfChoices,
bytes calldata _extraData
) external payable returns (uint256 disputeID);
/// @dev Create a dispute and pay for the fees in a supported ERC20 token.
/// Must be called by the arbitrable contract.
/// Must pay at least arbitrationCost(_extraData).
/// @param _numberOfChoices The number of choices the arbitrator can choose from in this dispute.
/// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).
/// @param _feeToken The ERC20 token used to pay fees.
/// @param _feeAmount Amount of the ERC20 token used to pay fees.
/// @return disputeID The identifier of the dispute created.
function createDispute(
uint256 _numberOfChoices,
bytes calldata _extraData,
IERC20 _feeToken,
uint256 _feeAmount
) external returns (uint256 disputeID);
/// @dev Compute the cost of arbitration denominated in the native currency, typically ETH.
/// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation.
/// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).
/// @return cost The arbitration cost in ETH.
function arbitrationCost(bytes calldata _extraData) external view returns (uint256 cost);
/// @dev Compute the cost of arbitration denominated in `_feeToken`.
/// It is recommended not to increase it often, as it can be highly time and gas consuming for the arbitrated contracts to cope with fee augmentation.
/// @param _extraData Additional info about the dispute. We use it to pass the ID of the dispute's court (first 32 bytes), the minimum number of jurors required (next 32 bytes) and the ID of the specific dispute kit (last 32 bytes).
/// @param _feeToken The ERC20 token used to pay fees.
/// @return cost The arbitration cost in `_feeToken`.
function arbitrationCost(bytes calldata _extraData, IERC20 _feeToken) external view returns (uint256 cost);
/// @dev Gets the current ruling of a specified dispute.
/// @param _disputeID The ID of the dispute.
/// @return ruling The current ruling.
/// @return tied Whether it's a tie or not.
/// @return overridden Whether the ruling was overridden by appeal funding or not.
function currentRuling(uint256 _disputeID) external view returns (uint256 ruling, bool tied, bool overridden);
}
interface IDisputeTemplateRegistry {
/// @dev To be emitted when a new dispute template is created.
/// @param _templateId The identifier of the dispute template.
/// @param _templateTag An optional tag for the dispute template, such as "registration" or "removal".
/// @param _templateData The template data.
/// @param _templateDataMappings The data mappings.
event DisputeTemplate(
uint256 indexed _templateId,
string indexed _templateTag,
string _templateData,
string _templateDataMappings
);
function setDisputeTemplate(
string memory _templateTag,
string memory _templateData,
string memory _templateDataMappings
) external returns (uint256 templateId);
}
/// @title DisputeResolver
/// DisputeResolver contract adapted for V2 from https://github.com/kleros/arbitrable-proxy-contracts/blob/master/contracts/ArbitrableProxy.sol.
contract DisputeResolverBugged is IArbitrableV2 {
// ************************************* //
// * Enums / Structs * //
// ************************************* //
struct DisputeStruct {
bytes arbitratorExtraData; // Extra data for the dispute.
bool isRuled; // True if the dispute has been ruled.
uint256 ruling; // Ruling given to the dispute.
uint256 numberOfRulingOptions; // The number of choices the arbitrator can give.
}
// ************************************* //
// * Storage * //
// ************************************* //
address public governor; // The governor.
IArbitratorV2 public arbitrator; // The arbitrator.
IDisputeTemplateRegistry public templateRegistry; // The dispute template registry.
DisputeStruct[] public disputes; // Local disputes.
mapping(uint256 => uint256) public arbitratorDisputeIDToLocalID; // Maps arbitrator-side dispute IDs to local dispute IDs.
// ************************************* //
// * Constructor * //
// ************************************* //
/// @dev Constructor
/// @param _arbitrator Target global arbitrator for any disputes.
constructor(IArbitratorV2 _arbitrator, IDisputeTemplateRegistry _templateRegistry) {
governor = msg.sender;
arbitrator = _arbitrator;
templateRegistry = _templateRegistry;
}
// ************************************* //
// * Governance * //
// ************************************* //
/// @dev Changes the governor.
/// @param _governor The address of the new governor.
function changeGovernor(address _governor) external {
// BUG: governor replaced with _governor
//require(governor == msg.sender, "Access not allowed: Governor only.");
require(_governor == msg.sender, "Access not allowed: Governor only.");
governor = _governor;
}
function changeArbitrator(IArbitratorV2 _arbitrator) external {
require(governor == msg.sender, "Access not allowed: Governor only.");
arbitrator = _arbitrator;
}
function changeTemplateRegistry(IDisputeTemplateRegistry _templateRegistry) external {
require(governor == msg.sender, "Access not allowed: Governor only.");
templateRegistry = _templateRegistry;
}
// ************************************* //
// * State Modifiers * //
// ************************************* //
/// @dev Calls createDispute function of the specified arbitrator to create a dispute.
/// Note that we don’t need to check that msg.value is enough to pay arbitration fees as it’s the responsibility of the arbitrator contract.
/// @param _arbitratorExtraData Extra data for the arbitrator of the dispute.
/// @param _disputeTemplate Dispute template.
/// @param _disputeTemplateDataMappings The data mappings.
/// @param _numberOfRulingOptions Number of ruling options.
/// @return disputeID Dispute id (on arbitrator side) of the created dispute.
function createDisputeForTemplate(
bytes calldata _arbitratorExtraData,
string calldata _disputeTemplate,
string memory _disputeTemplateDataMappings,
uint256 _numberOfRulingOptions
) external payable returns (uint256 disputeID) {
return
_createDispute(
_arbitratorExtraData,
_disputeTemplate,
_disputeTemplateDataMappings,
"",
_numberOfRulingOptions
);
}
/// @dev Calls createDispute function of the specified arbitrator to create a dispute.
/// Note that we don’t need to check that msg.value is enough to pay arbitration fees as it’s the responsibility of the arbitrator contract.
/// @param _arbitratorExtraData Extra data for the arbitrator of the dispute.
/// @param _disputeTemplateUri The URI to the dispute template. For example on IPFS: starting with '/ipfs/'.
/// @param _numberOfRulingOptions Number of ruling options.
/// @return disputeID Dispute id (on arbitrator side) of the created dispute.
function createDisputeForTemplateUri(
bytes calldata _arbitratorExtraData,
string calldata _disputeTemplateUri,
uint256 _numberOfRulingOptions
) external payable returns (uint256 disputeID) {
return _createDispute(_arbitratorExtraData, "", "", _disputeTemplateUri, _numberOfRulingOptions);
}
/// @dev To be called by the arbitrator of the dispute, to declare the winning ruling.
/// @param _arbitratorDisputeID ID of the dispute in arbitrator contract.
/// @param _ruling The ruling choice of the arbitration.
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.");
dispute.isRuled = true;
dispute.ruling = _ruling;
emit Ruling(IArbitratorV2(msg.sender), _arbitratorDisputeID, dispute.ruling);
}
// ************************************* //
// * Internal * //
// ************************************* //
function _createDispute(
bytes calldata _arbitratorExtraData,
string memory _disputeTemplate,
string memory _disputeTemplateDataMappings,
string memory _disputeTemplateUri,
uint256 _numberOfRulingOptions
) internal virtual returns (uint256 arbitratorDisputeID) {
require(_numberOfRulingOptions > 1, "Should be at least 2 ruling options.");
arbitratorDisputeID = arbitrator.createDispute{value: msg.value}(_numberOfRulingOptions, _arbitratorExtraData);
uint256 localDisputeID = disputes.length;
disputes.push(
DisputeStruct({
arbitratorExtraData: _arbitratorExtraData,
isRuled: false,
ruling: 0,
numberOfRulingOptions: _numberOfRulingOptions
})
);
arbitratorDisputeIDToLocalID[arbitratorDisputeID] = localDisputeID;
uint256 templateId = templateRegistry.setDisputeTemplate("", _disputeTemplate, _disputeTemplateDataMappings);
emit DisputeRequest(arbitrator, arbitratorDisputeID, localDisputeID, templateId, _disputeTemplateUri);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment