@reality.eth/contracts 3.0.48 → 4.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/abi/solc-0.8.20/RealityETH-4.0.abi.json +1 -1
- package/abi/solc-0.8.20/RealityETHFreezableExample_ERC20-4.0.abi.json +1 -0
- package/abi/solc-0.8.20/RealityETHReopenable-4.0.abi.json +1 -0
- package/abi/solc-0.8.20/RealityETHReopenable_ERC20-4.0.abi.json +1 -0
- package/abi/solc-0.8.20/RealityETH_ERC20-4.0.abi.json +1 -1
- package/bytecode/RealityETH-4.0.bin +1 -1
- package/bytecode/RealityETHFreezableExample_ERC20-4.0.bin +1 -0
- package/bytecode/RealityETHReopenable-4.0.bin +1 -0
- package/bytecode/RealityETHReopenable_ERC20-4.0.bin +1 -0
- package/bytecode/RealityETH_ERC20-4.0.bin +1 -1
- package/chains/supported.json +1 -1
- package/development/contracts/IBalanceHolder.sol +1 -0
- package/development/contracts/IRealityETH.sol +6 -2
- package/development/contracts/{IRealityETHCore.sol → IRealityETHCore_Common.sol} +4 -23
- package/development/contracts/IRealityETHCore_ERC20.sol +3 -124
- package/development/contracts/IRealityETHCore_Native.sol +12 -0
- package/development/contracts/IRealityETHCreateTemplateAndAskQuestion.sol +8 -0
- package/development/contracts/IRealityETHHistoryVerification.sol +8 -0
- package/development/contracts/IRealityETHReopenable.sol +22 -0
- package/development/contracts/IRealityETHReopenable_ERC20.sol +23 -0
- package/development/contracts/IRealityETH_ERC20.sol +5 -1
- package/development/contracts/RealityETH-4.0.sol +12 -614
- package/development/contracts/RealityETHCore_Common.sol +535 -0
- package/development/contracts/RealityETHFreezableExample_ERC20-4.0.sol +17 -0
- package/development/contracts/RealityETHFreezable_ERC20.sol +39 -0
- package/development/contracts/RealityETHReopenable-4.0.sol +119 -0
- package/development/contracts/RealityETHReopenable_ERC20-4.0.sol +122 -0
- package/development/contracts/RealityETH_ERC20-4.0.sol +13 -614
- package/generated/chains.json +1 -1
- package/package.json +2 -2
- package/tests/python/test.py +186 -5
- package/development/contracts/BalanceHolder_ERC20.sol +0 -21
- package/development/contracts/IBalanceHolder_ERC20.sol +0 -11
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import {IRealityETHReopenable} from "./IRealityETHReopenable.sol";
|
|
6
|
+
import {RealityETH_v4_0} from "./RealityETH-4.0.sol";
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* This version of reality.eth provides the ability to answer a question as "answered too soon", allowing it to be reopened.
|
|
10
|
+
* This is useful when you want to ask a question but you don't know when the answer will be available.
|
|
11
|
+
* You can set the settlement time at the earliest date it may be available, then if someone answers it too soon, it can be answered at such then later reopened.
|
|
12
|
+
* NB In v3 of reality.eth this feature was built in and there was no way to turn it off.
|
|
13
|
+
* From v4, we ship two versions: RealityETH without it, and RealityETHReopenable with it.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// solhint-disable-next-line contract-name-camelcase
|
|
17
|
+
contract RealityETHReopenable_v4_0 is IRealityETHReopenable, RealityETH_v4_0 {
|
|
18
|
+
// Special value representing a question that was answered too soon.
|
|
19
|
+
// bytes32(-2). By convention we use bytes32(-1) for "invalid", although the contract does not handle this.
|
|
20
|
+
bytes32 private constant UNRESOLVED_ANSWER = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe;
|
|
21
|
+
|
|
22
|
+
mapping(bytes32 => bytes32) public reopened_questions;
|
|
23
|
+
mapping(bytes32 => bool) public reopener_questions;
|
|
24
|
+
|
|
25
|
+
function _isBountyPayableOnAnswer(bytes32 answer) internal pure override returns (bool) {
|
|
26
|
+
return (answer != UNRESOLVED_ANSWER);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// @notice Returns whether the question was answered before it had an answer, ie resolved to UNRESOLVED_ANSWER
|
|
30
|
+
/// @param question_id The ID of the question
|
|
31
|
+
function isSettledTooSoon(bytes32 question_id) public view returns (bool) {
|
|
32
|
+
return (resultFor(question_id) == UNRESOLVED_ANSWER);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// @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
|
|
36
|
+
/// @param question_id The ID of the question
|
|
37
|
+
function resultForOnceSettled(bytes32 question_id) external view returns (bytes32) {
|
|
38
|
+
bytes32 result = resultFor(question_id);
|
|
39
|
+
if (result == UNRESOLVED_ANSWER) {
|
|
40
|
+
// Try the replacement
|
|
41
|
+
bytes32 replacement_id = reopened_questions[question_id];
|
|
42
|
+
if (replacement_id == bytes32(0x0)) revert QuestionWasSettledTooSoonAndHasNotBeenReopened();
|
|
43
|
+
// We only try one layer down rather than recursing to keep the gas costs predictable
|
|
44
|
+
result = resultFor(replacement_id);
|
|
45
|
+
if (result == UNRESOLVED_ANSWER) revert QuestionReplacementWasSettledTooSoonAndHasNotBeenReopened();
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// @notice Asks a new question reopening a previously-asked question that was settled too soon
|
|
51
|
+
/// @dev A special version of askQuestion() that replaces a previous question that was settled too soon
|
|
52
|
+
/// @param template_id The ID number of the template the question will use
|
|
53
|
+
/// @param question A string containing the parameters that will be passed into the template to make the question
|
|
54
|
+
/// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
|
|
55
|
+
/// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer
|
|
56
|
+
/// @param opening_ts If set, the earliest time it should be possible to answer the question.
|
|
57
|
+
/// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
|
|
58
|
+
/// @param min_bond The minimum bond that can be used to provide the first answer.
|
|
59
|
+
/// @param reopens_question_id The ID of the question this reopens
|
|
60
|
+
/// @return The ID of the newly-created question, created deterministically.
|
|
61
|
+
function reopenQuestion(
|
|
62
|
+
uint256 template_id,
|
|
63
|
+
string memory question,
|
|
64
|
+
address arbitrator,
|
|
65
|
+
uint32 timeout,
|
|
66
|
+
uint32 opening_ts,
|
|
67
|
+
uint256 nonce,
|
|
68
|
+
uint256 min_bond,
|
|
69
|
+
bytes32 reopens_question_id
|
|
70
|
+
)
|
|
71
|
+
public
|
|
72
|
+
payable
|
|
73
|
+
returns (
|
|
74
|
+
// stateNotCreated is enforced by the internal _askQuestion
|
|
75
|
+
bytes32
|
|
76
|
+
)
|
|
77
|
+
{
|
|
78
|
+
if (!isSettledTooSoon(reopens_question_id)) revert YouCanOnlyReopenQuestionsThatResolvedAsSettledTooSoon();
|
|
79
|
+
|
|
80
|
+
bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
|
|
81
|
+
|
|
82
|
+
// A reopening must exactly match the original question, except for the nonce and the creator
|
|
83
|
+
if (content_hash != questions[reopens_question_id].content_hash) revert ContentHashMismatch();
|
|
84
|
+
if (arbitrator != questions[reopens_question_id].arbitrator) revert ArbitratorMismatch();
|
|
85
|
+
if (timeout != questions[reopens_question_id].timeout) revert TimeoutMismatch();
|
|
86
|
+
if (opening_ts != questions[reopens_question_id].opening_ts) revert Opening_TsMismatch();
|
|
87
|
+
if (min_bond != questions[reopens_question_id].min_bond) revert Min_BondMismatch();
|
|
88
|
+
|
|
89
|
+
// If the the question was itself reopening some previous question, you'll have to re-reopen the previous question first.
|
|
90
|
+
// This ensures the bounty can be passed on to the next attempt of the original question.
|
|
91
|
+
if (reopener_questions[reopens_question_id]) revert QuestionIsAlreadyReopeningAPreviousQuestion();
|
|
92
|
+
|
|
93
|
+
// A question can only be reopened once, unless the reopening was also settled too soon in which case it can be replaced
|
|
94
|
+
bytes32 existing_reopen_question_id = reopened_questions[reopens_question_id];
|
|
95
|
+
|
|
96
|
+
// Normally when we reopen a question we will take its bounty and pass it on to the reopened version.
|
|
97
|
+
bytes32 take_bounty_from_question_id = reopens_question_id;
|
|
98
|
+
// If the question has already been reopened but was again settled too soon, we can transfer its bounty to the next attempt.
|
|
99
|
+
if (existing_reopen_question_id != bytes32(0)) {
|
|
100
|
+
if (!isSettledTooSoon(existing_reopen_question_id)) revert QuestionHasAlreadyBeenReopened();
|
|
101
|
+
// We'll overwrite the reopening with our new question and move the bounty.
|
|
102
|
+
// 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.
|
|
103
|
+
reopener_questions[existing_reopen_question_id] = false;
|
|
104
|
+
take_bounty_from_question_id = existing_reopen_question_id;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
bytes32 question_id = askQuestionWithMinBond(template_id, question, arbitrator, timeout, opening_ts, nonce, min_bond);
|
|
108
|
+
|
|
109
|
+
reopened_questions[reopens_question_id] = question_id;
|
|
110
|
+
reopener_questions[question_id] = true;
|
|
111
|
+
|
|
112
|
+
questions[question_id].bounty = questions[take_bounty_from_question_id].bounty + questions[question_id].bounty;
|
|
113
|
+
questions[take_bounty_from_question_id].bounty = 0;
|
|
114
|
+
|
|
115
|
+
emit LogReopenQuestion(question_id, reopens_question_id);
|
|
116
|
+
|
|
117
|
+
return question_id;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import {IRealityETHReopenable_ERC20} from "./IRealityETHReopenable_ERC20.sol";
|
|
6
|
+
import {RealityETH_ERC20_v4_0} from "./RealityETH_ERC20-4.0.sol";
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* This version of reality.eth provides the ability to answer a question as "answered too soon", allowing it to be reopened.
|
|
10
|
+
* This is useful when you want to ask a question but you don't know when the answer will be available.
|
|
11
|
+
* You can set the settlement time at the earliest date it may be available, then if someone answers it too soon, it can be answered at such then later reopened.
|
|
12
|
+
* NB In v3 of reality.eth this feature was built in and there was no way to turn it off.
|
|
13
|
+
* From v4, we ship two versions: RealityETH_ERC20 without it, and RealityETHReopenable_ERC20 with it.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// solhint-disable-next-line contract-name-camelcase
|
|
17
|
+
contract RealityETHReopenable_v4_0 is IRealityETHReopenable_ERC20, RealityETH_ERC20_v4_0 {
|
|
18
|
+
// Special value representing a question that was answered too soon.
|
|
19
|
+
// bytes32(-2). By convention we use bytes32(-1) for "invalid", although the contract does not handle this.
|
|
20
|
+
bytes32 private constant UNRESOLVED_ANSWER = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe;
|
|
21
|
+
|
|
22
|
+
mapping(bytes32 => bytes32) public reopened_questions;
|
|
23
|
+
mapping(bytes32 => bool) public reopener_questions;
|
|
24
|
+
|
|
25
|
+
function _isBountyPayableOnAnswer(bytes32 answer) internal pure override returns (bool) {
|
|
26
|
+
return (answer != UNRESOLVED_ANSWER);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// @notice Returns whether the question was answered before it had an answer, ie resolved to UNRESOLVED_ANSWER
|
|
30
|
+
/// @param question_id The ID of the question
|
|
31
|
+
function isSettledTooSoon(bytes32 question_id) public view returns (bool) {
|
|
32
|
+
return (resultFor(question_id) == UNRESOLVED_ANSWER);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// @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
|
|
36
|
+
/// @param question_id The ID of the question
|
|
37
|
+
function resultForOnceSettled(bytes32 question_id) external view returns (bytes32) {
|
|
38
|
+
bytes32 result = resultFor(question_id);
|
|
39
|
+
if (result == UNRESOLVED_ANSWER) {
|
|
40
|
+
// Try the replacement
|
|
41
|
+
bytes32 replacement_id = reopened_questions[question_id];
|
|
42
|
+
if (replacement_id == bytes32(0x0)) revert QuestionWasSettledTooSoonAndHasNotBeenReopened();
|
|
43
|
+
// We only try one layer down rather than recursing to keep the gas costs predictable
|
|
44
|
+
result = resultFor(replacement_id);
|
|
45
|
+
if (result == UNRESOLVED_ANSWER) revert QuestionReplacementWasSettledTooSoonAndHasNotBeenReopened();
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// @notice Asks a new question reopening a previously-asked question that was settled too soon
|
|
51
|
+
/// @dev A special version of askQuestion() that replaces a previous question that was settled too soon
|
|
52
|
+
/// @param template_id The ID number of the template the question will use
|
|
53
|
+
/// @param question A string containing the parameters that will be passed into the template to make the question
|
|
54
|
+
/// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
|
|
55
|
+
/// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer
|
|
56
|
+
/// @param opening_ts If set, the earliest time it should be possible to answer the question.
|
|
57
|
+
/// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
|
|
58
|
+
/// @param min_bond The minimum bond that can be used to provide the first answer.
|
|
59
|
+
/// @param reopens_question_id The ID of the question this reopens
|
|
60
|
+
/// @param tokens The number of tokens you want to use as an additional question reward for the reopened question.
|
|
61
|
+
/// @return The ID of the newly-created question, created deterministically.
|
|
62
|
+
function reopenQuestionERC20(
|
|
63
|
+
uint256 template_id,
|
|
64
|
+
string memory question,
|
|
65
|
+
address arbitrator,
|
|
66
|
+
uint32 timeout,
|
|
67
|
+
uint32 opening_ts,
|
|
68
|
+
uint256 nonce,
|
|
69
|
+
uint256 min_bond,
|
|
70
|
+
bytes32 reopens_question_id,
|
|
71
|
+
uint256 tokens
|
|
72
|
+
)
|
|
73
|
+
public
|
|
74
|
+
returns (
|
|
75
|
+
// stateNotCreated is enforced by the internal _askQuestion
|
|
76
|
+
bytes32
|
|
77
|
+
)
|
|
78
|
+
{
|
|
79
|
+
// _deductTokensOrRevert will be called when we call askQuestionWithMinBondERC20
|
|
80
|
+
|
|
81
|
+
if (!isSettledTooSoon(reopens_question_id)) revert YouCanOnlyReopenQuestionsThatResolvedAsSettledTooSoon();
|
|
82
|
+
|
|
83
|
+
bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
|
|
84
|
+
|
|
85
|
+
// A reopening must exactly match the original question, except for the nonce and the creator
|
|
86
|
+
if (content_hash != questions[reopens_question_id].content_hash) revert ContentHashMismatch();
|
|
87
|
+
if (arbitrator != questions[reopens_question_id].arbitrator) revert ArbitratorMismatch();
|
|
88
|
+
if (timeout != questions[reopens_question_id].timeout) revert TimeoutMismatch();
|
|
89
|
+
if (opening_ts != questions[reopens_question_id].opening_ts) revert Opening_TsMismatch();
|
|
90
|
+
if (min_bond != questions[reopens_question_id].min_bond) revert Min_BondMismatch();
|
|
91
|
+
|
|
92
|
+
// If the the question was itself reopening some previous question, you'll have to re-reopen the previous question first.
|
|
93
|
+
// This ensures the bounty can be passed on to the next attempt of the original question.
|
|
94
|
+
if (reopener_questions[reopens_question_id]) revert QuestionIsAlreadyReopeningAPreviousQuestion();
|
|
95
|
+
|
|
96
|
+
// A question can only be reopened once, unless the reopening was also settled too soon in which case it can be replaced
|
|
97
|
+
bytes32 existing_reopen_question_id = reopened_questions[reopens_question_id];
|
|
98
|
+
|
|
99
|
+
// Normally when we reopen a question we will take its bounty and pass it on to the reopened version.
|
|
100
|
+
bytes32 take_bounty_from_question_id = reopens_question_id;
|
|
101
|
+
// If the question has already been reopened but was again settled too soon, we can transfer its bounty to the next attempt.
|
|
102
|
+
if (existing_reopen_question_id != bytes32(0)) {
|
|
103
|
+
if (!isSettledTooSoon(existing_reopen_question_id)) revert QuestionHasAlreadyBeenReopened();
|
|
104
|
+
// We'll overwrite the reopening with our new question and move the bounty.
|
|
105
|
+
// 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.
|
|
106
|
+
reopener_questions[existing_reopen_question_id] = false;
|
|
107
|
+
take_bounty_from_question_id = existing_reopen_question_id;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
bytes32 question_id = askQuestionWithMinBondERC20(template_id, question, arbitrator, timeout, opening_ts, nonce, min_bond, tokens);
|
|
111
|
+
|
|
112
|
+
reopened_questions[reopens_question_id] = question_id;
|
|
113
|
+
reopener_questions[question_id] = true;
|
|
114
|
+
|
|
115
|
+
questions[question_id].bounty = questions[take_bounty_from_question_id].bounty + questions[question_id].bounty;
|
|
116
|
+
questions[take_bounty_from_question_id].bounty = 0;
|
|
117
|
+
|
|
118
|
+
emit LogReopenQuestion(question_id, reopens_question_id);
|
|
119
|
+
|
|
120
|
+
return question_id;
|
|
121
|
+
}
|
|
122
|
+
}
|