@reality.eth/contracts 3.0.48 → 4.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/abi/solc-0.8.20/RealityETH-4.0.abi.json +1 -1
  2. package/abi/solc-0.8.20/RealityETHFreezableExample_ERC20-4.0.abi.json +1 -0
  3. package/abi/solc-0.8.20/RealityETHReopenable-4.0.abi.json +1 -0
  4. package/abi/solc-0.8.20/RealityETHReopenable_ERC20-4.0.abi.json +1 -0
  5. package/abi/solc-0.8.20/RealityETH_ERC20-4.0.abi.json +1 -1
  6. package/bytecode/RealityETH-4.0.bin +1 -1
  7. package/bytecode/RealityETHFreezableExample_ERC20-4.0.bin +1 -0
  8. package/bytecode/RealityETHReopenable-4.0.bin +1 -0
  9. package/bytecode/RealityETHReopenable_ERC20-4.0.bin +1 -0
  10. package/bytecode/RealityETH_ERC20-4.0.bin +1 -1
  11. package/chains/supported.json +1 -1
  12. package/development/contracts/IBalanceHolder.sol +1 -0
  13. package/development/contracts/IRealityETH.sol +6 -2
  14. package/development/contracts/{IRealityETHCore.sol → IRealityETHCore_Common.sol} +4 -23
  15. package/development/contracts/IRealityETHCore_ERC20.sol +3 -124
  16. package/development/contracts/IRealityETHCore_Native.sol +12 -0
  17. package/development/contracts/IRealityETHCreateTemplateAndAskQuestion.sol +8 -0
  18. package/development/contracts/IRealityETHHistoryVerification.sol +8 -0
  19. package/development/contracts/IRealityETHReopenable.sol +22 -0
  20. package/development/contracts/IRealityETHReopenable_ERC20.sol +23 -0
  21. package/development/contracts/IRealityETH_ERC20.sol +5 -1
  22. package/development/contracts/RealityETH-4.0.sol +12 -614
  23. package/development/contracts/RealityETHCore_Common.sol +535 -0
  24. package/development/contracts/RealityETHFreezableExample_ERC20-4.0.sol +17 -0
  25. package/development/contracts/RealityETHFreezable_ERC20.sol +39 -0
  26. package/development/contracts/RealityETHReopenable-4.0.sol +119 -0
  27. package/development/contracts/RealityETHReopenable_ERC20-4.0.sol +122 -0
  28. package/development/contracts/RealityETH_ERC20-4.0.sol +13 -614
  29. package/generated/chains.json +1 -1
  30. package/package.json +2 -2
  31. package/tests/python/test.py +186 -5
  32. package/development/contracts/BalanceHolder_ERC20.sol +0 -21
  33. package/development/contracts/IBalanceHolder_ERC20.sol +0 -11
@@ -4,106 +4,12 @@ pragma solidity ^0.8.20;
4
4
 
5
5
  import {IERC20} from "./IERC20.sol";
6
6
  import {IRealityETHCore_ERC20} from "./IRealityETHCore_ERC20.sol";
7
- import {BalanceHolder_ERC20} from "./BalanceHolder_ERC20.sol";
8
7
 
9
- // solhint-disable-next-line contract-name-camelcase
10
- contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
11
- address private constant NULL_ADDRESS = address(0);
12
-
13
- // History hash when no history is created, or history has been cleared
14
- bytes32 private constant NULL_HASH = bytes32(0);
15
-
16
- // An unitinalized finalize_ts for a question will indicate an unanswered question.
17
- uint32 private constant UNANSWERED = 0;
18
-
19
- // Proportion withheld when you claim an earlier bond.
20
- uint256 private constant BOND_CLAIM_FEE_PROPORTION = 40; // One 40th ie 2.5%
21
-
22
- // Special value representing a question that was answered too soon.
23
- // bytes32(-2). By convention we use bytes32(-1) for "invalid", although the contract does not handle this.
24
- bytes32 private constant UNRESOLVED_ANSWER = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe;
25
-
26
- uint256 private nextTemplateID = 0;
27
- mapping(uint256 => uint256) public templates;
28
- mapping(uint256 => bytes32) public template_hashes;
29
- mapping(bytes32 => Question) public questions;
30
- mapping(bytes32 => Claim) public question_claims;
31
- mapping(address => uint256) public arbitrator_question_fees;
32
- mapping(bytes32 => bytes32) public reopened_questions;
33
- mapping(bytes32 => bool) public reopener_questions;
34
-
35
- modifier onlyArbitrator(bytes32 question_id) {
36
- if (msg.sender != questions[question_id].arbitrator) revert MsgSenderMustBeArbitrator();
37
- _;
38
- }
39
-
40
- modifier stateAny() {
41
- _;
42
- }
43
-
44
- modifier stateNotCreated(bytes32 question_id) {
45
- if (questions[question_id].timeout != 0) revert QuestionMustNotExist();
46
- _;
47
- }
48
-
49
- modifier stateOpen(bytes32 question_id) {
50
- if (questions[question_id].timeout == 0) revert QuestionMustExist();
51
- if (questions[question_id].is_pending_arbitration) revert QuestionMustNotBePendingArbitration();
52
- uint32 finalize_ts = questions[question_id].finalize_ts;
53
- if (finalize_ts != UNANSWERED && finalize_ts <= uint32(block.timestamp)) revert FinalizationDeadlineMustNotHavePassed();
54
- uint32 opening_ts = questions[question_id].opening_ts;
55
- if (opening_ts != 0 && opening_ts > uint32(block.timestamp)) revert OpeningDateMustHavePassed();
56
- _;
57
- }
8
+ import {RealityETHCore_Common} from "./RealityETHCore_Common.sol";
58
9
 
