@reality.eth/contracts 3.0.45 → 3.0.47

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 (71) hide show
  1. package/.prettierrc +16 -0
  2. package/abi/solc-0.8.20/RealityETH-4.0.abi.json +1 -0
  3. package/abi/solc-0.8.20/RealityETH_ERC20-4.0.abi.json +1 -0
  4. package/bytecode/Arbitrator.bin +1 -1
  5. package/bytecode/RealityETH-3.0.bin +1 -1
  6. package/bytecode/RealityETH-4.0.bin +1 -0
  7. package/bytecode/RealityETH_ERC20-3.0.bin +1 -1
  8. package/bytecode/RealityETH_ERC20-4.0.bin +1 -0
  9. package/chains/chainid.network.json +5033 -431
  10. package/chains/deployments/300/ETH/RealityETH-3.0.json +7 -0
  11. package/chains/supported.json +3 -0
  12. package/development/contracts/.solhint.json +9 -0
  13. package/development/contracts/Arbitrator.sol +32 -81
  14. package/development/contracts/BalanceHolder.sol +4 -10
  15. package/development/contracts/BalanceHolder_ERC20.sol +5 -11
  16. package/development/contracts/CallbackClient.sol +3 -10
  17. package/development/contracts/ERC20Token.sol +29 -30
  18. package/development/contracts/ExplodingCallbackClient.sol +1 -4
  19. package/development/contracts/IArbitrator.sol +23 -23
  20. package/development/contracts/IArbitratorCore.sol +14 -0
  21. package/development/contracts/IArbitratorCrowdFundable.sol +8 -0
  22. package/development/contracts/IArbitratorErrors.sol +10 -0
  23. package/development/contracts/IArbitratorForeignProxy.sol +15 -7
  24. package/development/contracts/IArbitratorLegacy.sol +9 -0
  25. package/development/contracts/IArbitratorManagement.sol +25 -0
  26. package/development/contracts/IBalanceHolder.sol +3 -3
  27. package/development/contracts/IBalanceHolder_ERC20.sol +5 -5
  28. package/development/contracts/IERC20.sol +4 -5
  29. package/development/contracts/IOwned.sol +3 -3
  30. package/development/contracts/IRealityETH.sol +5 -58
  31. package/development/contracts/IRealityETHCommitReveal.sol +17 -0
  32. package/development/contracts/IRealityETHCommitReveal_ERC20.sol +16 -0
  33. package/development/contracts/IRealityETHCore.sol +135 -0
  34. package/development/contracts/IRealityETHCore_ERC20.sol +149 -0
  35. package/development/contracts/IRealityETHErrors.sol +86 -0
  36. package/development/contracts/IRealityETHErrors_ERC20.sol +13 -0
  37. package/development/contracts/IRealityETH_ERC20.sol +5 -62
  38. package/development/contracts/Owned.sol +4 -6
  39. package/development/contracts/{RealityETH-3.0.sol → RealityETH-4.0.sol} +221 -383
  40. package/development/contracts/{RealityETH_ERC20-3.0.sol → RealityETH_ERC20-4.0.sol} +238 -474
  41. package/development/contracts/RealityETH_ERC20_Factory.sol +47 -42
  42. package/development/contracts/RegisteredWalletArbitrator.sol +8 -15
  43. package/development/contracts/compile.py +3 -0
  44. package/development/scripts/string_to_error.py +111 -0
  45. package/development/version_checksums/check_matches_flat.sh +20 -0
  46. package/development/version_checksums/check_matches_last.sh +37 -0
  47. package/development/version_checksums/log_bytecode_history.sh +41 -0
  48. package/generated/chains.json +33 -5
  49. package/generated/tokens.json +1 -0
  50. package/non_standard_deployments/zksync/LICENSE +21 -0
  51. package/non_standard_deployments/zksync/README.md +51 -0
  52. package/non_standard_deployments/zksync/deploy/deploy.ts +11 -0
  53. package/non_standard_deployments/zksync/deploy/utils.ts +168 -0
  54. package/non_standard_deployments/zksync/hardhat.config.ts +55 -0
  55. package/non_standard_deployments/zksync/package-lock.json +6295 -0
  56. package/non_standard_deployments/zksync/package.json +34 -0
  57. package/package.json +7 -3
  58. package/scripts/transfer_arbitrator.js +60 -0
  59. package/tests/python/requirements.txt +55 -54
  60. package/tests/python/test.py +150 -50
  61. package/tests/python/test_erc20.py +15 -14
  62. package/tokens/ETH.json +3 -2
  63. package/non_standard_deployments/zksync2/README +0 -21
  64. package/non_standard_deployments/zksync2/contracts/RealityETH_ERC20-3.0.sol +0 -1096
  65. package/non_standard_deployments/zksync2/deploy/deploy.ts +0 -62
  66. package/non_standard_deployments/zksync2/deploy/deploy_erc20.ts +0 -56
  67. package/non_standard_deployments/zksync2/deploy/deploy_mainnet.ts +0 -46
  68. package/non_standard_deployments/zksync2/hardhat.config.ts +0 -27
  69. package/non_standard_deployments/zksync2/package.json +0 -15
  70. package/non_standard_deployments/zksync2/yarn.lock +0 -2898
  71. /package/non_standard_deployments/{zksync2 → zksync}/contracts/RealityETH-3.0.sol +0 -0
@@ -1,76 +1,38 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
 
3
- pragma solidity ^0.8.10;
3
+ pragma solidity ^0.8.20;
4
4
 
5
- import './IRealityETH.sol';
5
+ import {IRealityETHCore} from "./IRealityETHCore.sol";
6
+ import {BalanceHolder} from "./BalanceHolder.sol";
6
7
 
