Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(RealitioProxy): update contract for V2 #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
275 changes: 180 additions & 95 deletions contracts/src/RealityV2.sol
Original file line number Diff line number Diff line change
@@ -1,142 +1,227 @@
// SPDX-License-Identifier: MIT

/// @authors: []
/// @reviewers: []
/// @auditors: []
/// @bounties: []
/**
* @authors: [@unknownunknown1]
* @reviewers: []
* @auditors: []
* @bounties: []
* @deployments: []
*/

pragma solidity 0.8.18;

import {IArbitrableV2, IArbitratorV2} from "@kleros/kleros-v2-contracts/arbitration/interfaces/IArbitrableV2.sol";
import {EvidenceModule} from "@kleros/kleros-v2-contracts/arbitration/evidence/EvidenceModule.sol";
import "@kleros/kleros-v2-contracts/arbitration/interfaces/IDisputeTemplateRegistry.sol";
import "./interfaces/IRealitio.sol";
import "./interfaces/IRealitioArbitrator.sol";

/// @title TODO
/// @dev TODO
contract RealityV2 is IArbitrableV2 {
// ************************************* //
// * Enums / Structs * //
// ************************************* //
/// @title RealitioProxyV2
/// @dev Realitio proxy contract compatible with V2.
contract RealitioProxyV2 is IRealitioArbitrator, IArbitrableV2 {
uint256 public constant REFUSE_TO_ARBITRATE_REALITIO = type(uint256).max; // Constant that represents "Refuse to rule" in realitio format.

struct Dispute {
bool isRuled; // Whether the dispute has been ruled or not.
uint256 ruling; // Ruling given by the arbitrator.
}

// ************************************* //
// * Storage * //
// ************************************* //
IRealitio public immutable override realitio; // Actual implementation of Realitio.
string public override metadata; // Metadata for Realitio. See IRealitioArbitrator.

address public immutable governor;
IArbitratorV2 public arbitrator; // Address of the arbitrator contract.
bytes public arbitratorExtraData; // Extra data to set up the arbitration.
address public governor; // The address that can make changes to the parameters of the contract.
IDisputeTemplateRegistry public templateRegistry; // The dispute template registry.
uint256 public templateId; // The current dispute template identifier.
mapping(uint256 => uint256) public externalIDtoLocalID; // Maps external (arbitrator side) dispute IDs to local dispute IDs.
Dispute[] public disputes; // Stores the disputes' info. disputes[disputeID].
uint256 public templateId; // Dispute template identifier.

// ************************************* //
// * Events * //
// ************************************* //
uint256 private constant NUMBER_OF_RULING_OPTIONS = type(uint256).max; // The amount of non 0 choices the arbitrator can give.

enum Status {
None, // The question hasn't been requested arbitration yet.
Disputed, // The question has been requested arbitration.
Ruled, // The question has been ruled by arbitrator.
Reported // The answer of the question has been reported to Realitio.
}

// TODO
// To track internal dispute state in this contract.
struct ArbitrationRequest {
Status status; // The current status of the question.
address requester; // The address that requested the arbitration.
uint256 disputeID; // The ID of the dispute raised in the arbitrator contract.
uint256 ruling; // The ruling given by the arbitrator.
uint256 arbitrationParamsIndex; // The index for the arbitration params for the request.
// TODO: should template registry be a part of the struct in case it's changed?
IDisputeTemplateRegistry templateRegistry; // Address of template registry used for the request.
}

struct ArbitrationParams {
IArbitratorV2 arbitrator; // The arbitrator trusted to solve disputes for this request.
bytes arbitratorExtraData; // The extra data for the trusted arbitrator of this request.
EvidenceModule evidenceModule; // The evidence module for the arbitrator.
}

// ************************************* //
// * Function Modifiers * //
// ************************************* //
mapping(uint256 => ArbitrationRequest) public arbitrationRequests; // Maps a question identifier in uint256 to its arbitration details. Example: arbitrationRequests[uint256(questionID)]
mapping(address => mapping(uint256 => uint256)) public arbitratorDisputeIDToQuestionID; // Maps a dispute ID to the ID of the question (converted into uint) with the disputed request in the form arbitratorDisputeIDToQuestionID[arbitrator][disputeID].
ArbitrationParams[] public arbitrationParamsChanges;

modifier onlyByGovernor() {
modifier onlyGovernor() {
if (governor != msg.sender) revert GovernorOnly();
_;
}

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

/// @dev Constructor.
/// @param _arbitrator The arbitrator of the contract.
/// @param _arbitratorExtraData Extra data for the arbitrator.
/// @dev Constructor
/// @param _governor The trusted governor of this contract.
/// @param _realitio The address of the Realitio contract.
/// @param _metadata The metadata required for RealitioArbitrator.
/// @param _arbitrator The address of the ERC792 arbitrator.
/// @param _arbitratorExtraData The extra data used to raise a dispute in the ERC792 arbitrator.
/// @param _evidenceModule The evidence contract for the arbitrator.
/// @param _templateRegistry The dispute template registry.
/// @param _templateData The dispute template data.
/// @param _templateDataMappings The dispute template data mappings.
/// @param _templateRegistry The dispute template registry.
constructor(
address _governor,
IRealitio _realitio,
string memory _metadata,
IArbitratorV2 _arbitrator,
bytes memory _arbitratorExtraData,
EvidenceModule _evidenceModule,
IDisputeTemplateRegistry _templateRegistry,
string memory _templateData,
string memory _templateDataMappings,
IDisputeTemplateRegistry _templateRegistry
string memory _templateDataMappings
) {
governor = msg.sender;
arbitrator = _arbitrator;
arbitratorExtraData = _arbitratorExtraData;
governor = _governor;
realitio = _realitio;
metadata = _metadata;
templateRegistry = _templateRegistry;

templateId = templateRegistry.setDisputeTemplate("", _templateData, _templateDataMappings);
arbitrationParamsChanges.push(
ArbitrationParams({
arbitrator: _arbitrator,
arbitratorExtraData: _arbitratorExtraData,
evidenceModule: _evidenceModule
})
);
}

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

function changeArbitrator(IArbitratorV2 _arbitrator) external onlyByGovernor {
arbitrator = _arbitrator;
/// @dev Changes the governor of the Reality proxy.
/// @param _governor The address of the new governor.
function changeGovernor(address _governor) external onlyGovernor {
governor = _governor;
}

function changeArbitratorExtraData(bytes calldata _arbitratorExtraData) external onlyByGovernor {
arbitratorExtraData = _arbitratorExtraData;
/// @notice Changes the params related to arbitration.
/// @param _arbitrator Arbitrator to resolve potential disputes. The arbitrator is trusted to support appeal periods and not reenter.
/// @param _arbitratorExtraData Extra data for the trusted arbitrator contract.
/// @param _evidenceModule The evidence module for the arbitrator.
function changeArbitrationParams(
IArbitratorV2 _arbitrator,
bytes calldata _arbitratorExtraData,
EvidenceModule _evidenceModule
) external onlyGovernor {
arbitrationParamsChanges.push(
ArbitrationParams({
arbitrator: _arbitrator,
arbitratorExtraData: _arbitratorExtraData,
evidenceModule: _evidenceModule
})
);
}

function changeTemplateRegistry(IDisputeTemplateRegistry _templateRegistry) external onlyByGovernor {
/// @dev Changes the address of Template Registry contract.
/// @param _templateRegistry The new template registry.
/// @param _templateData The new template data for template registry.
/// @param _templateDataMappings The new data mappings json.
function changeTemplateRegistry(
IDisputeTemplateRegistry _templateRegistry,
string memory _templateData,
string memory _templateDataMappings)
external onlyGovernor {
templateRegistry = _templateRegistry;
templateId = templateRegistry.setDisputeTemplate("", _templateData, _templateDataMappings);
}

function changeDisputeTemplate(
string memory _templateData,
string memory _templateDataMappings
) external onlyByGovernor {
/// @dev Changes the dispute template.
/// @param _templateData The new template data for requests.
/// @param _templateDataMappings The new data mappings json.
function changeDisputeTemplate(string memory _templateData, string memory _templateDataMappings) external onlyGovernor {
templateId = templateRegistry.setDisputeTemplate("", _templateData, _templateDataMappings);
}

// ************************************* //
// * 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.
/// @return disputeID Dispute id (on arbitrator side) of the dispute created.
function createDispute() external payable returns (uint256 disputeID) {
uint256 numberOfRulingOptions = 2;
uint256 localDisputeID = disputes.length;
disputes.push(Dispute({isRuled: false, ruling: 0}));

disputeID = arbitrator.createDispute{value: msg.value}(numberOfRulingOptions, arbitratorExtraData);
externalIDtoLocalID[disputeID] = localDisputeID;

uint256 externalDisputeID = uint256(keccak256(abi.encodePacked("Hello World!")));
emit DisputeRequest(arbitrator, disputeID, externalDisputeID, templateId, "");
/// @dev Request arbitration from Kleros for given _questionID.
/// @param _questionID The question identifier in Realitio contract.
/// @param _maxPrevious If specified, reverts if a bond higher than this was submitted after you sent your transaction.
/// @return disputeID ID of the resulting dispute in arbitrator.
function requestArbitration(bytes32 _questionID, uint256 _maxPrevious) external payable returns (uint256 disputeID) {
ArbitrationRequest storage arbitrationRequest = arbitrationRequests[uint256(_questionID)];
if(arbitrationRequest.status != Status.None) revert ArbitrationAlreadyRequested();
uint256 arbitrationParamsIndex = arbitrationParamsChanges.length - 1;
IArbitratorV2 arbitrator = arbitrationParamsChanges[arbitrationParamsIndex].arbitrator;
bytes storage arbitratorExtraData = arbitrationParamsChanges[arbitrationParamsIndex].arbitratorExtraData;

// Notify Kleros
disputeID = arbitrator.createDispute{value: msg.value}(NUMBER_OF_RULING_OPTIONS, arbitratorExtraData); /* If msg.value is greater than intended number of votes (specified in arbitratorExtraData),
Kleros will automatically spend excess for additional votes. */
arbitratorDisputeIDToQuestionID[address(arbitrator)][disputeID] = uint256(_questionID);

// Update internal state
arbitrationRequest.requester = msg.sender;
arbitrationRequest.status = Status.Disputed;
arbitrationRequest.disputeID = disputeID;
arbitrationRequest.arbitrationParamsIndex = arbitrationParamsIndex;
arbitrationRequest.templateRegistry = templateRegistry;

// Notify Realitio
realitio.notifyOfArbitrationRequest(_questionID, msg.sender, _maxPrevious);
emit DisputeRequest(arbitrator, disputeID, uint256(_questionID), templateId, "");
}

/// @dev To be called by the arbitrator of the dispute, to declare the winning ruling.
/// @param _externalDisputeID ID of the dispute in arbitrator contract.
/// @param _ruling The ruling choice of the arbitration.
function rule(uint256 _externalDisputeID, uint256 _ruling) external override {
if (msg.sender != address(arbitrator)) revert ArbitratorOnly();

uint256 localDisputeID = externalIDtoLocalID[_externalDisputeID];
Dispute storage dispute = disputes[localDisputeID];
if (dispute.isRuled) revert DisputeAlreadyResolved();

dispute.isRuled = true;
dispute.ruling = _ruling;
/// @dev Receives ruling from Kleros and enforces it.
/// @param _disputeID ID of Kleros dispute.
/// @param _ruling Ruling that is given by Kleros. This needs to be converted to Realitio answer before reporting the answer by shifting by 1.
function rule(uint256 _disputeID, uint256 _ruling) public override {
uint256 questionID = arbitratorDisputeIDToQuestionID[msg.sender][_disputeID];
ArbitrationRequest storage arbitrationRequest = arbitrationRequests[questionID];
IArbitratorV2 arbitrator = arbitrationParamsChanges[arbitrationRequest.arbitrationParamsIndex].arbitrator;

if(IArbitratorV2(msg.sender) != arbitrator) revert ArbitratorOnly();
if(arbitrationRequest.status != Status.Disputed) revert StatusNotDisputed();

arbitrationRequest.ruling = _ruling;
arbitrationRequest.status = Status.Ruled;

emit Ruling(IArbitratorV2(msg.sender), _disputeID, _ruling);
}

emit Ruling(IArbitratorV2(msg.sender), _externalDisputeID, _ruling);
/// @dev Reports the answer to a specified question from Kleros arbitrator to the Realitio contract.
/// This can be called by anyone, after the dispute gets a ruling from Kleros.
/// We can't directly call `assignWinnerAndSubmitAnswerByArbitrator` inside `rule` because last answerer is not stored on chain.
/// @param _questionID The ID of Realitio question.
/// @param _lastHistoryHash The history hash given with the last answer to the question in the Realitio contract.
/// @param _lastAnswerOrCommitmentID The last answer given, or its commitment ID if it was a commitment, to the question in the Realitio contract, in bytes32.
/// @param _lastAnswerer The last answerer to the question in the Realitio contract.
function reportAnswer(
bytes32 _questionID,
bytes32 _lastHistoryHash,
bytes32 _lastAnswerOrCommitmentID,
address _lastAnswerer
) external {
ArbitrationRequest storage arbitrationRequest = arbitrationRequests[uint256(_questionID)];
if(arbitrationRequest.status != Status.Ruled) revert StatusNotRuled();

arbitrationRequest.status = Status.Reported;
// Realitio ruling is shifted by 1 compared to Kleros.
uint256 realitioRuling = arbitrationRequest.ruling != 0 ? arbitrationRequest.ruling - 1 : REFUSE_TO_ARBITRATE_REALITIO;

realitio.assignWinnerAndSubmitAnswerByArbitrator(_questionID, bytes32(realitioRuling), arbitrationRequest.requester, _lastHistoryHash, _lastAnswerOrCommitmentID, _lastAnswerer);
}

// ************************************* //
// * Errors * //
// ************************************* //
/// @dev Returns arbitration fee by calling arbitrationCost function in the arbitrator contract.
/// @return fee Arbitration fee that needs to be paid.
function getDisputeFee(bytes32) external view override returns (uint256 fee) {
uint256 arbitrationParamsIndex = arbitrationParamsChanges.length - 1;
IArbitratorV2 arbitrator = arbitrationParamsChanges[arbitrationParamsIndex].arbitrator;
bytes storage arbitratorExtraData = arbitrationParamsChanges[arbitrationParamsIndex].arbitratorExtraData;
return arbitrator.arbitrationCost(arbitratorExtraData);
}

error GovernorOnly();
error StatusNotDisputed();
error StatusNotRuled();
error ArbitrationAlreadyRequested();
error ArbitratorOnly();
error DisputeAlreadyResolved();
}
error GovernorOnly();
}
45 changes: 45 additions & 0 deletions contracts/src/interfaces/IRealitio.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: MIT

/**
* @authors: [@ferittuncer]
* @reviewers: [@hbarcelos]
* @auditors: []
* @bounties: []
* @deployments: []
*/

pragma solidity 0.8.18;

/**
* @title IRealitio
* @dev Required subset of https://github.com/realitio/realitio-contracts/blob/master/truffle/contracts/IRealitio.sol to implement a Realitio arbitrator.
*/
interface IRealitio {
/// @notice Notify the contract that the arbitrator has been paid for a question, freezing it pending their decision.
/// @dev The arbitrator contract is trusted to only call this if they've been paid, and tell us who paid them.
/// @param question_id The ID of the question
/// @param requester The account that requested arbitration
/// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
function notifyOfArbitrationRequest(
bytes32 question_id,
address requester,
uint256 max_previous
) external;

/// @notice Submit the answer for a question, for use by the arbitrator, working out the appropriate winner based on the last answer details.
/// @dev Doesn't require (or allow) a bond. Required in v2.1 onwards.
/// @param question_id The ID of the question
/// @param answer The answer, encoded into bytes32
/// @param payee_if_wrong The account to by credited as winner if the last answer given is wrong, usually the account that paid the arbitrator
/// @param last_history_hash The history hash before the final one
/// @param last_answer_or_commitment_id The last answer given, or the commitment ID if it was a commitment.
/// @param last_answerer The address that supplied the last answer
function assignWinnerAndSubmitAnswerByArbitrator(
bytes32 question_id,
bytes32 answer,
address payee_if_wrong,
bytes32 last_history_hash,
bytes32 last_answer_or_commitment_id,
address last_answerer
) external;
}
Loading