@reality.eth/contracts 3.0.2 → 3.0.6

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 (62) hide show
  1. package/README.md +9 -3
  2. package/chains/chainid.network.json +7691 -1349
  3. package/chains/chainid.network.local.json +25 -0
  4. package/chains/deployments/1/ETH/RealityETH-3.0.json +2 -1
  5. package/chains/deployments/1/SWISE/RealityETH_ERC20-3.0.json +7 -0
  6. package/chains/deployments/10/OETH/RealityETH-3.0.json +6 -0
  7. package/chains/deployments/100/GNO/RealityETH_ERC20-3.0.json +7 -0
  8. package/chains/deployments/100/SWISE/RealityETH_ERC20-3.0.json +7 -0
  9. package/chains/deployments/100/XDAI/RealityETH-3.0.json +3 -1
  10. package/chains/deployments/1337702/ETH/RealityETH-3.0.json +6 -0
  11. package/chains/deployments/280/WETH/RealityETH_ERC20-3.0.json +8 -0
  12. package/chains/deployments/4/ETH/RealityETH-3.0.json +3 -1
  13. package/chains/deployments/42/ETH/RealityETH-3.0.json +2 -1
  14. package/chains/deployments/56/DEXE/RealityETH_ERC20-3.0.json +7 -0
  15. package/chains/deployments/69/OETH/RealityETH-3.0.json +6 -0
  16. package/chains/deployments/77/XDAI/RealityETH-2.1-rc1.json +1 -3
  17. package/chains/deployments/777/CTH/RealityETH-3.0.json +6 -0
  18. package/chains/supported.json +29 -3
  19. package/development/contracts/Arbitrator.sol +7 -6
  20. package/development/contracts/BalanceHolder.sol +4 -2
  21. package/development/contracts/{BalanceHolderERC20.sol → BalanceHolder_ERC20.sol} +3 -2
  22. package/development/contracts/IArbitrator.sol +4 -7
  23. package/development/contracts/IArbitratorForeignProxy.sol +12 -0
  24. package/development/contracts/IBalanceHolder.sol +3 -1
  25. package/development/contracts/IBalanceHolder_ERC20.sol +11 -0
  26. package/development/contracts/IERC20.sol +1 -1
  27. package/development/contracts/IOwned.sol +1 -1
  28. package/development/contracts/IRealityETH.sol +63 -0
  29. package/development/contracts/IRealityETH_ERC20.sol +67 -0
  30. package/development/contracts/Owned.sol +4 -2
  31. package/development/contracts/RealityETH-3.0.sol +4 -82
  32. package/development/contracts/RealityETH_ERC20-3.0.sol +3 -3
  33. package/generated/chains.json +114 -22
  34. package/generated/contracts.json +98 -7
  35. package/generated/tokens.json +41 -2
  36. package/index.js +7 -2
  37. package/non_standard_deployments/zksync2/README +21 -0
  38. package/{development/contracts/Realitio_v2_1.sol → non_standard_deployments/zksync2/contracts/RealityETH_ERC20-3.0.sol} +324 -47
  39. package/non_standard_deployments/zksync2/deploy/deploy.ts +53 -0
  40. package/non_standard_deployments/zksync2/hardhat.config.ts +30 -0
  41. package/non_standard_deployments/zksync2/package.json +15 -0
  42. package/non_standard_deployments/zksync2/yarn.lock +2907 -0
  43. package/package.json +3 -3
  44. package/scripts/deploy.js +14 -3
  45. package/scripts/fetch_and_reformat_chains_json.js +31 -0
  46. package/scripts/{format_abi.js → format_json.js} +0 -0
  47. package/scripts/generate_chains_json.js +3 -0
  48. package/tokens/CTH.json +7 -0
  49. package/tokens/DEXE.json +7 -0
  50. package/tokens/ETH.json +2 -1
  51. package/tokens/GNO.json +1 -0
  52. package/tokens/OETH.json +8 -0
  53. package/tokens/SWISE.json +8 -0
  54. package/tokens/WETH.json +7 -0
  55. package/development/contracts/IRealitio.sol +0 -44
  56. package/development/contracts/MultiSigWallet.sol +0 -391
  57. package/development/contracts/Realitio.sol +0 -756
  58. package/development/contracts/RealitioERC20.sol +0 -825
  59. package/development/contracts/RealitioSafeMath256.sol +0 -36
  60. package/development/contracts/RealitioSafeMath32.sol +0 -17
  61. package/development/contracts/SplitterWallet.sol +0 -142
  62. package/tests/python/test_wallet.py +0 -185
@@ -2,11 +2,53 @@
2
2
 
3
3
  pragma solidity ^0.8.6;
4
4
 
5
- import './BalanceHolder.sol';
6
5
 
