@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.
- package/README.md +9 -3
- package/chains/chainid.network.json +7691 -1349
- package/chains/chainid.network.local.json +25 -0
- package/chains/deployments/1/ETH/RealityETH-3.0.json +2 -1
- package/chains/deployments/1/SWISE/RealityETH_ERC20-3.0.json +7 -0
- package/chains/deployments/10/OETH/RealityETH-3.0.json +6 -0
- package/chains/deployments/100/GNO/RealityETH_ERC20-3.0.json +7 -0
- package/chains/deployments/100/SWISE/RealityETH_ERC20-3.0.json +7 -0
- package/chains/deployments/100/XDAI/RealityETH-3.0.json +3 -1
- package/chains/deployments/1337702/ETH/RealityETH-3.0.json +6 -0
- package/chains/deployments/280/WETH/RealityETH_ERC20-3.0.json +8 -0
- package/chains/deployments/4/ETH/RealityETH-3.0.json +3 -1
- package/chains/deployments/42/ETH/RealityETH-3.0.json +2 -1
- package/chains/deployments/56/DEXE/RealityETH_ERC20-3.0.json +7 -0
- package/chains/deployments/69/OETH/RealityETH-3.0.json +6 -0
- package/chains/deployments/77/XDAI/RealityETH-2.1-rc1.json +1 -3
- package/chains/deployments/777/CTH/RealityETH-3.0.json +6 -0
- package/chains/supported.json +29 -3
- package/development/contracts/Arbitrator.sol +7 -6
- package/development/contracts/BalanceHolder.sol +4 -2
- package/development/contracts/{BalanceHolderERC20.sol → BalanceHolder_ERC20.sol} +3 -2
- package/development/contracts/IArbitrator.sol +4 -7
- package/development/contracts/IArbitratorForeignProxy.sol +12 -0
- package/development/contracts/IBalanceHolder.sol +3 -1
- package/development/contracts/IBalanceHolder_ERC20.sol +11 -0
- package/development/contracts/IERC20.sol +1 -1
- package/development/contracts/IOwned.sol +1 -1
- package/development/contracts/IRealityETH.sol +63 -0
- package/development/contracts/IRealityETH_ERC20.sol +67 -0
- package/development/contracts/Owned.sol +4 -2
- package/development/contracts/RealityETH-3.0.sol +4 -82
- package/development/contracts/RealityETH_ERC20-3.0.sol +3 -3
- package/generated/chains.json +114 -22
- package/generated/contracts.json +98 -7
- package/generated/tokens.json +41 -2
- package/index.js +7 -2
- package/non_standard_deployments/zksync2/README +21 -0
- package/{development/contracts/Realitio_v2_1.sol → non_standard_deployments/zksync2/contracts/RealityETH_ERC20-3.0.sol} +324 -47
- package/non_standard_deployments/zksync2/deploy/deploy.ts +53 -0
- package/non_standard_deployments/zksync2/hardhat.config.ts +30 -0
- package/non_standard_deployments/zksync2/package.json +15 -0
- package/non_standard_deployments/zksync2/yarn.lock +2907 -0
- package/package.json +3 -3
- package/scripts/deploy.js +14 -3
- package/scripts/fetch_and_reformat_chains_json.js +31 -0
- package/scripts/{format_abi.js → format_json.js} +0 -0
- package/scripts/generate_chains_json.js +3 -0
- package/tokens/CTH.json +7 -0
- package/tokens/DEXE.json +7 -0
- package/tokens/ETH.json +2 -1
- package/tokens/GNO.json +1 -0
- package/tokens/OETH.json +8 -0
- package/tokens/SWISE.json +8 -0
- package/tokens/WETH.json +7 -0
- package/development/contracts/IRealitio.sol +0 -44
- package/development/contracts/MultiSigWallet.sol +0 -391
- package/development/contracts/Realitio.sol +0 -756
- package/development/contracts/RealitioERC20.sol +0 -825
- package/development/contracts/RealitioSafeMath256.sol +0 -36
- package/development/contracts/RealitioSafeMath32.sol +0 -17
- package/development/contracts/SplitterWallet.sol +0 -142
- 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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
178
|
-
require(
|
|
179
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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, "
|
|
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
|
-
|
|
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
|
-
|
|
472
|
+
/// @param tokens The number of tokens to fund
|
|
473
|
+
function fundAnswerBountyERC20(bytes32 question_id, uint256 tokens)
|
|
304
474
|
stateOpen(question_id)
|
|
305
|
-
external
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
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
|
-
|
|
490
|
+
bondMustDoubleAndMatchMinimum(question_id, tokens)
|
|
319
491
|
previousBondMustNotBeatMaxPrevious(question_id, max_previous)
|
|
320
|
-
external
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
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
|
-
|
|
508
|
+
bondMustDoubleAndMatchMinimum(question_id, tokens)
|
|
335
509
|
previousBondMustNotBeatMaxPrevious(question_id, max_previous)
|
|
336
|
-
external
|
|
510
|
+
external {
|
|
511
|
+
_deductTokensOrRevert(tokens);
|
|
337
512
|
require(answerer != NULL_ADDRESS, "answerer must be non-zero");
|
|
338
|
-
_addAnswerToHistory(question_id, answer, answerer,
|
|
339
|
-
_updateCurrentAnswer(question_id, answer
|
|
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
|
|
539
|
+
function submitAnswerCommitmentERC20(bytes32 question_id, bytes32 answer_hash, uint256 max_previous, address _answerer, uint256 tokens)
|
|
364
540
|
stateOpen(question_id)
|
|
365
|
-
|
|
541
|
+
bondMustDoubleAndMatchMinimum(question_id, tokens)
|
|
366
542
|
previousBondMustNotBeatMaxPrevious(question_id, max_previous)
|
|
367
|
-
external
|
|
543
|
+
external {
|
|
368
544
|
|
|
369
|
-
|
|
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,
|
|
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
|
|
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
|
|
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)
|
|
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].
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
681
|
-
questions[question_id].bounty
|
|
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
|
+
}
|