diff --git a/contracts/src/arbitration/ArbitrableExample.sol b/contracts/src/arbitration/arbitrables/ArbitrableExample.sol similarity index 97% rename from contracts/src/arbitration/ArbitrableExample.sol rename to contracts/src/arbitration/arbitrables/ArbitrableExample.sol index be01642df..b7b46bb48 100644 --- a/contracts/src/arbitration/ArbitrableExample.sol +++ b/contracts/src/arbitration/arbitrables/ArbitrableExample.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8; -import "./IArbitrable.sol"; -import "../evidence/IMetaEvidence.sol"; +import "../IArbitrable.sol"; +import "../../evidence/IMetaEvidence.sol"; /** * @title ArbitrableExample diff --git a/contracts/src/arbitration/arbitrables/DisputeResolver.sol b/contracts/src/arbitration/arbitrables/DisputeResolver.sol new file mode 100644 index 000000000..dde97799b --- /dev/null +++ b/contracts/src/arbitration/arbitrables/DisputeResolver.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT + +/** + * @authors: [@ferittuncer, @unknownunknown1] + * @reviewers: [] + * @auditors: [] + * @bounties: [] + */ + +import "../IArbitrable.sol"; +import "../../evidence/IMetaEvidence.sol"; + +pragma solidity ^0.8; + +/** + * @title DisputeResolver + * DisputeResolver contract adapted for V2 https://github.com/kleros/arbitrable-proxy-contracts/blob/master/contracts/ArbitrableProxy.sol. + */ +contract DisputeResolver is IArbitrable, IMetaEvidence { + 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. + } + + IArbitrator public immutable arbitrator; // Arbitrator is set in constructor and never changed. + + DisputeStruct[] public disputes; // Local disputes. + mapping(uint256 => uint256) public externalIDtoLocalID; // Maps external (arbitrator side) dispute IDs to local dispute IDs. + + /** @dev Constructor + * @param _arbitrator Target global arbitrator for any disputes. + */ + constructor(IArbitrator _arbitrator) { + arbitrator = _arbitrator; + } + + /** @dev TRUSTED. 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 _metaevidenceURI Link to metaevidence of the dispute. + * @param _numberOfRulingOptions Number of ruling options. + * @return disputeID Dispute id (on arbitrator side) of the created dispute. + */ + function createDispute( + bytes calldata _arbitratorExtraData, + string calldata _metaevidenceURI, + uint256 _numberOfRulingOptions + ) external payable returns (uint256 disputeID) { + require(_numberOfRulingOptions > 1, "Should be at least 2 ruling options."); + + disputeID = arbitrator.createDispute{value: msg.value}(_numberOfRulingOptions, _arbitratorExtraData); + uint256 localDisputeID = disputes.length; + disputes.push( + DisputeStruct({ + arbitratorExtraData: _arbitratorExtraData, + isRuled: false, + ruling: 0, + numberOfRulingOptions: _numberOfRulingOptions + }) + ); + + externalIDtoLocalID[disputeID] = localDisputeID; + + emit MetaEvidence(localDisputeID, _metaevidenceURI); + emit Dispute(arbitrator, disputeID, localDisputeID, localDisputeID); + } + + /** @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 { + uint256 localDisputeID = externalIDtoLocalID[_externalDisputeID]; + 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(IArbitrator(msg.sender), _externalDisputeID, dispute.ruling); + } +}