@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.
- package/.prettierrc +16 -0
- package/abi/solc-0.8.20/RealityETH-4.0.abi.json +1 -0
- package/abi/solc-0.8.20/RealityETH_ERC20-4.0.abi.json +1 -0
- package/bytecode/Arbitrator.bin +1 -1
- package/bytecode/RealityETH-3.0.bin +1 -1
- package/bytecode/RealityETH-4.0.bin +1 -0
- package/bytecode/RealityETH_ERC20-3.0.bin +1 -1
- package/bytecode/RealityETH_ERC20-4.0.bin +1 -0
- package/chains/chainid.network.json +5033 -431
- package/chains/deployments/300/ETH/RealityETH-3.0.json +7 -0
- package/chains/supported.json +3 -0
- package/development/contracts/.solhint.json +9 -0
- package/development/contracts/Arbitrator.sol +32 -81
- package/development/contracts/BalanceHolder.sol +4 -10
- package/development/contracts/BalanceHolder_ERC20.sol +5 -11
- package/development/contracts/CallbackClient.sol +3 -10
- package/development/contracts/ERC20Token.sol +29 -30
- package/development/contracts/ExplodingCallbackClient.sol +1 -4
- package/development/contracts/IArbitrator.sol +23 -23
- package/development/contracts/IArbitratorCore.sol +14 -0
- package/development/contracts/IArbitratorCrowdFundable.sol +8 -0
- package/development/contracts/IArbitratorErrors.sol +10 -0
- package/development/contracts/IArbitratorForeignProxy.sol +15 -7
- package/development/contracts/IArbitratorLegacy.sol +9 -0
- package/development/contracts/IArbitratorManagement.sol +25 -0
- package/development/contracts/IBalanceHolder.sol +3 -3
- package/development/contracts/IBalanceHolder_ERC20.sol +5 -5
- package/development/contracts/IERC20.sol +4 -5
- package/development/contracts/IOwned.sol +3 -3
- package/development/contracts/IRealityETH.sol +5 -58
- package/development/contracts/IRealityETHCommitReveal.sol +17 -0
- package/development/contracts/IRealityETHCommitReveal_ERC20.sol +16 -0
- package/development/contracts/IRealityETHCore.sol +135 -0
- package/development/contracts/IRealityETHCore_ERC20.sol +149 -0
- package/development/contracts/IRealityETHErrors.sol +86 -0
- package/development/contracts/IRealityETHErrors_ERC20.sol +13 -0
- package/development/contracts/IRealityETH_ERC20.sol +5 -62
- package/development/contracts/Owned.sol +4 -6
- package/development/contracts/{RealityETH-3.0.sol → RealityETH-4.0.sol} +221 -383
- package/development/contracts/{RealityETH_ERC20-3.0.sol → RealityETH_ERC20-4.0.sol} +238 -474
- package/development/contracts/RealityETH_ERC20_Factory.sol +47 -42
- package/development/contracts/RegisteredWalletArbitrator.sol +8 -15
- package/development/contracts/compile.py +3 -0
- package/development/scripts/string_to_error.py +111 -0
- package/development/version_checksums/check_matches_flat.sh +20 -0
- package/development/version_checksums/check_matches_last.sh +37 -0
- package/development/version_checksums/log_bytecode_history.sh +41 -0
- package/generated/chains.json +33 -5
- package/generated/tokens.json +1 -0
- package/non_standard_deployments/zksync/LICENSE +21 -0
- package/non_standard_deployments/zksync/README.md +51 -0
- package/non_standard_deployments/zksync/deploy/deploy.ts +11 -0
- package/non_standard_deployments/zksync/deploy/utils.ts +168 -0
- package/non_standard_deployments/zksync/hardhat.config.ts +55 -0
- package/non_standard_deployments/zksync/package-lock.json +6295 -0
- package/non_standard_deployments/zksync/package.json +34 -0
- package/package.json +7 -3
- package/scripts/transfer_arbitrator.js +60 -0
- package/tests/python/requirements.txt +55 -54
- package/tests/python/test.py +150 -50
- package/tests/python/test_erc20.py +15 -14
- package/tokens/ETH.json +3 -2
- package/non_standard_deployments/zksync2/README +0 -21
- package/non_standard_deployments/zksync2/contracts/RealityETH_ERC20-3.0.sol +0 -1096
- package/non_standard_deployments/zksync2/deploy/deploy.ts +0 -62
- package/non_standard_deployments/zksync2/deploy/deploy_erc20.ts +0 -56
- package/non_standard_deployments/zksync2/deploy/deploy_mainnet.ts +0 -46
- package/non_standard_deployments/zksync2/hardhat.config.ts +0 -27
- package/non_standard_deployments/zksync2/package.json +0 -15
- package/non_standard_deployments/zksync2/yarn.lock +0 -2898
- /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.
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import {IRealityETHCore} from "./IRealityETHCore.sol";
|
|
6
|
+
import {BalanceHolder} from "./BalanceHolder.sol";
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
44
|
+
if (questions[question_id].timeout != 0) revert QuestionMustNotExist();
|
|
83
45
|
_;
|
|
84
46
|
}
|
|
85
47
|
|
|
86
48
|
modifier stateOpen(bytes32 question_id) {
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
52
|
+
if (finalize_ts != UNANSWERED && finalize_ts <= uint32(block.timestamp)) revert FinalizationDeadlineMustNotHavePassed();
|
|
91
53
|
uint32 opening_ts = questions[question_id].opening_ts;
|
|
92
|
-
|
|
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
|
-
|
|
59
|
+
if (!questions[question_id].is_pending_arbitration) revert QuestionMustBePendingArbitration();
|
|
98
60
|
_;
|
|
99
61
|
}
|
|
100
62
|
|
|
101
63
|
modifier stateOpenOrPendingArbitration(bytes32 question_id) {
|
|
102
|
-
|
|
64
|
+
if (questions[question_id].timeout == 0) revert QuestionMustExist();
|
|
103
65
|
uint32 finalize_ts = questions[question_id].finalize_ts;
|
|
104
|
-
|
|
66
|
+
if (finalize_ts != UNANSWERED && finalize_ts <= uint32(block.timestamp)) revert FinalizationDealineMustNotHavePassed();
|
|
105
67
|
uint32 opening_ts = questions[question_id].opening_ts;
|
|
106
|
-
|
|
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
|
-
|
|
73
|
+
if (!isFinalized(question_id)) revert QuestionMustBeFinalized();
|
|
112
74
|
_;
|
|
113
75
|
}
|
|
114
76
|
|
|
115
|
-
modifier bondMustDoubleAndMatchMinimum(bytes32 question_id) {
|
|
116
|
-
|
|
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
|
-
|
|
81
|
+
if (tokens < (questions[question_id].min_bond)) revert BondMustExceedTheMinimum();
|
|
120
82
|
} else {
|
|
121
|
-
|
|
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
|
-
|
|
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,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
246
|
-
|
|
234
|
+
if (timeout == 0) revert TimeoutMustBePositive();
|
|
235
|
+
if (timeout >= 365 days) revert TimeoutMustBeLessThan365Days();
|
|
247
236
|
|
|
248
|
-
uint256 bounty =
|
|
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
|
-
|
|
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(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
external payable {
|
|
300
|
-
_addAnswerToHistory(question_id, answer, msg.sender, msg.value
|
|
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(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
322
|
-
|
|
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(
|
|
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(
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
external {
|
|
423
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
471
|
-
|
|
472
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
|
627
|
-
/// @param bonds Last-to-first, the bond supplied with each answer
|
|
628
|
-
/// @param answers Last-to-first, each answer supplied
|
|
629
|
-
function claimWinnings(
|
|
630
|
-
|
|
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 =
|
|
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
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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
|
-
//
|
|
678
|
-
|
|
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
|
-
|
|
705
|
-
|
|
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
|
|
784
|
-
/// @param bonds In a single list for all supplied questions, the bond supplied with each answer
|
|
785
|
-
/// @param answers In a single list for all supplied questions, each answer supplied
|
|
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,
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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
|
}
|