59
- modifier statePendingArbitration(bytes32 question_id) {
60
- if (!questions[question_id].is_pending_arbitration) revert QuestionMustBePendingArbitration();
61
- _;
62
- }
63
-
64
- modifier stateOpenOrPendingArbitration(bytes32 question_id) {
65
- if (questions[question_id].timeout == 0) revert QuestionMustExist();
66
- uint32 finalize_ts = questions[question_id].finalize_ts;
67
- if (finalize_ts != UNANSWERED && finalize_ts <= uint32(block.timestamp)) revert FinalizationDealineMustNotHavePassed();
68
- uint32 opening_ts = questions[question_id].opening_ts;
69
- if (opening_ts != 0 && opening_ts > uint32(block.timestamp)) revert OpeningDateMustHavePassed();
70
- _;
71
- }
72
-
73
- modifier stateFinalized(bytes32 question_id) {
74
- if (!isFinalized(question_id)) revert QuestionMustBeFinalized();
75
- _;
76
- }
77
-
78
- modifier bondMustDoubleAndMatchMinimum(bytes32 question_id, uint256 tokens) {
79
- if (tokens == 0) revert BondMustBePositive();
80
- uint256 current_bond = questions[question_id].bond;
81
- if (current_bond == 0) {
82
- if (tokens < (questions[question_id].min_bond)) revert BondMustExceedTheMinimum();
83
- } else {
84
- if (tokens < (current_bond * 2)) revert BondMustBeDoubleAtLeastPreviousBond();
85
- }
86
- _;
87
- }
88
-
89
- modifier previousBondMustNotBeatMaxPrevious(bytes32 question_id, uint256 max_previous) {
90
- if (max_previous > 0) {
91
- if (questions[question_id].bond > max_previous) revert BondMustExceedMax_Previous();
92
- }
93
- _;
94
- }
95
-
96
- /* solhint-disable quotes */
97
- /// @notice Constructor, sets up some initial templates
98
- /// @dev Creates some generalized templates for different question types used in the DApp.
99
- constructor() {
100
- createTemplate('{"title": "%s", "type": "bool", "category": "%s", "lang": "%s"}');
101
- createTemplate('{"title": "%s", "type": "uint", "decimals": 18, "category": "%s", "lang": "%s"}');
102
- createTemplate('{"title": "%s", "type": "single-select", "outcomes": [%s], "category": "%s", "lang": "%s"}');
103
- createTemplate('{"title": "%s", "type": "multiple-select", "outcomes": [%s], "category": "%s", "lang": "%s"}');
104
- createTemplate('{"title": "%s", "type": "datetime", "category": "%s", "lang": "%s"}');
105
- }
106
- /* solhint-enable quotes */
10
+ // solhint-disable-next-line contract-name-camelcase
11
+ contract RealityETH_ERC20_v4_0 is RealityETHCore_Common, IRealityETHCore_ERC20 {
12
+ IERC20 public token;
107
13
 
108
14
  /// @notice Set the address of the ERC20 token that will be used for bonds.
109
15
  /// @dev Should not be used with ERC20-like token contracts that implement callbacks like ERC777 that could cause re-entrancy issues
@@ -113,55 +19,6 @@ contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
113
19
  token = _token;
114
20
  }
115
21
 