7
- import './BalanceHolder.sol';
8
-
9
- contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
10
-
11
- address constant NULL_ADDRESS = address(0);
8
+ // solhint-disable-next-line contract-name-camelcase
9
+ contract RealityETH_v4_0 is BalanceHolder, IRealityETHCore {
10
+ address private constant NULL_ADDRESS = address(0);
12
11
 
13
12
  // History hash when no history is created, or history has been cleared
14
- bytes32 constant NULL_HASH = bytes32(0);
13
+ bytes32 private constant NULL_HASH = bytes32(0);
15
14
 
16
15
  // An unitinalized finalize_ts for a question will indicate an unanswered question.
17
- uint32 constant UNANSWERED = 0;
18
-
19
- // An unanswered reveal_ts for a commitment will indicate that it does not exist.
20
- uint256 constant COMMITMENT_NON_EXISTENT = 0;
21
-
22
- // Commit->reveal timeout is 1/8 of the question timeout (rounded down).
23
- uint32 constant COMMITMENT_TIMEOUT_RATIO = 8;
16
+ uint32 private constant UNANSWERED = 0;
24
17
 
25
18
  // Proportion withheld when you claim an earlier bond.
26
- uint256 constant BOND_CLAIM_FEE_PROPORTION = 40; // One 40th ie 2.5%
19
+ uint256 private constant BOND_CLAIM_FEE_PROPORTION = 40; // One 40th ie 2.5%
27
20
 
28
21
  // Special value representing a question that was answered too soon.
29
22
  // bytes32(-2). By convention we use bytes32(-1) for "invalid", although the contract does not handle this.
30
- bytes32 constant UNRESOLVED_ANSWER = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe;
31
-
32
- struct Question {
33
- bytes32 content_hash;
34
- address arbitrator;
35
- uint32 opening_ts;
36
- uint32 timeout;
37
- uint32 finalize_ts;
38
- bool is_pending_arbitration;
39
- uint256 bounty;
40
- bytes32 best_answer;
41
- bytes32 history_hash;
42
- uint256 bond;
43
- uint256 min_bond;
44
- }
45
-
46
- // Stored in a mapping indexed by commitment_id, a hash of commitment hash, question, bond.
47
- struct Commitment {
48
- uint32 reveal_ts;
49
- bool is_revealed;
50
- bytes32 revealed_answer;
51
- }
52
-
53
- // Only used when claiming more bonds than fits into a transaction
54
- // Stored in a mapping indexed by question_id.
55
- struct Claim {
56
- address payee;
57
- uint256 last_bond;
58
- uint256 queued_funds;
59
- }
60
-
61
- uint256 nextTemplateID = 0;
23
+ bytes32 private constant UNRESOLVED_ANSWER = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe;
24
+
25
+ uint256 private nextTemplateID = 0;
62
26
  mapping(uint256 => uint256) public templates;
63
27
  mapping(uint256 => bytes32) public template_hashes;
64
28
  mapping(bytes32 => Question) public questions;
65
29
  mapping(bytes32 => Claim) public question_claims;
66
- mapping(bytes32 => Commitment) public commitments;
67
- mapping(address => uint256) public arbitrator_question_fees;
30
+ mapping(address => uint256) public arbitrator_question_fees;
68
31
  mapping(bytes32 => bytes32) public reopened_questions;
69
32
  mapping(bytes32 => bool) public reopener_questions;
70
33
 
71
-
72
34
  modifier onlyArbitrator(bytes32 question_id) {
73
- require(msg.sender == questions[question_id].arbitrator, "msg.sender must be arbitrator");
35
+ if (msg.sender != questions[question_id].arbitrator) revert MsgSenderMustBeArbitrator();
74
36
  _;
75
37
  }
76
38
 
@@ -79,57 +41,58 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
79
41
  }
80
42
 
81
43
  modifier stateNotCreated(bytes32 question_id) {
82
- require(questions[question_id].timeout == 0, "question must not exist");
44
+ if (questions[question_id].timeout != 0) revert QuestionMustNotExist();
83
45
  _;
84
46
  }
85
47
 
86
48
  modifier stateOpen(bytes32 question_id) {
87
- require(questions[question_id].timeout > 0, "question must exist");
88
- require(!questions[question_id].is_pending_arbitration, "question must not be pending arbitration");
49
+ if (questions[question_id].timeout == 0) revert QuestionMustExist();
50
+ if (questions[question_id].is_pending_arbitration) revert QuestionMustNotBePendingArbitration();
89
51
  uint32 finalize_ts = questions[question_id].finalize_ts;
90
- require(finalize_ts == UNANSWERED || finalize_ts > uint32(block.timestamp), "finalization deadline must not have passed");
52
+ if (finalize_ts != UNANSWERED && finalize_ts <= uint32(block.timestamp)) revert FinalizationDeadlineMustNotHavePassed();
91
53
  uint32 opening_ts = questions[question_id].opening_ts;
92
- require(opening_ts == 0 || opening_ts <= uint32(block.timestamp), "opening date must have passed");
54
+ if (opening_ts != 0 && opening_ts > uint32(block.timestamp)) revert OpeningDateMustHavePassed();
93
55
  _;
94
56
  }
95
57
 
96
58
  modifier statePendingArbitration(bytes32 question_id) {
97
- require(questions[question_id].is_pending_arbitration, "question must be pending arbitration");
59
+ if (!questions[question_id].is_pending_arbitration) revert QuestionMustBePendingArbitration();
98
60
  _;
99
61
  }
100
62
 
101
63
  modifier stateOpenOrPendingArbitration(bytes32 question_id) {
102
- require(questions[question_id].timeout > 0, "question must exist");
64
+ if (questions[question_id].timeout == 0) revert QuestionMustExist();
103
65
  uint32 finalize_ts = questions[question_id].finalize_ts;
104
- require(finalize_ts == UNANSWERED || finalize_ts > uint32(block.timestamp), "finalization dealine must not have passed");
66
+ if (finalize_ts != UNANSWERED && finalize_ts <= uint32(block.timestamp)) revert FinalizationDealineMustNotHavePassed();
105
67
  uint32 opening_ts = questions[question_id].opening_ts;
106
- require(opening_ts == 0 || opening_ts <= uint32(block.timestamp), "opening date must have passed");
68
+ if (opening_ts != 0 && opening_ts > uint32(block.timestamp)) revert OpeningDateMustHavePassed();
107
69
  _;
108
70
  }
109
71
 
110
72
  modifier stateFinalized(bytes32 question_id) {
111
- require(isFinalized(question_id), "question must be finalized");
73
+ if (!isFinalized(question_id)) revert QuestionMustBeFinalized();
112
74
  _;
113
75
  }
114
76
 