7
- // Next version of Realitio v2, will be deployed on xdai, may be deployed to other networks in future
8
- // API-compatible with Realitio v2, address will be stored in Realitio.json
9
- contract Realitio_v2_1 is BalanceHolder {
6
+ /**
7
+ * @title ERC20 interface
8
+ * @dev see https://github.com/ethereum/EIPs/issues/20
9
+ */
10
+ interface IERC20 {
11
+ function totalSupply() external view returns (uint256);
12
+
13
+ function balanceOf(address who) external view returns (uint256);
14
+
15
+ function allowance(address owner, address spender) external view returns (uint256);
16
+
17
+ function transfer(address to, uint256 value) external returns (bool);
18
+
19
+ function approve(address spender, uint256 value) external returns (bool);
20
+
21
+ function transferFrom(address from, address to, uint256 value) external returns (bool);
22
+
23
+ event Transfer(address indexed from, address indexed to, uint256 value);
24
+
25
+ event Approval(address indexed owner, address indexed spender, uint256 value);
26
+ }
27
+
28
+
29
+ contract BalanceHolder {
30
+
31
+ IERC20 public token;
32
+
33
+ mapping(address => uint256) public balanceOf;
34
+
35
+ event LogWithdraw(
36
+ address indexed user,
37
+ uint256 amount
38
+ );
39
+
40
+ function withdraw()
41
+ public {
42
+ uint256 bal = balanceOf[msg.sender];
43
+ balanceOf[msg.sender] = 0;
44
+ require(token.transfer(msg.sender, bal));
45
+ emit LogWithdraw(msg.sender, bal);
46
+ }
47
+
48
+ }
49
+
50
+
51
+ contract RealityETH_ERC20_v3_0 is BalanceHolder {
10
52
 
11
53
  address constant NULL_ADDRESS = address(0);
12
54
 
@@ -25,6 +67,10 @@ contract Realitio_v2_1 is BalanceHolder {
25
67
  // Proportion withheld when you claim an earlier bond.
26
68
  uint256 constant BOND_CLAIM_FEE_PROPORTION = 40; // One 40th ie 2.5%
27
69
 
70
+ // Special value representing a question that was answered too soon.
71
+ // bytes32(-2). By convention we use bytes32(-1) for "invalid", although the contract does not handle this.
72
+ bytes32 constant UNRESOLVED_ANSWER = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe;
73
+
28
74
  event LogSetQuestionFee(
29
75
  address arbitrator,
30
76
  uint256 amount
@@ -49,6 +95,11 @@ contract Realitio_v2_1 is BalanceHolder {
49
95
  uint256 created
50
96
  );
51
97
 
98
+ event LogMinimumBond(
99
+ bytes32 indexed question_id,
100
+ uint256 min_bond
101
+ );
102
+
52
103
  event LogFundAnswerBounty(
53
104
  bytes32 indexed question_id,
54
105
  uint256 bounty_added,
@@ -95,6 +146,11 @@ contract Realitio_v2_1 is BalanceHolder {
95
146
  uint256 amount
96
147
  );
97
148
 
149
+ event LogReopenQuestion(
150
+ bytes32 indexed question_id,
151
+ bytes32 indexed reopened_question_id
152
+ );
153
+
98
154
  struct Question {
99
155
  bytes32 content_hash;
100
156
  address arbitrator;
@@ -106,6 +162,7 @@ contract Realitio_v2_1 is BalanceHolder {
106
162
  bytes32 best_answer;
107
163
  bytes32 history_hash;
108
164
  uint256 bond;
165
+ uint256 min_bond;
109
166
  }
110
167
 
111
168
  // Stored in a mapping indexed by commitment_id, a hash of commitment hash, question, bond.
@@ -130,6 +187,9 @@ contract Realitio_v2_1 is BalanceHolder {
130
187
  mapping(bytes32 => Claim) public question_claims;
131
188
  mapping(bytes32 => Commitment) public commitments;
132
189
  mapping(address => uint256) public arbitrator_question_fees;
190
+ mapping(bytes32 => bytes32) public reopened_questions;
191
+ mapping(bytes32 => bool) public reopener_questions;
192
+
133
193
 
134
194
  modifier onlyArbitrator(bytes32 question_id) {
135
195
  require(msg.sender == questions[question_id].arbitrator, "msg.sender must be arbitrator");
@@ -174,9 +234,14 @@ contract Realitio_v2_1 is BalanceHolder {
174
234
  _;
175
235
  }
176
236
 
177
- modifier bondMustDouble(bytes32 question_id) {
178
- require(msg.value > 0, "bond must be positive");
179
- require(msg.value >= (questions[question_id].bond * 2), "bond must be double at least previous bond");
237
+ modifier bondMustDoubleAndMatchMinimum(bytes32 question_id, uint256 tokens) {
238
+ require(tokens > 0, "bond must be positive");
239
+ uint256 current_bond = questions[question_id].bond;
240
+ if (current_bond == 0) {
241
+ require(tokens >= (questions[question_id].min_bond), "bond must exceed the minimum");
242
+ } else {
243
+ require(tokens >= (current_bond * 2), "bond must be double at least previous bond");
244
+ }
180
245
  _;
181
246
  }
182
247
 
@@ -197,6 +262,16 @@ contract Realitio_v2_1 is BalanceHolder {
197
262
  createTemplate('{"title": "%s", "type": "datetime", "category": "%s", "lang": "%s"}');
198
263
  }
199
264
 
265
+ /// @notice Set the address of the ERC20 token that will be used for bonds.
266
+ /// @dev Should not be used with ERC20-like token contracts that implement callbacks like ERC777 that could cause re-entrancy issues
267
+ /// @param _token The ERC20 token that will be used for bonds.
268
+ function setToken(IERC20 _token)
269
+ public
270
+ {
271
+ require(token == IERC20(address(0x0)), "Token can only be initialized once");
272
+ token = _token;
273
+ }
274
+
200
275
  /// @notice Function for arbitrator to set an optional per-question fee.
201
276
  /// @dev The per-question fee, charged when a question is asked, is intended as an anti-spam measure.
202
277
  /// @param fee The fee to be charged by the arbitrator when a question is asked
@@ -216,7 +291,7 @@ contract Realitio_v2_1 is BalanceHolder {
216
291
  stateAny()
217
292
  public returns (uint256) {
218
293
  uint256 id = nextTemplateID;
219
- templates[id] = block.number;
294
+ templates[id] = block.timestamp;
220
295
  template_hashes[id] = keccak256(abi.encodePacked(content));
221
296
  emit LogNewTemplate(id, msg.sender, content);
222
297
  nextTemplateID = id + 1;
@@ -237,13 +312,15 @@ contract Realitio_v2_1 is BalanceHolder {
237
312
  string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce
238
313
  )
239
314
  // stateNotCreated is enforced by the internal _askQuestion
240
- public payable returns (bytes32) {
315
+ public returns (bytes32) {
241
316
  uint256 template_id = createTemplate(content);
242
317
  return askQuestion(template_id, question, arbitrator, timeout, opening_ts, nonce);
243
318
  }
244
319
 
245
- /// @notice Ask a new question and return the ID
320
+ /// @notice Ask a new question without a bounty and return the ID
246
321
  /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage.
322
+ /// @dev Calling without the token param will only work if there is no arbitrator-set question fee.
323
+ /// @dev This has the same function signature as askQuestion() in the non-ERC20 version, which is optionally payable.
247
324
  /// @param template_id The ID number of the template the question will use
248
325
  /// @param question A string containing the parameters that will be passed into the template to make the question
249
326
  /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
@@ -253,38 +330,121 @@ contract Realitio_v2_1 is BalanceHolder {
253
330
  /// @return The ID of the newly-created question, created deterministically.
254
331
  function askQuestion(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce)
255
332
  // stateNotCreated is enforced by the internal _askQuestion
256
- public payable returns (bytes32) {
333
+ public returns (bytes32) {
334
+
335
+ require(templates[template_id] > 0, "template must exist");
336
+
337
+ bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
338
+ bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, uint256(0), address(this), msg.sender, nonce));
339
+
340
+ // We emit this event here because _askQuestion doesn't need to know the unhashed question. Other events are emitted by _askQuestion.
341
+ emit LogNewQuestion(question_id, msg.sender, template_id, question, content_hash, arbitrator, timeout, opening_ts, nonce, block.timestamp);
342
+ _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, 0, 0);
343
+
344
+ return question_id;
345
+ }
346
+
347
+ /// @notice Ask a new question with a bounty and return the ID
348
+ /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage.
349
+ /// @param template_id The ID number of the template the question will use
350
+ /// @param question A string containing the parameters that will be passed into the template to make the question
351
+ /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
352
+ /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer
353
+ /// @param opening_ts If set, the earliest time it should be possible to answer the question.
354
+ /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
355
+ /// @param tokens The combined initial question bounty and question fee
356
+ /// @return The ID of the newly-created question, created deterministically.
357
+ function askQuestionERC20(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 tokens)
358
+ // stateNotCreated is enforced by the internal _askQuestion
359
+ public returns (bytes32) {
360
+
361
+ _deductTokensOrRevert(tokens);
362
+
363
+ require(templates[template_id] > 0, "template must exist");
364
+
365
+ bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
366
+ bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, uint256(0), address(this), msg.sender, nonce));
367
+
368
+ // We emit this event here because _askQuestion doesn't need to know the unhashed question. Other events are emitted by _askQuestion.
369
+ emit LogNewQuestion(question_id, msg.sender, template_id, question, content_hash, arbitrator, timeout, opening_ts, nonce, block.timestamp);
370
+ _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, 0, tokens);
371
+
372
+ return question_id;
373
+ }
374
+
375
+ /// @notice Ask a new question and return the ID
376
+ /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage.
377
+ /// @param template_id The ID number of the template the question will use
378
+ /// @param question A string containing the parameters that will be passed into the template to make the question
379
+ /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
380
+ /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer
381
+ /// @param opening_ts If set, the earliest time it should be possible to answer the question.
382
+ /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
383
+ /// @param min_bond The minimum bond that may be used for an answer.
384
+ /// @param tokens Number of tokens sent
385
+ /// @return The ID of the newly-created question, created deterministically.
386
+ function askQuestionWithMinBondERC20(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 min_bond, uint256 tokens)
387
+ // stateNotCreated is enforced by the internal _askQuestion
388
+ public returns (bytes32) {
389
+
390
+ _deductTokensOrRevert(tokens);
257
391
 
258
392
  require(templates[template_id] > 0, "template must exist");
259
393
 
260
394
  bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
261
- bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, msg.sender, nonce));
395
+ bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, min_bond, address(this), msg.sender, nonce));
262
396
 
263
- _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts);
397
+ // We emit this event here because _askQuestion doesn't need to know the unhashed question.
398
+ // Other events are emitted by _askQuestion.
264
399
  emit LogNewQuestion(question_id, msg.sender, template_id, question, content_hash, arbitrator, timeout, opening_ts, nonce, block.timestamp);
400
+ _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, min_bond, tokens);
265
401
 
266
402
  return question_id;
267
403
  }
268
404
 
269
- function _askQuestion(bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 timeout, uint32 opening_ts)
405
+ function _deductTokensOrRevert(uint256 tokens)
406
+ internal {
407
+
408
+ if (tokens == 0) {
409
+ return;
410
+ }
411
+
412
+ uint256 bal = balanceOf[msg.sender];
413
+
414
+ // Deduct any tokens you have in your internal balance first
415
+ if (bal > 0) {
416
+ if (bal >= tokens) {
417
+ balanceOf[msg.sender] = bal - tokens;
418
+ return;
419
+ } else {
420
+ tokens = tokens - bal;
421
+ balanceOf[msg.sender] = 0;
422
+ }
423
+ }
424
+ // Now we need to charge the rest from
425
+ require(token.transferFrom(msg.sender, address(this), tokens), "Transfer of tokens failed, insufficient approved balance?");
426
+ return;
427
+
428
+ }
429
+
430
+ function _askQuestion(bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 min_bond, uint256 tokens)
270
431
  stateNotCreated(question_id)
271
432
  internal {
272
433
 
273
434
  // A timeout of 0 makes no sense, and we will use this to check existence
274
435
  require(timeout > 0, "timeout must be positive");
275
436
  require(timeout < 365 days, "timeout must be less than 365 days");
276
- require(arbitrator != NULL_ADDRESS, "arbitrator must be set");
277
437
 
278
- uint256 bounty = msg.value;
438
+ uint256 bounty = tokens;
279
439
 
280
440
  // The arbitrator can set a fee for asking a question.
281
441
  // This is intended as an anti-spam defence.
282
442
  // The fee is waived if the arbitrator is asking the question.
283
443
  // This allows them to set an impossibly high fee and make users proxy the question through them.
284
444
  // This would allow more sophisticated pricing, question whitelisting etc.
285
- if (msg.sender != arbitrator) {
445
+ if (arbitrator != NULL_ADDRESS && msg.sender != arbitrator) {
286
446
  uint256 question_fee = arbitrator_question_fees[arbitrator];
287
- require(bounty >= question_fee, "ETH provided must cover question fee");
447
+ require(bounty >= question_fee, "Tokens provided must cover question fee");
288
448
  bounty = bounty - question_fee;
289
449
  balanceOf[arbitrator] = balanceOf[arbitrator] + question_fee;
290
450
  }
@@ -293,18 +453,29 @@ contract Realitio_v2_1 is BalanceHolder {
293
453
  questions[question_id].arbitrator = arbitrator;
294
454
  questions[question_id].opening_ts = opening_ts;
295
455
  questions[question_id].timeout = timeout;
296
- questions[question_id].bounty = bounty;
456
+
457
+ if (bounty > 0) {
458
+ questions[question_id].bounty = bounty;
459
+ emit LogFundAnswerBounty(question_id, bounty, bounty, msg.sender);
460
+ }
461
+
462
+ if (min_bond > 0) {
463
+ questions[question_id].min_bond = min_bond;
464
+ emit LogMinimumBond(question_id, min_bond);
465
+ }
297
466
 
298
467
  }
299
468
 
300
469
  /// @notice Add funds to the bounty for a question
301
470
  /// @dev Add bounty funds after the initial question creation. Can be done any time until the question is finalized.
302
471
  /// @param question_id The ID of the question you wish to fund
303
- function fundAnswerBounty(bytes32 question_id)
472
+ /// @param tokens The number of tokens to fund
473
+ function fundAnswerBountyERC20(bytes32 question_id, uint256 tokens)
304
474
  stateOpen(question_id)
305
- external payable {
306
- questions[question_id].bounty = questions[question_id].bounty + msg.value;
307
- emit LogFundAnswerBounty(question_id, msg.value, questions[question_id].bounty, msg.sender);
475
+ external {
476
+ _deductTokensOrRevert(tokens);
477
+ questions[question_id].bounty = questions[question_id].bounty + tokens;
478
+ emit LogFundAnswerBounty(question_id, tokens, questions[question_id].bounty, msg.sender);
308
479
  }
309
480
 
310
481
  /// @notice Submit an answer for a question.
@@ -313,13 +484,15 @@ contract Realitio_v2_1 is BalanceHolder {
313
484
  /// @param question_id The ID of the question
314
485
  /// @param answer The answer, encoded into bytes32
315
486
  /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
316
- function submitAnswer(bytes32 question_id, bytes32 answer, uint256 max_previous)
487
+ /// @param tokens The amount of tokens to submit
488
+ function submitAnswerERC20(bytes32 question_id, bytes32 answer, uint256 max_previous, uint256 tokens)
317
489
  stateOpen(question_id)
318
- bondMustDouble(question_id)
490
+ bondMustDoubleAndMatchMinimum(question_id, tokens)
319
491
  previousBondMustNotBeatMaxPrevious(question_id, max_previous)
320
- external payable {
321
- _addAnswerToHistory(question_id, answer, msg.sender, msg.value, false);
322
- _updateCurrentAnswer(question_id, answer, questions[question_id].timeout);
492
+ external {
493
+ _deductTokensOrRevert(tokens);
494
+ _addAnswerToHistory(question_id, answer, msg.sender, tokens, false);
495
+ _updateCurrentAnswer(question_id, answer);
323
496
  }
324
497
 
325
498
  /// @notice Submit an answer for a question, crediting it to the specified account.
@@ -329,14 +502,16 @@ contract Realitio_v2_1 is BalanceHolder {
329
502
  /// @param answer The answer, encoded into bytes32
330
503
  /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
331
504
  /// @param answerer The account to which the answer should be credited
332
- function submitAnswerFor(bytes32 question_id, bytes32 answer, uint256 max_previous, address answerer)
505
+ /// @param tokens Number of tokens sent
506
+ function submitAnswerForERC20(bytes32 question_id, bytes32 answer, uint256 max_previous, address answerer, uint256 tokens)
333
507
  stateOpen(question_id)
334
- bondMustDouble(question_id)
508
+ bondMustDoubleAndMatchMinimum(question_id, tokens)
335
509
  previousBondMustNotBeatMaxPrevious(question_id, max_previous)
336
- external payable {
510
+ external {
511
+ _deductTokensOrRevert(tokens);
337
512
  require(answerer != NULL_ADDRESS, "answerer must be non-zero");
338
- _addAnswerToHistory(question_id, answer, answerer, msg.value, false);
339
- _updateCurrentAnswer(question_id, answer, questions[question_id].timeout);
513
+ _addAnswerToHistory(question_id, answer, answerer, tokens, false);
514
+ _updateCurrentAnswer(question_id, answer);
340
515
  }
341
516
 
342
517
  // @notice Verify and store a commitment, including an appropriate timeout
@@ -359,17 +534,21 @@ contract Realitio_v2_1 is BalanceHolder {
359
534
  /// @param answer_hash The hash of your answer, plus a nonce that you will later reveal
360
535
  /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
361
536
  /// @param _answerer If specified, the address to be given as the question answerer. Defaults to the sender.
537
+ /// @param tokens Number of tokens sent
362
538
  /// @dev Specifying the answerer is useful if you want to delegate the commit-and-reveal to a third-party.
363
- function submitAnswerCommitment(bytes32 question_id, bytes32 answer_hash, uint256 max_previous, address _answerer)
539
+ function submitAnswerCommitmentERC20(bytes32 question_id, bytes32 answer_hash, uint256 max_previous, address _answerer, uint256 tokens)
364
540
  stateOpen(question_id)
365
- bondMustDouble(question_id)
541
+ bondMustDoubleAndMatchMinimum(question_id, tokens)
366
542
  previousBondMustNotBeatMaxPrevious(question_id, max_previous)
367
- external payable {
543
+ external {
368
544
 
369
- bytes32 commitment_id = keccak256(abi.encodePacked(question_id, answer_hash, msg.value));
545
+ _deductTokensOrRevert(tokens);
546
+
547
+ bytes32 commitment_id = keccak256(abi.encodePacked(question_id, answer_hash, tokens));
370
548
  address answerer = (_answerer == NULL_ADDRESS) ? msg.sender : _answerer;
549
+
371
550
  _storeCommitment(question_id, commitment_id);
372
- _addAnswerToHistory(question_id, commitment_id, answerer, msg.value, true);
551
+ _addAnswerToHistory(question_id, commitment_id, answerer, tokens, true);
373
552
 
374
553
  }
375
554
 
@@ -397,7 +576,7 @@ contract Realitio_v2_1 is BalanceHolder {
397
576
  commitments[commitment_id].is_revealed = true;
398
577
 
399
578
  if (bond == questions[question_id].bond) {
400
- _updateCurrentAnswer(question_id, answer, questions[question_id].timeout);
579
+ _updateCurrentAnswer(question_id, answer);
401
580
  }
402
581
 
403
582
  emit LogAnswerReveal(question_id, msg.sender, answer_hash, answer, nonce, bond);
@@ -418,10 +597,17 @@ contract Realitio_v2_1 is BalanceHolder {
418
597
  emit LogNewAnswer(answer_or_commitment_id, question_id, new_history_hash, answerer, bond, block.timestamp, is_commitment);
419
598
  }
420
599
 
421
- function _updateCurrentAnswer(bytes32 question_id, bytes32 answer, uint32 timeout_secs)
600
+ function _updateCurrentAnswer(bytes32 question_id, bytes32 answer)
601
+ internal {
602
+ questions[question_id].best_answer = answer;
603
+ questions[question_id].finalize_ts = uint32(block.timestamp) + questions[question_id].timeout;
604
+ }
605
+
606
+ // Like _updateCurrentAnswer but without advancing the timeout
607
+ function _updateCurrentAnswerByArbitrator(bytes32 question_id, bytes32 answer)
422
608
  internal {
423
609
  questions[question_id].best_answer = answer;
424
- questions[question_id].finalize_ts = uint32(block.timestamp) + timeout_secs;
610
+ questions[question_id].finalize_ts = uint32(block.timestamp);
425
611
  }
426
612
 
427
613
  /// @notice Notify the contract that the arbitrator has been paid for a question, freezing it pending their decision.
@@ -434,7 +620,7 @@ contract Realitio_v2_1 is BalanceHolder {
434
620
  stateOpen(question_id)
435
621
  previousBondMustNotBeatMaxPrevious(question_id, max_previous)
436
622
  external {
437
- require(questions[question_id].bond > 0, "Question must already have an answer when arbitration is requested");
623
+ require(questions[question_id].finalize_ts > UNANSWERED, "Question must already have an answer when arbitration is requested");
438
624
  questions[question_id].is_pending_arbitration = true;
439
625
  emit LogNotifyOfArbitrationRequest(question_id, requester);
440
626
  }
@@ -469,8 +655,7 @@ contract Realitio_v2_1 is BalanceHolder {
469
655
 
470
656
  questions[question_id].is_pending_arbitration = false;
471
657
  _addAnswerToHistory(question_id, answer, answerer, 0, false);
472
- _updateCurrentAnswer(question_id, answer, 0);
473
-
658
+ _updateCurrentAnswerByArbitrator(question_id, answer);
474
659
  }
475
660
 
476
661
  /// @notice Submit the answer for a question, for use by the arbitrator, working out the appropriate winner based on the last answer details.
@@ -521,10 +706,92 @@ contract Realitio_v2_1 is BalanceHolder {
521
706
  /// @return The answer formatted as a bytes32
522
707
  function resultFor(bytes32 question_id)
523
708
  stateFinalized(question_id)
524
- external view returns (bytes32) {
709
+ public view returns (bytes32) {
525
710
  return questions[question_id].best_answer;
526
711
  }
527
712
 
713
+ /// @notice Returns whether the question was answered before it had an answer, ie resolved to UNRESOLVED_ANSWER
714
+ /// @param question_id The ID of the question
715
+ function isSettledTooSoon(bytes32 question_id)
716
+ public view returns(bool) {
717
+ return (resultFor(question_id) == UNRESOLVED_ANSWER);
718
+ }
719
+
720
+ /// @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
721
+ /// @param question_id The ID of the question
722
+ function resultForOnceSettled(bytes32 question_id)
723
+ external view returns(bytes32) {
724
+ bytes32 result = resultFor(question_id);
725
+ if (result == UNRESOLVED_ANSWER) {
726
+ // Try the replacement
727
+ bytes32 replacement_id = reopened_questions[question_id];
728
+ require(replacement_id != bytes32(0x0), "Question was settled too soon and has not been reopened");
729
+ // We only try one layer down rather than recursing to keep the gas costs predictable
730
+ result = resultFor(replacement_id);
731
+ require(result != UNRESOLVED_ANSWER, "Question replacement was settled too soon and has not been reopened");
732
+ }
733
+ return result;
734
+ }
735
+
736
+ /// @notice Asks a new question reopening a previously-asked question that was settled too soon
737
+ /// @dev A special version of askQuestion() that replaces a previous question that was settled too soon
738
+ /// @param template_id The ID number of the template the question will use
739
+ /// @param question A string containing the parameters that will be passed into the template to make the question
740
+ /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
741
+ /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer
742
+ /// @param opening_ts If set, the earliest time it should be possible to answer the question.
743
+ /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
744
+ /// @param min_bond The minimum bond that can be used to provide the first answer.
745
+ /// @param reopens_question_id The ID of the question this reopens
746
+ /// @param tokens The number of tokens you want to use as an additional question reward for the reopened question.
747
+ /// @return The ID of the newly-created question, created deterministically.
748
+ function reopenQuestionERC20(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 min_bond, bytes32 reopens_question_id, uint256 tokens)
749
+ // stateNotCreated is enforced by the internal _askQuestion
750
+ public returns (bytes32) {
751
+
752
+ // _deductTokensOrRevert will be called when we call askQuestionWithMinBondERC20
753
+
754
+ require(isSettledTooSoon(reopens_question_id), "You can only reopen questions that resolved as settled too soon");
755
+
756
+ bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
757
+
758
+ // A reopening must exactly match the original question, except for the nonce and the creator
759
+ require(content_hash == questions[reopens_question_id].content_hash, "content hash mismatch");
760
+ require(arbitrator == questions[reopens_question_id].arbitrator, "arbitrator mismatch");
761
+ require(timeout == questions[reopens_question_id].timeout, "timeout mismatch");
762
+ require(opening_ts == questions[reopens_question_id].opening_ts , "opening_ts mismatch");
763
+ require(min_bond == questions[reopens_question_id].min_bond, "min_bond mismatch");
764
+
765
+ // If the the question was itself reopening some previous question, you'll have to re-reopen the previous question first.
766
+ // This ensures the bounty can be passed on to the next attempt of the original question.
767
+ require(!reopener_questions[reopens_question_id], "Question is already reopening a previous question");
768
+
769
+ // A question can only be reopened once, unless the reopening was also settled too soon in which case it can be replaced
770
+ bytes32 existing_reopen_question_id = reopened_questions[reopens_question_id];
771
+
772
+ // Normally when we reopen a question we will take its bounty and pass it on to the reopened version.
773
+ bytes32 take_bounty_from_question_id = reopens_question_id;
774
+ // If the question has already been reopened but was again settled too soon, we can transfer its bounty to the next attempt.
775
+ if (existing_reopen_question_id != bytes32(0)) {
776
+ require(isSettledTooSoon(existing_reopen_question_id), "Question has already been reopened");
777
+ // We'll overwrite the reopening with our new question and move the bounty.
778
+ // 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.
779
+ reopener_questions[existing_reopen_question_id] = false;
780
+ take_bounty_from_question_id = existing_reopen_question_id;
781
+ }
782
+
783
+ bytes32 question_id = askQuestionWithMinBondERC20(template_id, question, arbitrator, timeout, opening_ts, nonce, min_bond, tokens);
784
+
785
+ reopened_questions[reopens_question_id] = question_id;
786
+ reopener_questions[question_id] = true;
787
+
788
+ questions[question_id].bounty = questions[take_bounty_from_question_id].bounty + questions[question_id].bounty;
789
+ questions[take_bounty_from_question_id].bounty = 0;
790
+
791
+ emit LogReopenQuestion(question_id, reopens_question_id);
792
+
793
+ return question_id;
794
+ }
528
795
 
529
796
  /// @notice Return the final answer to the specified question, provided it matches the specified criteria.
530
797
  /// @dev Reverts if the question is not finalized, or if it does not match the specified criteria.
@@ -677,8 +944,11 @@ contract Realitio_v2_1 is BalanceHolder {
677
944
  // The entry is for the first payee we come to, ie the winner.
678
945
  // They get the question bounty.
679
946
  payee = addr;
680
- queued_funds = queued_funds + questions[question_id].bounty;
681
- questions[question_id].bounty = 0;
947
+
948
+ if (best_answer != UNRESOLVED_ANSWER && questions[question_id].bounty > 0) {
949
+ _payPayee(question_id, payee, questions[question_id].bounty);
950
+ questions[question_id].bounty = 0;
951
+ }
682
952
 
683
953
  } else if (addr != payee) {
684
954
 
@@ -816,4 +1086,11 @@ contract Realitio_v2_1 is BalanceHolder {
816
1086
  return questions[question_id].bond;
817
1087
  }
818
1088
 
1089
+ /// @notice Returns the minimum bond that can answer the question
1090
+ /// @param question_id The ID of the question
1091
+ function getMinBond(bytes32 question_id)
1092
+ public view returns(uint256) {
1093
+ return questions[question_id].min_bond;
1094
+ }
1095
+
819
1096
  }
@@ -0,0 +1,53 @@
1
+
2
+ import { utils, Wallet } from "zksync-web3";
3
+ import * as ethers from "ethers";
4
+ import { HardhatRuntimeEnvironment } from "hardhat/types";
5
+ import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
6
+ const fs = require('fs');
7
+
8
+ // An example of a deploy script that will deploy and call a simple contract.
9
+ export default async function (hre: HardhatRuntimeEnvironment) {
10
+ console.log(`Running deploy script for the reality.eth contract`);
11
+
12
+ const key_file = '/home/ed/secrets/zksync_goerli.sec';
13
+ const priv = fs.readFileSync(key_file, 'utf8').replace(/\n$/, "");
14
+
15
+ // Initialize the wallet.
16
+ const wallet = new Wallet(priv);
17
+
18
+ // Create deployer object and load the artifact of the contract we want to deploy.
19
+ const deployer = new Deployer(hre, wallet);
20
+ const artifact = await deployer.loadArtifact("RealityETH_ERC20_v3_0");
21
+
22
+ /*
23
+ // Deposit some funds to L2 in order to be able to perform L2 transactions.
24
+ const depositAmount = ethers.utils.parseEther("0.00001");
25
+ const depositHandle = await deployer.zkWallet.deposit({
26
+ to: deployer.zkWallet.address,
27
+ token: utils.ETH_ADDRESS,
28
+ amount: depositAmount,
29
+ });
30
+ // Wait until the deposit is processed on zkSync
31
+ await depositHandle.wait();
32
+ */
33
+
34
+ // Deploy this contract. The returned object will be of a `Contract` type, similarly to ones in `ethers`.
35
+ const realityContract = await deployer.deploy(artifact, []);
36
+
37
+ // Show the contract info.
38
+ const contractAddress = realityContract.address;
39
+ console.log(`${artifact.contractName} was deployed to ${contractAddress}`);
40
+
41
+ // Edit the greeting of the contract
42
+ const token = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
43
+ const setTokenHandle = await realityContract.setToken(token);
44
+ await setTokenHandle.wait();
45
+
46
+ const tokenFromContract = await realityContract.token();
47
+ if (tokenFromContract == token) {
48
+ console.log(`Contract greets us with ${token}!`);
49
+ } else {
50
+ console.error(`Contract said something unexpected: ${tokenFromContract}`);
51
+ }
52
+ }
53
+
@@ -0,0 +1,30 @@
1
+ require("@matterlabs/hardhat-zksync-deploy");
2
+ require("@matterlabs/hardhat-zksync-solc");
3
+
4
+ module.exports = {
5
+ zksolc: {
6
+ version: "0.1.0",
7
+ compilerSource: "docker",
8
+ settings: {
9
+ optimizer: {
10
+ enabled: true,
11
+ },
12
+ experimental: {
13
+ dockerImage: "matterlabs/zksolc",
14
+ },
15
+ },
16
+ },
17
+ zkSyncDeploy: {
18
+ zkSyncNetwork: "https://zksync2-testnet.zksync.dev",
19
+ ethNetwork: "goerli", // Can also be the RPC URL of the network (e.g. `https://goerli.infura.io/v3/<API_KEY>`)
20
+ },
21
+ networks: {
22
+ // To compile with zksolc, this must be the default network.
23
+ hardhat: {
24
+ zksync: true,
25
+ },
26
+ },
27
+ solidity: {
28
+ version: "0.8.10",
29
+ },
30
+ };
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "zksync2",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "license": "MIT",
6
+ "devDependencies": {
7
+ "@matterlabs/hardhat-zksync-deploy": "^0.3.5",
8
+ "@matterlabs/hardhat-zksync-solc": "^0.3.5",
9
+ "ethers": "^5.6.9",
10
+ "hardhat": "^2.10.1",
11
+ "ts-node": "^10.9.1",
12
+ "typescript": "^4.7.4",
13
+ "zksync-web3": "^0.7.9"
14
+ }
15
+ }