116
- /// @notice Function for arbitrator to set an optional per-question fee.
117
- /// @dev The per-question fee, charged when a question is asked, is intended as an anti-spam measure.
118
- /// @param fee The fee to be charged by the arbitrator when a question is asked
119
- function setQuestionFee(uint256 fee) external stateAny {
120
- arbitrator_question_fees[msg.sender] = fee;
121
- emit LogSetQuestionFee(msg.sender, fee);
122
- }
123
-
124
- /// @notice Create a reusable template, which should be a JSON document.
125
- /// Placeholders should use gettext() syntax, eg %s.
126
- /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage.
127
- /// @param content The template content
128
- /// @return The ID of the newly-created template, which is created sequentially.
129
- function createTemplate(string memory content) public stateAny returns (uint256) {
130
- uint256 id = nextTemplateID;
131
- templates[id] = block.number;
132
- template_hashes[id] = keccak256(abi.encodePacked(content));
133
- emit LogNewTemplate(id, msg.sender, content);
134
- nextTemplateID = id + 1;
135
- return id;
136
- }
137
-
138
- /// @notice Create a new reusable template and use it to ask a question
139
- /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage.
140
- /// @param content The template content
141
- /// @param question A string containing the parameters that will be passed into the template to make the question
142
- /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
143
- /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer
144
- /// @param opening_ts If set, the earliest time it should be possible to answer the question.
145
- /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
146
- /// @return The ID of the newly-created template, which is created sequentially.
147
- function createTemplateAndAskQuestion(
148
- string memory content,
149
- string memory question,
150
- address arbitrator,
151
- uint32 timeout,
152
- uint32 opening_ts,
153
- uint256 nonce
154
- )
155
- public
156
- returns (
157
- // stateNotCreated is enforced by the internal _askQuestion
158
- bytes32
159
- )
160
- {
161
- uint256 template_id = createTemplate(content);
162
- return askQuestion(template_id, question, arbitrator, timeout, opening_ts, nonce);
163
- }
164
-
165
22
  /// @notice Ask a new question without a bounty and return the ID
166
23
  /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage.
167
24
  /// @dev Calling without the token param will only work if there is no arbitrator-set question fee.
@@ -302,46 +159,11 @@ contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
302
159
  return;
303
160
  }
304
161
 
305
- function _askQuestion(bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 min_bond, uint256 tokens) internal stateNotCreated(question_id) {
306
- // A timeout of 0 makes no sense, and we will use this to check existence
307
- if (timeout == 0) revert TimeoutMustBePositive();
308
- if (timeout >= 365 days) revert TimeoutMustBeLessThan365Days();
309
-
310
- uint256 bounty = tokens;
311
-
312
- // The arbitrator can set a fee for asking a question.
313
- // This is intended as an anti-spam defence.
314
- // The fee is waived if the arbitrator is asking the question.
315
- // This allows them to set an impossibly high fee and make users proxy the question through them.
316
- // This would allow more sophisticated pricing, question whitelisting etc.
317
- if (arbitrator != NULL_ADDRESS && msg.sender != arbitrator) {
318
- uint256 question_fee = arbitrator_question_fees[arbitrator];
319
- if (bounty < question_fee) revert TokensProvidedMustCoverQuestionFee();
320
- bounty = bounty - question_fee;
321
- balanceOf[arbitrator] = balanceOf[arbitrator] + question_fee;
322
- }
323
-
324
- questions[question_id].content_hash = content_hash;
325
- questions[question_id].arbitrator = arbitrator;
326
- questions[question_id].opening_ts = opening_ts;
327
- questions[question_id].timeout = timeout;
328
-
329
- if (bounty > 0) {
330
- questions[question_id].bounty = bounty;
331
- emit LogFundAnswerBounty(question_id, bounty, bounty, msg.sender);
332
- }
333
-
334
- if (min_bond > 0) {
335
- questions[question_id].min_bond = min_bond;
336
- emit LogMinimumBond(question_id, min_bond);
337
- }
338
- }
339
-
340
162
  /// @notice Add funds to the bounty for a question
341
163
  /// @dev Add bounty funds after the initial question creation. Can be done any time until the question is finalized.
342
164
  /// @param question_id The ID of the question you wish to fund
343
165
  /// @param tokens The number of tokens to fund