115
- modifier bondMustDoubleAndMatchMinimum(bytes32 question_id) {
116
- require(msg.value > 0, "bond must be positive");
77
+ modifier bondMustDoubleAndMatchMinimum(bytes32 question_id, uint256 tokens) {
78
+ if (tokens == 0) revert BondMustBePositive();
117
79
  uint256 current_bond = questions[question_id].bond;
118
80
  if (current_bond == 0) {
119
- require(msg.value >= (questions[question_id].min_bond), "bond must exceed the minimum");
81
+ if (tokens < (questions[question_id].min_bond)) revert BondMustExceedTheMinimum();
120
82
  } else {
121
- require(msg.value >= (current_bond * 2), "bond must be double at least previous bond");
83
+ if (tokens < (current_bond * 2)) revert BondMustBeDoubleAtLeastPreviousBond();
122
84
  }
123
85
  _;
124
86
  }
125
87
 
126
88
  modifier previousBondMustNotBeatMaxPrevious(bytes32 question_id, uint256 max_previous) {
127
89
  if (max_previous > 0) {
128
- require(questions[question_id].bond <= max_previous, "bond must exceed max_previous");
90
+ if (questions[question_id].bond > max_previous) revert BondMustExceedMax_Previous();
129
91
  }
130
92
  _;
131
93
  }
132
94
 
95
+ /* solhint-disable quotes */
133
96
  /// @notice Constructor, sets up some initial templates
134
97
  /// @dev Creates some generalized templates for different question types used in the DApp.
135
98
  constructor() {
@@ -139,13 +102,12 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
139
102
  createTemplate('{"title": "%s", "type": "multiple-select", "outcomes": [%s], "category": "%s", "lang": "%s"}');
140
103
  createTemplate('{"title": "%s", "type": "datetime", "category": "%s", "lang": "%s"}');
141
104
  }
105
+ /* solhint-enable quotes */
142
106
 
143
- /// @notice Function for arbitrator to set an optional per-question fee.
107
+ /// @notice Function for arbitrator to set an optional per-question fee.
144
108
  /// @dev The per-question fee, charged when a question is asked, is intended as an anti-spam measure.
145
109
  /// @param fee The fee to be charged by the arbitrator when a question is asked
146
- function setQuestionFee(uint256 fee)
147
- stateAny()
148
- external {
110
+ function setQuestionFee(uint256 fee) external stateAny {
149
111
  arbitrator_question_fees[msg.sender] = fee;
150
112
  emit LogSetQuestionFee(msg.sender, fee);
151
113
  }
@@ -155,9 +117,7 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
155
117
  /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage.
156
118
  /// @param content The template content
157
119
  /// @return The ID of the newly-created template, which is created sequentially.
158
- function createTemplate(string memory content)
159
- stateAny()
160
- public returns (uint256) {
120
+ function createTemplate(string memory content) public stateAny returns (uint256) {
161
121
  uint256 id = nextTemplateID;
162
122
  templates[id] = block.number;
163
123
  template_hashes[id] = keccak256(abi.encodePacked(content));
@@ -176,11 +136,20 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
176
136
  /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
177
137
  /// @return The ID of the newly-created template, which is created sequentially.
178
138
  function createTemplateAndAskQuestion(
179
- string memory content,
180
- string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce
181
- )
182
- // stateNotCreated is enforced by the internal _askQuestion
183
- public payable returns (bytes32) {
139
+ string memory content,
140
+ string memory question,
141
+ address arbitrator,
142
+ uint32 timeout,
143
+ uint32 opening_ts,
144
+ uint256 nonce
145
+ )
146
+ public
147
+ payable
148
+ returns (
149
+ // stateNotCreated is enforced by the internal _askQuestion
150
+ bytes32
151
+ )
152
+ {
184
153
  uint256 template_id = createTemplate(content);
185
154
  return askQuestion(template_id, question, arbitrator, timeout, opening_ts, nonce);
186
155
  }
@@ -194,18 +163,29 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
194
163
  /// @param opening_ts If set, the earliest time it should be possible to answer the question.
195
164
  /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
196
165
  /// @return The ID of the newly-created question, created deterministically.
197
- function askQuestion(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce)
198
- // stateNotCreated is enforced by the internal _askQuestion
199
- public payable returns (bytes32) {
200
-
201
- require(templates[template_id] > 0, "template must exist");
166
+ function askQuestion(
167
+ uint256 template_id,
168
+ string memory question,
169
+ address arbitrator,
170
+ uint32 timeout,
171
+ uint32 opening_ts,
172
+ uint256 nonce
173
+ )
174
+ public
175
+ payable
176
+ returns (
177
+ // stateNotCreated is enforced by the internal _askQuestion
178
+ bytes32
179
+ )
180
+ {
181
+ if (templates[template_id] == 0) revert TemplateMustExist();
202
182
 
203
183
  bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
204
184
  bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, uint256(0), address(this), msg.sender, nonce));
205
185
 
206
186
  // We emit this event here because _askQuestion doesn't need to know the unhashed question. Other events are emitted by _askQuestion.
207
187
  emit LogNewQuestion(question_id, msg.sender, template_id, question, content_hash, arbitrator, timeout, opening_ts, nonce, block.timestamp);
208
- _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, 0);
188
+ _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, 0, msg.value);
209
189
 
210
190
  return question_id;
211
191
  }
@@ -220,11 +200,23 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
220
200
  /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
221
201
  /// @param min_bond The minimum bond that may be used for an answer.
222
202
  /// @return The ID of the newly-created question, created deterministically.
223
- function askQuestionWithMinBond(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 min_bond)
224
- // stateNotCreated is enforced by the internal _askQuestion
225
- public payable returns (bytes32) {
226
-
227
- require(templates[template_id] > 0, "template must exist");
203
+ function askQuestionWithMinBond(
204
+ uint256 template_id,
205
+ string memory question,
206
+ address arbitrator,
207
+ uint32 timeout,
208
+ uint32 opening_ts,
209
+ uint256 nonce,
210
+ uint256 min_bond
211
+ )
212
+ public
213
+ payable
214
+ returns (
215
+ // stateNotCreated is enforced by the internal _askQuestion
216
+ bytes32
217
+ )
218
+ {
219
+ if (templates[template_id] == 0) revert TemplateMustExist();
228
220
 
229
221
  bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
230
222
  bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, min_bond, address(this), msg.sender, nonce));
@@ -232,29 +224,26 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
232
224
  // We emit this event here because _askQuestion doesn't need to know the unhashed question.
233
225
  // Other events are emitted by _askQuestion.
234
226
  emit LogNewQuestion(question_id, msg.sender, template_id, question, content_hash, arbitrator, timeout, opening_ts, nonce, block.timestamp);
235
- _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, min_bond);
227
+ _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, min_bond, msg.value);
236
228
 
237
229
  return question_id;
238
230
  }
239
231
 
240
- function _askQuestion(bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 min_bond)
241
- stateNotCreated(question_id)
242
- internal {
243
-
232
+ function _askQuestion(bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 min_bond, uint256 tokens) internal stateNotCreated(question_id) {
244
233
  // A timeout of 0 makes no sense, and we will use this to check existence
245
- require(timeout > 0, "timeout must be positive");
246
- require(timeout < 365 days, "timeout must be less than 365 days");
234
+ if (timeout == 0) revert TimeoutMustBePositive();
235
+ if (timeout >= 365 days) revert TimeoutMustBeLessThan365Days();
247
236
 
248
- uint256 bounty = msg.value;
237
+ uint256 bounty = tokens;
249
238
 
250
- // The arbitrator can set a fee for asking a question.
239
+ // The arbitrator can set a fee for asking a question.
251
240
  // This is intended as an anti-spam defence.
252
241
  // The fee is waived if the arbitrator is asking the question.
253
242
  // This allows them to set an impossibly high fee and make users proxy the question through them.
254
243
  // This would allow more sophisticated pricing, question whitelisting etc.
255
244
  if (arbitrator != NULL_ADDRESS && msg.sender != arbitrator) {
256
245
  uint256 question_fee = arbitrator_question_fees[arbitrator];
257
- require(bounty >= question_fee, "ETH provided must cover question fee");
246
+ if (bounty < question_fee) revert TokensProvidedMustCoverQuestionFee();
258
247
  bounty = bounty - question_fee;
259
248
  balanceOf[arbitrator] = balanceOf[arbitrator] + question_fee;
260
249
  }
@@ -273,120 +262,49 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
273
262
  questions[question_id].min_bond = min_bond;
274
263
  emit LogMinimumBond(question_id, min_bond);
275
264
  }
276
-
277
265
  }
278
266
 
279
267
  /// @notice Add funds to the bounty for a question
280
268
  /// @dev Add bounty funds after the initial question creation. Can be done any time until the question is finalized.
281
269
  /// @param question_id The ID of the question you wish to fund
