Skip to content

Commit fa8fb1f

Browse files
authoredNov 30, 2021
Merge pull request #1 from unknownunknown1/master
feat(Arbitration): standard update
2 parents 4a70475 + 0ba4f29 commit fa8fb1f

File tree

5 files changed

+520
-2
lines changed

5 files changed

+520
-2
lines changed
 

‎CHANGELOG.md

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
## 0.1.0 (2021-11-11)
1+
## 0.1.0 (2021-11-30)
22

3+
- fix(Arbitrator): memory to calldata ([4770b1f](https://github.com/kleros/kleros-v2/commit/4770b1f))
4+
- fix(IArbitrator): appeals removed from the standard ([02c20ce](https://github.com/kleros/kleros-v2/commit/02c20ce))
5+
- fix(IArbitrator): interface simplification ([e81fb8b](https://github.com/kleros/kleros-v2/commit/e81fb8b))
6+
- fix(IArbitrator): replaced appealCost with fundingStatus ([f189dd9](https://github.com/kleros/kleros-v2/commit/f189dd9))
7+
- feat: modern toolchain setup and simple RNG smart contracts ([17f6a76](https://github.com/kleros/kleros-v2/commit/17f6a76))
8+
- feat(Arbitration): standard update ([ed930de](https://github.com/kleros/kleros-v2/commit/ed930de))
9+
- chore: added GitHub code scanning ([4a70475](https://github.com/kleros/kleros-v2/commit/4a70475))
10+
- chore: added the hardhat config for layer 2 networks, added hardhat-deploy and mocha ([a12ea0e](https://github.com/kleros/kleros-v2/commit/a12ea0e))
311
- test: added a test for IncrementalNG ([65a996b](https://github.com/kleros/kleros-v2/commit/65a996b))
412
- docs: initial commit ([23356e7](https://github.com/kleros/kleros-v2/commit/23356e7))
513
- docs: license file added ([cb62d2c](https://github.com/kleros/kleros-v2/commit/cb62d2c))
614
- docs: readme and spdx headers ([8a5b397](https://github.com/kleros/kleros-v2/commit/8a5b397))
715
- docs: updated ([5b9a8f1](https://github.com/kleros/kleros-v2/commit/5b9a8f1))
8-
- feat: modern toolchain setup and simple RNG smart contracts ([17f6a76](https://github.com/kleros/kleros-v2/commit/17f6a76))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8;
4+
5+
import "./IArbitrable.sol";
6+
7+
/**
8+
* @title ArbitrableExample
9+
* An example of the arbitrable contract which connects to the arbitator that implements IArbitrator interface.
10+
*/
11+
contract ArbitrableExample is IArbitrable {
12+
struct DisputeStruct {
13+
bool isRuled; // Whether the dispute has been ruled or not.
14+
uint256 ruling; // Ruling given by the arbitrator.
15+
uint256 numberOfRulingOptions; // The number of choices the arbitrator can give.
16+
}
17+
18+
IArbitrator public immutable arbitrator; // Arbitrator is set in constructor and never changed.
19+
20+
mapping(uint256 => uint256) public externalIDtoLocalID; // Maps external (arbitrator side) dispute IDs to local dispute IDs.
21+
22+
DisputeStruct[] public disputes; // Stores the disputes' info. disputes[disputeID].
23+
24+
/** @dev Constructor
25+
* @param _arbitrator The arbitrator to rule on created disputes.
26+
*/
27+
constructor(IArbitrator _arbitrator) {
28+
arbitrator = _arbitrator;
29+
}
30+
31+
/** @dev TRUSTED. Calls createDispute function of the specified arbitrator to create a dispute.
32+
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.
33+
* @param _numberOfRulingOptions Number of ruling options. Must be greater than 1, otherwise there is nothing to choose from.
34+
* @param _arbitratorExtraData Extra data for the arbitrator.
35+
* @return disputeID Dispute id (on arbitrator side) of the dispute created.
36+
*/
37+
function createDispute(uint256 _numberOfRulingOptions, bytes calldata _arbitratorExtraData)
38+
external
39+
payable
40+
returns (uint256 disputeID)
41+
{
42+
require(_numberOfRulingOptions > 1, "Incorrect number of choices");
43+
44+
uint256 localDisputeID = disputes.length;
45+
disputes.push(DisputeStruct({isRuled: false, ruling: 0, numberOfRulingOptions: _numberOfRulingOptions}));
46+
47+
disputeID = arbitrator.createDispute{value: msg.value}(_numberOfRulingOptions, _arbitratorExtraData);
48+
49+
externalIDtoLocalID[disputeID] = localDisputeID;
50+
}
51+
52+
/** @dev To be called by the arbitrator of the dispute, to declare the winning ruling.
53+
* @param _externalDisputeID ID of the dispute in arbitrator contract.
54+
* @param _ruling The ruling choice of the arbitration.
55+
*/
56+
function rule(uint256 _externalDisputeID, uint256 _ruling) external override {
57+
uint256 localDisputeID = externalIDtoLocalID[_externalDisputeID];
58+
DisputeStruct storage dispute = disputes[localDisputeID];
59+
require(msg.sender == address(arbitrator), "Only the arbitrator can execute this.");
60+
require(_ruling <= dispute.numberOfRulingOptions, "Invalid ruling.");
61+
require(dispute.isRuled == false, "This dispute has been ruled already.");
62+
63+
dispute.isRuled = true;
64+
dispute.ruling = _ruling;
65+
66+
emit Ruling(IArbitrator(msg.sender), _externalDisputeID, dispute.ruling);
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8;
4+
5+
import "./IArbitrator.sol";
6+
7+
/** @title Centralized Arbitrator
8+
* @dev This is a centralized arbitrator deciding alone on the result of disputes. It illustrates how IArbitrator interface can be implemented.
9+
* Note that this contract supports appeals. The ruling given by the arbitrator can be appealed by crowdfunding a desired choice.
10+
*/
11+
contract CentralizedArbitrator is IArbitrator {
12+
/* Constants */
13+
14+
// The required fee stake that a party must pay depends on who won the previous round and is proportional to the appeal cost such that the fee stake for a round is stake multiplier * appeal cost for that round.
15+
uint256 public constant WINNER_STAKE_MULTIPLIER = 10000; // Multiplier of the appeal cost that the winner has to pay as fee stake for a round in basis points. Default is 1x of appeal fee.
16+
uint256 public constant LOSER_STAKE_MULTIPLIER = 20000; // Multiplier of the appeal cost that the loser has to pay as fee stake for a round in basis points. Default is 2x of appeal fee.
17+
uint256 public constant LOSER_APPEAL_PERIOD_MULTIPLIER = 5000; // Multiplier of the appeal period for the choice that wasn't voted for in the previous round, in basis points. Default is 1/2 of original appeal period.
18+
uint256 public constant MULTIPLIER_DIVISOR = 10000;
19+
20+
/* Enums */
21+
22+
enum DisputeStatus {
23+
Waiting, // The dispute is waiting for the ruling or not created.
24+
Appealable, // The dispute can be appealed.
25+
Solved // The dispute is resolved.
26+
}
27+
28+
/* Structs */
29+
30+
struct DisputeStruct {
31+
IArbitrable arbitrated; // The address of the arbitrable contract.
32+
bytes arbitratorExtraData; // Extra data for the arbitrator.
33+
uint256 choices; // The number of choices the arbitrator can choose from.
34+
uint256 appealPeriodStart; // Time when the appeal funding becomes possible.
35+
uint256 arbitrationFee; // Fee paid by the arbitrable for the arbitration. Must be equal or higher than arbitration cost.
36+
uint256 ruling; // Ruling given by the arbitrator.
37+
DisputeStatus status; // A current status of the dispute.
38+
}
39+
40+
struct Round {
41+
mapping(uint256 => uint256) paidFees; // Tracks the fees paid for each choice in this round.
42+
mapping(uint256 => bool) hasPaid; // True if this choice was fully funded, false otherwise.
43+
mapping(address => mapping(uint256 => uint256)) contributions; // Maps contributors to their contributions for each choice.
44+
uint256 feeRewards; // Sum of reimbursable appeal fees available to the parties that made contributions to the ruling that ultimately wins a dispute.
45+
uint256[] fundedChoices; // Stores the choices that are fully funded.
46+
}
47+
48+
/* Storage */
49+
50+
address public owner = msg.sender; // Owner of the contract.
51+
uint256 public appealDuration; // The duration of the appeal period.
52+
53+
uint256 private arbitrationFee; // The cost to create a dispute. Made private because of the arbitrationCost() getter.
54+
uint256 public appealFee; // The cost to fund one of the choices, not counting the additional fee stake amount.
55+
56+
DisputeStruct[] public disputes; // Stores the dispute info. disputes[disputeID].
57+
mapping(uint256 => Round[]) public disputeIDtoRoundArray; // Maps dispute IDs to Round array that contains the info about crowdfunding.
58+
59+
/* Events */
60+
61+
/**
62+
* @dev To be emitted when a dispute can be appealed.
63+
* @param _disputeID ID of the dispute.
64+
* @param _arbitrable The contract which created the dispute.
65+
*/
66+
event AppealPossible(uint256 indexed _disputeID, IArbitrable indexed _arbitrable);
67+
68+
/**
69+
* @dev To be emitted when the current ruling is appealed.
70+
* @param _disputeID ID of the dispute.
71+
* @param _arbitrable The contract which created the dispute.
72+
*/
73+
event AppealDecision(uint256 indexed _disputeID, IArbitrable indexed _arbitrable);
74+
75+
/** @dev Raised when a contribution is made, inside fundAppeal function.
76+
* @param _disputeID ID of the dispute.
77+
* @param _round The round the contribution was made to.
78+
* @param _choice Indicates the choice option which got the contribution.
79+
* @param _contributor Caller of fundAppeal function.
80+
* @param _amount Contribution amount.
81+
*/
82+
event Contribution(
83+
uint256 indexed _disputeID,
84+
uint256 indexed _round,
85+
uint256 _choice,
86+
address indexed _contributor,
87+
uint256 _amount
88+
);
89+
90+
/** @dev Raised when a contributor withdraws a non-zero value.
91+
* @param _disputeID ID of the dispute.
92+
* @param _round The round the withdrawal was made from.
93+
* @param _choice Indicates the choice which contributor gets rewards from.
94+
* @param _contributor The beneficiary of the withdrawal.
95+
* @param _amount Total withdrawn amount, consists of reimbursed deposits and rewards.
96+
*/
97+
event Withdrawal(
98+
uint256 indexed _disputeID,
99+
uint256 indexed _round,
100+
uint256 _choice,
101+
address indexed _contributor,
102+
uint256 _amount
103+
);
104+
105+
/** @dev To be raised when a choice is fully funded for appeal.
106+
* @param _disputeID ID of the dispute.
107+
* @param _round ID of the round where the choice was funded.
108+
* @param _choice The choice that just got fully funded.
109+
*/
110+
event ChoiceFunded(uint256 indexed _disputeID, uint256 indexed _round, uint256 indexed _choice);
111+
112+
/* Modifiers */
113+
114+
modifier onlyOwner() {
115+
require(msg.sender == owner, "Can only be called by the owner.");
116+
_;
117+
}
118+
119+
/** @dev Constructor.
120+
* @param _arbitrationFee Amount to be paid for arbitration.
121+
* @param _appealDuration Duration of the appeal period.
122+
* @param _appealFee Amount to be paid to fund one of the appeal choices, not counting the additional fee stake amount.
123+
*/
124+
constructor(
125+
uint256 _arbitrationFee,
126+
uint256 _appealDuration,
127+
uint256 _appealFee
128+
) public {
129+
arbitrationFee = _arbitrationFee;
130+
appealDuration = _appealDuration;
131+
appealFee = _appealFee;
132+
}
133+
134+
/* External and Public */
135+
136+
/** @dev Set the arbitration fee. Only callable by the owner.
137+
* @param _arbitrationFee Amount to be paid for arbitration.
138+
*/
139+
function setArbitrationFee(uint256 _arbitrationFee) external onlyOwner {
140+
arbitrationFee = _arbitrationFee;
141+
}
142+
143+
/** @dev Set the duration of the appeal period. Only callable by the owner.
144+
* @param _appealDuration New duration of the appeal period.
145+
*/
146+
function setAppealDuration(uint256 _appealDuration) external onlyOwner {
147+
appealDuration = _appealDuration;
148+
}
149+
150+
/** @dev Set the appeal fee. Only callable by the owner.
151+
* @param _appealFee Amount to be paid for appeal.
152+
*/
153+
function setAppealFee(uint256 _appealFee) external onlyOwner {
154+
appealFee = _appealFee;
155+
}
156+
157+
/** @dev Create a dispute. Must be called by the arbitrable contract.
158+
* Must be paid at least arbitrationCost().
159+
* @param _choices Amount of choices the arbitrator can make in this dispute.
160+
* @param _extraData Can be used to give additional info on the dispute to be created.
161+
* @return disputeID ID of the dispute created.
162+
*/
163+
function createDispute(uint256 _choices, bytes calldata _extraData)
164+
external
165+
payable
166+
override
167+
returns (uint256 disputeID)
168+
{
169+
uint256 localArbitrationCost = arbitrationCost(_extraData);
170+
require(msg.value >= localArbitrationCost, "Not enough ETH to cover arbitration costs.");
171+
disputeID = disputes.length;
172+
disputes.push(
173+
DisputeStruct({
174+
arbitrated: IArbitrable(msg.sender),
175+
arbitratorExtraData: _extraData,
176+
choices: _choices,
177+
appealPeriodStart: 0,
178+
arbitrationFee: msg.value,
179+
ruling: 0,
180+
status: DisputeStatus.Waiting
181+
})
182+
);
183+
184+
disputeIDtoRoundArray[disputeID].push();
185+
emit DisputeCreation(disputeID, IArbitrable(msg.sender));
186+
}
187+
188+
/** @dev TRUSTED. Manages contributions, and appeals a dispute if at least two choices are fully funded. This function allows the appeals to be crowdfunded.
189+
* Note that the surplus deposit will be reimbursed.
190+
* @param _disputeID Index of the dispute to appeal.
191+
* @param _choice A choice that receives funding.
192+
*/
193+
function fundAppeal(uint256 _disputeID, uint256 _choice) external payable {
194+
DisputeStruct storage dispute = disputes[_disputeID];
195+
require(dispute.status == DisputeStatus.Appealable, "Dispute not appealable.");
196+
require(_choice <= dispute.choices, "There is no such ruling to fund.");
197+
198+
(uint256 appealPeriodStart, uint256 appealPeriodEnd) = appealPeriod(_disputeID);
199+
require(block.timestamp >= appealPeriodStart && block.timestamp < appealPeriodEnd, "Appeal period is over.");
200+
201+
uint256 multiplier;
202+
if (dispute.ruling == _choice) {
203+
multiplier = WINNER_STAKE_MULTIPLIER;
204+
} else {
205+
require(
206+
block.timestamp - appealPeriodStart <
207+
((appealPeriodEnd - appealPeriodStart) * LOSER_APPEAL_PERIOD_MULTIPLIER) / MULTIPLIER_DIVISOR,
208+
"Appeal period is over for loser"
209+
);
210+
multiplier = LOSER_STAKE_MULTIPLIER;
211+
}
212+
213+
Round[] storage rounds = disputeIDtoRoundArray[_disputeID];
214+
uint256 lastRoundIndex = rounds.length - 1;
215+
Round storage lastRound = rounds[lastRoundIndex];
216+
require(!lastRound.hasPaid[_choice], "Appeal fee is already paid.");
217+
218+
uint256 totalCost = appealFee + (appealFee * multiplier) / MULTIPLIER_DIVISOR;
219+
220+
// Take up to the amount necessary to fund the current round at the current costs.
221+
uint256 contribution;
222+
if (totalCost > lastRound.paidFees[_choice]) {
223+
contribution = totalCost - lastRound.paidFees[_choice] > msg.value // Overflows and underflows will be managed on the compiler level.
224+
? msg.value
225+
: totalCost - lastRound.paidFees[_choice];
226+
emit Contribution(_disputeID, lastRoundIndex, _choice, msg.sender, contribution);
227+
}
228+
229+
lastRound.contributions[msg.sender][_choice] += contribution;
230+
lastRound.paidFees[_choice] += contribution;
231+
if (lastRound.paidFees[_choice] >= totalCost) {
232+
lastRound.feeRewards += lastRound.paidFees[_choice];
233+
lastRound.fundedChoices.push(_choice);
234+
lastRound.hasPaid[_choice] = true;
235+
emit ChoiceFunded(_disputeID, lastRoundIndex, _choice);
236+
}
237+
238+
if (lastRound.fundedChoices.length > 1) {
239+
// At least two sides are fully funded.
240+
rounds.push();
241+
lastRound.feeRewards = lastRound.feeRewards - appealFee;
242+
243+
dispute.status = DisputeStatus.Waiting;
244+
dispute.appealPeriodStart = 0;
245+
emit AppealDecision(_disputeID, dispute.arbitrated);
246+
}
247+
248+
if (msg.value > contribution) payable(msg.sender).send(msg.value - contribution);
249+
}
250+
251+
/** @dev Give a ruling to a dispute. Once it's given the dispute can be appealed, and after the appeal period has passed this function should be called again to finalize the ruling.
252+
* Accounts for the situation where the winner loses a case due to paying less appeal fees than expected.
253+
* @param _disputeID ID of the dispute to rule.
254+
* @param _ruling Ruling given by the arbitrator. Note that 0 means that arbitrator chose "Refused to rule".
255+
*/
256+
function giveRuling(uint256 _disputeID, uint256 _ruling) external onlyOwner {
257+
DisputeStruct storage dispute = disputes[_disputeID];
258+
require(_ruling <= dispute.choices, "Invalid ruling.");
259+
require(dispute.status != DisputeStatus.Solved, "The dispute must not be solved.");
260+
261+
if (dispute.status == DisputeStatus.Waiting) {
262+
dispute.ruling = _ruling;
263+
dispute.status = DisputeStatus.Appealable;
264+
dispute.appealPeriodStart = block.timestamp;
265+
emit AppealPossible(_disputeID, dispute.arbitrated);
266+
} else {
267+
require(block.timestamp > dispute.appealPeriodStart + appealDuration, "Appeal period not passed yet.");
268+
dispute.ruling = _ruling;
269+
dispute.status = DisputeStatus.Solved;
270+
271+
Round[] storage rounds = disputeIDtoRoundArray[_disputeID];
272+
Round storage lastRound = rounds[rounds.length - 1];
273+
// If only one ruling option is funded, it wins by default. Note that if any other ruling had funded, an appeal would have been created.
274+
if (lastRound.fundedChoices.length == 1) {
275+
dispute.ruling = lastRound.fundedChoices[0];
276+
}
277+
278+
payable(msg.sender).send(dispute.arbitrationFee); // Avoid blocking.
279+
dispute.arbitrated.rule(_disputeID, dispute.ruling);
280+
}
281+
}
282+
283+
/** @dev Allows to withdraw any reimbursable fees or rewards after the dispute gets resolved.
284+
* @param _disputeID Index of the dispute in disputes array.
285+
* @param _beneficiary The address which rewards to withdraw.
286+
* @param _round The round the caller wants to withdraw from.
287+
* @param _choice The ruling option that the caller wants to withdraw from.
288+
* @return amount The withdrawn amount.
289+
*/
290+
function withdrawFeesAndRewards(
291+
uint256 _disputeID,
292+
address payable _beneficiary,
293+
uint256 _round,
294+
uint256 _choice
295+
) external returns (uint256 amount) {
296+
DisputeStruct storage dispute = disputes[_disputeID];
297+
require(dispute.status == DisputeStatus.Solved, "Dispute should be resolved.");
298+
Round storage round = disputeIDtoRoundArray[_disputeID][_round];
299+
300+
if (!round.hasPaid[_choice]) {
301+
// Allow to reimburse if funding was unsuccessful for this ruling option.
302+
amount = round.contributions[_beneficiary][_choice];
303+
} else {
304+
// Funding was successful for this ruling option.
305+
if (_choice == dispute.ruling) {
306+
// This ruling option is the ultimate winner.
307+
amount = round.paidFees[_choice] > 0
308+
? (round.contributions[_beneficiary][_choice] * round.feeRewards) / round.paidFees[_choice]
309+
: 0;
310+
} else if (!round.hasPaid[dispute.ruling]) {
311+
// The ultimate winner was not funded in this round. In this case funded ruling option(s) are reimbursed.
312+
amount =
313+
(round.contributions[_beneficiary][_choice] * round.feeRewards) /
314+
(round.paidFees[round.fundedChoices[0]] + round.paidFees[round.fundedChoices[1]]);
315+
}
316+
}
317+
round.contributions[_beneficiary][_choice] = 0;
318+
319+
if (amount != 0) {
320+
_beneficiary.send(amount); // Deliberate use of send to prevent reverting fallback. It's the user's responsibility to accept ETH.
321+
emit Withdrawal(_disputeID, _round, _choice, _beneficiary, amount);
322+
}
323+
}
324+
325+
// ************************ //
326+
// * Getters * //
327+
// ************************ //
328+
329+
/** @dev Cost of arbitration.
330+
* @return fee The required amount.
331+
*/
332+
function arbitrationCost(
333+
bytes calldata /*_extraData*/
334+
) public view override returns (uint256 fee) {
335+
return arbitrationFee;
336+
}
337+
338+
/** @dev Return the funded amount and funding goal for one of the choices.
339+
* @param _disputeID The ID of the dispute to appeal.
340+
* @param _choice The choice to check the funding status of.
341+
* @return funded The amount funded so far for this choice in wei.
342+
* @return goal The amount to fully fund this choice in wei.
343+
*/
344+
function fundingStatus(uint256 _disputeID, uint256 _choice) external view returns (uint256 funded, uint256 goal) {
345+
DisputeStruct storage dispute = disputes[_disputeID];
346+
require(_choice <= dispute.choices, "There is no such ruling to fund.");
347+
require(dispute.status == DisputeStatus.Appealable, "Dispute not appealable.");
348+
349+
if (dispute.ruling == _choice) {
350+
goal = appealFee + (appealFee * WINNER_STAKE_MULTIPLIER) / MULTIPLIER_DIVISOR;
351+
} else {
352+
goal = appealFee + (appealFee * LOSER_STAKE_MULTIPLIER) / MULTIPLIER_DIVISOR;
353+
}
354+
355+
Round[] storage rounds = disputeIDtoRoundArray[_disputeID];
356+
Round storage lastRound = rounds[rounds.length - 1];
357+
358+
return (lastRound.paidFees[_choice], goal);
359+
}
360+
361+
/** @dev Compute the start and end of the dispute's appeal period, if possible. If the dispute is not appealble return (0, 0).
362+
* @param _disputeID ID of the dispute.
363+
* @return start The start of the period.
364+
* @return end The end of the period.
365+
*/
366+
function appealPeriod(uint256 _disputeID) public view returns (uint256 start, uint256 end) {
367+
DisputeStruct storage dispute = disputes[_disputeID];
368+
if (dispute.status == DisputeStatus.Appealable) {
369+
start = dispute.appealPeriodStart;
370+
end = start + appealDuration;
371+
}
372+
return (start, end);
373+
}
374+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8;
4+
5+
import "./IArbitrator.sol";
6+
7+
/**
8+
* @title IArbitrable
9+
* Arbitrable interface. Note that this interface follows the ERC-792 standard.
10+
* When developing arbitrable contracts, we need to:
11+
* - Define the action taken when a ruling is received by the contract.
12+
* - Allow dispute creation. For this a function must call arbitrator.createDispute{value: _fee}(_choices,_extraData);
13+
*/
14+
interface IArbitrable {
15+
/**
16+
* @dev To be raised when a ruling is given.
17+
* @param _arbitrator The arbitrator giving the ruling.
18+
* @param _disputeID ID of the dispute in the Arbitrator contract.
19+
* @param _ruling The ruling which was given.
20+
*/
21+
event Ruling(IArbitrator indexed _arbitrator, uint256 indexed _disputeID, uint256 _ruling);
22+
23+
/**
24+
* @dev Give a ruling for a dispute. Must be called by the arbitrator.
25+
* The purpose of this function is to ensure that the address calling it has the right to rule on the contract.
26+
* @param _disputeID ID of the dispute in the Arbitrator contract.
27+
* @param _ruling Ruling given by the arbitrator. Note that 0 is reserved for "Not able/wanting to make a decision".
28+
*/
29+
function rule(uint256 _disputeID, uint256 _ruling) external;
30+
}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8;
4+
5+
import "./IArbitrable.sol";
6+
7+
/**
8+
* @title Arbitrator
9+
* Arbitrator interface that implements the new arbitration standard.
10+
* Unlike the ERC-792 this standard doesn't have anything related to appeals, so each arbitrator can implement an appeal system that suits it the most.
11+
* When developing arbitrator contracts we need to:
12+
* - Define the functions for dispute creation (createDispute). Don't forget to store the arbitrated contract and the disputeID (which should be unique, may nbDisputes).
13+
* - Define the functions for cost display (arbitrationCost).
14+
* - Allow giving rulings. For this a function must call arbitrable.rule(disputeID, ruling).
15+
*/
16+
interface IArbitrator {
17+
/**
18+
* @dev To be emitted when a dispute is created.
19+
* @param _disputeID ID of the dispute.
20+
* @param _arbitrable The contract which created the dispute.
21+
*/
22+
event DisputeCreation(uint256 indexed _disputeID, IArbitrable indexed _arbitrable);
23+
24+
/**
25+
* @dev Create a dispute. Must be called by the arbitrable contract.
26+
* Must pay at least arbitrationCost(_extraData).
27+
* @param _choices Amount of choices the arbitrator can make in this dispute.
28+
* @param _extraData Can be used to give additional info on the dispute to be created.
29+
* @return disputeID ID of the dispute created.
30+
*/
31+
function createDispute(uint256 _choices, bytes calldata _extraData) external payable returns (uint256 disputeID);
32+
33+
/**
34+
* @dev Compute the cost of arbitration. 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.
35+
* @param _extraData Can be used to give additional info on the dispute to be created.
36+
* @return cost Required cost of arbitration.
37+
*/
38+
function arbitrationCost(bytes calldata _extraData) external view returns (uint256 cost);
39+
}

0 commit comments

Comments
 (0)
Please sign in to comment.