344
- function fundAnswerBountyERC20(bytes32 question_id, uint256 tokens) external stateOpen(question_id) {
166
+ function fundAnswerBountyERC20(bytes32 question_id, uint256 tokens) external stateOpen(question_id) notFrozen {
345
167
  _deductTokensOrRevert(tokens);
346
168
  questions[question_id].bounty = questions[question_id].bounty + tokens;
347
169
  emit LogFundAnswerBounty(question_id, tokens, questions[question_id].bounty, msg.sender);
@@ -358,7 +180,7 @@ contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
358
180
  bytes32 answer,
359
181
  uint256 max_previous,
360
182
  uint256 tokens
361
- ) external stateOpen(question_id) bondMustDoubleAndMatchMinimum(question_id, tokens) previousBondMustNotBeatMaxPrevious(question_id, max_previous) {
183
+ ) external stateOpen(question_id) bondMustDoubleAndMatchMinimum(question_id, tokens) previousBondMustNotBeatMaxPrevious(question_id, max_previous) notFrozen {
362
184
  _deductTokensOrRevert(tokens);
363
185
  _addAnswerToHistory(question_id, answer, msg.sender, tokens);
364
186
  _updateCurrentAnswer(question_id, answer);
@@ -377,440 +199,17 @@ contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
377
199
  uint256 max_previous,
378
200
  address answerer,
379
201
  uint256 tokens
380
- ) external stateOpen(question_id) bondMustDoubleAndMatchMinimum(question_id, tokens) previousBondMustNotBeatMaxPrevious(question_id, max_previous) {
202
+ ) external stateOpen(question_id) bondMustDoubleAndMatchMinimum(question_id, tokens) previousBondMustNotBeatMaxPrevious(question_id, max_previous) notFrozen {
381
203
  _deductTokensOrRevert(tokens);
382
- if (answerer == NULL_ADDRESS) revert AnswererMustBeNonZero();
204
+ if (answerer == address(0)) revert AnswererMustBeNonZero();
383
205
  _addAnswerToHistory(question_id, answer, answerer, tokens);
384
206
  _updateCurrentAnswer(question_id, answer);
385
207
  }
386
208
 
387
- function _addAnswerToHistory(bytes32 question_id, bytes32 answer, address answerer, uint256 bond) internal {
388
- bytes32 new_history_hash = keccak256(abi.encodePacked(questions[question_id].history_hash, answer, bond, answerer, false));
389
-
390
- // Update the current bond level, if there's a bond (ie anything except arbitration)
391
- if (bond > 0) {
392
- questions[question_id].bond = bond;
393
- }
394
- questions[question_id].history_hash = new_history_hash;
395
-
396
- emit LogNewAnswer(answer, question_id, new_history_hash, answerer, bond, block.timestamp, false);
397
- }
398
-
399
- function _updateCurrentAnswer(bytes32 question_id, bytes32 answer) internal {
400
- questions[question_id].best_answer = answer;
401
- questions[question_id].finalize_ts = uint32(block.timestamp) + questions[question_id].timeout;
402
- }
403
-
404
- // Like _updateCurrentAnswer but without advancing the timeout
405
- function _updateCurrentAnswerByArbitrator(bytes32 question_id, bytes32 answer) internal {
406
- questions[question_id].best_answer = answer;
407
- questions[question_id].finalize_ts = uint32(block.timestamp);
408
- }
409
-
410
- /// @notice Notify the contract that the arbitrator has been paid for a question, freezing it pending their decision.
411
- /// @dev The arbitrator contract is trusted to only call this if they've been paid, and tell us who paid them.
412
- /// @param question_id The ID of the question
413
- /// @param requester The account that requested arbitration
414
- /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
415
- function notifyOfArbitrationRequest(
416
- bytes32 question_id,
417
- address requester,
418
- uint256 max_previous
419
- ) external onlyArbitrator(question_id) stateOpen(question_id) previousBondMustNotBeatMaxPrevious(question_id, max_previous) {
420
- if (questions[question_id].finalize_ts <= UNANSWERED) revert QuestionMustAlreadyHaveAnAnswerWhenArbitrationIsRequested();
421
- questions[question_id].is_pending_arbitration = true;
422
- emit LogNotifyOfArbitrationRequest(question_id, requester);
423
- }
424
-
425
- /// @notice Cancel a previously-requested arbitration and extend the timeout
426
- /// @dev Useful when doing arbitration across chains that can't be requested atomically
427
- /// @param question_id The ID of the question
428
- function cancelArbitration(bytes32 question_id) external onlyArbitrator(question_id) statePendingArbitration(question_id) {
429
- questions[question_id].is_pending_arbitration = false;
430
- questions[question_id].finalize_ts = uint32(block.timestamp) + questions[question_id].timeout;
431
- emit LogCancelArbitration(question_id);
432
- }
433
-
434
- /// @notice Submit the answer for a question, for use by the arbitrator.
435
- /// @dev Doesn't require (or allow) a bond.
436
- /// If the current final answer is correct, the account should be whoever submitted it.
437
- /// If the current final answer is wrong, the account should be whoever paid for arbitration.
438
- /// However, the answerer stipulations are not enforced by the contract.
439
- /// @param question_id The ID of the question
440
- /// @param answer The answer, encoded into bytes32
441
- /// @param answerer The account credited with this answer for the purpose of bond claims
442
- function submitAnswerByArbitrator(bytes32 question_id, bytes32 answer, address answerer) public onlyArbitrator(question_id) statePendingArbitration(question_id) {
443
- if (answerer == NULL_ADDRESS) revert AnswererMustBeProvided();
444
- emit LogFinalize(question_id, answer);
445
-
446
- questions[question_id].is_pending_arbitration = false;
447
- _addAnswerToHistory(question_id, answer, answerer, 0);
448
- _updateCurrentAnswerByArbitrator(question_id, answer);
449
- }
450
-
451
- /// @notice Submit the answer for a question, for use by the arbitrator, working out the appropriate winner based on the last answer details.
452
- /// @dev Doesn't require (or allow) a bond.
453
- /// @param question_id The ID of the question
454
- /// @param answer The answer, encoded into bytes32
455
- /// @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
456
- /// @param last_history_hash The history hash before the final one
457
- /// @param last_answer The last answer given
458
- /// @param last_answerer The address that supplied the last answer
459
- function assignWinnerAndSubmitAnswerByArbitrator(bytes32 question_id, bytes32 answer, address payee_if_wrong, bytes32 last_history_hash, bytes32 last_answer, address last_answerer) external {
460
- _verifyHistoryInputOrRevert(questions[question_id].history_hash, last_history_hash, last_answer, questions[question_id].bond, last_answerer);
461
-
462
- address payee = (questions[question_id].best_answer == answer) ? last_answerer : payee_if_wrong;
463
- submitAnswerByArbitrator(question_id, answer, payee);
464
- }
465
-
466
- /// @notice Report whether the answer to the specified question is finalized
467
- /// @param question_id The ID of the question
468
- /// @return Return true if finalized
469
- function isFinalized(bytes32 question_id) public view returns (bool) {
470
- uint32 finalize_ts = questions[question_id].finalize_ts;
471
- return (!questions[question_id].is_pending_arbitration && (finalize_ts > UNANSWERED) && (finalize_ts <= uint32(block.timestamp)));
472
- }
473
-
474
- /// @notice (Deprecated) Return the final answer to the specified question, or revert if there isn't one
475
- /// @param question_id The ID of the question
476
- /// @return The answer formatted as a bytes32
477
- function getFinalAnswer(bytes32 question_id) external view stateFinalized(question_id) returns (bytes32) {
478
- return questions[question_id].best_answer;
479
- }
480
-
481
- /// @notice Return the final answer to the specified question, or revert if there isn't one
482
- /// @param question_id The ID of the question
483
- /// @return The answer formatted as a bytes32
484
- function resultFor(bytes32 question_id) public view stateFinalized(question_id) returns (bytes32) {
485
- return questions[question_id].best_answer;
486
- }
487
-
488
- /// @notice Returns whether the question was answered before it had an answer, ie resolved to UNRESOLVED_ANSWER
489
- /// @param question_id The ID of the question
490
- function isSettledTooSoon(bytes32 question_id) public view returns (bool) {
491
- return (resultFor(question_id) == UNRESOLVED_ANSWER);
492
- }
493
-
494
- /// @notice Like resultFor(), but errors out if settled too soon, or returns the result of a replacement if it was reopened at the right time and settled
495
- /// @param question_id The ID of the question
496
- function resultForOnceSettled(bytes32 question_id) external view returns (bytes32) {
497
- bytes32 result = resultFor(question_id);
498
- if (result == UNRESOLVED_ANSWER) {
499
- // Try the replacement
500
- bytes32 replacement_id = reopened_questions[question_id];
501
- if (replacement_id == bytes32(0x0)) revert QuestionWasSettledTooSoonAndHasNotBeenReopened();
502
- // We only try one layer down rather than recursing to keep the gas costs predictable
503
- result = resultFor(replacement_id);
504
- if (result == UNRESOLVED_ANSWER) revert QuestionReplacementWasSettledTooSoonAndHasNotBeenReopened();
505
- }
506
- return result;
507
- }
508
-
509
- /// @notice Asks a new question reopening a previously-asked question that was settled too soon
510
- /// @dev A special version of askQuestion() that replaces a previous question that was settled too soon
511
- /// @param template_id The ID number of the template the question will use
512
- /// @param question A string containing the parameters that will be passed into the template to make the question
513
- /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
514
- /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer
515
- /// @param opening_ts If set, the earliest time it should be possible to answer the question.
516
- /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
517
- /// @param min_bond The minimum bond that can be used to provide the first answer.
518
- /// @param reopens_question_id The ID of the question this reopens
519
- /// @param tokens The number of tokens you want to use as an additional question reward for the reopened question.
520
- /// @return The ID of the newly-created question, created deterministically.
521
- function reopenQuestionERC20(
522
- uint256 template_id,
523
- string memory question,
524
- address arbitrator,
525
- uint32 timeout,
526
- uint32 opening_ts,
527
- uint256 nonce,
528
- uint256 min_bond,
529
- bytes32 reopens_question_id,
530
- uint256 tokens
531
- )
532
- public
533
- returns (
534
- // stateNotCreated is enforced by the internal _askQuestion
535
- bytes32
536
- )
537
- {
538
- // _deductTokensOrRevert will be called when we call askQuestionWithMinBondERC20
539
-
540
- if (!isSettledTooSoon(reopens_question_id)) revert YouCanOnlyReopenQuestionsThatResolvedAsSettledTooSoon();
541
-
542
- bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
543
-
544
- // A reopening must exactly match the original question, except for the nonce and the creator
545
- if (content_hash != questions[reopens_question_id].content_hash) revert ContentHashMismatch();
546
- if (arbitrator != questions[reopens_question_id].arbitrator) revert ArbitratorMismatch();
547
- if (timeout != questions[reopens_question_id].timeout) revert TimeoutMismatch();
548
- if (opening_ts != questions[reopens_question_id].opening_ts) revert Opening_TsMismatch();
549
- if (min_bond != questions[reopens_question_id].min_bond) revert Min_BondMismatch();
550
-
551
- // If the the question was itself reopening some previous question, you'll have to re-reopen the previous question first.
552
- // This ensures the bounty can be passed on to the next attempt of the original question.
553
- if (reopener_questions[reopens_question_id]) revert QuestionIsAlreadyReopeningAPreviousQuestion();
554
-
555
- // A question can only be reopened once, unless the reopening was also settled too soon in which case it can be replaced
556
- bytes32 existing_reopen_question_id = reopened_questions[reopens_question_id];
557
-
558
- // Normally when we reopen a question we will take its bounty and pass it on to the reopened version.
559
- bytes32 take_bounty_from_question_id = reopens_question_id;
560
- // If the question has already been reopened but was again settled too soon, we can transfer its bounty to the next attempt.
561
- if (existing_reopen_question_id != bytes32(0)) {
562
- if (!isSettledTooSoon(existing_reopen_question_id)) revert QuestionHasAlreadyBeenReopened();
563
- // We'll overwrite the reopening with our new question and move the bounty.
564
- // Once that's done we'll detach the failed reopener and you'll be able to reopen that too if you really want, but without the bounty.
565
- reopener_questions[existing_reopen_question_id] = false;
566
- take_bounty_from_question_id = existing_reopen_question_id;
567
- }
568
-
569
- bytes32 question_id = askQuestionWithMinBondERC20(template_id, question, arbitrator, timeout, opening_ts, nonce, min_bond, tokens);
570
-
571
- reopened_questions[reopens_question_id] = question_id;
572
- reopener_questions[question_id] = true;
573
-
574
- questions[question_id].bounty = questions[take_bounty_from_question_id].bounty + questions[question_id].bounty;
575
- questions[take_bounty_from_question_id].bounty = 0;
576
-
577
- emit LogReopenQuestion(question_id, reopens_question_id);
578
-
579
- return question_id;
580
- }
581
-
582
- /// @notice Return the final answer to the specified question, provided it matches the specified criteria.
583
- /// @dev Reverts if the question is not finalized, or if it does not match the specified criteria.
584
- /// @param question_id The ID of the question
585
- /// @param content_hash The hash of the question content (template ID + opening time + question parameter string)
586
- /// @param arbitrator The arbitrator chosen for the question (regardless of whether they are asked to arbitrate)
587
- /// @param min_timeout The timeout set in the initial question settings must be this high or higher
588
- /// @param min_bond The bond sent with the final answer must be this high or higher
589
- /// @return The answer formatted as a bytes32
590
- function getFinalAnswerIfMatches(bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 min_timeout, uint256 min_bond) external view stateFinalized(question_id) returns (bytes32) {
591
- if (content_hash != questions[question_id].content_hash) revert ContentHashMustMatch();
592
- if (arbitrator != questions[question_id].arbitrator) revert ArbitratorMustMatch();
593
- if (min_timeout > questions[question_id].timeout) revert TimeoutMustBeLongEnough();
594
- if (min_bond > questions[question_id].bond) revert BondMustBeHighEnough();
595
- return questions[question_id].best_answer;
596
- }
597
-
598
- /// @notice Assigns the winnings (bounty and bonds) to everyone who gave the accepted answer
599
- /// Caller must provide the answer history, in reverse order
600
- /// @dev Works up the chain and assign bonds to the person who gave the right answer
601
- /// If someone gave the winning answer earlier, they must get paid from the higher bond
602
- /// That means we can't pay out the bond added at n until we have looked at n-1
603
- /// The first answer is authenticated by checking against the stored history_hash.
604
- /// One of the inputs to history_hash is the history_hash before it, so we use that to authenticate the next entry, etc
605
- /// Once we get to a null hash we'll know we're done and there are no more answers.
606
- /// Usually you would call the whole thing in a single transaction, but if not then the data is persisted to pick up later.
607
- /// @param question_id The ID of the question
608
- /// @param history_hashes Second-last-to-first, the hash of each history entry. (Final one should be empty).
609
- /// @param addrs Last-to-first, the address of each answerer
610
- /// @param bonds Last-to-first, the bond supplied with each answer
611
- /// @param answers Last-to-first, each answer supplied
612
- function claimWinnings(bytes32 question_id, bytes32[] memory history_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers) public stateFinalized(question_id) {
613
- if (history_hashes.length == 0) revert AtLeastOneHistoryHashEntryMustBeProvided();
614
-
615
- // These are only set if we split our claim over multiple transactions.
616
- address payee = question_claims[question_id].payee;
617
- uint256 last_bond = question_claims[question_id].last_bond;
618
- uint256 queued_funds = 0;
619
-
620
- // Starts as the hash of the final answer submitted. It'll be cleared when we're done.
621
- // If we're splitting the claim over multiple transactions, it'll be the hash where we left off last time
622
- bytes32 last_history_hash = questions[question_id].history_hash;
623
-
624
- bytes32 best_answer = questions[question_id].best_answer;
625
-
626
- uint256 i;
627
- for (i = 0; i < history_hashes.length; i++) {
628
- _verifyHistoryInputOrRevert(last_history_hash, history_hashes[i], answers[i], bonds[i], addrs[i]);
629
-
630
- queued_funds = queued_funds + last_bond;
631
- (queued_funds, payee) = _processHistoryItem(question_id, best_answer, queued_funds, payee, addrs[i], bonds[i], answers[i]);
632
-
633
- // Line the bond up for next time, when it will be added to somebody's queued_funds
634
- last_bond = bonds[i];
635
-
636
- // Burn (just leave in contract balance) a fraction of all bonds except the final one.
637
- // This creates a cost to increasing your own bond, which could be used to delay resolution maliciously
638
- if (last_bond != questions[question_id].bond) {
639
- last_bond = last_bond - last_bond / BOND_CLAIM_FEE_PROPORTION;
640
- }
641
-
642
- last_history_hash = history_hashes[i];
643
- }
644
-
645
- if (last_history_hash != NULL_HASH) {
646
- // We haven't yet got to the null hash (1st answer), ie the caller didn't supply the full answer chain.
647
- // Persist the details so we can pick up later where we left off later.
648
-
649
- // Pay out the latest payee, only keeping back last_bond which the next may have a claim on
650
- _payPayee(question_id, payee, queued_funds);
651
-
652
- question_claims[question_id].payee = payee;
653
- question_claims[question_id].last_bond = last_bond;
654
- } else {
655
- // There is nothing left below us so the payee can keep what remains
656
- _payPayee(question_id, payee, queued_funds + last_bond);
657
- delete question_claims[question_id];
658
- }
659
-
660
- questions[question_id].history_hash = last_history_hash;
661
- }
662
-
663
- function _payPayee(bytes32 question_id, address payee, uint256 value) internal {
664
- balanceOf[payee] = balanceOf[payee] + value;
665
- emit LogClaim(question_id, payee, value);
666
- }
667
-
668
- function _verifyHistoryInputOrRevert(bytes32 last_history_hash, bytes32 history_hash, bytes32 answer, uint256 bond, address addr) internal pure {
669
- if (last_history_hash != keccak256(abi.encodePacked(history_hash, answer, bond, addr, false))) {
670
- revert HistoryInputProvidedDidNotMatchTheExpectedHash();
671
- }
672
- }
673
-
674
- function _processHistoryItem(bytes32 question_id, bytes32 best_answer, uint256 queued_funds, address payee, address addr, uint256 bond, bytes32 answer) internal returns (uint256, address) {
675
- if (answer == best_answer) {
676
- if (payee == NULL_ADDRESS) {
677
- // The entry is for the first payee we come to, ie the winner.
678
- // They get the question bounty.
679
- payee = addr;
680
-
681
- if (best_answer != UNRESOLVED_ANSWER && questions[question_id].bounty > 0) {
682
- _payPayee(question_id, payee, questions[question_id].bounty);
683
- questions[question_id].bounty = 0;
684
- }
685
- } else if (addr != payee) {
686
- // Answerer has changed, ie we found someone lower down who needs to be paid
687
-
688
- // The lower answerer will take over receiving bonds from higher answerer.
689
- // They should also be paid the takeover fee, which is set at a rate equivalent to their bond.
690
- // (This is our arbitrary rule, to give consistent right-answerers a defence against high-rollers.)
691
-
692
- // There should be enough for the fee, but if not, take what we have.
693
- // There's an edge case involving weird arbitrator behaviour where we may be short.
694
- uint256 answer_takeover_fee = (queued_funds >= bond) ? bond : queued_funds;
695
- // Settle up with the old (higher-bonded) payee
696
- _payPayee(question_id, payee, queued_funds - answer_takeover_fee);
697
-
698
- // Now start queued_funds again for the new (lower-bonded) payee
699
- payee = addr;
700
- queued_funds = answer_takeover_fee;
701
- }
702
- }
703
-
704
- return (queued_funds, payee);
705
- }
706
-
707
- /// @notice Convenience function to assign bounties/bonds for multiple questions in one go, then withdraw all your funds.
708
- /// Caller must provide the answer history for each question, in reverse order
709
- /// @dev Can be called by anyone to assign bonds/bounties, but funds are only withdrawn for the user making the call.
710
- /// @param question_ids The IDs of the questions you want to claim for
711
- /// @param lengths The number of history entries you will supply for each question ID
712
- /// @param hist_hashes In a single list for all supplied questions, the hash of each history entry.
713
- /// @param addrs In a single list for all supplied questions, the address of each answerer
714
- /// @param bonds In a single list for all supplied questions, the bond supplied with each answer
715
- /// @param answers In a single list for all supplied questions, each answer supplied
716
- function claimMultipleAndWithdrawBalance(
717
- bytes32[] memory question_ids,
718
- uint256[] memory lengths,
719
- bytes32[] memory hist_hashes,
720
- address[] memory addrs,
721
- uint256[] memory bonds,
722
- bytes32[] memory answers
723
- )
724
- public
725
- stateAny // The finalization checks are done in the claimWinnings function
726
- {
727
- uint256 qi;
728
- uint256 i;
729
- for (qi = 0; qi < question_ids.length; qi++) {
730
- bytes32 qid = question_ids[qi];
731
- uint256 ln = lengths[qi];
732
- bytes32[] memory hh = new bytes32[](ln);
733
- address[] memory ad = new address[](ln);
734
- uint256[] memory bo = new uint256[](ln);
735
- bytes32[] memory an = new bytes32[](ln);
736
- uint256 j;
737
- for (j = 0; j < ln; j++) {
738
- hh[j] = hist_hashes[i];
739
- ad[j] = addrs[i];
740
- bo[j] = bonds[i];
741
- an[j] = answers[i];
742
- i++;
743
- }
744
- claimWinnings(qid, hh, ad, bo, an);
745
- }
746
- withdraw();
747
- }
748
-
749
- /// @notice Returns the questions's content hash, identifying the question content
750
- /// @param question_id The ID of the question
751
- function getContentHash(bytes32 question_id) public view returns (bytes32) {
752
- return questions[question_id].content_hash;
753
- }
754
-
755
- /// @notice Returns the arbitrator address for the question
756
- /// @param question_id The ID of the question
757
- function getArbitrator(bytes32 question_id) public view returns (address) {
758
- return questions[question_id].arbitrator;
759
- }
760
-
761
- /// @notice Returns the timestamp when the question can first be answered
762
- /// @param question_id The ID of the question
763
- function getOpeningTS(bytes32 question_id) public view returns (uint32) {
764
- return questions[question_id].opening_ts;
765
- }
766
-
767
- /// @notice Returns the timeout in seconds used after each answer
768
- /// @param question_id The ID of the question
769
- function getTimeout(bytes32 question_id) public view returns (uint32) {
770
- return questions[question_id].timeout;
771
- }
772
-
773
- /// @notice Returns the timestamp at which the question will be/was finalized
774
- /// @param question_id The ID of the question
775
- function getFinalizeTS(bytes32 question_id) public view returns (uint32) {
776
- return questions[question_id].finalize_ts;
777
- }
778
-
779
- /// @notice Returns whether the question is pending arbitration
780
- /// @param question_id The ID of the question
781
- function isPendingArbitration(bytes32 question_id) public view returns (bool) {
782
- return questions[question_id].is_pending_arbitration;
783
- }
784
-
785
- /// @notice Returns the current total unclaimed bounty
786
- /// @dev Set back to zero once the bounty has been claimed
787
- /// @param question_id The ID of the question
788
- function getBounty(bytes32 question_id) public view returns (uint256) {
789
- return questions[question_id].bounty;
790
- }
791
-
792
- /// @notice Returns the current best answer
793
- /// @param question_id The ID of the question
794
- function getBestAnswer(bytes32 question_id) public view returns (bytes32) {
795
- return questions[question_id].best_answer;
796
- }
797
-
798
- /// @notice Returns the history hash of the question
799
- /// @param question_id The ID of the question
800
- /// @dev Updated on each answer, then rewound as each is claimed
801
- function getHistoryHash(bytes32 question_id) public view returns (bytes32) {
802
- return questions[question_id].history_hash;
803
- }
804
-
805
- /// @notice Returns the highest bond posted so far for a question
806
- /// @param question_id The ID of the question
807
- function getBond(bytes32 question_id) public view returns (uint256) {
808
- return questions[question_id].bond;
809
- }
810
-
811
- /// @notice Returns the minimum bond that can answer the question
812
- /// @param question_id The ID of the question
813
- function getMinBond(bytes32 question_id) public view returns (uint256) {
814
- return questions[question_id].min_bond;
209
+ function withdraw() public override notFrozen {
210
+ uint256 bal = balanceOf[msg.sender];
211
+ balanceOf[msg.sender] = 0;
212
+ require(token.transfer(msg.sender, bal));
213
+ emit LogWithdraw(msg.sender, bal);
815
214
  }
816
215
  }