282
- function fundAnswerBounty(bytes32 question_id)
283
- stateOpen(question_id)
284
- external payable {
270
+ function fundAnswerBounty(bytes32 question_id) external payable stateOpen(question_id) {
285
271
  questions[question_id].bounty = questions[question_id].bounty + msg.value;
286
272
  emit LogFundAnswerBounty(question_id, msg.value, questions[question_id].bounty, msg.sender);
287
273
  }
288
274
 
289
275
  /// @notice Submit an answer for a question.
290
276
  /// @dev Adds the answer to the history and updates the current "best" answer.
291
- /// May be subject to front-running attacks; Substitute submitAnswerCommitment()->submitAnswerReveal() to prevent them.
292
277
  /// @param question_id The ID of the question
293
278
  /// @param answer The answer, encoded into bytes32
294
279
  /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
295
- function submitAnswer(bytes32 question_id, bytes32 answer, uint256 max_previous)
296
- stateOpen(question_id)
297
- bondMustDoubleAndMatchMinimum(question_id)
298
- previousBondMustNotBeatMaxPrevious(question_id, max_previous)
299
- external payable {
300
- _addAnswerToHistory(question_id, answer, msg.sender, msg.value, false);
280
+ function submitAnswer(
281
+ bytes32 question_id,
282
+ bytes32 answer,
283
+ uint256 max_previous
284
+ ) external payable stateOpen(question_id) bondMustDoubleAndMatchMinimum(question_id, msg.value) previousBondMustNotBeatMaxPrevious(question_id, max_previous) {
285
+ _addAnswerToHistory(question_id, answer, msg.sender, msg.value);
301
286
  _updateCurrentAnswer(question_id, answer);
302
287
  }
303
288
 
304
289
  /// @notice Submit an answer for a question, crediting it to the specified account.
305
290
  /// @dev Adds the answer to the history and updates the current "best" answer.
306
- /// May be subject to front-running attacks; Substitute submitAnswerCommitment()->submitAnswerReveal() to prevent them.
307
291
  /// @param question_id The ID of the question
308
292
  /// @param answer The answer, encoded into bytes32
309
293
  /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
310
294
  /// @param answerer The account to which the answer should be credited
311
- function submitAnswerFor(bytes32 question_id, bytes32 answer, uint256 max_previous, address answerer)
312
- stateOpen(question_id)
313
- bondMustDoubleAndMatchMinimum(question_id)
314
- previousBondMustNotBeatMaxPrevious(question_id, max_previous)
315
- external payable {
316
- require(answerer != NULL_ADDRESS, "answerer must be non-zero");
317
- _addAnswerToHistory(question_id, answer, answerer, msg.value, false);
295
+ function submitAnswerFor(
296
+ bytes32 question_id,
297
+ bytes32 answer,
298
+ uint256 max_previous,
299
+ address answerer
300
+ ) external payable stateOpen(question_id) bondMustDoubleAndMatchMinimum(question_id, msg.value) previousBondMustNotBeatMaxPrevious(question_id, max_previous) {
301
+ if (answerer == NULL_ADDRESS) revert AnswererMustBeNonZero();
302
+ _addAnswerToHistory(question_id, answer, answerer, msg.value);
318
303
  _updateCurrentAnswer(question_id, answer);
319
304
  }
320
305
 
321
- // @notice Verify and store a commitment, including an appropriate timeout
322
- // @param question_id The ID of the question to store
323
- // @param commitment The ID of the commitment
324
- function _storeCommitment(bytes32 question_id, bytes32 commitment_id)
325
- internal
326
- {
327
- require(commitments[commitment_id].reveal_ts == COMMITMENT_NON_EXISTENT, "commitment must not already exist");
328
-
329
- uint32 commitment_timeout = questions[question_id].timeout / COMMITMENT_TIMEOUT_RATIO;
330
- commitments[commitment_id].reveal_ts = uint32(block.timestamp) + commitment_timeout;
331
- }
332
-
333
- /// @notice Submit the hash of an answer, laying your claim to that answer if you reveal it in a subsequent transaction.
334
- /// @dev Creates a hash, commitment_id, uniquely identifying this answer, to this question, with this bond.
335
- /// The commitment_id is stored in the answer history where the answer would normally go.
336
- /// Does not update the current best answer - this is left to the later submitAnswerReveal() transaction.
337
- /// @param question_id The ID of the question
338
- /// @param answer_hash The hash of your answer, plus a nonce that you will later reveal
339
- /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
340
- /// @param _answerer If specified, the address to be given as the question answerer. Defaults to the sender.
341
- /// @dev Specifying the answerer is useful if you want to delegate the commit-and-reveal to a third-party.
342
- function submitAnswerCommitment(bytes32 question_id, bytes32 answer_hash, uint256 max_previous, address _answerer)
343
- stateOpen(question_id)
344
- bondMustDoubleAndMatchMinimum(question_id)
345
- previousBondMustNotBeatMaxPrevious(question_id, max_previous)
346
- external payable {
347
-
348
- bytes32 commitment_id = keccak256(abi.encodePacked(question_id, answer_hash, msg.value));
349
- address answerer = (_answerer == NULL_ADDRESS) ? msg.sender : _answerer;
350
- _storeCommitment(question_id, commitment_id);
351
- _addAnswerToHistory(question_id, commitment_id, answerer, msg.value, true);
352
-
353
- }
354
-
355
- /// @notice Submit the answer whose hash you sent in a previous submitAnswerCommitment() transaction
356
- /// @dev Checks the parameters supplied recreate an existing commitment, and stores the revealed answer
357
- /// Updates the current answer unless someone has since supplied a new answer with a higher bond
358
- /// msg.sender is intentionally not restricted to the user who originally sent the commitment;
359
- /// For example, the user may want to provide the answer+nonce to a third-party service and let them send the tx
360
- /// NB If we are pending arbitration, it will be up to the arbitrator to wait and see any outstanding reveal is sent
361
- /// @param question_id The ID of the question
362
- /// @param answer The answer, encoded as bytes32
363
- /// @param nonce The nonce that, combined with the answer, recreates the answer_hash you gave in submitAnswerCommitment()
364
- /// @param bond The bond that you paid in your submitAnswerCommitment() transaction
365
- function submitAnswerReveal(bytes32 question_id, bytes32 answer, uint256 nonce, uint256 bond)
366
- stateOpenOrPendingArbitration(question_id)
367
- external {
368
-
369
- bytes32 answer_hash = keccak256(abi.encodePacked(answer, nonce));
370
- bytes32 commitment_id = keccak256(abi.encodePacked(question_id, answer_hash, bond));
371
-
372
- require(!commitments[commitment_id].is_revealed, "commitment must not have been revealed yet");
373
- require(commitments[commitment_id].reveal_ts > uint32(block.timestamp), "reveal deadline must not have passed");
374
-
375
- commitments[commitment_id].revealed_answer = answer;
376
- commitments[commitment_id].is_revealed = true;
377
-
378
- if (bond == questions[question_id].bond) {
379
- _updateCurrentAnswer(question_id, answer);
380
- }
381
-
382
- emit LogAnswerReveal(question_id, msg.sender, answer_hash, answer, nonce, bond);
383
-
384
- }
385
-
386
- function _addAnswerToHistory(bytes32 question_id, bytes32 answer_or_commitment_id, address answerer, uint256 bond, bool is_commitment)
387
- internal
388
- {
389
- bytes32 new_history_hash = keccak256(abi.encodePacked(questions[question_id].history_hash, answer_or_commitment_id, bond, answerer, is_commitment));
306
+ function _addAnswerToHistory(bytes32 question_id, bytes32 answer, address answerer, uint256 bond) internal {
307
+ bytes32 new_history_hash = keccak256(abi.encodePacked(questions[question_id].history_hash, answer, bond, answerer, false));
390
308
 
391
309
  // Update the current bond level, if there's a bond (ie anything except arbitration)
392
310
  if (bond > 0) {
@@ -394,18 +312,16 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
394
312
  }
395
313
  questions[question_id].history_hash = new_history_hash;
396
314
 
397
- emit LogNewAnswer(answer_or_commitment_id, question_id, new_history_hash, answerer, bond, block.timestamp, is_commitment);
315
+ emit LogNewAnswer(answer, question_id, new_history_hash, answerer, bond, block.timestamp, false);
398
316
  }
399
317
 
400
- function _updateCurrentAnswer(bytes32 question_id, bytes32 answer)
401
- internal {
318
+ function _updateCurrentAnswer(bytes32 question_id, bytes32 answer) internal {
402
319
  questions[question_id].best_answer = answer;
403
320
  questions[question_id].finalize_ts = uint32(block.timestamp) + questions[question_id].timeout;
404
321
  }
405
322
 
406
323
  // Like _updateCurrentAnswer but without advancing the timeout
407
- function _updateCurrentAnswerByArbitrator(bytes32 question_id, bytes32 answer)
408
- internal {
324
+ function _updateCurrentAnswerByArbitrator(bytes32 question_id, bytes32 answer) internal {
409
325
  questions[question_id].best_answer = answer;
410
326
  questions[question_id].finalize_ts = uint32(block.timestamp);
411
327
  }
@@ -415,12 +331,12 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
415
331
  /// @param question_id The ID of the question
416
332
  /// @param requester The account that requested arbitration
417
333
  /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
418
- function notifyOfArbitrationRequest(bytes32 question_id, address requester, uint256 max_previous)
419
- onlyArbitrator(question_id)
420
- stateOpen(question_id)
421
- previousBondMustNotBeatMaxPrevious(question_id, max_previous)
422
- external {
423
- require(questions[question_id].finalize_ts > UNANSWERED, "Question must already have an answer when arbitration is requested");
334
+ function notifyOfArbitrationRequest(
335
+ bytes32 question_id,
336
+ address requester,
337
+ uint256 max_previous
338
+ ) external onlyArbitrator(question_id) stateOpen(question_id) previousBondMustNotBeatMaxPrevious(question_id, max_previous) {
339
+ if (questions[question_id].finalize_ts <= UNANSWERED) revert QuestionMustAlreadyHaveAnAnswerWhenArbitrationIsRequested();
424
340
  questions[question_id].is_pending_arbitration = true;
425
341
  emit LogNotifyOfArbitrationRequest(question_id, requester);
426
342
  }
@@ -428,10 +344,7 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
428
344
  /// @notice Cancel a previously-requested arbitration and extend the timeout
429
345
  /// @dev Useful when doing arbitration across chains that can't be requested atomically
430
346
  /// @param question_id The ID of the question
431
- function cancelArbitration(bytes32 question_id)
432
- onlyArbitrator(question_id)
433
- statePendingArbitration(question_id)
434
- external {
347
+ function cancelArbitration(bytes32 question_id) external onlyArbitrator(question_id) statePendingArbitration(question_id) {
435
348
  questions[question_id].is_pending_arbitration = false;
436
349
  questions[question_id].finalize_ts = uint32(block.timestamp) + questions[question_id].timeout;
437
350
  emit LogCancelArbitration(question_id);
@@ -445,18 +358,13 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
445
358
  /// @param question_id The ID of the question
446
359
  /// @param answer The answer, encoded into bytes32
447
360
  /// @param answerer The account credited with this answer for the purpose of bond claims
448
- function submitAnswerByArbitrator(bytes32 question_id, bytes32 answer, address answerer)
449
- onlyArbitrator(question_id)
450
- statePendingArbitration(question_id)
451
- public {
452
-
453
- require(answerer != NULL_ADDRESS, "answerer must be provided");
361
+ function submitAnswerByArbitrator(bytes32 question_id, bytes32 answer, address answerer) public onlyArbitrator(question_id) statePendingArbitration(question_id) {
362
+ if (answerer == NULL_ADDRESS) revert AnswererMustBeProvided();
454
363
  emit LogFinalize(question_id, answer);
455
364
 
456
365
  questions[question_id].is_pending_arbitration = false;
457
- _addAnswerToHistory(question_id, answer, answerer, 0, false);
366
+ _addAnswerToHistory(question_id, answer, answerer, 0);
458
367
  _updateCurrentAnswerByArbitrator(question_id, answer);
459
-
460
368
  }
461
369
 
462
370
  /// @notice Submit the answer for a question, for use by the arbitrator, working out the appropriate winner based on the last answer details.
@@ -465,71 +373,54 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
465
373
  /// @param answer The answer, encoded into bytes32
466
374
  /// @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
467
375
  /// @param last_history_hash The history hash before the final one
468
- /// @param last_answer_or_commitment_id The last answer given, or the commitment ID if it was a commitment.
376
+ /// @param last_answer The last answer given
469
377
  /// @param last_answerer The address that supplied the last answer
470
- function assignWinnerAndSubmitAnswerByArbitrator(bytes32 question_id, bytes32 answer, address payee_if_wrong, bytes32 last_history_hash, bytes32 last_answer_or_commitment_id, address last_answerer)
471
- external {
472
- bool is_commitment = _verifyHistoryInputOrRevert(questions[question_id].history_hash, last_history_hash, last_answer_or_commitment_id, questions[question_id].bond, last_answerer);
473
-
474
- address payee;
475
- // If the last answer is an unrevealed commit, it's always wrong.
476
- // For anything else, the last answer was set as the "best answer" in submitAnswer or submitAnswerReveal.
477
- if (is_commitment && !commitments[last_answer_or_commitment_id].is_revealed) {
478
- require(commitments[last_answer_or_commitment_id].reveal_ts < uint32(block.timestamp), "You must wait for the reveal deadline before finalizing");
479
- payee = payee_if_wrong;
480
- } else {
481
- payee = (questions[question_id].best_answer == answer) ? last_answerer : payee_if_wrong;
482
- }
378
+ function assignWinnerAndSubmitAnswerByArbitrator(bytes32 question_id, bytes32 answer, address payee_if_wrong, bytes32 last_history_hash, bytes32 last_answer, address last_answerer) external {
379
+ _verifyHistoryInputOrRevert(questions[question_id].history_hash, last_history_hash, last_answer, questions[question_id].bond, last_answerer);
380
+
381
+ address payee = (questions[question_id].best_answer == answer) ? last_answerer : payee_if_wrong;
483
382
  submitAnswerByArbitrator(question_id, answer, payee);
484
383
  }
485
384
 
486
-
487
385
  /// @notice Report whether the answer to the specified question is finalized
488
386
  /// @param question_id The ID of the question
489
387
  /// @return Return true if finalized
490
- function isFinalized(bytes32 question_id)
491
- view public returns (bool) {
388
+ function isFinalized(bytes32 question_id) public view returns (bool) {
492
389
  uint32 finalize_ts = questions[question_id].finalize_ts;
493
- return ( !questions[question_id].is_pending_arbitration && (finalize_ts > UNANSWERED) && (finalize_ts <= uint32(block.timestamp)) );
390
+ return (!questions[question_id].is_pending_arbitration && (finalize_ts > UNANSWERED) && (finalize_ts <= uint32(block.timestamp)));
494
391
  }
495
392
 
496
393
  /// @notice (Deprecated) Return the final answer to the specified question, or revert if there isn't one
497
394
  /// @param question_id The ID of the question
498
395
  /// @return The answer formatted as a bytes32
499
- function getFinalAnswer(bytes32 question_id)
500
- stateFinalized(question_id)
501
- external view returns (bytes32) {
396
+ function getFinalAnswer(bytes32 question_id) external view stateFinalized(question_id) returns (bytes32) {
502
397
  return questions[question_id].best_answer;
503
398
  }
504
399
 
505
400
  /// @notice Return the final answer to the specified question, or revert if there isn't one
506
401
  /// @param question_id The ID of the question
507
402
  /// @return The answer formatted as a bytes32
508
- function resultFor(bytes32 question_id)
509
- stateFinalized(question_id)
510
- public view returns (bytes32) {
403
+ function resultFor(bytes32 question_id) public view stateFinalized(question_id) returns (bytes32) {
511
404
  return questions[question_id].best_answer;
512
405
  }
513
406
 
514
407
  /// @notice Returns whether the question was answered before it had an answer, ie resolved to UNRESOLVED_ANSWER
515
- /// @param question_id The ID of the question
516
- function isSettledTooSoon(bytes32 question_id)
517
- public view returns(bool) {
408
+ /// @param question_id The ID of the question
409
+ function isSettledTooSoon(bytes32 question_id) public view returns (bool) {
518
410
  return (resultFor(question_id) == UNRESOLVED_ANSWER);
519
411
  }
520
412
 
521
413
  /// @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
522
- /// @param question_id The ID of the question
523
- function resultForOnceSettled(bytes32 question_id)
524
- external view returns(bytes32) {
414
+ /// @param question_id The ID of the question
415
+ function resultForOnceSettled(bytes32 question_id) external view returns (bytes32) {
525
416
  bytes32 result = resultFor(question_id);
526
417
  if (result == UNRESOLVED_ANSWER) {
527
418
  // Try the replacement
528
419
  bytes32 replacement_id = reopened_questions[question_id];
529
- require(replacement_id != bytes32(0x0), "Question was settled too soon and has not been reopened");
420
+ if (replacement_id == bytes32(0x0)) revert QuestionWasSettledTooSoonAndHasNotBeenReopened();
530
421
  // We only try one layer down rather than recursing to keep the gas costs predictable
531
422
  result = resultFor(replacement_id);
532
- require(result != UNRESOLVED_ANSWER, "Question replacement was settled too soon and has not been reopened");
423
+ if (result == UNRESOLVED_ANSWER) revert QuestionReplacementWasSettledTooSoonAndHasNotBeenReopened();
533
424
  }
534
425
  return result;
535
426
  }
@@ -545,24 +436,37 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
545
436
  /// @param min_bond The minimum bond that can be used to provide the first answer.
546
437
  /// @param reopens_question_id The ID of the question this reopens
547
438
  /// @return The ID of the newly-created question, created deterministically.
548
- function reopenQuestion(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 min_bond, bytes32 reopens_question_id)
549
- // stateNotCreated is enforced by the internal _askQuestion
550
- public payable returns (bytes32) {
551
-
552
- require(isSettledTooSoon(reopens_question_id), "You can only reopen questions that resolved as settled too soon");
439
+ function reopenQuestion(
440
+ uint256 template_id,
441
+ string memory question,
442
+ address arbitrator,
443
+ uint32 timeout,
444
+ uint32 opening_ts,
445
+ uint256 nonce,
446
+ uint256 min_bond,
447
+ bytes32 reopens_question_id
448
+ )
449
+ public
450
+ payable
451
+ returns (
452
+ // stateNotCreated is enforced by the internal _askQuestion
453
+ bytes32
454
+ )
455
+ {
456
+ if (!isSettledTooSoon(reopens_question_id)) revert YouCanOnlyReopenQuestionsThatResolvedAsSettledTooSoon();
553
457
 
554
458
  bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
555
459
 
556
460
  // A reopening must exactly match the original question, except for the nonce and the creator
557
- require(content_hash == questions[reopens_question_id].content_hash, "content hash mismatch");
558
- require(arbitrator == questions[reopens_question_id].arbitrator, "arbitrator mismatch");
559
- require(timeout == questions[reopens_question_id].timeout, "timeout mismatch");
560
- require(opening_ts == questions[reopens_question_id].opening_ts , "opening_ts mismatch");
561
- require(min_bond == questions[reopens_question_id].min_bond, "min_bond mismatch");
461
+ if (content_hash != questions[reopens_question_id].content_hash) revert ContentHashMismatch();
462
+ if (arbitrator != questions[reopens_question_id].arbitrator) revert ArbitratorMismatch();
463
+ if (timeout != questions[reopens_question_id].timeout) revert TimeoutMismatch();
464
+ if (opening_ts != questions[reopens_question_id].opening_ts) revert Opening_TsMismatch();
465
+ if (min_bond != questions[reopens_question_id].min_bond) revert Min_BondMismatch();
562
466
 
563
467
  // If the the question was itself reopening some previous question, you'll have to re-reopen the previous question first.
564
468
  // This ensures the bounty can be passed on to the next attempt of the original question.
565
- require(!reopener_questions[reopens_question_id], "Question is already reopening a previous question");
469
+ if (reopener_questions[reopens_question_id]) revert QuestionIsAlreadyReopeningAPreviousQuestion();
566
470
 
567
471
  // A question can only be reopened once, unless the reopening was also settled too soon in which case it can be replaced
568
472
  bytes32 existing_reopen_question_id = reopened_questions[reopens_question_id];
@@ -571,7 +475,7 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
571
475
  bytes32 take_bounty_from_question_id = reopens_question_id;
572
476
  // If the question has already been reopened but was again settled too soon, we can transfer its bounty to the next attempt.
573
477
  if (existing_reopen_question_id != bytes32(0)) {
574
- require(isSettledTooSoon(existing_reopen_question_id), "Question has already been reopened");
478
+ if (!isSettledTooSoon(existing_reopen_question_id)) revert QuestionHasAlreadyBeenReopened();
575
479
  // We'll overwrite the reopening with our new question and move the bounty.
576
480
  // 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.
577
481
  reopener_questions[existing_reopen_question_id] = false;
@@ -599,16 +503,11 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
599
503
  /// @param min_timeout The timeout set in the initial question settings must be this high or higher
600
504
  /// @param min_bond The bond sent with the final answer must be this high or higher
601
505
  /// @return The answer formatted as a bytes32
602
- function getFinalAnswerIfMatches(
603
- bytes32 question_id,
604
- bytes32 content_hash, address arbitrator, uint32 min_timeout, uint256 min_bond
605
- )
606
- stateFinalized(question_id)
607
- external view returns (bytes32) {
608
- require(content_hash == questions[question_id].content_hash, "content hash must match");
609
- require(arbitrator == questions[question_id].arbitrator, "arbitrator must match");
610
- require(min_timeout <= questions[question_id].timeout, "timeout must be long enough");
611
- require(min_bond <= questions[question_id].bond, "bond must be high enough");
506
+ function getFinalAnswerIfMatches(bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 min_timeout, uint256 min_bond) external view stateFinalized(question_id) returns (bytes32) {
507
+ if (content_hash != questions[question_id].content_hash) revert ContentHashMustMatch();
508
+ if (arbitrator != questions[question_id].arbitrator) revert ArbitratorMustMatch();
509
+ if (min_timeout > questions[question_id].timeout) revert TimeoutMustBeLongEnough();
510
+ if (min_bond > questions[question_id].bond) revert BondMustBeHighEnough();
612
511
  return questions[question_id].best_answer;
613
512
  }
614
513
 
@@ -623,22 +522,16 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
623
522
  /// Usually you would call the whole thing in a single transaction, but if not then the data is persisted to pick up later.
624
523
  /// @param question_id The ID of the question
625
524
  /// @param history_hashes Second-last-to-first, the hash of each history entry. (Final one should be empty).
626
- /// @param addrs Last-to-first, the address of each answerer or commitment sender
627
- /// @param bonds Last-to-first, the bond supplied with each answer or commitment
628
- /// @param answers Last-to-first, each answer supplied, or commitment ID if the answer was supplied with commit->reveal
629
- function claimWinnings(
630
- bytes32 question_id,
631
- bytes32[] memory history_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers
632
- )
633
- stateFinalized(question_id)
634
- public {
635
-
636
- require(history_hashes.length > 0, "at least one history hash entry must be provided");
525
+ /// @param addrs Last-to-first, the address of each answerer
526
+ /// @param bonds Last-to-first, the bond supplied with each answer
527
+ /// @param answers Last-to-first, each answer supplied
528
+ function claimWinnings(bytes32 question_id, bytes32[] memory history_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers) public stateFinalized(question_id) {
529
+ if (history_hashes.length == 0) revert AtLeastOneHistoryHashEntryMustBeProvided();
637
530
 
638
531
  // These are only set if we split our claim over multiple transactions.
639
- address payee = question_claims[question_id].payee;
640
- uint256 last_bond = question_claims[question_id].last_bond;
641
- uint256 queued_funds = question_claims[question_id].queued_funds;
532
+ address payee = question_claims[question_id].payee;
533
+ uint256 last_bond = question_claims[question_id].last_bond;
534
+ uint256 queued_funds = 0;
642
535
 
643
536
  // Starts as the hash of the final answer submitted. It'll be cleared when we're done.
644
537
  // If we're splitting the claim over multiple transactions, it'll be the hash where we left off last time
@@ -648,15 +541,11 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
648
541
 
649
542
  uint256 i;
650
543
  for (i = 0; i < history_hashes.length; i++) {
651
-
652
- // Check input against the history hash, and see which of 2 possible values of is_commitment fits.
653
- bool is_commitment = _verifyHistoryInputOrRevert(last_history_hash, history_hashes[i], answers[i], bonds[i], addrs[i]);
654
-
655
- queued_funds = queued_funds + last_bond;
656
- (queued_funds, payee) = _processHistoryItem(
657
- question_id, best_answer, queued_funds, payee,
658
- addrs[i], bonds[i], answers[i], is_commitment);
659
-
544
+ _verifyHistoryInputOrRevert(last_history_hash, history_hashes[i], answers[i], bonds[i], addrs[i]);
545
+
546
+ queued_funds = queued_funds + last_bond;
547
+ (queued_funds, payee) = _processHistoryItem(question_id, best_answer, queued_funds, payee, addrs[i], bonds[i], answers[i]);
548
+
660
549
  // Line the bond up for next time, when it will be added to somebody's queued_funds
661
550
  last_bond = bonds[i];
662
551
 
@@ -667,23 +556,17 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
667
556
  }
668
557
 
669
558
  last_history_hash = history_hashes[i];
670
-
671
559
  }
672
-
560
+
673
561
  if (last_history_hash != NULL_HASH) {
674
562
  // We haven't yet got to the null hash (1st answer), ie the caller didn't supply the full answer chain.
675
563
  // Persist the details so we can pick up later where we left off later.
676
564
 
677
- // If we know who to pay we can go ahead and pay them out, only keeping back last_bond
678
- // (We always know who to pay unless all we saw were unrevealed commits)
679
- if (payee != NULL_ADDRESS) {
680
- _payPayee(question_id, payee, queued_funds);
681
- queued_funds = 0;
682
- }
565
+ // Pay out the latest payee, only keeping back last_bond which the next may have a claim on
566
+ _payPayee(question_id, payee, queued_funds);
683
567
 
684
568
  question_claims[question_id].payee = payee;
685
569
  question_claims[question_id].last_bond = last_bond;
686
- question_claims[question_id].queued_funds = queued_funds;
687
570
  } else {
688
571
  // There is nothing left below us so the payee can keep what remains
689
572
  _payPayee(question_id, payee, queued_funds + last_bond);
@@ -691,54 +574,22 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
691
574
  }
692
575
 
693
576
  questions[question_id].history_hash = last_history_hash;
694
-
695
577
  }
696
578
 
697
- function _payPayee(bytes32 question_id, address payee, uint256 value)
698
- internal {
579
+ function _payPayee(bytes32 question_id, address payee, uint256 value) internal {
699
580
  balanceOf[payee] = balanceOf[payee] + value;
700
581
  emit LogClaim(question_id, payee, value);
701
582
  }
702
583
 
703
- function _verifyHistoryInputOrRevert(
704
- bytes32 last_history_hash,
705
- bytes32 history_hash, bytes32 answer, uint256 bond, address addr
706
- )
707
- internal pure returns (bool) {
708
- if (last_history_hash == keccak256(abi.encodePacked(history_hash, answer, bond, addr, true)) ) {
709
- return true;
584
+ function _verifyHistoryInputOrRevert(bytes32 last_history_hash, bytes32 history_hash, bytes32 answer, uint256 bond, address addr) internal pure {
585
+ if (last_history_hash != keccak256(abi.encodePacked(history_hash, answer, bond, addr, false))) {
586
+ revert HistoryInputProvidedDidNotMatchTheExpectedHash();
710
587
  }
711
- if (last_history_hash == keccak256(abi.encodePacked(history_hash, answer, bond, addr, false)) ) {
712
- return false;
713
- }
714
- revert("History input provided did not match the expected hash");
715
588
  }
716
589
 
717
- function _processHistoryItem(
718
- bytes32 question_id, bytes32 best_answer,
719
- uint256 queued_funds, address payee,
720
- address addr, uint256 bond, bytes32 answer, bool is_commitment
721
- )
722
- internal returns (uint256, address) {
723
-
724
- // For commit-and-reveal, the answer history holds the commitment ID instead of the answer.
725
- // We look at the referenced commitment ID and switch in the actual answer.
726
- if (is_commitment) {
727
- bytes32 commitment_id = answer;
728
- // If it's a commit but it hasn't been revealed, it will always be considered wrong.
729
- if (!commitments[commitment_id].is_revealed) {
730
- delete commitments[commitment_id];
731
- return (queued_funds, payee);
732
- } else {
733
- answer = commitments[commitment_id].revealed_answer;
734
- delete commitments[commitment_id];
735
- }
736
- }
737
-
590
+ function _processHistoryItem(bytes32 question_id, bytes32 best_answer, uint256 queued_funds, address payee, address addr, uint256 bond, bytes32 answer) internal returns (uint256, address) {
738
591
  if (answer == best_answer) {
739
-
740
592
  if (payee == NULL_ADDRESS) {
741
-
742
593
  // The entry is for the first payee we come to, ie the winner.
743
594
  // They get the question bounty.
744
595
  payee = addr;
@@ -747,13 +598,11 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
747
598
  _payPayee(question_id, payee, questions[question_id].bounty);
748
599
  questions[question_id].bounty = 0;
749
600
  }
750
-
751
601
  } else if (addr != payee) {
752
-
753
602
  // Answerer has changed, ie we found someone lower down who needs to be paid
754
603
 
755
604
  // The lower answerer will take over receiving bonds from higher answerer.
756
- // They should also be paid the takeover fee, which is set at a rate equivalent to their bond.
605
+ // They should also be paid the takeover fee, which is set at a rate equivalent to their bond.
757
606
  // (This is our arbitrary rule, to give consistent right-answerers a defence against high-rollers.)
758
607
 
759
608
  // There should be enough for the fee, but if not, take what we have.
@@ -765,13 +614,10 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
765
614
  // Now start queued_funds again for the new (lower-bonded) payee
766
615
  payee = addr;
767
616
  queued_funds = answer_takeover_fee;
768
-
769
617
  }
770
-
771
618
  }
772
619
 
773
620
  return (queued_funds, payee);
774
-
775
621
  }
776
622
 
777
623
  /// @notice Convenience function to assign bounties/bonds for multiple questions in one go, then withdraw all your funds.
@@ -780,16 +626,20 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
780
626
  /// @param question_ids The IDs of the questions you want to claim for
781
627
  /// @param lengths The number of history entries you will supply for each question ID
782
628
  /// @param hist_hashes In a single list for all supplied questions, the hash of each history entry.
783
- /// @param addrs In a single list for all supplied questions, the address of each answerer or commitment sender
784
- /// @param bonds In a single list for all supplied questions, the bond supplied with each answer or commitment
785
- /// @param answers In a single list for all supplied questions, each answer supplied, or commitment ID
629
+ /// @param addrs In a single list for all supplied questions, the address of each answerer
630
+ /// @param bonds In a single list for all supplied questions, the bond supplied with each answer
631
+ /// @param answers In a single list for all supplied questions, each answer supplied
786
632
  function claimMultipleAndWithdrawBalance(
787
- bytes32[] memory question_ids, uint256[] memory lengths,
788
- bytes32[] memory hist_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers
789
- )
790
- stateAny() // The finalization checks are done in the claimWinnings function
791
- public {
792
-
633
+ bytes32[] memory question_ids,
634
+ uint256[] memory lengths,
635
+ bytes32[] memory hist_hashes,
636
+ address[] memory addrs,
637
+ uint256[] memory bonds,
638
+ bytes32[] memory answers
639
+ )
640
+ public
641
+ stateAny // The finalization checks are done in the claimWinnings function
642
+ {
793
643
  uint256 qi;
794
644
  uint256 i;
795
645
  for (qi = 0; qi < question_ids.length; qi++) {
@@ -813,82 +663,70 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH {
813
663
  }
814
664
 
815
665
  /// @notice Returns the questions's content hash, identifying the question content
816
- /// @param question_id The ID of the question
817
- function getContentHash(bytes32 question_id)
818
- public view returns(bytes32) {
666
+ /// @param question_id The ID of the question
667
+ function getContentHash(bytes32 question_id) public view returns (bytes32) {
819
668
  return questions[question_id].content_hash;
820
669
  }
821
670
 
822
671
  /// @notice Returns the arbitrator address for the question
823
- /// @param question_id The ID of the question
824
- function getArbitrator(bytes32 question_id)
825
- public view returns(address) {
672
+ /// @param question_id The ID of the question
673
+ function getArbitrator(bytes32 question_id) public view returns (address) {
826
674
  return questions[question_id].arbitrator;
827
675
  }
828
676
 
829
677
  /// @notice Returns the timestamp when the question can first be answered
830
- /// @param question_id The ID of the question
831
- function getOpeningTS(bytes32 question_id)
832
- public view returns(uint32) {
678
+ /// @param question_id The ID of the question
679
+ function getOpeningTS(bytes32 question_id) public view returns (uint32) {
833
680
  return questions[question_id].opening_ts;
834
681
  }
835
682
 
836
683
  /// @notice Returns the timeout in seconds used after each answer
837
- /// @param question_id The ID of the question
838
- function getTimeout(bytes32 question_id)
839
- public view returns(uint32) {
684
+ /// @param question_id The ID of the question
685
+ function getTimeout(bytes32 question_id) public view returns (uint32) {
840
686
  return questions[question_id].timeout;
841
687
  }
842
688
 
843
689
  /// @notice Returns the timestamp at which the question will be/was finalized
844
- /// @param question_id The ID of the question
845
- function getFinalizeTS(bytes32 question_id)
846
- public view returns(uint32) {
690
+ /// @param question_id The ID of the question
691
+ function getFinalizeTS(bytes32 question_id) public view returns (uint32) {
847
692
  return questions[question_id].finalize_ts;
848
693
  }
849
694
 
850
695
  /// @notice Returns whether the question is pending arbitration
851
- /// @param question_id The ID of the question
852
- function isPendingArbitration(bytes32 question_id)
853
- public view returns(bool) {
696
+ /// @param question_id The ID of the question
697
+ function isPendingArbitration(bytes32 question_id) public view returns (bool) {
854
698
  return questions[question_id].is_pending_arbitration;
855
699
  }
856
700
 
857
701
  /// @notice Returns the current total unclaimed bounty
858
702
  /// @dev Set back to zero once the bounty has been claimed
859
- /// @param question_id The ID of the question
860
- function getBounty(bytes32 question_id)
861
- public view returns(uint256) {
703
+ /// @param question_id The ID of the question
704
+ function getBounty(bytes32 question_id) public view returns (uint256) {
862
705
  return questions[question_id].bounty;
863
706
  }
864
707
 
865
708
  /// @notice Returns the current best answer
866
- /// @param question_id The ID of the question
867
- function getBestAnswer(bytes32 question_id)
868
- public view returns(bytes32) {
709
+ /// @param question_id The ID of the question
710
+ function getBestAnswer(bytes32 question_id) public view returns (bytes32) {
869
711
  return questions[question_id].best_answer;
870
712
  }
871
713
 
872
- /// @notice Returns the history hash of the question
873
- /// @param question_id The ID of the question
714
+ /// @notice Returns the history hash of the question
715
+ /// @param question_id The ID of the question
874
716
  /// @dev Updated on each answer, then rewound as each is claimed
875
- function getHistoryHash(bytes32 question_id)
876
- public view returns(bytes32) {
717
+ function getHistoryHash(bytes32 question_id) public view returns (bytes32) {
877
718
  return questions[question_id].history_hash;
878
719
  }
879
720
 
880
721
  /// @notice Returns the highest bond posted so far for a question
881
- /// @param question_id The ID of the question
882
- function getBond(bytes32 question_id)
883
- public view returns(uint256) {
722
+ /// @param question_id The ID of the question
723
+ function getBond(bytes32 question_id) public view returns (uint256) {
884
724
  return questions[question_id].bond;
885
725
  }
886
726
 
887
727
  /// @notice Returns the minimum bond that can answer the question
888
728
  /// @param question_id The ID of the question
889
- function getMinBond(bytes32 question_id)
890
- public view returns(uint256) {
729
+ function getMinBond(bytes32 question_id) public view returns (uint256) {
891
730
  return questions[question_id].min_bond;
892
731
  }
893
-
894
732
  }