diff --git a/contracts/AdjudicationFramework/MinimalAdjudicationFramework.sol b/contracts/AdjudicationFramework/MinimalAdjudicationFramework.sol index 831acc52..70b534ea 100644 --- a/contracts/AdjudicationFramework/MinimalAdjudicationFramework.sol +++ b/contracts/AdjudicationFramework/MinimalAdjudicationFramework.sol @@ -5,7 +5,8 @@ pragma solidity ^0.8.20; /* solhint-disable quotes */ /* solhint-disable not-rely-on-time */ -import {IRealityETH} from "./../lib/reality-eth/interfaces/IRealityETH.sol"; +import {IRealityETH} from "@reality.eth/contracts/development/contracts/IRealityETH.sol"; +import {IRealityETHHistoryVerification} from "@reality.eth/contracts/development/contracts/IRealityETHHistoryVerification.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {IMinimalAdjudicationFramework} from "./interface/IMinimalAdjudicationFramework.sol"; @@ -19,6 +20,8 @@ Also, arbitrators who are challenged by a removal question, can be temporarily f contract MinimalAdjudicationFramework is IMinimalAdjudicationFramework { using EnumerableSet for EnumerableSet.AddressSet; + event LogWithdraw(address indexed user, uint256 amount); + // Question delimiter for arbitrator modification questions for reality.eth string internal constant _QUESTION_DELIM = "\u241f"; @@ -27,6 +30,8 @@ contract MinimalAdjudicationFramework is IMinimalAdjudicationFramework { error OnlyAllowlistedActor(); /// @dev Error thrown when multiple modifications are requested at once error NoMultipleModificationsAtOnce(); + /// @dev Invalid history supplied for unfinalized question + error InvalidHistorySupplied(); // Iterable list contains list of allowlisted arbitrators uint256 public constant ARB_DISPUTE_TIMEOUT = 86400; @@ -56,6 +61,8 @@ contract MinimalAdjudicationFramework is IMinimalAdjudicationFramework { uint256 public forkActivationDelay; IRealityETH public realityETH; + mapping(address => uint256) public balanceOf; + modifier onlyArbitrator() { if (!_arbitrators.contains(msg.sender)) { revert OnlyAllowlistedActor(); @@ -216,20 +223,24 @@ contract MinimalAdjudicationFramework is IMinimalAdjudicationFramework { bytes32 answer; uint256 bond; - // Normally you call this right after posting your answer so your final answer will be the current answer - // If someone has since submitted a different answer, you need to pass in the history from now until yours + // Normally you call this right after posting your answer so your final answer will be the current best answer + // If someone has since submitted a different answer, you need to pass in the history from now back to yours if (historyHashes.length == 0) { answer = realityETH.getBestAnswer(questionId); bond = realityETH.getBond(questionId); } else { - (answer, bond) = realityETH - .getEarliestAnswerFromSuppliedHistoryOrRevert( - questionId, - historyHashes, - addrs, - bonds, - answers - ); + if ( + !IRealityETHHistoryVerification(address(realityETH)) + .isHistoryOfUnfinalizedQuestionValid( + questionId, + historyHashes, + addrs, + bonds, + answers + ) + ) revert InvalidHistorySupplied(); + answer = answers[answers.length - 1]; + bond = bonds[bonds.length - 1]; } if (answer != bytes32(uint256(1))) { @@ -285,4 +296,11 @@ contract MinimalAdjudicationFramework is IMinimalAdjudicationFramework { function getInvestigationDelay() external view returns (uint256) { return forkActivationDelay; } + + function withdraw() public { + uint256 bal = balanceOf[msg.sender]; + balanceOf[msg.sender] = 0; + payable(msg.sender).transfer(bal); + emit LogWithdraw(msg.sender, bal); + } } diff --git a/contracts/AdjudicationFramework/Pull/AdjudicationFrameworkRequests.sol b/contracts/AdjudicationFramework/Pull/AdjudicationFrameworkRequests.sol index 11db8732..4d872ff6 100644 --- a/contracts/AdjudicationFramework/Pull/AdjudicationFrameworkRequests.sol +++ b/contracts/AdjudicationFramework/Pull/AdjudicationFrameworkRequests.sol @@ -6,7 +6,6 @@ pragma solidity ^0.8.20; /* solhint-disable quotes */ /* solhint-disable not-rely-on-time */ -import {BalanceHolder} from "./../../lib/reality-eth/BalanceHolder.sol"; import {MinimalAdjudicationFramework} from "../MinimalAdjudicationFramework.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; @@ -19,10 +18,7 @@ To Reality.eth it looks like a normal arbitrator, implementing the Arbitrator in To the normal Arbitrator contracts that does its arbitration jobs, it looks like Reality.eth. */ -contract AdjudicationFrameworkRequests is - MinimalAdjudicationFramework, - BalanceHolder -{ +contract AdjudicationFrameworkRequests is MinimalAdjudicationFramework { using EnumerableSet for EnumerableSet.AddressSet; /// @dev Error thrown when challenge deadline has not passed error ChallengeDeadlineNotPassed(); diff --git a/contracts/L2ForkArbitrator.sol b/contracts/L2ForkArbitrator.sol index 4604c8d6..9b35859b 100644 --- a/contracts/L2ForkArbitrator.sol +++ b/contracts/L2ForkArbitrator.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.20; /* solhint-disable var-name-mixedcase */ import {L2ChainInfo} from "./L2ChainInfo.sol"; import {L1GlobalForkRequester} from "./L1GlobalForkRequester.sol"; -import {IRealityETH} from "./lib/reality-eth/interfaces/IRealityETH.sol"; +import {IRealityETH} from "@reality.eth/contracts/development/contracts/IRealityETH.sol"; import {CalculateMoneyBoxAddress} from "./lib/CalculateMoneyBoxAddress.sol"; import {IPolygonZkEVMBridge} from "@RealityETH/zkevm-contracts/contracts/interfaces/IPolygonZkEVMBridge.sol"; diff --git a/contracts/lib/reality-eth/Arbitrator.sol b/contracts/lib/reality-eth/Arbitrator.sol deleted file mode 100644 index bb87c3f2..00000000 --- a/contracts/lib/reality-eth/Arbitrator.sol +++ /dev/null @@ -1,206 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.20; - -import "./interfaces/IArbitrator.sol"; -import "./interfaces/IRealityETH.sol"; -import "./interfaces/IERC20.sol"; -import "./Owned.sol"; - -contract Arbitrator is Owned, IArbitrator { - IRealityETH public realitio; - - mapping(bytes32 => uint256) public arbitration_bounties; - - uint256 public dispute_fee; - mapping(bytes32 => uint256) custom_dispute_fees; - - string public metadata; - - event LogRequestArbitration( - bytes32 indexed question_id, - uint256 fee_paid, - address requester, - uint256 remaining - ); - - event LogSetRealitio(address realitio); - - event LogSetQuestionFee(uint256 fee); - - event LogSetDisputeFee(uint256 fee); - - event LogSetCustomDisputeFee(bytes32 indexed question_id, uint256 fee); - - /// @notice Constructor. Sets the deploying address as owner. - constructor() { - owner = msg.sender; - } - - /// @notice Returns the Realitio contract address - deprecated in favour of realitio() - function realitycheck() external view returns (IRealityETH) { - return realitio; - } - - /// @notice Set the Reality Check contract address - /// @param addr The address of the Reality Check contract - function setRealitio(address addr) public onlyOwner { - realitio = IRealityETH(addr); - emit LogSetRealitio(addr); - } - - /// @notice Set the default fee - /// @param fee The default fee amount - function setDisputeFee(uint256 fee) public onlyOwner { - dispute_fee = fee; - emit LogSetDisputeFee(fee); - } - - /// @notice Set a custom fee for this particular question - /// @param question_id The question in question - /// @param fee The fee amount - function setCustomDisputeFee( - bytes32 question_id, - uint256 fee - ) public onlyOwner { - custom_dispute_fees[question_id] = fee; - emit LogSetCustomDisputeFee(question_id, fee); - } - - /// @notice Return the dispute fee for the specified question. 0 indicates that we won't arbitrate it. - /// @param question_id The question in question - /// @dev Uses a general default, but can be over-ridden on a question-by-question basis. - function getDisputeFee(bytes32 question_id) public view returns (uint256) { - return - (custom_dispute_fees[question_id] > 0) - ? custom_dispute_fees[question_id] - : dispute_fee; - } - - /// @notice Set a fee for asking a question with us as the arbitrator - /// @param fee The fee amount - /// @dev Default is no fee. Unlike the dispute fee, 0 is an acceptable setting. - /// You could set an impossibly high fee if you want to prevent us being used as arbitrator unless we submit the question. - /// (Submitting the question ourselves is not implemented here.) - /// This fee can be used as a revenue source, an anti-spam measure, or both. - function setQuestionFee(uint256 fee) public onlyOwner { - realitio.setQuestionFee(fee); - emit LogSetQuestionFee(fee); - } - - /// @notice Submit the arbitrator's answer to a question. - /// @param question_id The question in question - /// @param answer The answer - /// @param answerer The answerer. If arbitration changed the answer, it should be the payer. If not, the old answerer. - function submitAnswerByArbitrator( - bytes32 question_id, - bytes32 answer, - address answerer - ) public onlyOwner { - delete arbitration_bounties[question_id]; - realitio.submitAnswerByArbitrator(question_id, answer, answerer); - } - - /// @notice Submit the arbitrator's answer to a question, assigning the winner automatically. - /// @param question_id The question in question - /// @param answer The answer - /// @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 - /// @param last_history_hash The history hash before the final one - /// @param last_answer_or_commitment_id The last answer given, or the commitment ID if it was a commitment. - /// @param last_answerer The address that supplied the last answer - function assignWinnerAndSubmitAnswerByArbitrator( - bytes32 question_id, - bytes32 answer, - address payee_if_wrong, - bytes32 last_history_hash, - bytes32 last_answer_or_commitment_id, - address last_answerer - ) public onlyOwner { - delete arbitration_bounties[question_id]; - realitio.assignWinnerAndSubmitAnswerByArbitrator( - question_id, - answer, - payee_if_wrong, - last_history_hash, - last_answer_or_commitment_id, - last_answerer - ); - } - - /// @notice Cancel a previous arbitration request - /// @dev This is intended for situations where the arbitration is happening non-atomically and the fee or something change. - /// @param question_id The question in question - function cancelArbitration(bytes32 question_id) public onlyOwner { - realitio.cancelArbitration(question_id); - } - - /// @notice Request arbitration, freezing the question until we send submitAnswerByArbitrator - /// @dev The bounty can be paid only in part, in which case the last person to pay will be considered the payer - /// Will trigger an error if the notification fails, eg because the question has already been finalized - /// @param question_id The question in question - /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction. - function requestArbitration( - bytes32 question_id, - uint256 max_previous - ) external payable returns (bool) { - uint256 arbitration_fee = getDisputeFee(question_id); - require( - arbitration_fee > 0, - "The arbitrator must have set a non-zero fee for the question" - ); - - arbitration_bounties[question_id] += msg.value; - uint256 paid = arbitration_bounties[question_id]; - - if (paid >= arbitration_fee) { - realitio.notifyOfArbitrationRequest( - question_id, - msg.sender, - max_previous - ); - emit LogRequestArbitration(question_id, msg.value, msg.sender, 0); - return true; - } else { - require( - !realitio.isFinalized(question_id), - "The question must not have been finalized" - ); - emit LogRequestArbitration( - question_id, - msg.value, - msg.sender, - arbitration_fee - paid - ); - return false; - } - } - - /// @notice Withdraw any accumulated ETH fees to the specified address - /// @param addr The address to which the balance should be sent - function withdraw(address addr) public onlyOwner { - payable(addr).transfer(address(this).balance); - } - - /// @notice Withdraw any accumulated token fees to the specified address - /// @param addr The address to which the balance should be sent - /// @dev Only needed if the Realitio contract used is using an ERC20 token - /// @dev Also only normally useful if a per-question fee is set, otherwise we only have ETH. - function withdrawERC20(IERC20 _token, address addr) public onlyOwner { - uint256 bal = _token.balanceOf(address(this)); - IERC20(_token).transfer(addr, bal); - } - - receive() external payable {} - - /// @notice Withdraw any accumulated question fees from the specified address into this contract - /// @dev Funds can then be liberated from this contract with our withdraw() function - /// @dev This works in the same way whether the realitio contract is using ETH or an ERC20 token - function callWithdraw() public onlyOwner { - realitio.withdraw(); - } - - /// @notice Set a metadata string, expected to be JSON, containing things like arbitrator TOS address - function setMetaData(string memory _metadata) external onlyOwner { - metadata = _metadata; - } -} diff --git a/contracts/lib/reality-eth/BalanceHolder.sol b/contracts/lib/reality-eth/BalanceHolder.sol deleted file mode 100644 index 8eba497e..00000000 --- a/contracts/lib/reality-eth/BalanceHolder.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.20; - -import "./interfaces/IBalanceHolder.sol"; - -contract BalanceHolder is IBalanceHolder { - mapping(address => uint256) public balanceOf; - - event LogWithdraw(address indexed user, uint256 amount); - - function withdraw() public { - uint256 bal = balanceOf[msg.sender]; - balanceOf[msg.sender] = 0; - payable(msg.sender).transfer(bal); - emit LogWithdraw(msg.sender, bal); - } -} diff --git a/contracts/lib/reality-eth/BalanceHolder_ERC20.sol b/contracts/lib/reality-eth/BalanceHolder_ERC20.sol deleted file mode 100644 index 8fbbbcc1..00000000 --- a/contracts/lib/reality-eth/BalanceHolder_ERC20.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.20; - -/* solhint-disable contract-name-camelcase */ - -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IBalanceHolder_ERC20} from "./interfaces/IBalanceHolder_ERC20.sol"; - -contract BalanceHolder_ERC20 is IBalanceHolder_ERC20 { - IERC20 public token; - - mapping(address => uint256) public balanceOf; - - event LogWithdraw(address indexed user, uint256 amount); - - function withdraw() public { - uint256 bal = balanceOf[msg.sender]; - balanceOf[msg.sender] = 0; - require(token.transfer(msg.sender, bal), "Transfer failed"); - emit LogWithdraw(msg.sender, bal); - } -} diff --git a/contracts/lib/reality-eth/Owned.sol b/contracts/lib/reality-eth/Owned.sol deleted file mode 100644 index 2e3bed3d..00000000 --- a/contracts/lib/reality-eth/Owned.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.20; - -contract Owned { - address public owner; - - constructor() { - owner = msg.sender; - } - - modifier onlyOwner() { - require(msg.sender == owner, "Caller is not the owner"); - _; - } - - function transferOwnership(address newOwner) external onlyOwner { - owner = newOwner; - } -} diff --git a/contracts/lib/reality-eth/RealityETH-3.0.sol b/contracts/lib/reality-eth/RealityETH-3.0.sol deleted file mode 100644 index be65af97..00000000 --- a/contracts/lib/reality-eth/RealityETH-3.0.sol +++ /dev/null @@ -1,1297 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.20; - -import "./interfaces/IRealityETH.sol"; -import "./BalanceHolder.sol"; - -contract RealityETH_v3_0 is BalanceHolder, IRealityETH { - address constant NULL_ADDRESS = address(0); - - // History hash when no history is created, or history has been cleared - bytes32 constant NULL_HASH = bytes32(0); - - // An unitinalized finalize_ts for a question will indicate an unanswered question. - uint32 constant UNANSWERED = 0; - - // An unanswered reveal_ts for a commitment will indicate that it does not exist. - uint256 constant COMMITMENT_NON_EXISTENT = 0; - - // Commit->reveal timeout is 1/8 of the question timeout (rounded down). - uint32 constant COMMITMENT_TIMEOUT_RATIO = 8; - - // Proportion withheld when you claim an earlier bond. - uint256 constant BOND_CLAIM_FEE_PROPORTION = 40; // One 40th ie 2.5% - - // Special value representing a question that was answered too soon. - // bytes32(-2). By convention we use bytes32(-1) for "invalid", although the contract does not handle this. - bytes32 constant UNRESOLVED_ANSWER = - 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe; - - struct Question { - bytes32 content_hash; - address arbitrator; - uint32 opening_ts; - uint32 timeout; - uint32 finalize_ts; - bool is_pending_arbitration; - uint256 bounty; - bytes32 best_answer; - bytes32 history_hash; - uint256 bond; - uint256 min_bond; - } - - // Stored in a mapping indexed by commitment_id, a hash of commitment hash, question, bond. - struct Commitment { - uint32 reveal_ts; - bool is_revealed; - bytes32 revealed_answer; - } - - // Only used when claiming more bonds than fits into a transaction - // Stored in a mapping indexed by question_id. - struct Claim { - address payee; - uint256 last_bond; - uint256 queued_funds; - } - - uint256 nextTemplateID = 0; - mapping(uint256 => uint256) public templates; - mapping(uint256 => bytes32) public template_hashes; - mapping(bytes32 => Question) public questions; - mapping(bytes32 => Claim) public question_claims; - mapping(bytes32 => Commitment) public commitments; - mapping(address => uint256) public arbitrator_question_fees; - mapping(bytes32 => bytes32) public reopened_questions; - mapping(bytes32 => bool) public reopener_questions; - - modifier onlyArbitrator(bytes32 question_id) { - require( - msg.sender == questions[question_id].arbitrator, - "msg.sender must be arbitrator" - ); - _; - } - - modifier stateAny() { - _; - } - - modifier stateNotCreated(bytes32 question_id) { - require(questions[question_id].timeout == 0, "question must not exist"); - _; - } - - modifier stateOpen(bytes32 question_id) { - require(questions[question_id].timeout > 0, "question must exist"); - require( - !questions[question_id].is_pending_arbitration, - "question must not be pending arbitration" - ); - uint32 finalize_ts = questions[question_id].finalize_ts; - require( - finalize_ts == UNANSWERED || finalize_ts > uint32(block.timestamp), - "finalization deadline must not have passed" - ); - uint32 opening_ts = questions[question_id].opening_ts; - require( - opening_ts == 0 || opening_ts <= uint32(block.timestamp), - "opening date must have passed" - ); - _; - } - - modifier statePendingArbitration(bytes32 question_id) { - require( - questions[question_id].is_pending_arbitration, - "question must be pending arbitration" - ); - _; - } - - modifier stateOpenOrPendingArbitration(bytes32 question_id) { - require(questions[question_id].timeout > 0, "question must exist"); - uint32 finalize_ts = questions[question_id].finalize_ts; - require( - finalize_ts == UNANSWERED || finalize_ts > uint32(block.timestamp), - "finalization dealine must not have passed" - ); - uint32 opening_ts = questions[question_id].opening_ts; - require( - opening_ts == 0 || opening_ts <= uint32(block.timestamp), - "opening date must have passed" - ); - _; - } - - modifier stateFinalized(bytes32 question_id) { - require(isFinalized(question_id), "question must be finalized"); - _; - } - - modifier bondMustDoubleAndMatchMinimum(bytes32 question_id) { - require(msg.value > 0, "bond must be positive"); - uint256 current_bond = questions[question_id].bond; - if (current_bond == 0) { - require( - msg.value >= (questions[question_id].min_bond), - "bond must exceed the minimum" - ); - } else { - require( - msg.value >= (current_bond * 2), - "bond must be double at least previous bond" - ); - } - _; - } - - modifier previousBondMustNotBeatMaxPrevious( - bytes32 question_id, - uint256 max_previous - ) { - if (max_previous > 0) { - require( - questions[question_id].bond <= max_previous, - "bond must exceed max_previous" - ); - } - _; - } - - /// @notice Constructor, sets up some initial templates - /// @dev Creates some generalized templates for different question types used in the DApp. - constructor() { - createTemplate( - '{"title": "%s", "type": "bool", "category": "%s", "lang": "%s"}' - ); - createTemplate( - '{"title": "%s", "type": "uint", "decimals": 18, "category": "%s", "lang": "%s"}' - ); - createTemplate( - '{"title": "%s", "type": "single-select", "outcomes": [%s], "category": "%s", "lang": "%s"}' - ); - createTemplate( - '{"title": "%s", "type": "multiple-select", "outcomes": [%s], "category": "%s", "lang": "%s"}' - ); - createTemplate( - '{"title": "%s", "type": "datetime", "category": "%s", "lang": "%s"}' - ); - } - - /// @notice Function for arbitrator to set an optional per-question fee. - /// @dev The per-question fee, charged when a question is asked, is intended as an anti-spam measure. - /// @param fee The fee to be charged by the arbitrator when a question is asked - function setQuestionFee(uint256 fee) external stateAny { - arbitrator_question_fees[msg.sender] = fee; - emit LogSetQuestionFee(msg.sender, fee); - } - - /// @notice Create a reusable template, which should be a JSON document. - /// Placeholders should use gettext() syntax, eg %s. - /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage. - /// @param content The template content - /// @return The ID of the newly-created template, which is created sequentially. - function createTemplate( - string memory content - ) public stateAny returns (uint256) { - uint256 id = nextTemplateID; - templates[id] = block.number; - template_hashes[id] = keccak256(abi.encodePacked(content)); - emit LogNewTemplate(id, msg.sender, content); - nextTemplateID = id + 1; - return id; - } - - /// @notice Create a new reusable template and use it to ask a question - /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage. - /// @param content The template content - /// @param question A string containing the parameters that will be passed into the template to make the question - /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute - /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer - /// @param opening_ts If set, the earliest time it should be possible to answer the question. - /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question. - /// @return The ID of the newly-created template, which is created sequentially. - function createTemplateAndAskQuestion( - string memory content, - string memory question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce - ) - public - payable - returns ( - // stateNotCreated is enforced by the internal _askQuestion - bytes32 - ) - { - uint256 template_id = createTemplate(content); - return - askQuestion( - template_id, - question, - arbitrator, - timeout, - opening_ts, - nonce - ); - } - - /// @notice Ask a new question and return the ID - /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage. - /// @param template_id The ID number of the template the question will use - /// @param question A string containing the parameters that will be passed into the template to make the question - /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute - /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer - /// @param opening_ts If set, the earliest time it should be possible to answer the question. - /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question. - /// @return The ID of the newly-created question, created deterministically. - function askQuestion( - uint256 template_id, - string memory question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce - ) - public - payable - returns ( - // stateNotCreated is enforced by the internal _askQuestion - bytes32 - ) - { - require(templates[template_id] > 0, "template must exist"); - - bytes32 content_hash = keccak256( - abi.encodePacked(template_id, opening_ts, question) - ); - bytes32 question_id = keccak256( - abi.encodePacked( - content_hash, - arbitrator, - timeout, - uint256(0), - address(this), - msg.sender, - nonce - ) - ); - - // We emit this event here because _askQuestion doesn't need to know the unhashed question. Other events are emitted by _askQuestion. - emit LogNewQuestion( - question_id, - msg.sender, - template_id, - question, - content_hash, - arbitrator, - timeout, - opening_ts, - nonce, - block.timestamp - ); - _askQuestion( - question_id, - content_hash, - arbitrator, - timeout, - opening_ts, - 0 - ); - - return question_id; - } - - /// @notice Ask a new question and return the ID - /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage. - /// @param template_id The ID number of the template the question will use - /// @param question A string containing the parameters that will be passed into the template to make the question - /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute - /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer - /// @param opening_ts If set, the earliest time it should be possible to answer the question. - /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question. - /// @param min_bond The minimum bond that may be used for an answer. - /// @return The ID of the newly-created question, created deterministically. - function askQuestionWithMinBond( - uint256 template_id, - string memory question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce, - uint256 min_bond - ) - public - payable - returns ( - // stateNotCreated is enforced by the internal _askQuestion - bytes32 - ) - { - require(templates[template_id] > 0, "template must exist"); - - bytes32 content_hash = keccak256( - abi.encodePacked(template_id, opening_ts, question) - ); - bytes32 question_id = keccak256( - abi.encodePacked( - content_hash, - arbitrator, - timeout, - min_bond, - address(this), - msg.sender, - nonce - ) - ); - - // We emit this event here because _askQuestion doesn't need to know the unhashed question. - // Other events are emitted by _askQuestion. - emit LogNewQuestion( - question_id, - msg.sender, - template_id, - question, - content_hash, - arbitrator, - timeout, - opening_ts, - nonce, - block.timestamp - ); - _askQuestion( - question_id, - content_hash, - arbitrator, - timeout, - opening_ts, - min_bond - ); - - return question_id; - } - - function _askQuestion( - bytes32 question_id, - bytes32 content_hash, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 min_bond - ) internal stateNotCreated(question_id) { - // A timeout of 0 makes no sense, and we will use this to check existence - require(timeout > 0, "timeout must be positive"); - require(timeout < 365 days, "timeout must be less than 365 days"); - - uint256 bounty = msg.value; - - // The arbitrator can set a fee for asking a question. - // This is intended as an anti-spam defence. - // The fee is waived if the arbitrator is asking the question. - // This allows them to set an impossibly high fee and make users proxy the question through them. - // This would allow more sophisticated pricing, question whitelisting etc. - if (arbitrator != NULL_ADDRESS && msg.sender != arbitrator) { - uint256 question_fee = arbitrator_question_fees[arbitrator]; - require( - bounty >= question_fee, - "ETH provided must cover question fee" - ); - bounty = bounty - question_fee; - balanceOf[arbitrator] = balanceOf[arbitrator] + question_fee; - } - - questions[question_id].content_hash = content_hash; - questions[question_id].arbitrator = arbitrator; - questions[question_id].opening_ts = opening_ts; - questions[question_id].timeout = timeout; - - if (bounty > 0) { - questions[question_id].bounty = bounty; - emit LogFundAnswerBounty(question_id, bounty, bounty, msg.sender); - } - - if (min_bond > 0) { - questions[question_id].min_bond = min_bond; - emit LogMinimumBond(question_id, min_bond); - } - } - - /// @notice Add funds to the bounty for a question - /// @dev Add bounty funds after the initial question creation. Can be done any time until the question is finalized. - /// @param question_id The ID of the question you wish to fund - function fundAnswerBounty( - bytes32 question_id - ) external payable stateOpen(question_id) { - questions[question_id].bounty = - questions[question_id].bounty + - msg.value; - emit LogFundAnswerBounty( - question_id, - msg.value, - questions[question_id].bounty, - msg.sender - ); - } - - /// @notice Submit an answer for a question. - /// @dev Adds the answer to the history and updates the current "best" answer. - /// May be subject to front-running attacks; Substitute submitAnswerCommitment()->submitAnswerReveal() to prevent them. - /// @param question_id The ID of the question - /// @param answer The answer, encoded into bytes32 - /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction. - function submitAnswer( - bytes32 question_id, - bytes32 answer, - uint256 max_previous - ) - external - payable - stateOpen(question_id) - bondMustDoubleAndMatchMinimum(question_id) - previousBondMustNotBeatMaxPrevious(question_id, max_previous) - { - _addAnswerToHistory(question_id, answer, msg.sender, msg.value, false); - _updateCurrentAnswer(question_id, answer); - } - - /// @notice Submit an answer for a question, crediting it to the specified account. - /// @dev Adds the answer to the history and updates the current "best" answer. - /// May be subject to front-running attacks; Substitute submitAnswerCommitment()->submitAnswerReveal() to prevent them. - /// @param question_id The ID of the question - /// @param answer The answer, encoded into bytes32 - /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction. - /// @param answerer The account to which the answer should be credited - function submitAnswerFor( - bytes32 question_id, - bytes32 answer, - uint256 max_previous, - address answerer - ) - external - payable - stateOpen(question_id) - bondMustDoubleAndMatchMinimum(question_id) - previousBondMustNotBeatMaxPrevious(question_id, max_previous) - { - require(answerer != NULL_ADDRESS, "answerer must be non-zero"); - _addAnswerToHistory(question_id, answer, answerer, msg.value, false); - _updateCurrentAnswer(question_id, answer); - } - - // @notice Verify and store a commitment, including an appropriate timeout - // @param question_id The ID of the question to store - // @param commitment The ID of the commitment - function _storeCommitment( - bytes32 question_id, - bytes32 commitment_id - ) internal { - require( - commitments[commitment_id].reveal_ts == COMMITMENT_NON_EXISTENT, - "commitment must not already exist" - ); - - uint32 commitment_timeout = questions[question_id].timeout / - COMMITMENT_TIMEOUT_RATIO; - commitments[commitment_id].reveal_ts = - uint32(block.timestamp) + - commitment_timeout; - } - - /// @notice Submit the hash of an answer, laying your claim to that answer if you reveal it in a subsequent transaction. - /// @dev Creates a hash, commitment_id, uniquely identifying this answer, to this question, with this bond. - /// The commitment_id is stored in the answer history where the answer would normally go. - /// Does not update the current best answer - this is left to the later submitAnswerReveal() transaction. - /// @param question_id The ID of the question - /// @param answer_hash The hash of your answer, plus a nonce that you will later reveal - /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction. - /// @param _answerer If specified, the address to be given as the question answerer. Defaults to the sender. - /// @dev Specifying the answerer is useful if you want to delegate the commit-and-reveal to a third-party. - function submitAnswerCommitment( - bytes32 question_id, - bytes32 answer_hash, - uint256 max_previous, - address _answerer - ) - external - payable - stateOpen(question_id) - bondMustDoubleAndMatchMinimum(question_id) - previousBondMustNotBeatMaxPrevious(question_id, max_previous) - { - bytes32 commitment_id = keccak256( - abi.encodePacked(question_id, answer_hash, msg.value) - ); - address answerer = (_answerer == NULL_ADDRESS) ? msg.sender : _answerer; - _storeCommitment(question_id, commitment_id); - _addAnswerToHistory( - question_id, - commitment_id, - answerer, - msg.value, - true - ); - } - - /// @notice Submit the answer whose hash you sent in a previous submitAnswerCommitment() transaction - /// @dev Checks the parameters supplied recreate an existing commitment, and stores the revealed answer - /// Updates the current answer unless someone has since supplied a new answer with a higher bond - /// msg.sender is intentionally not restricted to the user who originally sent the commitment; - /// For example, the user may want to provide the answer+nonce to a third-party service and let them send the tx - /// NB If we are pending arbitration, it will be up to the arbitrator to wait and see any outstanding reveal is sent - /// @param question_id The ID of the question - /// @param answer The answer, encoded as bytes32 - /// @param nonce The nonce that, combined with the answer, recreates the answer_hash you gave in submitAnswerCommitment() - /// @param bond The bond that you paid in your submitAnswerCommitment() transaction - function submitAnswerReveal( - bytes32 question_id, - bytes32 answer, - uint256 nonce, - uint256 bond - ) external stateOpenOrPendingArbitration(question_id) { - bytes32 answer_hash = keccak256(abi.encodePacked(answer, nonce)); - bytes32 commitment_id = keccak256( - abi.encodePacked(question_id, answer_hash, bond) - ); - - require( - !commitments[commitment_id].is_revealed, - "commitment must not have been revealed yet" - ); - require( - commitments[commitment_id].reveal_ts > uint32(block.timestamp), - "reveal deadline must not have passed" - ); - - commitments[commitment_id].revealed_answer = answer; - commitments[commitment_id].is_revealed = true; - - if (bond == questions[question_id].bond) { - _updateCurrentAnswer(question_id, answer); - } - - emit LogAnswerReveal( - question_id, - msg.sender, - answer_hash, - answer, - nonce, - bond - ); - } - - function _addAnswerToHistory( - bytes32 question_id, - bytes32 answer_or_commitment_id, - address answerer, - uint256 bond, - bool is_commitment - ) internal { - bytes32 new_history_hash = keccak256( - abi.encodePacked( - questions[question_id].history_hash, - answer_or_commitment_id, - bond, - answerer, - is_commitment - ) - ); - - // Update the current bond level, if there's a bond (ie anything except arbitration) - if (bond > 0) { - questions[question_id].bond = bond; - } - questions[question_id].history_hash = new_history_hash; - - emit LogNewAnswer( - answer_or_commitment_id, - question_id, - new_history_hash, - answerer, - bond, - block.timestamp, - is_commitment - ); - } - - function _updateCurrentAnswer( - bytes32 question_id, - bytes32 answer - ) internal { - questions[question_id].best_answer = answer; - questions[question_id].finalize_ts = - uint32(block.timestamp) + - questions[question_id].timeout; - } - - // Like _updateCurrentAnswer but without advancing the timeout - function _updateCurrentAnswerByArbitrator( - bytes32 question_id, - bytes32 answer - ) internal { - questions[question_id].best_answer = answer; - questions[question_id].finalize_ts = uint32(block.timestamp); - } - - /// @notice Notify the contract that the arbitrator has been paid for a question, freezing it pending their decision. - /// @dev The arbitrator contract is trusted to only call this if they've been paid, and tell us who paid them. - /// @param question_id The ID of the question - /// @param requester The account that requested arbitration - /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction. - function notifyOfArbitrationRequest( - bytes32 question_id, - address requester, - uint256 max_previous - ) - external - onlyArbitrator(question_id) - stateOpen(question_id) - previousBondMustNotBeatMaxPrevious(question_id, max_previous) - { - require( - questions[question_id].finalize_ts > UNANSWERED, - "Question must already have an answer when arbitration is requested" - ); - questions[question_id].is_pending_arbitration = true; - emit LogNotifyOfArbitrationRequest(question_id, requester); - } - - /// @notice Cancel a previously-requested arbitration and extend the timeout - /// @dev Useful when doing arbitration across chains that can't be requested atomically - /// @param question_id The ID of the question - function cancelArbitration( - bytes32 question_id - ) - external - onlyArbitrator(question_id) - statePendingArbitration(question_id) - { - questions[question_id].is_pending_arbitration = false; - questions[question_id].finalize_ts = - uint32(block.timestamp) + - questions[question_id].timeout; - emit LogCancelArbitration(question_id); - } - - /// @notice Submit the answer for a question, for use by the arbitrator. - /// @dev Doesn't require (or allow) a bond. - /// If the current final answer is correct, the account should be whoever submitted it. - /// If the current final answer is wrong, the account should be whoever paid for arbitration. - /// However, the answerer stipulations are not enforced by the contract. - /// @param question_id The ID of the question - /// @param answer The answer, encoded into bytes32 - /// @param answerer The account credited with this answer for the purpose of bond claims - function submitAnswerByArbitrator( - bytes32 question_id, - bytes32 answer, - address answerer - ) public onlyArbitrator(question_id) statePendingArbitration(question_id) { - require(answerer != NULL_ADDRESS, "answerer must be provided"); - emit LogFinalize(question_id, answer); - - questions[question_id].is_pending_arbitration = false; - _addAnswerToHistory(question_id, answer, answerer, 0, false); - _updateCurrentAnswerByArbitrator(question_id, answer); - } - - /// @notice Submit the answer for a question, for use by the arbitrator, working out the appropriate winner based on the last answer details. - /// @dev Doesn't require (or allow) a bond. - /// @param question_id The ID of the question - /// @param answer The answer, encoded into bytes32 - /// @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 - /// @param last_history_hash The history hash before the final one - /// @param last_answer_or_commitment_id The last answer given, or the commitment ID if it was a commitment. - /// @param last_answerer The address that supplied the last answer - function assignWinnerAndSubmitAnswerByArbitrator( - bytes32 question_id, - bytes32 answer, - address payee_if_wrong, - bytes32 last_history_hash, - bytes32 last_answer_or_commitment_id, - address last_answerer - ) external { - bool is_commitment = _verifyHistoryInputOrRevert( - questions[question_id].history_hash, - last_history_hash, - last_answer_or_commitment_id, - questions[question_id].bond, - last_answerer - ); - - address payee; - // If the last answer is an unrevealed commit, it's always wrong. - // For anything else, the last answer was set as the "best answer" in submitAnswer or submitAnswerReveal. - if ( - is_commitment && - !commitments[last_answer_or_commitment_id].is_revealed - ) { - require( - commitments[last_answer_or_commitment_id].reveal_ts < - uint32(block.timestamp), - "You must wait for the reveal deadline before finalizing" - ); - payee = payee_if_wrong; - } else { - payee = (questions[question_id].best_answer == answer) - ? last_answerer - : payee_if_wrong; - } - submitAnswerByArbitrator(question_id, answer, payee); - } - - /// @notice Report whether the answer to the specified question is finalized - /// @param question_id The ID of the question - /// @return Return true if finalized - function isFinalized(bytes32 question_id) public view returns (bool) { - uint32 finalize_ts = questions[question_id].finalize_ts; - return (!questions[question_id].is_pending_arbitration && - (finalize_ts > UNANSWERED) && - (finalize_ts <= uint32(block.timestamp))); - } - - /// @notice (Deprecated) Return the final answer to the specified question, or revert if there isn't one - /// @param question_id The ID of the question - /// @return The answer formatted as a bytes32 - function getFinalAnswer( - bytes32 question_id - ) external view stateFinalized(question_id) returns (bytes32) { - return questions[question_id].best_answer; - } - - /// @notice Return the final answer to the specified question, or revert if there isn't one - /// @param question_id The ID of the question - /// @return The answer formatted as a bytes32 - function resultFor( - bytes32 question_id - ) public view stateFinalized(question_id) returns (bytes32) { - return questions[question_id].best_answer; - } - - /// @notice Returns whether the question was answered before it had an answer, ie resolved to UNRESOLVED_ANSWER - /// @param question_id The ID of the question - function isSettledTooSoon(bytes32 question_id) public view returns (bool) { - return (resultFor(question_id) == UNRESOLVED_ANSWER); - } - - /// @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 - /// @param question_id The ID of the question - function resultForOnceSettled( - bytes32 question_id - ) external view returns (bytes32) { - bytes32 result = resultFor(question_id); - if (result == UNRESOLVED_ANSWER) { - // Try the replacement - bytes32 replacement_id = reopened_questions[question_id]; - require( - replacement_id != bytes32(0x0), - "Question was settled too soon and has not been reopened" - ); - // We only try one layer down rather than recursing to keep the gas costs predictable - result = resultFor(replacement_id); - require( - result != UNRESOLVED_ANSWER, - "Question replacement was settled too soon and has not been reopened" - ); - } - return result; - } - - /// @notice Asks a new question reopening a previously-asked question that was settled too soon - /// @dev A special version of askQuestion() that replaces a previous question that was settled too soon - /// @param template_id The ID number of the template the question will use - /// @param question A string containing the parameters that will be passed into the template to make the question - /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute - /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer - /// @param opening_ts If set, the earliest time it should be possible to answer the question. - /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question. - /// @param min_bond The minimum bond that can be used to provide the first answer. - /// @param reopens_question_id The ID of the question this reopens - /// @return The ID of the newly-created question, created deterministically. - function reopenQuestion( - uint256 template_id, - string memory question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce, - uint256 min_bond, - bytes32 reopens_question_id - ) - public - payable - returns ( - // stateNotCreated is enforced by the internal _askQuestion - bytes32 - ) - { - require( - isSettledTooSoon(reopens_question_id), - "You can only reopen questions that resolved as settled too soon" - ); - - bytes32 content_hash = keccak256( - abi.encodePacked(template_id, opening_ts, question) - ); - - // A reopening must exactly match the original question, except for the nonce and the creator - require( - content_hash == questions[reopens_question_id].content_hash, - "content hash mismatch" - ); - require( - arbitrator == questions[reopens_question_id].arbitrator, - "arbitrator mismatch" - ); - require( - timeout == questions[reopens_question_id].timeout, - "timeout mismatch" - ); - require( - opening_ts == questions[reopens_question_id].opening_ts, - "opening_ts mismatch" - ); - require( - min_bond == questions[reopens_question_id].min_bond, - "min_bond mismatch" - ); - - // If the the question was itself reopening some previous question, you'll have to re-reopen the previous question first. - // This ensures the bounty can be passed on to the next attempt of the original question. - require( - !reopener_questions[reopens_question_id], - "Question is already reopening a previous question" - ); - - // A question can only be reopened once, unless the reopening was also settled too soon in which case it can be replaced - bytes32 existing_reopen_question_id = reopened_questions[ - reopens_question_id - ]; - - // Normally when we reopen a question we will take its bounty and pass it on to the reopened version. - bytes32 take_bounty_from_question_id = reopens_question_id; - // If the question has already been reopened but was again settled too soon, we can transfer its bounty to the next attempt. - if (existing_reopen_question_id != bytes32(0)) { - require( - isSettledTooSoon(existing_reopen_question_id), - "Question has already been reopened" - ); - // We'll overwrite the reopening with our new question and move the bounty. - // 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. - reopener_questions[existing_reopen_question_id] = false; - take_bounty_from_question_id = existing_reopen_question_id; - } - - bytes32 question_id = askQuestionWithMinBond( - template_id, - question, - arbitrator, - timeout, - opening_ts, - nonce, - min_bond - ); - - reopened_questions[reopens_question_id] = question_id; - reopener_questions[question_id] = true; - - questions[question_id].bounty = - questions[take_bounty_from_question_id].bounty + - questions[question_id].bounty; - questions[take_bounty_from_question_id].bounty = 0; - - emit LogReopenQuestion(question_id, reopens_question_id); - - return question_id; - } - - /// @notice Return the final answer to the specified question, provided it matches the specified criteria. - /// @dev Reverts if the question is not finalized, or if it does not match the specified criteria. - /// @param question_id The ID of the question - /// @param content_hash The hash of the question content (template ID + opening time + question parameter string) - /// @param arbitrator The arbitrator chosen for the question (regardless of whether they are asked to arbitrate) - /// @param min_timeout The timeout set in the initial question settings must be this high or higher - /// @param min_bond The bond sent with the final answer must be this high or higher - /// @return The answer formatted as a bytes32 - function getFinalAnswerIfMatches( - bytes32 question_id, - bytes32 content_hash, - address arbitrator, - uint32 min_timeout, - uint256 min_bond - ) external view stateFinalized(question_id) returns (bytes32) { - require( - content_hash == questions[question_id].content_hash, - "content hash must match" - ); - require( - arbitrator == questions[question_id].arbitrator, - "arbitrator must match" - ); - require( - min_timeout <= questions[question_id].timeout, - "timeout must be long enough" - ); - require( - min_bond <= questions[question_id].bond, - "bond must be high enough" - ); - return questions[question_id].best_answer; - } - - /// @notice Assigns the winnings (bounty and bonds) to everyone who gave the accepted answer - /// Caller must provide the answer history, in reverse order - /// @dev Works up the chain and assign bonds to the person who gave the right answer - /// If someone gave the winning answer earlier, they must get paid from the higher bond - /// That means we can't pay out the bond added at n until we have looked at n-1 - /// The first answer is authenticated by checking against the stored history_hash. - /// One of the inputs to history_hash is the history_hash before it, so we use that to authenticate the next entry, etc - /// Once we get to a null hash we'll know we're done and there are no more answers. - /// Usually you would call the whole thing in a single transaction, but if not then the data is persisted to pick up later. - /// @param question_id The ID of the question - /// @param history_hashes Second-last-to-first, the hash of each history entry. (Final one should be empty). - /// @param addrs Last-to-first, the address of each answerer or commitment sender - /// @param bonds Last-to-first, the bond supplied with each answer or commitment - /// @param answers Last-to-first, each answer supplied, or commitment ID if the answer was supplied with commit->reveal - function claimWinnings( - bytes32 question_id, - bytes32[] memory history_hashes, - address[] memory addrs, - uint256[] memory bonds, - bytes32[] memory answers - ) public stateFinalized(question_id) { - require( - history_hashes.length > 0, - "at least one history hash entry must be provided" - ); - - // These are only set if we split our claim over multiple transactions. - address payee = question_claims[question_id].payee; - uint256 last_bond = question_claims[question_id].last_bond; - uint256 queued_funds = question_claims[question_id].queued_funds; - - // Starts as the hash of the final answer submitted. It'll be cleared when we're done. - // If we're splitting the claim over multiple transactions, it'll be the hash where we left off last time - bytes32 last_history_hash = questions[question_id].history_hash; - - bytes32 best_answer = questions[question_id].best_answer; - - uint256 i; - for (i = 0; i < history_hashes.length; i++) { - // Check input against the history hash, and see which of 2 possible values of is_commitment fits. - bool is_commitment = _verifyHistoryInputOrRevert( - last_history_hash, - history_hashes[i], - answers[i], - bonds[i], - addrs[i] - ); - - queued_funds = queued_funds + last_bond; - (queued_funds, payee) = _processHistoryItem( - question_id, - best_answer, - queued_funds, - payee, - addrs[i], - bonds[i], - answers[i], - is_commitment - ); - - // Line the bond up for next time, when it will be added to somebody's queued_funds - last_bond = bonds[i]; - - // Burn (just leave in contract balance) a fraction of all bonds except the final one. - // This creates a cost to increasing your own bond, which could be used to delay resolution maliciously - if (last_bond != questions[question_id].bond) { - last_bond = last_bond - last_bond / BOND_CLAIM_FEE_PROPORTION; - } - - last_history_hash = history_hashes[i]; - } - - if (last_history_hash != NULL_HASH) { - // We haven't yet got to the null hash (1st answer), ie the caller didn't supply the full answer chain. - // Persist the details so we can pick up later where we left off later. - - // If we know who to pay we can go ahead and pay them out, only keeping back last_bond - // (We always know who to pay unless all we saw were unrevealed commits) - if (payee != NULL_ADDRESS) { - _payPayee(question_id, payee, queued_funds); - queued_funds = 0; - } - - question_claims[question_id].payee = payee; - question_claims[question_id].last_bond = last_bond; - question_claims[question_id].queued_funds = queued_funds; - } else { - // There is nothing left below us so the payee can keep what remains - _payPayee(question_id, payee, queued_funds + last_bond); - delete question_claims[question_id]; - } - - questions[question_id].history_hash = last_history_hash; - } - - function _payPayee( - bytes32 question_id, - address payee, - uint256 value - ) internal { - balanceOf[payee] = balanceOf[payee] + value; - emit LogClaim(question_id, payee, value); - } - - /// @notice Returns the value of the earliest answer in the supplied history - /// Caller must provide the answer history, in reverse order back to the item they want to check - /// @dev Not necessarily the entire history - /// @param question_id The ID of the question - /// @param history_hashes Second-last-to-first, the hash of each history entry. (Final one should be empty). - /// @param addrs Last-to-first, the address of each answerer or commitment sender - /// @param bonds Last-to-first, the bond supplied with each answer or commitment - /// @param answers Last-to-first, each answer supplied, or commitment ID if the answer was supplied with commit->reveal - function getEarliestAnswerFromSuppliedHistoryOrRevert( - bytes32 question_id, - bytes32[] memory history_hashes, - address[] memory addrs, - uint256[] memory bonds, - bytes32[] memory answers - ) external view stateAny returns (bytes32, uint256) { - // Go through the history reverting if any of it is wrong - bool is_commitment; - bytes32 last_history_hash = questions[question_id].history_hash; - uint256 i; - for (i = 0; i < history_hashes.length; i++) { - is_commitment = _verifyHistoryInputOrRevert( - last_history_hash, - history_hashes[i], - answers[i], - bonds[i], - addrs[i] - ); - last_history_hash = history_hashes[i]; - } - - // When we get to the final answer, unwrap the commitment if required and return - uint256 last_i = history_hashes.length - 1; - bytes32 answer_or_commitment_id = answers[last_i]; - bytes32 answer; - if (is_commitment) { - require( - commitments[answer_or_commitment_id].is_revealed, - "Earliest answer is an unrevealed commitment" - ); - answer = commitments[answer_or_commitment_id].revealed_answer; - } else { - answer = answer_or_commitment_id; - } - return (answer, bonds[last_i]); - } - - function _verifyHistoryInputOrRevert( - bytes32 last_history_hash, - bytes32 history_hash, - bytes32 answer, - uint256 bond, - address addr - ) internal pure returns (bool) { - if ( - last_history_hash == - keccak256(abi.encodePacked(history_hash, answer, bond, addr, true)) - ) { - return true; - } - if ( - last_history_hash == - keccak256(abi.encodePacked(history_hash, answer, bond, addr, false)) - ) { - return false; - } - revert("History input provided did not match the expected hash"); - } - - function _processHistoryItem( - bytes32 question_id, - bytes32 best_answer, - uint256 queued_funds, - address payee, - address addr, - uint256 bond, - bytes32 answer, - bool is_commitment - ) internal returns (uint256, address) { - // For commit-and-reveal, the answer history holds the commitment ID instead of the answer. - // We look at the referenced commitment ID and switch in the actual answer. - if (is_commitment) { - bytes32 commitment_id = answer; - // If it's a commit but it hasn't been revealed, it will always be considered wrong. - if (!commitments[commitment_id].is_revealed) { - delete commitments[commitment_id]; - return (queued_funds, payee); - } else { - answer = commitments[commitment_id].revealed_answer; - delete commitments[commitment_id]; - } - } - - if (answer == best_answer) { - if (payee == NULL_ADDRESS) { - // The entry is for the first payee we come to, ie the winner. - // They get the question bounty. - payee = addr; - - if ( - best_answer != UNRESOLVED_ANSWER && - questions[question_id].bounty > 0 - ) { - _payPayee( - question_id, - payee, - questions[question_id].bounty - ); - questions[question_id].bounty = 0; - } - } else if (addr != payee) { - // Answerer has changed, ie we found someone lower down who needs to be paid - - // The lower answerer will take over receiving bonds from higher answerer. - // They should also be paid the takeover fee, which is set at a rate equivalent to their bond. - // (This is our arbitrary rule, to give consistent right-answerers a defence against high-rollers.) - - // There should be enough for the fee, but if not, take what we have. - // There's an edge case involving weird arbitrator behaviour where we may be short. - uint256 answer_takeover_fee = (queued_funds >= bond) - ? bond - : queued_funds; - // Settle up with the old (higher-bonded) payee - _payPayee( - question_id, - payee, - queued_funds - answer_takeover_fee - ); - - // Now start queued_funds again for the new (lower-bonded) payee - payee = addr; - queued_funds = answer_takeover_fee; - } - } - - return (queued_funds, payee); - } - - /// @notice Convenience function to assign bounties/bonds for multiple questions in one go, then withdraw all your funds. - /// Caller must provide the answer history for each question, in reverse order - /// @dev Can be called by anyone to assign bonds/bounties, but funds are only withdrawn for the user making the call. - /// @param question_ids The IDs of the questions you want to claim for - /// @param lengths The number of history entries you will supply for each question ID - /// @param hist_hashes In a single list for all supplied questions, the hash of each history entry. - /// @param addrs In a single list for all supplied questions, the address of each answerer or commitment sender - /// @param bonds In a single list for all supplied questions, the bond supplied with each answer or commitment - /// @param answers In a single list for all supplied questions, each answer supplied, or commitment ID - function claimMultipleAndWithdrawBalance( - bytes32[] memory question_ids, - uint256[] memory lengths, - bytes32[] memory hist_hashes, - address[] memory addrs, - uint256[] memory bonds, - bytes32[] memory answers - ) - public - stateAny // The finalization checks are done in the claimWinnings function - { - uint256 qi; - uint256 i; - for (qi = 0; qi < question_ids.length; qi++) { - bytes32 qid = question_ids[qi]; - uint256 ln = lengths[qi]; - bytes32[] memory hh = new bytes32[](ln); - address[] memory ad = new address[](ln); - uint256[] memory bo = new uint256[](ln); - bytes32[] memory an = new bytes32[](ln); - uint256 j; - for (j = 0; j < ln; j++) { - hh[j] = hist_hashes[i]; - ad[j] = addrs[i]; - bo[j] = bonds[i]; - an[j] = answers[i]; - i++; - } - claimWinnings(qid, hh, ad, bo, an); - } - withdraw(); - } - - /// @notice Returns the questions's content hash, identifying the question content - /// @param question_id The ID of the question - function getContentHash(bytes32 question_id) public view returns (bytes32) { - return questions[question_id].content_hash; - } - - /// @notice Returns the arbitrator address for the question - /// @param question_id The ID of the question - function getArbitrator(bytes32 question_id) public view returns (address) { - return questions[question_id].arbitrator; - } - - /// @notice Returns the timestamp when the question can first be answered - /// @param question_id The ID of the question - function getOpeningTS(bytes32 question_id) public view returns (uint32) { - return questions[question_id].opening_ts; - } - - /// @notice Returns the timeout in seconds used after each answer - /// @param question_id The ID of the question - function getTimeout(bytes32 question_id) public view returns (uint32) { - return questions[question_id].timeout; - } - - /// @notice Returns the timestamp at which the question will be/was finalized - /// @param question_id The ID of the question - function getFinalizeTS(bytes32 question_id) public view returns (uint32) { - return questions[question_id].finalize_ts; - } - - /// @notice Returns whether the question is pending arbitration - /// @param question_id The ID of the question - function isPendingArbitration( - bytes32 question_id - ) public view returns (bool) { - return questions[question_id].is_pending_arbitration; - } - - /// @notice Returns the current total unclaimed bounty - /// @dev Set back to zero once the bounty has been claimed - /// @param question_id The ID of the question - function getBounty(bytes32 question_id) public view returns (uint256) { - return questions[question_id].bounty; - } - - /// @notice Returns the current best answer - /// @param question_id The ID of the question - function getBestAnswer(bytes32 question_id) public view returns (bytes32) { - return questions[question_id].best_answer; - } - - /// @notice Returns the history hash of the question - /// @param question_id The ID of the question - /// @dev Updated on each answer, then rewound as each is claimed - function getHistoryHash(bytes32 question_id) public view returns (bytes32) { - return questions[question_id].history_hash; - } - - /// @notice Returns the highest bond posted so far for a question - /// @param question_id The ID of the question - function getBond(bytes32 question_id) public view returns (uint256) { - return questions[question_id].bond; - } - - /// @notice Returns the minimum bond that can answer the question - /// @param question_id The ID of the question - function getMinBond(bytes32 question_id) public view returns (uint256) { - return questions[question_id].min_bond; - } -} diff --git a/contracts/lib/reality-eth/interfaces/IArbitrator.sol b/contracts/lib/reality-eth/interfaces/IArbitrator.sol deleted file mode 100644 index 47175922..00000000 --- a/contracts/lib/reality-eth/interfaces/IArbitrator.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.10; - -import "./IRealityETH.sol"; -import "./IERC20.sol"; -import "./IOwned.sol"; - -interface IArbitrator { - function metadata() external view returns (string memory); - - function arbitration_bounties(bytes32) external view returns (uint256); - - function realitio() external view returns (IRealityETH); - - function realitycheck() external view returns (IRealityETH); - - function setRealitio(address addr) external; - - function setDisputeFee(uint256 fee) external; - - function setCustomDisputeFee(bytes32 question_id, uint256 fee) external; - - function getDisputeFee(bytes32 question_id) external view returns (uint256); - - function setQuestionFee(uint256 fee) external; - - function submitAnswerByArbitrator( - bytes32 question_id, - bytes32 answer, - address answerer - ) external; - - function requestArbitration( - bytes32 questionId, - uint256 maxPrevious - ) external payable returns (bool); - - function withdraw(address addr) external; - - function withdrawERC20(IERC20 _token, address addr) external; - - function callWithdraw() external; - - function setMetaData(string memory _metadata) external; -} diff --git a/contracts/lib/reality-eth/interfaces/IBalanceHolder.sol b/contracts/lib/reality-eth/interfaces/IBalanceHolder.sol deleted file mode 100644 index 366f0ffa..00000000 --- a/contracts/lib/reality-eth/interfaces/IBalanceHolder.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.10; - -interface IBalanceHolder { - function withdraw() external; - - function balanceOf(address) external view returns (uint256); -} diff --git a/contracts/lib/reality-eth/interfaces/IBalanceHolder_ERC20.sol b/contracts/lib/reality-eth/interfaces/IBalanceHolder_ERC20.sol deleted file mode 100644 index c9c3df16..00000000 --- a/contracts/lib/reality-eth/interfaces/IBalanceHolder_ERC20.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.10; - -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -interface IBalanceHolder_ERC20 { - function withdraw() external; - - function balanceOf(address) external view returns (uint256); - - function token() external view returns (IERC20); -} diff --git a/contracts/lib/reality-eth/interfaces/IERC20.sol b/contracts/lib/reality-eth/interfaces/IERC20.sol deleted file mode 100644 index ed71ade6..00000000 --- a/contracts/lib/reality-eth/interfaces/IERC20.sol +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) - -pragma solidity ^0.8.10; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance( - address owner, - address spender - ) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); -} diff --git a/contracts/lib/reality-eth/interfaces/IERC20Mint.sol b/contracts/lib/reality-eth/interfaces/IERC20Mint.sol deleted file mode 100644 index 80d0d71a..00000000 --- a/contracts/lib/reality-eth/interfaces/IERC20Mint.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.10; - -import "./IERC20.sol"; - -/** - * @title ERC20 interface - * @dev see https://github.com/ethereum/EIPs/issues/20 - */ -interface IERC20Mint is IERC20 { - function mint(address to, uint256 value) external; -} diff --git a/contracts/lib/reality-eth/interfaces/IOwned.sol b/contracts/lib/reality-eth/interfaces/IOwned.sol deleted file mode 100644 index 70f2b66d..00000000 --- a/contracts/lib/reality-eth/interfaces/IOwned.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.10; - -interface IOwned { - function owner() external view returns (address); - - function transferOwnership(address newOwner) external; -} diff --git a/contracts/lib/reality-eth/interfaces/IRealityETH.sol b/contracts/lib/reality-eth/interfaces/IRealityETH.sol deleted file mode 100644 index 16084a8a..00000000 --- a/contracts/lib/reality-eth/interfaces/IRealityETH.sol +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.10; - -import "./IBalanceHolder.sol"; - -interface IRealityETH is IBalanceHolder { - event LogAnswerReveal( - bytes32 indexed question_id, - address indexed user, - bytes32 indexed answer_hash, - bytes32 answer, - uint256 nonce, - uint256 bond - ); - event LogCancelArbitration(bytes32 indexed question_id); - event LogClaim( - bytes32 indexed question_id, - address indexed user, - uint256 amount - ); - event LogFinalize(bytes32 indexed question_id, bytes32 indexed answer); - event LogFundAnswerBounty( - bytes32 indexed question_id, - uint256 bounty_added, - uint256 bounty, - address indexed user - ); - event LogMinimumBond(bytes32 indexed question_id, uint256 min_bond); - event LogNewAnswer( - bytes32 answer, - bytes32 indexed question_id, - bytes32 history_hash, - address indexed user, - uint256 bond, - uint256 ts, - bool is_commitment - ); - event LogNewQuestion( - bytes32 indexed question_id, - address indexed user, - uint256 template_id, - string question, - bytes32 indexed content_hash, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce, - uint256 created - ); - event LogNewTemplate( - uint256 indexed template_id, - address indexed user, - string question_text - ); - event LogNotifyOfArbitrationRequest( - bytes32 indexed question_id, - address indexed user - ); - event LogReopenQuestion( - bytes32 indexed question_id, - bytes32 indexed reopened_question_id - ); - event LogSetQuestionFee(address arbitrator, uint256 amount); - - function assignWinnerAndSubmitAnswerByArbitrator( - bytes32 question_id, - bytes32 answer, - address payee_if_wrong, - bytes32 last_history_hash, - bytes32 last_answer_or_commitment_id, - address last_answerer - ) external; - - function cancelArbitration(bytes32 question_id) external; - - function claimMultipleAndWithdrawBalance( - bytes32[] calldata question_ids, - uint256[] calldata lengths, - bytes32[] calldata hist_hashes, - address[] calldata addrs, - uint256[] calldata bonds, - bytes32[] calldata answers - ) external; - - function claimWinnings( - bytes32 question_id, - bytes32[] calldata history_hashes, - address[] calldata addrs, - uint256[] calldata bonds, - bytes32[] calldata answers - ) external; - - function createTemplate(string calldata content) external returns (uint256); - - function notifyOfArbitrationRequest( - bytes32 question_id, - address requester, - uint256 max_previous - ) external; - - function setQuestionFee(uint256 fee) external; - - function submitAnswerByArbitrator( - bytes32 question_id, - bytes32 answer, - address answerer - ) external; - - function submitAnswerReveal( - bytes32 question_id, - bytes32 answer, - uint256 nonce, - uint256 bond - ) external; - - function askQuestion( - uint256 template_id, - string calldata question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce - ) external payable returns (bytes32); - - function askQuestionWithMinBond( - uint256 template_id, - string calldata question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce, - uint256 min_bond - ) external payable returns (bytes32); - - function createTemplateAndAskQuestion( - string calldata content, - string calldata question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce - ) external payable returns (bytes32); - - function fundAnswerBounty(bytes32 question_id) external payable; - - function reopenQuestion( - uint256 template_id, - string calldata question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce, - uint256 min_bond, - bytes32 reopens_question_id - ) external payable returns (bytes32); - - function submitAnswer( - bytes32 question_id, - bytes32 answer, - uint256 max_previous - ) external payable; - - function submitAnswerCommitment( - bytes32 question_id, - bytes32 answer_hash, - uint256 max_previous, - address _answerer - ) external payable; - - function submitAnswerFor( - bytes32 question_id, - bytes32 answer, - uint256 max_previous, - address answerer - ) external payable; - - function arbitrator_question_fees(address) external view returns (uint256); - - function commitments( - bytes32 - ) - external - view - returns (uint32 reveal_ts, bool is_revealed, bytes32 revealed_answer); - - function getArbitrator(bytes32 question_id) external view returns (address); - - function getBestAnswer(bytes32 question_id) external view returns (bytes32); - - function getBond(bytes32 question_id) external view returns (uint256); - - function getBounty(bytes32 question_id) external view returns (uint256); - - function getContentHash( - bytes32 question_id - ) external view returns (bytes32); - - function getEarliestAnswerFromSuppliedHistoryOrRevert( - bytes32 question_id, - bytes32[] memory history_hashes, - address[] memory addrs, - uint256[] memory bonds, - bytes32[] memory answers - ) external view returns (bytes32, uint256); - - function getFinalAnswer( - bytes32 question_id - ) external view returns (bytes32); - - function getFinalAnswerIfMatches( - bytes32 question_id, - bytes32 content_hash, - address arbitrator, - uint32 min_timeout, - uint256 min_bond - ) external view returns (bytes32); - - function getFinalizeTS(bytes32 question_id) external view returns (uint32); - - function getHistoryHash( - bytes32 question_id - ) external view returns (bytes32); - - function getMinBond(bytes32 question_id) external view returns (uint256); - - function getOpeningTS(bytes32 question_id) external view returns (uint32); - - function getTimeout(bytes32 question_id) external view returns (uint32); - - function isFinalized(bytes32 question_id) external view returns (bool); - - function isPendingArbitration( - bytes32 question_id - ) external view returns (bool); - - function isSettledTooSoon(bytes32 question_id) external view returns (bool); - - function question_claims( - bytes32 - ) - external - view - returns (address payee, uint256 last_bond, uint256 queued_funds); - - function questions( - bytes32 - ) - external - view - returns ( - bytes32 content_hash, - address arbitrator, - uint32 opening_ts, - uint32 timeout, - uint32 finalize_ts, - bool is_pending_arbitration, - uint256 bounty, - bytes32 best_answer, - bytes32 history_hash, - uint256 bond, - uint256 min_bond - ); - - function reopened_questions(bytes32) external view returns (bytes32); - - function reopener_questions(bytes32) external view returns (bool); - - function resultFor(bytes32 question_id) external view returns (bytes32); - - function resultForOnceSettled( - bytes32 question_id - ) external view returns (bytes32); - - function template_hashes(uint256) external view returns (bytes32); - - function templates(uint256) external view returns (uint256); -} diff --git a/contracts/lib/reality-eth/interfaces/IRealityETH_ERC20.sol b/contracts/lib/reality-eth/interfaces/IRealityETH_ERC20.sol deleted file mode 100644 index 5556ae76..00000000 --- a/contracts/lib/reality-eth/interfaces/IRealityETH_ERC20.sol +++ /dev/null @@ -1,290 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -pragma solidity ^0.8.10; - -import "./IBalanceHolder_ERC20.sol"; - -interface IRealityETH_ERC20 is IBalanceHolder_ERC20 { - event LogAnswerReveal( - bytes32 indexed question_id, - address indexed user, - bytes32 indexed answer_hash, - bytes32 answer, - uint256 nonce, - uint256 bond - ); - event LogCancelArbitration(bytes32 indexed question_id); - event LogClaim( - bytes32 indexed question_id, - address indexed user, - uint256 amount - ); - event LogFinalize(bytes32 indexed question_id, bytes32 indexed answer); - event LogFundAnswerBounty( - bytes32 indexed question_id, - uint256 bounty_added, - uint256 bounty, - address indexed user - ); - event LogMinimumBond(bytes32 indexed question_id, uint256 min_bond); - event LogNewAnswer( - bytes32 answer, - bytes32 indexed question_id, - bytes32 history_hash, - address indexed user, - uint256 bond, - uint256 ts, - bool is_commitment - ); - event LogNewQuestion( - bytes32 indexed question_id, - address indexed user, - uint256 template_id, - string question, - bytes32 indexed content_hash, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce, - uint256 created - ); - event LogNewTemplate( - uint256 indexed template_id, - address indexed user, - string question_text - ); - event LogNotifyOfArbitrationRequest( - bytes32 indexed question_id, - address indexed user - ); - event LogReopenQuestion( - bytes32 indexed question_id, - bytes32 indexed reopened_question_id - ); - event LogSetQuestionFee(address arbitrator, uint256 amount); - - function askQuestion( - uint256 template_id, - string calldata question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce - ) external returns (bytes32); - - function askQuestionERC20( - uint256 template_id, - string calldata question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce, - uint256 tokens - ) external returns (bytes32); - - function askQuestionWithMinBondERC20( - uint256 template_id, - string calldata question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce, - uint256 min_bond, - uint256 tokens - ) external returns (bytes32); - - function assignWinnerAndSubmitAnswerByArbitrator( - bytes32 question_id, - bytes32 answer, - address payee_if_wrong, - bytes32 last_history_hash, - bytes32 last_answer_or_commitment_id, - address last_answerer - ) external; - - function cancelArbitration(bytes32 question_id) external; - - function claimMultipleAndWithdrawBalance( - bytes32[] calldata question_ids, - uint256[] calldata lengths, - bytes32[] calldata hist_hashes, - address[] calldata addrs, - uint256[] calldata bonds, - bytes32[] calldata answers - ) external; - - function claimWinnings( - bytes32 question_id, - bytes32[] calldata history_hashes, - address[] calldata addrs, - uint256[] calldata bonds, - bytes32[] calldata answers - ) external; - - function createTemplate(string calldata content) external returns (uint256); - - function createTemplateAndAskQuestion( - string calldata content, - string calldata question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce - ) external returns (bytes32); - - function fundAnswerBountyERC20( - bytes32 question_id, - uint256 tokens - ) external; - - function notifyOfArbitrationRequest( - bytes32 question_id, - address requester, - uint256 max_previous - ) external; - - function reopenQuestionERC20( - uint256 template_id, - string calldata question, - address arbitrator, - uint32 timeout, - uint32 opening_ts, - uint256 nonce, - uint256 min_bond, - bytes32 reopens_question_id, - uint256 tokens - ) external returns (bytes32); - - function setQuestionFee(uint256 fee) external; - - function setToken(address _token) external; - - function submitAnswerByArbitrator( - bytes32 question_id, - bytes32 answer, - address answerer - ) external; - - function submitAnswerCommitmentERC20( - bytes32 question_id, - bytes32 answer_hash, - uint256 max_previous, - address _answerer, - uint256 tokens - ) external; - - function submitAnswerERC20( - bytes32 question_id, - bytes32 answer, - uint256 max_previous, - uint256 tokens - ) external; - - function submitAnswerForERC20( - bytes32 question_id, - bytes32 answer, - uint256 max_previous, - address answerer, - uint256 tokens - ) external; - - function submitAnswerReveal( - bytes32 question_id, - bytes32 answer, - uint256 nonce, - uint256 bond - ) external; - - function arbitrator_question_fees(address) external view returns (uint256); - - function commitments( - bytes32 - ) - external - view - returns (uint32 reveal_ts, bool is_revealed, bytes32 revealed_answer); - - function getArbitrator(bytes32 question_id) external view returns (address); - - function getBestAnswer(bytes32 question_id) external view returns (bytes32); - - function getBond(bytes32 question_id) external view returns (uint256); - - function getBounty(bytes32 question_id) external view returns (uint256); - - function getContentHash( - bytes32 question_id - ) external view returns (bytes32); - - function getFinalAnswer( - bytes32 question_id - ) external view returns (bytes32); - - function getFinalAnswerIfMatches( - bytes32 question_id, - bytes32 content_hash, - address arbitrator, - uint32 min_timeout, - uint256 min_bond - ) external view returns (bytes32); - - function getFinalizeTS(bytes32 question_id) external view returns (uint32); - - function getHistoryHash( - bytes32 question_id - ) external view returns (bytes32); - - function getMinBond(bytes32 question_id) external view returns (uint256); - - function getOpeningTS(bytes32 question_id) external view returns (uint32); - - function getTimeout(bytes32 question_id) external view returns (uint32); - - function isFinalized(bytes32 question_id) external view returns (bool); - - function isPendingArbitration( - bytes32 question_id - ) external view returns (bool); - - function isSettledTooSoon(bytes32 question_id) external view returns (bool); - - function question_claims( - bytes32 - ) - external - view - returns (address payee, uint256 last_bond, uint256 queued_funds); - - function questions( - bytes32 - ) - external - view - returns ( - bytes32 content_hash, - address arbitrator, - uint32 opening_ts, - uint32 timeout, - uint32 finalize_ts, - bool is_pending_arbitration, - uint256 bounty, - bytes32 best_answer, - bytes32 history_hash, - uint256 bond, - uint256 min_bond - ); - - function reopened_questions(bytes32) external view returns (bytes32); - - function reopener_questions(bytes32) external view returns (bool); - - function resultFor(bytes32 question_id) external view returns (bytes32); - - function resultForOnceSettled( - bytes32 question_id - ) external view returns (bytes32); - - function template_hashes(uint256) external view returns (bytes32); - - function templates(uint256) external view returns (uint256); -} diff --git a/test/AdjudicationFramework/AdjudicationFrameworkFeeds.t.sol b/test/AdjudicationFramework/AdjudicationFrameworkFeeds.t.sol index 6de703d9..a7030a41 100644 --- a/test/AdjudicationFramework/AdjudicationFrameworkFeeds.t.sol +++ b/test/AdjudicationFramework/AdjudicationFrameworkFeeds.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {AdjudicationFrameworkFeeds} from "../../contracts/AdjudicationFramework/Push/AdjudicationFrameworkFeeds.sol"; -import {RealityETH_v3_0} from "../../contracts/lib/reality-eth/RealityETH-3.0.sol"; +import {RealityETH_v4_0} from "@reality.eth/contracts/development/contracts/RealityETH-4.0.sol"; contract FeedsTest is Test { AdjudicationFrameworkFeeds public feeds; @@ -19,7 +19,7 @@ contract FeedsTest is Test { initialArbitrators = new address[](1); initialArbitrators[0] = arbitrator1; - RealityETH_v3_0 l2RealityEth = new RealityETH_v3_0(); + RealityETH_v4_0 l2RealityEth = new RealityETH_v4_0(); feeds = new AdjudicationFrameworkFeeds( address(l2RealityEth), diff --git a/test/AdjudicationFramework/AdjudicationFrameworkMinimal.t.sol b/test/AdjudicationFramework/AdjudicationFrameworkMinimal.t.sol index 2dc1e442..91bb60f3 100644 --- a/test/AdjudicationFramework/AdjudicationFrameworkMinimal.t.sol +++ b/test/AdjudicationFramework/AdjudicationFrameworkMinimal.t.sol @@ -7,12 +7,13 @@ pragma solidity ^0.8.20; import {Vm} from "forge-std/Vm.sol"; import {Test} from "forge-std/Test.sol"; -import {Arbitrator} from "../../contracts/lib/reality-eth/Arbitrator.sol"; +import {Arbitrator} from "@reality.eth/contracts/development/contracts/Arbitrator.sol"; -import {IRealityETH} from "../../contracts/lib/reality-eth/interfaces/IRealityETH.sol"; +import {IRealityETH} from "@reality.eth/contracts/development/contracts/IRealityETH.sol"; +import {IRealityETHErrors} from "@reality.eth/contracts/development/contracts/IRealityETHErrors.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ForkableRealityETH_ERC20} from "../../contracts/ForkableRealityETH_ERC20.sol"; -import {RealityETH_v3_0} from "../../contracts/lib/reality-eth/RealityETH-3.0.sol"; +import {RealityETH_v4_0} from "@reality.eth/contracts/development/contracts//RealityETH-4.0.sol"; import {AdjudicationFrameworkRequests} from "../../contracts/AdjudicationFramework/Pull/AdjudicationFrameworkRequests.sol"; import {MinimalAdjudicationFramework} from "../../contracts/AdjudicationFramework/MinimalAdjudicationFramework.sol"; import {L2ForkArbitrator} from "../../contracts/L2ForkArbitrator.sol"; @@ -29,7 +30,7 @@ contract AdjudicationIntegrationTest is Test { IERC20(0x1234567890123456789012345678901234567890); ForkableRealityETH_ERC20 internal l1RealityEth; - RealityETH_v3_0 internal l2RealityEth; + RealityETH_v4_0 internal l2RealityEth; bytes32 internal addArbitratorQID1; bytes32 internal addArbitratorQID2; @@ -152,10 +153,10 @@ contract AdjudicationIntegrationTest is Test { user2.transfer(1000000); // NB we're modelling this on the same chain but it should really be the l2 - l2RealityEth = new RealityETH_v3_0(); + l2RealityEth = new RealityETH_v4_0(); l2ForkArbitrator = new L2ForkArbitrator( - IRealityETH(l2RealityEth), + IRealityETH(address(l2RealityEth)), L2ChainInfo(l2ChainInfo), L1GlobalForkRequester(l1GlobalForkRequester), forkingFee @@ -209,14 +210,14 @@ contract AdjudicationIntegrationTest is Test { "finalization ts should be passed block ts" ); - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); l2RealityEth.resultFor(addArbitratorQID1); assertTrue( finalizeTs > block.timestamp, "finalization ts should be passed block ts" ); - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); adjudicationFramework1.executeModificationArbitratorFromAllowList( addArbitratorQID1 ); @@ -334,7 +335,7 @@ contract AdjudicationIntegrationTest is Test { ); // Scenario 3: Invalid case - twice the same arbitrators - vm.expectRevert("question must not exist"); + vm.expectRevert(IRealityETHErrors.QuestionMustNotExist.selector); adjudicationFramework1.requestModificationOfArbitrators( initialArbitrator1, address(0) @@ -391,7 +392,7 @@ contract AdjudicationIntegrationTest is Test { bytes32(tempAnswerInt), 0 ); - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); adjudicationFramework1.clearFailedProposition(questionId); // Assume freezeArbitrator() will be called here with appropriate parameters diff --git a/test/AdjudicationFramework/AdjudicationFrameworkRequests.t.sol b/test/AdjudicationFramework/AdjudicationFrameworkRequests.t.sol index 6c64bb00..3599c58a 100644 --- a/test/AdjudicationFramework/AdjudicationFrameworkRequests.t.sol +++ b/test/AdjudicationFramework/AdjudicationFrameworkRequests.t.sol @@ -9,9 +9,10 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ForkableRealityETH_ERC20} from "../../contracts/ForkableRealityETH_ERC20.sol"; import {AdjudicationFrameworkRequests} from "../../contracts/AdjudicationFramework/Pull/AdjudicationFrameworkRequests.sol"; -import {IRealityETH} from "../../contracts/lib/reality-eth/interfaces/IRealityETH.sol"; -import {RealityETH_v3_0} from "../../contracts/lib/reality-eth/RealityETH-3.0.sol"; -import {Arbitrator} from "../../contracts/lib/reality-eth/Arbitrator.sol"; +import {IRealityETH} from "@reality.eth/contracts/development/contracts/IRealityETH.sol"; +import {IRealityETHErrors} from "@reality.eth/contracts/development/contracts/IRealityETHErrors.sol"; +import {RealityETH_v4_0} from "@reality.eth/contracts/development/contracts/RealityETH-4.0.sol"; +import {Arbitrator} from "@reality.eth/contracts/development/contracts/Arbitrator.sol"; import {L2ForkArbitrator} from "../../contracts/L2ForkArbitrator.sol"; import {L1GlobalForkRequester} from "../../contracts/L1GlobalForkRequester.sol"; import {L2ChainInfo} from "../../contracts/L2ChainInfo.sol"; @@ -27,7 +28,7 @@ contract AdjudicationIntegrationTest is Test { IERC20(0x1234567890123456789012345678901234567890); ForkableRealityETH_ERC20 internal l1RealityEth; - RealityETH_v3_0 internal l2RealityEth; + RealityETH_v4_0 internal l2RealityEth; bytes32 internal addArbitratorQID1; bytes32 internal addArbitratorQID2; @@ -131,7 +132,7 @@ contract AdjudicationIntegrationTest is Test { address(l1ForkingManager), address(0), address(tokenMock), - (0) + bytes32(0) ); /* @@ -151,10 +152,10 @@ contract AdjudicationIntegrationTest is Test { user2.transfer(1000000); // NB we're modelling this on the same chain but it should really be the l2 - l2RealityEth = new RealityETH_v3_0(); + l2RealityEth = new RealityETH_v4_0(); l2ForkArbitrator = new L2ForkArbitrator( - IRealityETH(l2RealityEth), + IRealityETH(address(l2RealityEth)), L2ChainInfo(l2ChainInfo), L1GlobalForkRequester(l1GlobalForkRequester), forkingFee @@ -208,14 +209,14 @@ contract AdjudicationIntegrationTest is Test { "finalization ts should be passed block ts" ); - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); l2RealityEth.resultFor(addArbitratorQID1); assertTrue( finalizeTs > block.timestamp, "finalization ts should be passed block ts" ); - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); adjudicationFramework1.executeModificationArbitratorFromAllowList( addArbitratorQID1 ); @@ -401,10 +402,10 @@ contract AdjudicationIntegrationTest is Test { // Currently in the "yes" state, so once it times out we can complete the removal // Now wait for the timeout and settle the proposition - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); l2RealityEth.resultFor(removalQuestionId); - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); adjudicationFramework1.executeModificationArbitratorFromAllowList( removalQuestionId ); @@ -429,10 +430,10 @@ contract AdjudicationIntegrationTest is Test { // Now wait for the timeout and settle the proposition - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); l2RealityEth.resultFor(removalQuestionId); - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); adjudicationFramework1.executeModificationArbitratorFromAllowList( removalQuestionId ); @@ -475,7 +476,7 @@ contract AdjudicationIntegrationTest is Test { // Currently in the "yes" state, so once it times out we can complete the removal // Now wait for the timeout and settle the proposition - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); l2RealityEth.resultFor(removalQuestionId); assertEq( @@ -594,7 +595,7 @@ contract AdjudicationIntegrationTest is Test { // Currently in the "yes" state, so once it times out we can complete the removal // Now wait for the timeout and settle the proposition - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); bytes32 result = l2RealityEth.resultFor(removalQuestionId); assertEq(result, bytes32(uint256(0))); @@ -714,7 +715,7 @@ contract AdjudicationIntegrationTest is Test { // Currently in the "yes" state, so once it times out we can complete the removal // Now wait for the timeout and settle the proposition - vm.expectRevert("question must be finalized"); + vm.expectRevert(IRealityETHErrors.QuestionMustBeFinalized.selector); bytes32 result = l2RealityEth.resultFor(removalQuestionId); assertEq(result, bytes32(uint256(0))); diff --git a/test/Arbitrator.t.sol b/test/Arbitrator.t.sol deleted file mode 100644 index f44b9030..00000000 --- a/test/Arbitrator.t.sol +++ /dev/null @@ -1,55 +0,0 @@ -pragma solidity ^0.8.20; - -import {Test} from "forge-std/Test.sol"; -import {Arbitrator} from "../contracts/lib/reality-eth/Arbitrator.sol"; - -import {IRealityETH} from "../contracts/lib/reality-eth/interfaces/IRealityETH.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -contract ArbitratorTest is Test { - Arbitrator public arb; - - IERC20 internal tokenMock = - IERC20(0x1234567890123456789012345678901234567890); - IRealityETH internal realityMock = - IRealityETH(0x1234567890123456789012345678901234567891); - - function setUp() public { - arb = new Arbitrator(); - arb.setRealitio(address(realityMock)); - } - - function testSetRealitio() public { - arb.setRealitio(address(123)); - assertEq(address(arb.realitio()), address(123)); - } - - function testSetDisputeFee() public { - arb.setDisputeFee(10); - assertEq(arb.dispute_fee(), 10); - } - - function testSetCustomDisputeFee() public { - bytes32 questionId = keccak256(abi.encodePacked("Question 1")); - arb.setCustomDisputeFee(questionId, 20); - assertEq(arb.getDisputeFee(questionId), 20); - } - - function testRequestArbitration() public { - bytes32 questionId = keccak256(abi.encodePacked("Question 1")); - arb.setDisputeFee(50); - vm.mockCall( - address(realityMock), - abi.encodeWithSelector(IRealityETH.isFinalized.selector), - abi.encode(true) - ); - - assertTrue(arb.requestArbitration{value: 50}(questionId, 0)); - assertEq(arb.arbitration_bounties(questionId), 50); - } - - function testSetMetaData() public { - arb.setMetaData("Some metadata"); - assertEq(arb.metadata(), "Some metadata"); - } -} diff --git a/test/L2ForkArbitrator.t.sol b/test/L2ForkArbitrator.t.sol index db0bc83d..611e2a9b 100644 --- a/test/L2ForkArbitrator.t.sol +++ b/test/L2ForkArbitrator.t.sol @@ -2,7 +2,8 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {L2ForkArbitrator} from "../contracts/L2ForkArbitrator.sol"; -import {IRealityETH} from "../contracts/lib/reality-eth/interfaces/IRealityETH.sol"; +import {IRealityETH} from "@reality.eth/contracts/development/contracts/IRealityETH.sol"; +import {IRealityETHCore_Common} from "@reality.eth/contracts/development/contracts/IRealityETHCore_Common.sol"; import {L2ChainInfo} from "../contracts/L2ChainInfo.sol"; import {L1GlobalForkRequester} from "../contracts/L1GlobalForkRequester.sol"; import {IL2ForkArbitrator} from "../contracts/interfaces/IL2ForkArbitrator.sol"; @@ -43,7 +44,7 @@ contract L2ForkArbitratorTest is Test { vm.mockCall( realitio, abi.encodeWithSelector( - IRealityETH.notifyOfArbitrationRequest.selector, + IRealityETHCore_Common.notifyOfArbitrationRequest.selector, questionId, address(this), maxPrevious @@ -93,7 +94,7 @@ contract L2ForkArbitratorTest is Test { vm.mockCall( realitio, abi.encodeWithSelector( - IRealityETH.notifyOfArbitrationRequest.selector, + IRealityETHCore_Common.notifyOfArbitrationRequest.selector, questionId, address(this), maxPrevious @@ -183,7 +184,7 @@ contract L2ForkArbitratorTest is Test { vm.mockCall( realitio, abi.encodeWithSelector( - IRealityETH.notifyOfArbitrationRequest.selector, + IRealityETHCore_Common.notifyOfArbitrationRequest.selector, questionId, address(this), maxPrevious @@ -300,7 +301,7 @@ contract L2ForkArbitratorTest is Test { vm.mockCall( address(realitio), abi.encodeWithSelector( - IRealityETH.notifyOfArbitrationRequest.selector, + IRealityETHCore_Common.notifyOfArbitrationRequest.selector, questionId, address(this), maxPrevious @@ -364,7 +365,7 @@ contract L2ForkArbitratorTest is Test { vm.mockCall( address(realitio), abi.encodeWithSelector( - IRealityETH.notifyOfArbitrationRequest.selector, + IRealityETHCore_Common.notifyOfArbitrationRequest.selector, questionId, address(this), maxPrevious diff --git a/test/RealityETH.t.sol b/test/RealityETH.t.sol deleted file mode 100644 index 8d1f487a..00000000 --- a/test/RealityETH.t.sol +++ /dev/null @@ -1,243 +0,0 @@ -pragma solidity ^0.8.20; - -/* -Tests for new features added to reality.eth while developing Subjectivocracy interaction. -Ultimately these will probably be moved to the reality.eth repo and included in a normal release -...unless it turns out that we need subjectivocracy-specific changes that we don't want in the normal version. -*/ - -/* solhint-disable not-rely-on-time */ -/* solhint-disable reentrancy */ -/* solhint-disable quotes */ - -import {Vm} from "forge-std/Vm.sol"; - -import {Test} from "forge-std/Test.sol"; -import {Arbitrator} from "../contracts/lib/reality-eth/Arbitrator.sol"; -import {RealityETH_v3_0} from "../contracts/lib/reality-eth/RealityETH-3.0.sol"; - -contract RealityETHTest is Test { - Arbitrator internal arb; - RealityETH_v3_0 internal realityEth; - bytes32 internal q1; - bytes32 internal q2; - - address payable internal user1 = payable(address(0xbabe01)); - address payable internal user2 = payable(address(0xbabe02)); - - bytes32 internal constant BYTES32_YES = bytes32(uint256(1)); - bytes32 internal constant BYTES32_NO = bytes32(uint256(0)); - - bytes32[] internal historyHashes; - address[] internal addrs; - uint256[] internal bonds; - bytes32[] internal answers; - - // Store the history for the number of entries set in numEntries in historyHashes etc - function _logsToHistory(Vm.Log[] memory logs) internal { - /* - Some features need us to send the contract the answer history. - This function will construct it from the logs in the order required. - event LogNewAnswer(bytes32 answer, bytes32 indexed question_id, bytes32 history_hash, address indexed user, uint256 bond, uint256 ts, bool is_commitment) - */ - - bytes32 logNewAnswerSignature = keccak256( - "LogNewAnswer(bytes32,bytes32,bytes32,address,uint256,uint256,bool)" - ); - - for (uint256 idx = logs.length; idx > 0; idx--) { - uint256 i = idx - 1; - - // Skip any other log - if (logs[i].topics[0] != logNewAnswerSignature) { - continue; - } - - ( - bytes32 logAnswer, - bytes32 logHistoryHash, - uint256 logBond, - , - - ) = abi.decode( - logs[i].data, - (bytes32, bytes32, uint256, uint256, bool) - ); - address logUser = address(uint160(uint256(logs[i].topics[2]))); - - addrs.push(logUser); - bonds.push(logBond); - answers.push(logAnswer); - historyHashes.push(logHistoryHash); - } - - // historyHashes is in the reverse order (highest bond to lowest), go forwards now - for (uint256 j = 0; j < historyHashes.length; j++) { - // For the final element there is no next one, it's empty - if (j < historyHashes.length - 1) { - historyHashes[j] = historyHashes[j + 1]; - } else { - historyHashes[j] = bytes32(0); - } - } - } - - function _trimLogs() internal { - historyHashes.pop(); - addrs.pop(); - bonds.pop(); - answers.pop(); - } - - function setUp() public { - realityEth = new RealityETH_v3_0(); - - arb = new Arbitrator(); - arb.setRealitio(address(realityEth)); - arb.setDisputeFee(50); - - user1.transfer(1000000); - user2.transfer(1000000); - - q1 = realityEth.askQuestion( - 0, - "Question 1", - address(arb), - uint32(6000), - 0, - 0 - ); - q2 = realityEth.askQuestion( - 0, - "Question 2", - address(arb), - uint32(6000), - 0, - 0 - ); - - vm.recordLogs(); - - vm.prank(user1); - realityEth.submitAnswer{value: 5}(q1, BYTES32_YES, 0); - - vm.prank(user2); - realityEth.submitAnswer{value: 25}(q1, BYTES32_NO, 0); - - vm.prank(user1); - realityEth.submitAnswer{value: 500}(q1, BYTES32_YES, 0); - - // Put an unrevealed commit at 1000 - uint256 nonce1 = uint256(555554321); - bytes32 answerHash1 = keccak256(abi.encodePacked(BYTES32_NO, nonce1)); - uint256 bond1 = 1000; - vm.prank(user1); - realityEth.submitAnswerCommitment{value: bond1}( - q1, - answerHash1, - 0, - user1 - ); - - vm.prank(user2); - realityEth.submitAnswer{value: 2500}(q1, BYTES32_NO, 0); - - // We'll do this one as a commit-reveal - uint256 nonce2 = uint256(1232); - bytes32 answerHash2 = keccak256(abi.encodePacked(BYTES32_NO, nonce2)); - uint256 bond2 = 5000; - vm.prank(user2); - realityEth.submitAnswerCommitment{value: bond2}( - q1, - answerHash2, - 0, - user2 - ); - realityEth.submitAnswerReveal(q1, BYTES32_NO, nonce2, bond2); - - // Do a commit-reveal for yes - uint256 nonce3 = uint256(9876); - bytes32 answerHash3 = keccak256(abi.encodePacked(BYTES32_YES, nonce3)); - uint256 bond3 = 10000; - vm.prank(user1); - realityEth.submitAnswerCommitment{value: bond3}( - q1, - answerHash3, - 0, - user1 - ); - realityEth.submitAnswerReveal(q1, BYTES32_YES, nonce3, bond3); - - vm.prank(user1); - realityEth.submitAnswer{value: 20000}(q1, BYTES32_YES, 0); - - vm.prank(user2); - realityEth.submitAnswer{value: 40000}(q1, BYTES32_NO, 0); - - _logsToHistory(vm.getRecordedLogs()); - } - - function _checkSuppliedHistory( - bytes32 expectedAnswer, - uint256 expectedBond - ) internal { - (bytes32 finalAnswer, uint256 finalBond) = realityEth - .getEarliestAnswerFromSuppliedHistoryOrRevert( - q1, - historyHashes, - addrs, - bonds, - answers - ); - assertEq(finalAnswer, expectedAnswer); - assertEq(finalBond, expectedBond); - } - - function _checkSuppliedHistoryUnrevealedCommit() internal { - vm.expectRevert("Earliest answer is an unrevealed commitment"); - realityEth.getEarliestAnswerFromSuppliedHistoryOrRevert( - q1, - historyHashes, - addrs, - bonds, - answers - ); - } - - function testGetEarliestAnswerFromSuppliedHistoryOrRevert() public { - _checkSuppliedHistory(BYTES32_YES, 5); - _trimLogs(); - _checkSuppliedHistory(BYTES32_NO, 25); - _trimLogs(); - _checkSuppliedHistory(BYTES32_YES, 500); - _trimLogs(); - _checkSuppliedHistoryUnrevealedCommit(); - _trimLogs(); - _checkSuppliedHistory(BYTES32_NO, 2500); - _trimLogs(); - _checkSuppliedHistory(BYTES32_NO, 5000); - _trimLogs(); - _checkSuppliedHistory(BYTES32_YES, 10000); - _trimLogs(); - _checkSuppliedHistory(BYTES32_YES, 20000); - _trimLogs(); - _checkSuppliedHistory(BYTES32_NO, 40000); - } - - function testGetEarliestAnswerFromSuppliedHistoryOrRevertWrongHashReverts() - public - { - // Make one of the history hashes wrong - historyHashes[2] = bytes32(0); - vm.expectRevert( - "History input provided did not match the expected hash" - ); - realityEth.getEarliestAnswerFromSuppliedHistoryOrRevert( - q1, - historyHashes, - addrs, - bonds, - answers - ); - } -}