diff --git a/contracts/AdjudicationFramework.sol b/contracts/AdjudicationFramework.sol index 2d337023..09482d47 100644 --- a/contracts/AdjudicationFramework.sol +++ b/contracts/AdjudicationFramework.sol @@ -24,7 +24,6 @@ To the normal Arbitrator contracts that does its arbitration jobs, it looks like */ contract AdjudicationFramework is BalanceHolder { - uint256 public constant ARB_DISPUTE_TIMEOUT = 86400; uint256 public constant QUESTION_UNHANDLED_TIMEOUT = 86400; @@ -59,14 +58,14 @@ contract AdjudicationFramework is BalanceHolder { } // Reality.eth questions for propositions we may be asked to rule on - struct ArbitratorProposition{ + struct ArbitratorProposition { PropositionType proposition_type; address arbitrator; bool isFrozen; } mapping(bytes32 => ArbitratorProposition) public propositions; - // Keep a count of active propositions that freeze an arbitrator. + // Keep a count of active propositions that freeze an arbitrator. // When they're all cleared they can be unfrozen. mapping(address => uint256) public countArbitratorFreezePropositions; @@ -106,21 +105,31 @@ contract AdjudicationFramework is BalanceHolder { // We'll identify ourselves in the template so we only need a single parameter for questions, the arbitrator in question. // TODO: We may want to specify a document with the terms that guide this decision here, rather than just leaving it implicit. - string memory templatePrefixAdd = '{"title": "Should we add arbitrator %s to the framework '; - string memory templatePrefixRemove = '{"title": "Should we remove arbitrator %s from the framework '; - string memory templateSuffix = '?", "type": "bool", "category": "adjudication", "lang": "en"}'; + string + memory templatePrefixAdd = '{"title": "Should we add arbitrator %s to the framework '; + string + memory templatePrefixRemove = '{"title": "Should we remove arbitrator %s from the framework '; + string + memory templateSuffix = '?", "type": "bool", "category": "adjudication", "lang": "en"}'; string memory thisContractStr = Strings.toHexString(address(this)); - string memory addTemplate = string.concat(templatePrefixAdd, thisContractStr, templateSuffix); - string memory removeTemplate = string.concat(templatePrefixRemove, thisContractStr, templateSuffix); + string memory addTemplate = string.concat( + templatePrefixAdd, + thisContractStr, + templateSuffix + ); + string memory removeTemplate = string.concat( + templatePrefixRemove, + thisContractStr, + templateSuffix + ); templateIdAddArbitrator = realityETH.createTemplate(addTemplate); templateIdRemoveArbitrator = realityETH.createTemplate(removeTemplate); for (uint256 i = 0; i < _initialArbitrators.length; i++) { - arbitrators[_initialArbitrators[i]] = true; + arbitrators[_initialArbitrators[i]] = true; } - } /// @notice Return the dispute fee for the specified question. 0 indicates that we won't arbitrate it. @@ -303,10 +312,7 @@ contract AdjudicationFramework is BalanceHolder { uint256 finalize_ts = question_arbitrations[question_id].finalize_ts; require(finalize_ts > 0, "Submission must have been queued"); - require( - finalize_ts < block.timestamp, - "Challenge deadline not passed" - ); + require(finalize_ts < block.timestamp, "Challenge deadline not passed"); balanceOf[question_arbitrations[question_id].payer] = balanceOf[question_arbitrations[question_id].payer] + @@ -324,40 +330,83 @@ contract AdjudicationFramework is BalanceHolder { // 2) Prove sufficient bond posted, freeze // 3) Complete operation or Undo freeze - function beginAddArbitratorToAllowList(address arbitrator_to_add) - external returns (bytes32) { + function beginAddArbitratorToAllowList( + address arbitrator_to_add + ) external returns (bytes32) { string memory question = Strings.toHexString(arbitrator_to_add); - bytes32 question_id = realityETH.askQuestionWithMinBond(templateIdAddArbitrator, question, forkArbitrator, REALITY_ETH_TIMEOUT, uint32(block.timestamp), 0, REALITY_ETH_BOND_ARBITRATOR_ADD); - require(propositions[question_id].proposition_type == PropositionType.NONE, "Proposition already exists"); - propositions[question_id] = ArbitratorProposition(PropositionType.ADD_ARBITRATOR, arbitrator_to_add, false); + bytes32 question_id = realityETH.askQuestionWithMinBond( + templateIdAddArbitrator, + question, + forkArbitrator, + REALITY_ETH_TIMEOUT, + uint32(block.timestamp), + 0, + REALITY_ETH_BOND_ARBITRATOR_ADD + ); + require( + propositions[question_id].proposition_type == PropositionType.NONE, + "Proposition already exists" + ); + propositions[question_id] = ArbitratorProposition( + PropositionType.ADD_ARBITRATOR, + arbitrator_to_add, + false + ); return question_id; } - function beginRemoveArbitratorFromAllowList(address arbitrator_to_remove) - external returns (bytes32) { + function beginRemoveArbitratorFromAllowList( + address arbitrator_to_remove + ) external returns (bytes32) { string memory question = Strings.toHexString(arbitrator_to_remove); - bytes32 question_id = realityETH.askQuestionWithMinBond(templateIdRemoveArbitrator, question, forkArbitrator, REALITY_ETH_TIMEOUT, uint32(block.timestamp), 0, REALITY_ETH_BOND_ARBITRATOR_REMOVE); - require(propositions[question_id].proposition_type == PropositionType.NONE, "Proposition already exists"); - propositions[question_id] = ArbitratorProposition(PropositionType.REMOVE_ARBITRATOR, arbitrator_to_remove, false); + bytes32 question_id = realityETH.askQuestionWithMinBond( + templateIdRemoveArbitrator, + question, + forkArbitrator, + REALITY_ETH_TIMEOUT, + uint32(block.timestamp), + 0, + REALITY_ETH_BOND_ARBITRATOR_REMOVE + ); + require( + propositions[question_id].proposition_type == PropositionType.NONE, + "Proposition already exists" + ); + propositions[question_id] = ArbitratorProposition( + PropositionType.REMOVE_ARBITRATOR, + arbitrator_to_remove, + false + ); return question_id; } function executeAddArbitratorToAllowList(bytes32 question_id) external { - - require(propositions[question_id].proposition_type == PropositionType.ADD_ARBITRATOR, "Wrong Proposition type"); + require( + propositions[question_id].proposition_type == + PropositionType.ADD_ARBITRATOR, + "Wrong Proposition type" + ); address arbitrator = propositions[question_id].arbitrator; require(!arbitrators[arbitrator], "Arbitrator already on allowlist"); - require(realityETH.resultFor(question_id) == bytes32(uint256(1)), "Question did not return yes"); - delete(propositions[question_id]); + require( + realityETH.resultFor(question_id) == bytes32(uint256(1)), + "Question did not return yes" + ); + delete (propositions[question_id]); // NB They may still be in a frozen state because of some other proposition arbitrators[arbitrator] = true; } - function executeRemoveArbitratorFromAllowList(bytes32 question_id) external { - - require(propositions[question_id].proposition_type == PropositionType.REMOVE_ARBITRATOR, "Wrong Proposition type"); + function executeRemoveArbitratorFromAllowList( + bytes32 question_id + ) external { + require( + propositions[question_id].proposition_type == + PropositionType.REMOVE_ARBITRATOR, + "Wrong Proposition type" + ); // NB This will run even if the arbitrator has already been removed by another proposition. // This is needed so that the freeze can be cleared if the arbitrator is then reinstated. @@ -367,9 +416,11 @@ contract AdjudicationFramework is BalanceHolder { require(realityEthResult == bytes32(uint256(1)), "Result was not 1"); if (propositions[question_id].isFrozen) { - countArbitratorFreezePropositions[arbitrator] = countArbitratorFreezePropositions[arbitrator] - 1; + countArbitratorFreezePropositions[arbitrator] = + countArbitratorFreezePropositions[arbitrator] - + 1; } - delete(propositions[question_id]); + delete (propositions[question_id]); arbitrators[arbitrator] = false; } @@ -377,16 +428,26 @@ contract AdjudicationFramework is BalanceHolder { // When an arbitrator is listed for removal, they can be frozen given a sufficient bond function freezeArbitrator( bytes32 question_id, - bytes32[] memory history_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers + bytes32[] memory history_hashes, + address[] memory addrs, + uint256[] memory bonds, + bytes32[] memory answers ) public { - require(propositions[question_id].proposition_type == PropositionType.REMOVE_ARBITRATOR, "Wrong Proposition type"); + require( + propositions[question_id].proposition_type == + PropositionType.REMOVE_ARBITRATOR, + "Wrong Proposition type" + ); address arbitrator = propositions[question_id].arbitrator; require( arbitrators[arbitrator], "Arbitrator not allowlisted" // Not allowlisted in the first place ); - require(!propositions[question_id].isFrozen, "Arbitrator already frozen"); + require( + !propositions[question_id].isFrozen, + "Arbitrator already frozen" + ); // Require a bond of at least the specified level // This is only relevant if REALITY_ETH_BOND_ARBITRATOR_FREEZE is higher than REALITY_ETH_BOND_ARBITRATOR_REMOVE @@ -399,30 +460,43 @@ contract AdjudicationFramework is BalanceHolder { answer = realityETH.getBestAnswer(question_id); bond = realityETH.getBond(question_id); } else { - (answer, bond) = realityETH.getEarliestAnswerFromSuppliedHistoryOrRevert(question_id, history_hashes, addrs, bonds, answers); + (answer, bond) = realityETH + .getEarliestAnswerFromSuppliedHistoryOrRevert( + question_id, + history_hashes, + addrs, + bonds, + answers + ); } require(answer == bytes32(uint256(1)), "Supplied answer is not yes"); - require(bond >= REALITY_ETH_BOND_ARBITRATOR_FREEZE, "Bond too low to freeze"); + require( + bond >= REALITY_ETH_BOND_ARBITRATOR_FREEZE, + "Bond too low to freeze" + ); - // TODO: Ideally we would check the bond is for the "remove" answer. + // TODO: Ideally we would check the bond is for the "remove" answer. // #92 propositions[question_id].isFrozen = true; - countArbitratorFreezePropositions[arbitrator] = countArbitratorFreezePropositions[arbitrator] + 1; + countArbitratorFreezePropositions[arbitrator] = + countArbitratorFreezePropositions[arbitrator] + + 1; } function clearFailedProposition(bytes32 question_id) public { address arbitrator = propositions[question_id].arbitrator; require(arbitrator != address(0), "Proposition not found"); if (propositions[question_id].isFrozen) { - countArbitratorFreezePropositions[arbitrator] = countArbitratorFreezePropositions[arbitrator] - 1; + countArbitratorFreezePropositions[arbitrator] = + countArbitratorFreezePropositions[arbitrator] - + 1; } - delete(propositions[question_id]); + delete (propositions[question_id]); } function realitio() external view returns (address) { return address(realityETH); } - } diff --git a/contracts/ChainIdManager.sol b/contracts/ChainIdManager.sol index 03e71c40..698f3c27 100644 --- a/contracts/ChainIdManager.sol +++ b/contracts/ChainIdManager.sol @@ -12,7 +12,9 @@ contract ChainIdManager is Owned { // Fee to use up a Chain ID uint256 public immutable gasBurnAmount = 1000000; - constructor(uint64 _chainIdCounter) Owned() {chainIdCounter = _chainIdCounter;} + constructor(uint64 _chainIdCounter) Owned() { + chainIdCounter = _chainIdCounter; + } /** * @dev Adds a Chain ID to the deny list, this can be done if the chainId is used by another project @@ -37,7 +39,7 @@ contract ChainIdManager is Owned { * @return chainId The next usable Chain ID */ function getNextUsableChainId() public returns (uint64 chainId) { - // The burnGas function introduces a cost to use up chainIds. + // The burnGas function introduces a cost to use up chainIds. // There are uint64(2**63=9.223372e+18) chainIds minus the publicly used chainIds available. // Using all of the chainIds would cost 9.223372e+18 * gasBurnAmount = 9.223372e+24 gas = 6.1489147e+17 blocks = 237226647377 years burnGas(); @@ -51,6 +53,6 @@ contract ChainIdManager is Owned { function burnGas() public view { uint256 counter = 0; uint256 _lowestLimit = gasleft() - gasBurnAmount; - while(gasleft() > _lowestLimit) counter++; + while (gasleft() > _lowestLimit) counter++; } } diff --git a/contracts/ForkingManager.sol b/contracts/ForkingManager.sol index 521c477d..99e319de 100644 --- a/contracts/ForkingManager.sol +++ b/contracts/ForkingManager.sol @@ -64,8 +64,8 @@ contract ForkingManager is IForkingManager, ForkableStructure { } function isForkingInitiated() external view returns (bool) { - return (executionTimeForProposal > 0); - } + return (executionTimeForProposal > 0); + } function isForkingExecuted() external view returns (bool) { return (children[0] != address(0) || children[1] != address(0)); @@ -177,7 +177,9 @@ contract ForkingManager is IForkingManager, ForkableStructure { ); initializePackedParameters.chainID = ChainIdManager(chainIdManager) .getNextUsableChainId(); - initializePackedParameters.forkID = newImplementations.forkID > 0 ? newImplementations.forkID : IPolygonZkEVM(zkEVM).forkID(); + initializePackedParameters.forkID = newImplementations.forkID > 0 + ? newImplementations.forkID + : IPolygonZkEVM(zkEVM).forkID(); IForkableZkEVM(newInstances.zkEVM.two).initialize( newInstances.forkingManager.two, zkEVM, @@ -272,5 +274,4 @@ contract ForkingManager is IForkingManager, ForkableStructure { newInstances.bridge.two ); } - } diff --git a/contracts/L1GlobalChainInfoPublisher.sol b/contracts/L1GlobalChainInfoPublisher.sol index ee48f6fd..e04a9d48 100644 --- a/contracts/L1GlobalChainInfoPublisher.sol +++ b/contracts/L1GlobalChainInfoPublisher.sol @@ -8,17 +8,22 @@ import {IPolygonZkEVMBridge} from "@RealityETH/zkevm-contracts/contracts/interfa import {IPolygonZkEVM} from "@RealityETH/zkevm-contracts/contracts/interfaces/IPolygonZkEVM.sol"; contract L1GlobalChainInfoPublisher { - /// @notice Function to send the data about a fork to a contract on L2. /// @param _bridge The bridge to send the data through /// @param _l2ChainInfo The L2ChainInfo contract on L2 to send the data to /// @param _ancestorForkingManager The ForkingManager to send data about, if referring to a previous fork (unusual) /// @param _maxAncestors The number of forks back to look when looking for the _ancestorForkingManager /// @dev Normally someone would call this right after a fork, _ancestorForkingManager and _maxAncestors should only be used in wierd cases - function updateL2ChainInfo(address _bridge, address _l2ChainInfo, address _ancestorForkingManager, uint256 _maxAncestors) external { - + function updateL2ChainInfo( + address _bridge, + address _l2ChainInfo, + address _ancestorForkingManager, + uint256 _maxAncestors + ) external { // Ask the bridge its forkmanager - IForkingManager forkingManager = IForkingManager(IForkableStructure(_bridge).forkmanager()); + IForkingManager forkingManager = IForkingManager( + IForkableStructure(_bridge).forkmanager() + ); // If we passed an _ancestorForkingManager, crawl up and find that as our ancestor and send data for that over the current bridge. // We will refuse to send data about a forkingManager that isn't an ancestor of the one used by the bridge. @@ -27,8 +32,10 @@ contract L1GlobalChainInfoPublisher { // NB If we keep forking every week forever you will eventually become unable to get the earliest before running out of gas if (_ancestorForkingManager != address(0)) { bool found = false; - for(uint256 i = 0; i < _maxAncestors; i++) { - forkingManager = IForkingManager(forkingManager.parentContract()); + for (uint256 i = 0; i < _maxAncestors; i++) { + forkingManager = IForkingManager( + forkingManager.parentContract() + ); if (address(forkingManager) == address(0)) { break; // No more ancestors } @@ -41,18 +48,21 @@ contract L1GlobalChainInfoPublisher { } // Dispute results will need to come from the parent ForkingManager - IForkingManager parentForkingManager = IForkingManager(forkingManager.parentContract()); + IForkingManager parentForkingManager = IForkingManager( + forkingManager.parentContract() + ); - bool isL1; - address disputeContract; + bool isL1; + address disputeContract; bytes32 disputeContent; - // Fork results: 0 for the genesis, 1 for yes, 2 for no + // Fork results: 0 for the genesis, 1 for yes, 2 for no uint8 forkResult = 0; // Find out whether we are the "yes" fork or the "no" fork if (address(parentForkingManager) != address(0)) { - (address child1, address child2) = parentForkingManager.getChildren(); + (address child1, address child2) = parentForkingManager + .getChildren(); if (child1 == address(forkingManager)) { forkResult = 1; } else if (child2 == address(forkingManager)) { @@ -60,14 +70,23 @@ contract L1GlobalChainInfoPublisher { } else { revert("Unexpected child address"); } - (isL1, disputeContract, disputeContent) = forkingManager.disputeData(); + (isL1, disputeContract, disputeContent) = forkingManager + .disputeData(); } uint256 arbitrationFee = forkingManager.arbitrationFee(); address forkonomicToken = forkingManager.forkonomicToken(); uint64 chainId = IPolygonZkEVM(forkingManager.zkEVM()).chainID(); - bytes memory data = abi.encode(chainId, forkonomicToken, arbitrationFee, isL1, disputeContract, disputeContent, forkResult); + bytes memory data = abi.encode( + chainId, + forkonomicToken, + arbitrationFee, + isL1, + disputeContract, + disputeContent, + forkResult + ); IPolygonZkEVMBridge(_bridge).bridgeMessage( uint32(1), @@ -76,5 +95,4 @@ contract L1GlobalChainInfoPublisher { data ); } - } diff --git a/contracts/L1GlobalForkRequester.sol b/contracts/L1GlobalForkRequester.sol index ed4e333b..5189155e 100644 --- a/contracts/L1GlobalForkRequester.sol +++ b/contracts/L1GlobalForkRequester.sol @@ -23,92 +23,118 @@ import {MoneyBox} from "./mixin/MoneyBox.sol"; import {CalculateMoneyBoxAddress} from "./lib/CalculateMoneyBoxAddress.sol"; contract L1GlobalForkRequester { - struct FailedForkRequest { uint256 amount; uint256 amountMigratedYes; uint256 amountMigratedNo; } - // Token => Beneficiary => ID => FailedForkRequest - mapping(address=>mapping(address=>mapping(bytes32=>FailedForkRequest))) public failedRequests; + // Token => Beneficiary => ID => FailedForkRequest + mapping(address => mapping(address => mapping(bytes32 => FailedForkRequest))) + public failedRequests; // Anybody can say, "Hey, you got a payment for this fork to happen" // Normally this would only happen if the L2 contract send a payment but in theory someone else could fund it directly on L1. - function handlePayment(address token, address beneficiary, bytes32 requestId) external { - - // Normally the "beneficiary" would be the sender on L2. + function handlePayment( + address token, + address beneficiary, + bytes32 requestId + ) external { + // Normally the "beneficiary" would be the sender on L2. // But if some other kind person sent funds to this address we would still send it back to the beneficiary. bytes32 salt = keccak256(abi.encodePacked(beneficiary, requestId)); // Check the MoneyBox has funds - address moneyBox = CalculateMoneyBoxAddress._calculateMoneyBoxAddress(address(this), salt, token); + address moneyBox = CalculateMoneyBoxAddress._calculateMoneyBoxAddress( + address(this), + salt, + token + ); - // If for some reason we've already got part of a payment, include it. - uint256 initialBalance = failedRequests[token][beneficiary][requestId].amount; + // If for some reason we've already got part of a payment, include it. + uint256 initialBalance = failedRequests[token][beneficiary][requestId] + .amount; - uint256 transferredBalance = initialBalance + IForkonomicToken(token).balanceOf(moneyBox); + uint256 transferredBalance = initialBalance + + IForkonomicToken(token).balanceOf(moneyBox); if (moneyBox.code.length == 0) { new MoneyBox{salt: salt}(token); } - require(IForkonomicToken(token).transferFrom(moneyBox, address(this), transferredBalance), "Preparing payment failed"); + require( + IForkonomicToken(token).transferFrom( + moneyBox, + address(this), + transferredBalance + ), + "Preparing payment failed" + ); // If the token is already being or has already been forked, record the request as failed. // Somebody can split the token after the fork, then send the failure message and the funds back on both the child forks. // TODO: Replace this with an isForked() method in ForkingStructure.sol? - IForkingManager forkingManager = IForkingManager(IForkonomicToken(token).forkmanager()); + IForkingManager forkingManager = IForkingManager( + IForkonomicToken(token).forkmanager() + ); bool isForkGuaranteedNotToRevert = true; if (transferredBalance < forkingManager.arbitrationFee()) { isForkGuaranteedNotToRevert = false; - } + } if (!forkingManager.canFork()) { isForkGuaranteedNotToRevert = false; } if (isForkGuaranteedNotToRevert) { - if (initialBalance > 0) { - delete(failedRequests[token][beneficiary][requestId]); + delete (failedRequests[token][beneficiary][requestId]); } - IForkonomicToken(token).approve(address(forkingManager), transferredBalance); + IForkonomicToken(token).approve( + address(forkingManager), + transferredBalance + ); // Assume the data contains the questionId and pass it directly to the forkmanager in the fork request IForkingManager.NewImplementations memory newImplementations; - IForkingManager.DisputeData memory disputeData = IForkingManager.DisputeData(false, beneficiary, requestId); + IForkingManager.DisputeData memory disputeData = IForkingManager + .DisputeData(false, beneficiary, requestId); forkingManager.initiateFork(disputeData, newImplementations); - } else { - // Store the request so we can return the tokens across the bridge // If the fork has already happened we may have to split them first and do this twice. - failedRequests[token][beneficiary][requestId].amount = transferredBalance; - + failedRequests[token][beneficiary][requestId] + .amount = transferredBalance; } - } // If something was queued after a fork had happened, we need to be able to return then to both bridges - function splitTokensIntoChildTokens(address token, address requester, bytes32 requestId) external { - //function splitTokensIntoChildTokens(address token, address requester, bytes32 requestId, bool doYesToken, bool doNoToken) external {} + function splitTokensIntoChildTokens( + address token, + address requester, + bytes32 requestId + ) external { + //function splitTokensIntoChildTokens(address token, address requester, bytes32 requestId, bool doYesToken, bool doNoToken) external {} // TODO: We need to update ForkonomicToken to handle each side separately in case one bridge reverts maliciously. // Then handle only one side being requested, or only one side being left uint256 amount = failedRequests[token][requester][requestId].amount; // You need to call registerPayment before you call this. - require(amount > 0, "Nothing to split"); + require(amount > 0, "Nothing to split"); - (address yesToken, address noToken) = IForkonomicToken(token).getChildren(); - require(yesToken != address(0) && noToken != address(0), "Token not forked"); + (address yesToken, address noToken) = IForkonomicToken(token) + .getChildren(); + require( + yesToken != address(0) && noToken != address(0), + "Token not forked" + ); // Current version only has a single function so we have to migrate both sides IForkonomicToken(token).splitTokensIntoChildTokens(amount); failedRequests[yesToken][requester][requestId].amount += amount; failedRequests[noToken][requester][requestId].amount += amount; - delete(failedRequests[token][requester][requestId]); + delete (failedRequests[token][requester][requestId]); /* // Probably need something like: @@ -137,14 +163,25 @@ contract L1GlobalForkRequester { */ } - function returnTokens(address token, address requester, bytes32 requestId) external { - - IForkingManager forkingManager = IForkingManager(IForkonomicToken(token).forkmanager()); + function returnTokens( + address token, + address requester, + bytes32 requestId + ) external { + IForkingManager forkingManager = IForkingManager( + IForkonomicToken(token).forkmanager() + ); ForkableBridge bridge = ForkableBridge(forkingManager.bridge()); // Check the relations in the other direction to make sure we don't lie to the bridge somehow - require(address(bridge.forkmanager()) == address(forkingManager), "Bridge/manager mismatch, WTF"); - require(address(forkingManager.forkonomicToken()) == token, "Token/manager mismatch, WTF"); + require( + address(bridge.forkmanager()) == address(forkingManager), + "Bridge/manager mismatch, WTF" + ); + require( + address(forkingManager.forkonomicToken()) == token, + "Token/manager mismatch, WTF" + ); uint256 amount = failedRequests[token][requester][requestId].amount; @@ -163,15 +200,8 @@ contract L1GlobalForkRequester { // TODO: It might be useful to send information about the failure eg fork timestamp bytes memory data = bytes.concat(requestId); - bridge.bridgeMessage( - uint32(1), - requester, - true, - data - ); - - delete(failedRequests[token][requester][requestId]); + bridge.bridgeMessage(uint32(1), requester, true, data); + delete (failedRequests[token][requester][requestId]); } - } diff --git a/contracts/L2ChainInfo.sol b/contracts/L2ChainInfo.sol index b14cde48..d08c1b9f 100644 --- a/contracts/L2ChainInfo.sol +++ b/contracts/L2ChainInfo.sol @@ -13,14 +13,13 @@ Queries against it will revert until the update is done. import {IBridgeMessageReceiver} from "@RealityETH/zkevm-contracts/contracts/interfaces/IBridgeMessageReceiver.sol"; -contract L2ChainInfo is IBridgeMessageReceiver{ - +contract L2ChainInfo is IBridgeMessageReceiver { // These are the same for all forks address public l2Bridge; address public l1GlobalChainInfoPublisher; uint32 public constant L1_NETWORK_ID = 0; - struct ChainInfo{ + struct ChainInfo { address forkonomicToken; uint256 forkFee; } @@ -28,47 +27,71 @@ contract L2ChainInfo is IBridgeMessageReceiver{ // Questions are stored by isL2->forker->id // The forker is assumed to have created a unique id within itself for the dispute it's forking over - mapping(bool=>mapping(address=>mapping(bytes32=>bytes32))) public forkQuestionResults; - mapping(bool=>mapping(address=>mapping(bytes32=>uint64))) public questionToChainID; + mapping(bool => mapping(address => mapping(bytes32 => bytes32))) + public forkQuestionResults; + mapping(bool => mapping(address => mapping(bytes32 => uint64))) + public questionToChainID; constructor(address _l2Bridge, address _l1GlobalChainInfoPublisher) { - l2Bridge = _l2Bridge; + l2Bridge = _l2Bridge; l1GlobalChainInfoPublisher = _l1GlobalChainInfoPublisher; } - modifier isUpToDate { - require(chainInfo[uint64(block.chainid)].forkonomicToken != address(0), "Current chain must be known"); + modifier isUpToDate() { + require( + chainInfo[uint64(block.chainid)].forkonomicToken != address(0), + "Current chain must be known" + ); _; } - function getForkonomicToken() external view isUpToDate returns(address) { - return chainInfo[uint64(block.chainid)].forkonomicToken; + function getForkonomicToken() external view isUpToDate returns (address) { + return chainInfo[uint64(block.chainid)].forkonomicToken; } - function getForkFee() external view isUpToDate returns(uint256) { - return chainInfo[uint64(block.chainid)].forkFee; + function getForkFee() external view isUpToDate returns (uint256) { + return chainInfo[uint64(block.chainid)].forkFee; } - function getForkQuestionResult(bool isL1, address forker, bytes32 questionId) external view isUpToDate returns(bytes32) { - return forkQuestionResults[isL1][forker][questionId]; + function getForkQuestionResult( + bool isL1, + address forker, + bytes32 questionId + ) external view isUpToDate returns (bytes32) { + return forkQuestionResults[isL1][forker][questionId]; } // Get a message via the bridge from a contract we trust on L1 reporting to us the details of a fork. // It should normally be used right after a fork to send us the current chain. // It could also send us information about a previous chain that's a parent of ours if we forked again before getting it for some reason. - function onMessageReceived(address _originAddress, uint32 _originNetwork, bytes memory _data) external payable { - + function onMessageReceived( + address _originAddress, + uint32 _originNetwork, + bytes memory _data + ) external payable { require(msg.sender == l2Bridge, "not the expected bridge"); - require(_originAddress == l1GlobalChainInfoPublisher, "only publisher can call us"); + require( + _originAddress == l1GlobalChainInfoPublisher, + "only publisher can call us" + ); require(_originNetwork == L1_NETWORK_ID, "Bad origin network"); - (uint64 chainId, address forkonomicToken, uint256 forkFee, bool isL1, address forker, bytes32 questionId, bytes32 result) = abi.decode(_data, (uint64, address, uint256, bool, address, bytes32, bytes32)); + ( + uint64 chainId, + address forkonomicToken, + uint256 forkFee, + bool isL1, + address forker, + bytes32 questionId, + bytes32 result + ) = abi.decode( + _data, + (uint64, address, uint256, bool, address, bytes32, bytes32) + ); chainInfo[chainId] = ChainInfo(forkonomicToken, forkFee); questionToChainID[isL1][forker][questionId] = chainId; forkQuestionResults[isL1][forker][questionId] = result; - } - } diff --git a/contracts/L2ForkArbitrator.sol b/contracts/L2ForkArbitrator.sol index ec3afbc7..a1dbf8ad 100644 --- a/contracts/L2ForkArbitrator.sol +++ b/contracts/L2ForkArbitrator.sol @@ -22,23 +22,22 @@ If there is already a dispute in progress (ie another fork has been requested bu // NB This doesn't implement IArbitrator because that requires slightly more functions than we need // TODO: Would be good to make a stripped-down IArbitrator that only has the essential functions contract L2ForkArbitrator is IBridgeMessageReceiver { - bool public isForkInProgress; IRealityETH public realitio; enum RequestStatus { NONE, - QUEUED, // We got our payment and put the reality.eth process on hold, but haven't requested initialization yet + QUEUED, // We got our payment and put the reality.eth process on hold, but haven't requested initialization yet FORK_REQUESTED, // Fork request set to L1, result unknown so far FORK_REQUEST_FAILED // Fork request failed, eg another process was forking } struct ArbitrationRequest { - RequestStatus status; - address payable payer; + RequestStatus status; + address payable payer; uint256 paid; - bytes32 result; - } + bytes32 result; + } event LogRequestArbitration( bytes32 indexed question_id, @@ -55,9 +54,14 @@ contract L2ForkArbitrator is IBridgeMessageReceiver { uint256 public disputeFee; // Normally dispute fee should generally only go down in a fork - constructor(IRealityETH _realitio, L2ChainInfo _chainInfo, L1GlobalForkRequester _l1GlobalForkRequester, uint256 _initialDisputeFee) { + constructor( + IRealityETH _realitio, + L2ChainInfo _chainInfo, + L1GlobalForkRequester _l1GlobalForkRequester, + uint256 _initialDisputeFee + ) { realitio = _realitio; - chainInfo = _chainInfo; + chainInfo = _chainInfo; l1GlobalForkRequester = _l1GlobalForkRequester; disputeFee = _initialDisputeFee; } @@ -78,41 +82,47 @@ contract L2ForkArbitrator is IBridgeMessageReceiver { uint256 max_previous ) external payable returns (bool) { uint256 arbitration_fee = getDisputeFee(question_id); + require(arbitration_fee > 0, "fee must be positive"); + require( - arbitration_fee > 0, - "fee must be positive" + arbitrationRequests[question_id].status == RequestStatus.NONE, + "Already requested" ); - require(arbitrationRequests[question_id].status == RequestStatus.NONE, "Already requested"); - - arbitrationRequests[question_id] = ArbitrationRequest(RequestStatus.QUEUED, payable(msg.sender), msg.value, bytes32(0)); + arbitrationRequests[question_id] = ArbitrationRequest( + RequestStatus.QUEUED, + payable(msg.sender), + msg.value, + bytes32(0) + ); - realitio.notifyOfArbitrationRequest( - question_id, - msg.sender, - max_previous - ); - emit LogRequestArbitration(question_id, msg.value, msg.sender, 0); + realitio.notifyOfArbitrationRequest( + question_id, + msg.sender, + max_previous + ); + emit LogRequestArbitration(question_id, msg.value, msg.sender, 0); if (!isForkInProgress) { - requestActivateFork(question_id); + requestActivateFork(question_id); } - return true; + return true; } /// @notice Request a fork via the bridge /// @dev Talks to the L1 ForkingManager asynchronously, and may fail. /// @param question_id The question in question - function requestActivateFork( - bytes32 question_id - ) public { - + function requestActivateFork(bytes32 question_id) public { require(!isForkInProgress, "Already forking"); // Forking over something else RequestStatus status = arbitrationRequests[question_id].status; - require(status == RequestStatus.QUEUED || status == RequestStatus.FORK_REQUEST_FAILED, "not awaiting activation"); - arbitrationRequests[question_id].status = RequestStatus.FORK_REQUESTED; + require( + status == RequestStatus.QUEUED || + status == RequestStatus.FORK_REQUEST_FAILED, + "not awaiting activation" + ); + arbitrationRequests[question_id].status = RequestStatus.FORK_REQUESTED; uint256 forkFee = chainInfo.getForkFee(); uint256 paid = arbitrationRequests[question_id].paid; @@ -130,7 +140,11 @@ contract L2ForkArbitrator is IBridgeMessageReceiver { // The L1GlobalForkRequester will deploy this as and when it's needed. // TODO: For now we assume only 1 request is in-flight at a time. If there might be more, differentiate them in the salt. bytes32 salt = keccak256(abi.encodePacked(address(this), question_id)); - address moneyBox = CalculateMoneyBoxAddress._calculateMoneyBoxAddress(address(l1GlobalForkRequester), salt, address(forkonomicToken)); + address moneyBox = CalculateMoneyBoxAddress._calculateMoneyBoxAddress( + address(l1GlobalForkRequester), + salt, + address(forkonomicToken) + ); bytes memory permitData; bridge.bridgeAsset{value: forkFee}( @@ -146,24 +160,33 @@ contract L2ForkArbitrator is IBridgeMessageReceiver { } // If the fork request fails, we will get a message back through the bridge telling us about it - // We will set FORK_REQUEST_FAILED which will allow anyone to request cancellation - function onMessageReceived(address _originAddress, uint32 _originNetwork, bytes memory _data) external payable { - + // We will set FORK_REQUEST_FAILED which will allow anyone to request cancellation + function onMessageReceived( + address _originAddress, + uint32 _originNetwork, + bytes memory _data + ) external payable { address l2Bridge = chainInfo.l2Bridge(); require(msg.sender == l2Bridge, "Not our bridge"); require(_originNetwork == uint32(0), "Wrong network, WTF"); - require(_originAddress == address(l1GlobalForkRequester), "Unexpected sender"); + require( + _originAddress == address(l1GlobalForkRequester), + "Unexpected sender" + ); bytes32 question_id = bytes32(_data); RequestStatus status = arbitrationRequests[question_id].status; - require(status == RequestStatus.FORK_REQUESTED, "not in fork-requested state"); + require( + status == RequestStatus.FORK_REQUESTED, + "not in fork-requested state" + ); require(isForkInProgress, "No fork in progress to clear"); isForkInProgress = false; - arbitrationRequests[question_id].status = RequestStatus.FORK_REQUEST_FAILED; + arbitrationRequests[question_id].status = RequestStatus + .FORK_REQUEST_FAILED; // We don't check the funds are back here, just assume L1GlobalForkRequester send them and they can be recovered. - } /// @notice Submit the arbitrator's answer to a question, assigning the winner automatically. @@ -177,15 +200,24 @@ contract L2ForkArbitrator is IBridgeMessageReceiver { bytes32 last_answer_or_commitment_id, address last_answerer ) external { - // Read from directory what the result was RequestStatus status = arbitrationRequests[question_id].status; - require(status == RequestStatus.FORK_REQUESTED, "not in fork-requested state"); + require( + status == RequestStatus.FORK_REQUESTED, + "not in fork-requested state" + ); - require(chainInfo.questionToChainID(false, address(this), question_id) > 0, "Dispute not found in ChainInfo"); + require( + chainInfo.questionToChainID(false, address(this), question_id) > 0, + "Dispute not found in ChainInfo" + ); // We get the fork result from the L2ChainInfo contract - bytes32 answer = chainInfo.forkQuestionResults(false, address(this), question_id); + bytes32 answer = chainInfo.forkQuestionResults( + false, + address(this), + question_id + ); realitio.assignWinnerAndSubmitAnswerByArbitrator( question_id, @@ -196,9 +228,8 @@ contract L2ForkArbitrator is IBridgeMessageReceiver { last_answerer ); - isForkInProgress = false; - delete(arbitrationRequests[question_id]); - + isForkInProgress = false; + delete (arbitrationRequests[question_id]); } /// @notice Cancel a previous arbitration request @@ -207,19 +238,22 @@ contract L2ForkArbitrator is IBridgeMessageReceiver { /// @dev NB This may revert if the contract has returned funds in the bridge but claimAsset hasn't been called yet /// @param question_id The question in question function cancelArbitration(bytes32 question_id) external { - // For simplicity we won't let you cancel until forking is sorted, as you might retry and keep failing for the same reason require(!isForkInProgress, "Fork in progress"); RequestStatus status = arbitrationRequests[question_id].status; - require(status == RequestStatus.FORK_REQUEST_FAILED, "Not in fork-failed state"); + require( + status == RequestStatus.FORK_REQUEST_FAILED, + "Not in fork-failed state" + ); address payable payer = arbitrationRequests[question_id].payer; realitio.cancelArbitration(question_id); - refundsDue[payer] = refundsDue[payer] + arbitrationRequests[question_id].paid; - delete(arbitrationRequests[question_id]); - + refundsDue[payer] = + refundsDue[payer] + + arbitrationRequests[question_id].paid; + delete (arbitrationRequests[question_id]); } function claimRefund() external { @@ -227,5 +261,4 @@ contract L2ForkArbitrator is IBridgeMessageReceiver { refundsDue[msg.sender] = refundsDue[msg.sender] - due; payable(msg.sender).transfer(due); } - } diff --git a/contracts/interfaces/IForkingManager.sol b/contracts/interfaces/IForkingManager.sol index b3889904..6eebcef9 100644 --- a/contracts/interfaces/IForkingManager.sol +++ b/contracts/interfaces/IForkingManager.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.20; import {IForkableStructure} from "./IForkableStructure.sol"; interface IForkingManager is IForkableStructure { - // Dispute contract and call to identify the dispute // that will be used to initiate/justify the fork struct DisputeData { @@ -25,15 +24,25 @@ interface IForkingManager is IForkableStructure { } function zkEVM() external returns (address); + function bridge() external returns (address); + function forkonomicToken() external returns (address); + function globalExitRoot() external returns (address); + function arbitrationFee() external returns (uint256); - function disputeData() external returns (bool isL1, address disputeContract, bytes32 disputeContent); + + function disputeData() + external + returns (bool isL1, address disputeContract, bytes32 disputeContent); + function executionTimeForProposal() external returns (uint256); function isForkingInitiated() external returns (bool); + function isForkingExecuted() external returns (bool); + function canFork() external returns (bool); // Struct that holds an address pair used to store the new child contracts @@ -65,5 +74,4 @@ interface IForkingManager is IForkableStructure { DisputeData memory _disputeData, NewImplementations calldata newImplementations ) external; - } diff --git a/contracts/interfaces/IRealityETH.sol b/contracts/interfaces/IRealityETH.sol index c83ad848..16084a8a 100644 --- a/contracts/interfaces/IRealityETH.sol +++ b/contracts/interfaces/IRealityETH.sol @@ -197,7 +197,11 @@ interface IRealityETH is IBalanceHolder { ) external view returns (bytes32); function getEarliestAnswerFromSuppliedHistoryOrRevert( - bytes32 question_id, bytes32[] memory history_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers + bytes32 question_id, + bytes32[] memory history_hashes, + address[] memory addrs, + uint256[] memory bonds, + bytes32[] memory answers ) external view returns (bytes32, uint256); function getFinalAnswer( diff --git a/contracts/lib/CalculateMoneyBoxAddress.sol b/contracts/lib/CalculateMoneyBoxAddress.sol index 0dda935e..265a7df7 100644 --- a/contracts/lib/CalculateMoneyBoxAddress.sol +++ b/contracts/lib/CalculateMoneyBoxAddress.sol @@ -6,18 +6,21 @@ import {MoneyBox} from "../mixin/MoneyBox.sol"; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; library CalculateMoneyBoxAddress { - - function _calculateMoneyBoxAddress(address _creator, bytes32 _salt, address _token) internal pure returns (address) { - - return Create2.computeAddress( - _salt, - keccak256(abi.encodePacked( - type(MoneyBox).creationCode, - abi.encode(_token) - )), - _creator - ); - + function _calculateMoneyBoxAddress( + address _creator, + bytes32 _salt, + address _token + ) internal pure returns (address) { + return + Create2.computeAddress( + _salt, + keccak256( + abi.encodePacked( + type(MoneyBox).creationCode, + abi.encode(_token) + ) + ), + _creator + ); } - } diff --git a/contracts/lib/CreateChildren.sol b/contracts/lib/CreateChildren.sol index 516b870a..472e9f53 100644 --- a/contracts/lib/CreateChildren.sol +++ b/contracts/lib/CreateChildren.sol @@ -50,7 +50,7 @@ library CreateChildren { ) ); if (address(implementation) == address(0)) { - implementation = _getImplementation(); + implementation = _getImplementation(); } // Fork 2 can introduce a new implementation, if a different implementation contract // is passed in diff --git a/contracts/lib/reality-eth/RealityETH-3.0.sol b/contracts/lib/reality-eth/RealityETH-3.0.sol index e9778f74..75ee4a39 100644 --- a/contracts/lib/reality-eth/RealityETH-3.0.sol +++ b/contracts/lib/reality-eth/RealityETH-3.0.sol @@ -2,12 +2,11 @@ pragma solidity ^0.8.20; -import './../../interfaces/IRealityETH.sol'; +import "./../../interfaces/IRealityETH.sol"; -import './BalanceHolder.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 @@ -27,7 +26,8 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { // 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; + bytes32 constant UNRESOLVED_ANSWER = + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe; struct Question { bytes32 content_hash; @@ -43,7 +43,7 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { uint256 min_bond; } - // Stored in a mapping indexed by commitment_id, a hash of commitment hash, question, bond. + // Stored in a mapping indexed by commitment_id, a hash of commitment hash, question, bond. struct Commitment { uint32 reveal_ts; bool is_revealed; @@ -64,13 +64,15 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { mapping(bytes32 => Question) public questions; mapping(bytes32 => Claim) public question_claims; mapping(bytes32 => Commitment) public commitments; - mapping(address => uint256) public arbitrator_question_fees; + 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"); + require( + msg.sender == questions[question_id].arbitrator, + "msg.sender must be arbitrator" + ); _; } @@ -85,25 +87,43 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { 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"); + 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"); + 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"); + 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"); + 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"); + 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"); + require( + opening_ts == 0 || opening_ts <= uint32(block.timestamp), + "opening date must have passed" + ); _; } @@ -113,19 +133,31 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { } modifier bondMustDoubleAndMatchMinimum(bytes32 question_id) { - require(msg.value > 0, "bond must be positive"); + 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"); + 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"); + require( + msg.value >= (current_bond * 2), + "bond must be double at least previous bond" + ); } _; } - modifier previousBondMustNotBeatMaxPrevious(bytes32 question_id, uint256 max_previous) { + modifier previousBondMustNotBeatMaxPrevious( + bytes32 question_id, + uint256 max_previous + ) { if (max_previous > 0) { - require(questions[question_id].bond <= max_previous, "bond must exceed max_previous"); + require( + questions[question_id].bond <= max_previous, + "bond must exceed max_previous" + ); } _; } @@ -133,19 +165,27 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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"}'); + 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. + /// @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) - stateAny() - external { + function setQuestionFee(uint256 fee) external stateAny { arbitrator_question_fees[msg.sender] = fee; emit LogSetQuestionFee(msg.sender, fee); } @@ -155,9 +195,9 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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) - stateAny() - public returns (uint256) { + function createTemplate( + string memory content + ) public stateAny returns (uint256) { uint256 id = nextTemplateID; templates[id] = block.number; template_hashes[id] = keccak256(abi.encodePacked(content)); @@ -176,13 +216,30 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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 - ) - // stateNotCreated is enforced by the internal _askQuestion - public payable returns (bytes32) { + 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); + return + askQuestion( + template_id, + question, + arbitrator, + timeout, + opening_ts, + nonce + ); } /// @notice Ask a new question and return the ID @@ -194,18 +251,59 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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) - // stateNotCreated is enforced by the internal _askQuestion - public payable returns (bytes32) { - + 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)); + 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); + 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; } @@ -220,41 +318,90 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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) - // stateNotCreated is enforced by the internal _askQuestion - public payable returns (bytes32) { - + 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)); + 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); + 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) - stateNotCreated(question_id) - internal { - + 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"); + 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. + // 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"); + require( + bounty >= question_fee, + "ETH provided must cover question fee" + ); bounty = bounty - question_fee; balanceOf[arbitrator] = balanceOf[arbitrator] + question_fee; } @@ -273,17 +420,23 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { 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) - stateOpen(question_id) - external payable { - questions[question_id].bounty = questions[question_id].bounty + msg.value; - emit LogFundAnswerBounty(question_id, msg.value, questions[question_id].bounty, msg.sender); + 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. @@ -292,11 +445,17 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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) + function submitAnswer( + bytes32 question_id, + bytes32 answer, + uint256 max_previous + ) + external + payable stateOpen(question_id) bondMustDoubleAndMatchMinimum(question_id) previousBondMustNotBeatMaxPrevious(question_id, max_previous) - external payable { + { _addAnswerToHistory(question_id, answer, msg.sender, msg.value, false); _updateCurrentAnswer(question_id, answer); } @@ -308,11 +467,18 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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) + 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) - external payable { + { require(answerer != NULL_ADDRESS, "answerer must be non-zero"); _addAnswerToHistory(question_id, answer, answerer, msg.value, false); _updateCurrentAnswer(question_id, answer); @@ -321,13 +487,20 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { // @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; + 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. @@ -339,38 +512,61 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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) + 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) - external payable { - - bytes32 commitment_id = keccak256(abi.encodePacked(question_id, answer_hash, msg.value)); + { + 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); - + _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; + /// 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) - stateOpenOrPendingArbitration(question_id) - external { - + 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"); + 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; @@ -379,14 +575,32 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { _updateCurrentAnswer(question_id, answer); } - emit LogAnswerReveal(question_id, msg.sender, answer_hash, answer, nonce, bond); - + 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)); + 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) { @@ -394,18 +608,32 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { } 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); + 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 { + 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; + 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 { + function _updateCurrentAnswerByArbitrator( + bytes32 question_id, + bytes32 answer + ) internal { questions[question_id].best_answer = answer; questions[question_id].finalize_ts = uint32(block.timestamp); } @@ -415,12 +643,20 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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) + function notifyOfArbitrationRequest( + bytes32 question_id, + address requester, + uint256 max_previous + ) + external onlyArbitrator(question_id) stateOpen(question_id) previousBondMustNotBeatMaxPrevious(question_id, max_previous) - external { - require(questions[question_id].finalize_ts > UNANSWERED, "Question must already have an answer when arbitration is requested"); + { + 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); } @@ -428,12 +664,17 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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) + function cancelArbitration( + bytes32 question_id + ) + external onlyArbitrator(question_id) statePendingArbitration(question_id) - external { + { questions[question_id].is_pending_arbitration = false; - questions[question_id].finalize_ts = uint32(block.timestamp) + questions[question_id].timeout; + questions[question_id].finalize_ts = + uint32(block.timestamp) + + questions[question_id].timeout; emit LogCancelArbitration(question_id); } @@ -445,18 +686,17 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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) - onlyArbitrator(question_id) - statePendingArbitration(question_id) - public { - + 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. @@ -467,69 +707,96 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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); + 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"); + 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; + 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) - view public returns (bool) { + 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)) ); + 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) - stateFinalized(question_id) - external view returns (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) - stateFinalized(question_id) - public view returns (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) { + /// @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) { + /// @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"); + 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"); + require( + result != UNRESOLVED_ANSWER, + "Question replacement was settled too soon and has not been reopened" + ); } return result; } @@ -545,45 +812,96 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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) - // stateNotCreated is enforced by the internal _askQuestion - public payable returns (bytes32) { - - require(isSettledTooSoon(reopens_question_id), "You can only reopen questions that resolved as settled too soon"); + 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)); + 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"); + 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"); + 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]; + 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"); + 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); + 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[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); @@ -600,15 +918,28 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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 - ) - stateFinalized(question_id) - external view 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"); + 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; } @@ -627,18 +958,21 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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 - ) - stateFinalized(question_id) - public { - - require(history_hashes.length > 0, "at least one history hash entry must be provided"); + 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; + 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 @@ -648,15 +982,27 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { 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; + 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); - + 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]; @@ -667,9 +1013,8 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { } 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. @@ -691,11 +1036,13 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { } questions[question_id].history_hash = last_history_hash; - } - function _payPayee(bytes32 question_id, address payee, uint256 value) - internal { + function _payPayee( + bytes32 question_id, + address payee, + uint256 value + ) internal { balanceOf[payee] = balanceOf[payee] + value; emit LogClaim(question_id, payee, value); } @@ -710,25 +1057,35 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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 - ) - stateAny() - external view returns (bytes32, uint256) { + 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]); + 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; + 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"); + 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; @@ -738,25 +1095,36 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { 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)) ) { + 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)) ) { + 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) { - + 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) { @@ -772,42 +1140,48 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { } 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); + 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. + // 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; + 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); + _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. @@ -818,14 +1192,18 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { /// @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 + /// @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 - ) - stateAny() // The finalization checks are done in the claimWinnings function - public { - + 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++) { @@ -849,82 +1227,72 @@ contract RealityETH_v3_0 is BalanceHolder, IRealityETH { } /// @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) { + /// @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) { + /// @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) { + /// @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) { + /// @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) { + /// @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) { + /// @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) { + /// @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) { + /// @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 + /// @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) { + 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) { + /// @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) { + function getMinBond(bytes32 question_id) public view returns (uint256) { return questions[question_id].min_bond; } - } diff --git a/contracts/mixin/MoneyBox.sol b/contracts/mixin/MoneyBox.sol index 0048efd3..032b4a3e 100644 --- a/contracts/mixin/MoneyBox.sol +++ b/contracts/mixin/MoneyBox.sol @@ -9,9 +9,7 @@ pragma solidity ^0.8.20; import {IERC20} from "./../interfaces/IERC20.sol"; contract MoneyBox { - constructor(address _token) { - IERC20(_token).approve(msg.sender, type(uint256).max); + IERC20(_token).approve(msg.sender, type(uint256).max); } - } diff --git a/lib/zkevm-contracts b/lib/zkevm-contracts index 5d70866e..4c90c6a6 160000 --- a/lib/zkevm-contracts +++ b/lib/zkevm-contracts @@ -1 +1 @@ -Subproject commit 5d70866e1e4811df16d4c5fefb95100c962cc009 +Subproject commit 4c90c6a6ed347a049a2a50f0e94caba9471747e6 diff --git a/test/AdjudicationIntegration.t.sol b/test/AdjudicationIntegration.t.sol index 4401c652..f9b96e3f 100644 --- a/test/AdjudicationIntegration.t.sol +++ b/test/AdjudicationIntegration.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; /* solhint-disable reentrancy */ /* solhint-disable quotes */ -import { Vm } from 'forge-std/Vm.sol'; +import {Vm} from "forge-std/Vm.sol"; import {Test} from "forge-std/Test.sol"; import {Arbitrator} from "../contracts/lib/reality-eth/Arbitrator.sol"; @@ -26,10 +26,10 @@ import {L2ChainInfo} from "../contracts/L2ChainInfo.sol"; import {MockPolygonZkEVMBridge} from "./testcontract/MockPolygonZkEVMBridge.sol"; contract AdjudicationIntegrationTest is Test { - Arbitrator public govArb; - IERC20 internal tokenMock = IERC20(0x1234567890123456789012345678901234567890); + IERC20 internal tokenMock = + IERC20(0x1234567890123456789012345678901234567890); ForkableRealityETH_ERC20 internal l1RealityEth; RealityETH_v3_0 internal l2RealityEth; @@ -63,7 +63,7 @@ contract AdjudicationIntegrationTest is Test { address payable internal user2 = payable(address(0xbabe10)); // We'll use a different address to deploy AdjudicationFramework because we want to logs with its address in - address payable internal adjudictionDeployer = payable(address(0xbabe11)); + address payable internal adjudictionDeployer = payable(address(0xbabe11)); string internal constant QUESTION_DELIM = "\u241f"; @@ -75,12 +75,13 @@ contract AdjudicationIntegrationTest is Test { TODO: Consider whether we should gate the realityeth instance to approved AdjudicationFramework contracts (via bridge) and an upgrade manager contract. */ - uint32 constant internal REALITY_ETH_TIMEOUT = 86400; + uint32 internal constant REALITY_ETH_TIMEOUT = 86400; // Dummy addresses for things we message on l1 // The following should be the same on all forks MockPolygonZkEVMBridge internal l2Bridge; - address internal l1GlobalForkRequester = address(new L1GlobalForkRequester()); + address internal l1GlobalForkRequester = + address(new L1GlobalForkRequester()); address internal l1GlobalChainInfoPublisher = address(0xbabe12); // The following will change when we fork so we fake multiple versions here @@ -98,20 +99,36 @@ contract AdjudicationIntegrationTest is Test { uint256 internal forkingFee = 5000; // Should ultimately come from l1 forkingmanager function setUp() public { - l2Bridge = new MockPolygonZkEVMBridge(); // For now the values of the l1 contracts are all made up // Ultimately our tests should include a deployment on l1 - l2ChainInfo = new L2ChainInfo(address(l2Bridge), l1GlobalChainInfoPublisher); + l2ChainInfo = new L2ChainInfo( + address(l2Bridge), + l1GlobalChainInfoPublisher + ); // Pretend to send the initial setup to the l2 directory via the bridge // Triggers: // l2ChainInfo.onMessageReceived(l1GlobalChainInfoPublisher, l1ChainId, fakeMessageData); // In reality this would originate on L1. vm.chainId(l2ChainIdInit); - bytes memory fakeMessageData = abi.encode(l2ChainIdInit, address(l1ForkingManager), uint256(forkingFee), false, address(l2ForkArbitrator), bytes32(0x0), bytes32(0x0)); - l2Bridge.fakeClaimMessage(address(l1GlobalChainInfoPublisher), uint32(0), address(l2ChainInfo), fakeMessageData, uint256(0)); + bytes memory fakeMessageData = abi.encode( + l2ChainIdInit, + address(l1ForkingManager), + uint256(forkingFee), + false, + address(l2ForkArbitrator), + bytes32(0x0), + bytes32(0x0) + ); + l2Bridge.fakeClaimMessage( + address(l1GlobalChainInfoPublisher), + uint32(0), + address(l2ChainInfo), + fakeMessageData, + uint256(0) + ); l1RealityEth = new ForkableRealityETH_ERC20(); l1RealityEth.init(tokenMock, address(0), bytes32(0)); @@ -135,7 +152,12 @@ contract AdjudicationIntegrationTest is Test { // NB we're modelling this on the same chain but it should really be the l2 l2RealityEth = new RealityETH_v3_0(); - l2ForkArbitrator = new L2ForkArbitrator(IRealityETH(l2RealityEth), L2ChainInfo(l2ChainInfo), L1GlobalForkRequester(l1GlobalForkRequester), forkingFee); + l2ForkArbitrator = new L2ForkArbitrator( + IRealityETH(l2RealityEth), + L2ChainInfo(l2ChainInfo), + L1GlobalForkRequester(l1GlobalForkRequester), + forkingFee + ); // The adjudication framework can act like a regular reality.eth arbitrator. // It will also use reality.eth to arbitrate its own governance, using the L2ForkArbitrator which makes L1 fork requests. @@ -143,7 +165,12 @@ contract AdjudicationIntegrationTest is Test { initialArbitrators[0] = initialArbitrator1; initialArbitrators[1] = initialArbitrator2; vm.prank(adjudictionDeployer); - adjudicationFramework1 = new AdjudicationFramework(address(l2RealityEth), 123, address(l2ForkArbitrator), initialArbitrators); + adjudicationFramework1 = new AdjudicationFramework( + address(l2RealityEth), + 123, + address(l2ForkArbitrator), + initialArbitrators + ); l2Arbitrator1 = new Arbitrator(); // NB The adjudication framework looks to individual arbitrators like a reality.eth question, so they can use it without being changed. @@ -159,27 +186,41 @@ contract AdjudicationIntegrationTest is Test { // For the setup we'll do this as an uncontested addition. // Contested cases should also be tested. - addArbitratorQID1 = adjudicationFramework1.beginAddArbitratorToAllowList(address(l2Arbitrator1)); - l2RealityEth.submitAnswer{value: 10000}(addArbitratorQID1, bytes32(uint256(1)), 0); + addArbitratorQID1 = adjudicationFramework1 + .beginAddArbitratorToAllowList(address(l2Arbitrator1)); + l2RealityEth.submitAnswer{value: 10000}( + addArbitratorQID1, + bytes32(uint256(1)), + 0 + ); uint32 to = l2RealityEth.getTimeout(addArbitratorQID1); assertEq(to, REALITY_ETH_TIMEOUT); uint32 finalizeTs = l2RealityEth.getFinalizeTS(addArbitratorQID1); - assertTrue(finalizeTs > block.timestamp, "finalization ts should be passed block ts"); - + assertTrue( + finalizeTs > block.timestamp, + "finalization ts should be passed block ts" + ); + vm.expectRevert("question must be finalized"); l2RealityEth.resultFor(addArbitratorQID1); - assertTrue(finalizeTs > block.timestamp, "finalization ts should be passed block ts"); + assertTrue( + finalizeTs > block.timestamp, + "finalization ts should be passed block ts" + ); vm.expectRevert("question must be finalized"); - adjudicationFramework1.executeAddArbitratorToAllowList(addArbitratorQID1); + adjudicationFramework1.executeAddArbitratorToAllowList( + addArbitratorQID1 + ); skip(86401); - adjudicationFramework1.executeAddArbitratorToAllowList(addArbitratorQID1); + adjudicationFramework1.executeAddArbitratorToAllowList( + addArbitratorQID1 + ); assertTrue(adjudicationFramework1.arbitrators(address(l2Arbitrator1))); - } function testInitialArbitrators() public { @@ -191,23 +232,38 @@ contract AdjudicationIntegrationTest is Test { } function testContestedAddArbitrator() public { + addArbitratorQID2 = adjudicationFramework1 + .beginAddArbitratorToAllowList(address(l2Arbitrator2)); + l2RealityEth.submitAnswer{value: 10000}( + addArbitratorQID2, + bytes32(uint256(1)), + 0 + ); + l2RealityEth.submitAnswer{value: 20000}( + addArbitratorQID2, + bytes32(uint256(0)), + 0 + ); - addArbitratorQID2 = adjudicationFramework1.beginAddArbitratorToAllowList(address(l2Arbitrator2)); - l2RealityEth.submitAnswer{value: 10000}(addArbitratorQID2, bytes32(uint256(1)), 0); - l2RealityEth.submitAnswer{value: 20000}(addArbitratorQID2, bytes32(uint256(0)), 0); - - l2ForkArbitrator.requestArbitration{value: 500000}(addArbitratorQID2, 0); + l2ForkArbitrator.requestArbitration{value: 500000}( + addArbitratorQID2, + 0 + ); // This talks to the bridge, we fake what happens next. // TODO: Hook this up to the real bridge so we can test it properly. - } - function _setupContestableQuestion() - internal returns (bytes32) { - + function _setupContestableQuestion() internal returns (bytes32) { // ask a question - bytes32 qid = l2RealityEth.askQuestion(0, "Question 1", address(adjudicationFramework1), 123, uint32(block.timestamp), 0); + bytes32 qid = l2RealityEth.askQuestion( + 0, + "Question 1", + address(adjudicationFramework1), + 123, + uint32(block.timestamp), + 0 + ); // do some bond escalation vm.prank(user1); @@ -216,23 +272,23 @@ contract AdjudicationIntegrationTest is Test { l2RealityEth.submitAnswer{value: 20}(qid, bytes32(0), 0); return qid; - } - function _setupArbitratedQuestion(bytes32 questionId) - internal { - + function _setupArbitratedQuestion(bytes32 questionId) internal { // request adjudication from the framework vm.prank(user1); - assertTrue(adjudicationFramework1.requestArbitration{value: 500000}(questionId, 0)); + assertTrue( + adjudicationFramework1.requestArbitration{value: 500000}( + questionId, + 0 + ) + ); vm.expectRevert("Arbitrator not allowlisted"); l2Arbitrator2.requestArbitration{value: 500000}(questionId, 0); - } function testL2RequestQuestionArbitration() public { - bytes32 qid = _setupContestableQuestion(); _setupArbitratedQuestion(qid); @@ -241,31 +297,59 @@ contract AdjudicationIntegrationTest is Test { l2Arbitrator1.submitAnswerByArbitrator(qid, bytes32(uint256(1)), user1); vm.expectRevert("Challenge deadline not passed"); - adjudicationFramework1.completeArbitration(qid, bytes32(uint256(1)), user1); - - skip(86401); - adjudicationFramework1.completeArbitration(qid, bytes32(uint256(1)), user1); + adjudicationFramework1.completeArbitration( + qid, + bytes32(uint256(1)), + user1 + ); - assertEq(l2RealityEth.resultFor(qid), bytes32(uint256(1)), "reality.eth question should be settled"); + skip(86401); + adjudicationFramework1.completeArbitration( + qid, + bytes32(uint256(1)), + user1 + ); + assertEq( + l2RealityEth.resultFor(qid), + bytes32(uint256(1)), + "reality.eth question should be settled" + ); } - function _setupContestedArbitration() internal returns (bytes32 questionId, bytes32 removalQuestionId, bytes32 lastHistoryHash, bytes32 lastAnswer, address lastAnswerer) { - + function _setupContestedArbitration() + internal + returns ( + bytes32 questionId, + bytes32 removalQuestionId, + bytes32 lastHistoryHash, + bytes32 lastAnswer, + address lastAnswerer + ) + { bytes32 qid = _setupContestableQuestion(); _setupArbitratedQuestion(qid); - // TODO: Separate this part out to reuse in different tests for uncontested freeze and fork? + // TODO: Separate this part out to reuse in different tests for uncontested freeze and fork? // permitted arbitrator grabs the question off the queue and locks it so nobody else can get it assertTrue(l2Arbitrator1.requestArbitration{value: 500000}(qid, 0)); l2Arbitrator1.submitAnswerByArbitrator(qid, bytes32(uint256(1)), user1); vm.expectRevert("Challenge deadline not passed"); - adjudicationFramework1.completeArbitration(qid, bytes32(uint256(1)), user1); + adjudicationFramework1.completeArbitration( + qid, + bytes32(uint256(1)), + user1 + ); // now before we can complete this somebody challenges it - removalQuestionId = adjudicationFramework1.beginRemoveArbitratorFromAllowList(address(l2Arbitrator1)); - l2RealityEth.submitAnswer{value: 10000}(removalQuestionId, bytes32(uint256(1)), 0); + removalQuestionId = adjudicationFramework1 + .beginRemoveArbitratorFromAllowList(address(l2Arbitrator1)); + l2RealityEth.submitAnswer{value: 10000}( + removalQuestionId, + bytes32(uint256(1)), + 0 + ); bytes32[] memory hashes; address[] memory users; @@ -273,13 +357,34 @@ contract AdjudicationIntegrationTest is Test { bytes32[] memory answers; vm.expectRevert("Bond too low to freeze"); - adjudicationFramework1.freezeArbitrator(removalQuestionId, hashes, users, bonds, answers); + adjudicationFramework1.freezeArbitrator( + removalQuestionId, + hashes, + users, + bonds, + answers + ); lastHistoryHash = l2RealityEth.getHistoryHash(removalQuestionId); vm.prank(user2); - l2RealityEth.submitAnswer{value: 20000}(removalQuestionId, bytes32(uint256(1)), 0); - adjudicationFramework1.freezeArbitrator(removalQuestionId, hashes, users, bonds, answers); - assertEq(adjudicationFramework1.countArbitratorFreezePropositions(address(l2Arbitrator1)), uint256(1)); + l2RealityEth.submitAnswer{value: 20000}( + removalQuestionId, + bytes32(uint256(1)), + 0 + ); + adjudicationFramework1.freezeArbitrator( + removalQuestionId, + hashes, + users, + bonds, + answers + ); + assertEq( + adjudicationFramework1.countArbitratorFreezePropositions( + address(l2Arbitrator1) + ), + uint256(1) + ); //skip(86401); //vm.expectRevert("Arbitrator must not be under dispute"); @@ -291,37 +396,47 @@ contract AdjudicationIntegrationTest is Test { c) Make a fork request on L2, get the response via the bridge */ - return (qid, removalQuestionId, lastHistoryHash, bytes32(uint256(1)), user2); - + return ( + qid, + removalQuestionId, + lastHistoryHash, + bytes32(uint256(1)), + user2 + ); } function testArbitrationContestPassedWithoutFork() public { - // (bytes32 qid, bytes32 removalQuestionId, bytes32 lastHistoryHash, bytes32 lastAnswer, address lastAnswerer) = _setupContestedArbitration(); (, bytes32 removalQuestionId, , , ) = _setupContestedArbitration(); - // Currently in the "yes" state, so once it times out we can complete the removal + // 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"); l2RealityEth.resultFor(removalQuestionId); vm.expectRevert("question must be finalized"); - adjudicationFramework1.executeRemoveArbitratorFromAllowList(removalQuestionId); + adjudicationFramework1.executeRemoveArbitratorFromAllowList( + removalQuestionId + ); skip(86401); - adjudicationFramework1.executeRemoveArbitratorFromAllowList(removalQuestionId); - + adjudicationFramework1.executeRemoveArbitratorFromAllowList( + removalQuestionId + ); } function testArbitrationContestRejectedWithoutFork() public { - //(bytes32 qid, bytes32 removalQuestionId, bytes32 lastHistoryHash, bytes32 lastAnswer, address lastAnswerer) = _setupContestedArbitration(); (, bytes32 removalQuestionId, , , ) = _setupContestedArbitration(); // Put the proposition to remove the arbitrator into the "no" state - l2RealityEth.submitAnswer{value: 40000}(removalQuestionId, bytes32(uint256(0)), 0); + l2RealityEth.submitAnswer{value: 40000}( + removalQuestionId, + bytes32(uint256(0)), + 0 + ); // Now wait for the timeout and settle the proposition @@ -329,36 +444,69 @@ contract AdjudicationIntegrationTest is Test { l2RealityEth.resultFor(removalQuestionId); vm.expectRevert("question must be finalized"); - adjudicationFramework1.executeRemoveArbitratorFromAllowList(removalQuestionId); + adjudicationFramework1.executeRemoveArbitratorFromAllowList( + removalQuestionId + ); skip(86401); vm.expectRevert("Result was not 1"); - adjudicationFramework1.executeRemoveArbitratorFromAllowList(removalQuestionId); + adjudicationFramework1.executeRemoveArbitratorFromAllowList( + removalQuestionId + ); - assertEq(adjudicationFramework1.countArbitratorFreezePropositions(address(l2Arbitrator1)), uint256(1)); + assertEq( + adjudicationFramework1.countArbitratorFreezePropositions( + address(l2Arbitrator1) + ), + uint256(1) + ); adjudicationFramework1.clearFailedProposition(removalQuestionId); - assertEq(adjudicationFramework1.countArbitratorFreezePropositions(address(l2Arbitrator1)), uint256(0)); - + assertEq( + adjudicationFramework1.countArbitratorFreezePropositions( + address(l2Arbitrator1) + ), + uint256(0) + ); } function testArbitrationContestPassedWithFork() public { - // (bytes32 qid, bytes32 removalQuestionId, bytes32 lastHistoryHash, bytes32 lastAnswer, address lastAnswerer) = _setupContestedArbitration(); - (, bytes32 removalQuestionId, bytes32 lastHistoryHash, bytes32 lastAnswer, address lastAnswerer) = _setupContestedArbitration(); + ( + , + bytes32 removalQuestionId, + bytes32 lastHistoryHash, + bytes32 lastAnswer, + address lastAnswerer + ) = _setupContestedArbitration(); - // Currently in the "yes" state, so once it times out we can complete the removal + // 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"); l2RealityEth.resultFor(removalQuestionId); - assertEq(address(l2ForkArbitrator.realitio()), address(l2RealityEth), "l2ForkArbitrator expects to arbitrate our l2RealityEth"); - assertEq(address(adjudicationFramework1.realityETH()), address(l2RealityEth), "adjudicationFramework1 expects to use our l2RealityEth"); - assertEq(address(l2ForkArbitrator), l2RealityEth.getArbitrator(removalQuestionId), "Arbitrator of the removalQuestionId is l2ForkArbitrator"); + assertEq( + address(l2ForkArbitrator.realitio()), + address(l2RealityEth), + "l2ForkArbitrator expects to arbitrate our l2RealityEth" + ); + assertEq( + address(adjudicationFramework1.realityETH()), + address(l2RealityEth), + "adjudicationFramework1 expects to use our l2RealityEth" + ); + assertEq( + address(l2ForkArbitrator), + l2RealityEth.getArbitrator(removalQuestionId), + "Arbitrator of the removalQuestionId is l2ForkArbitrator" + ); uint256 forkFee = l2ForkArbitrator.getDisputeFee(removalQuestionId); - l2ForkArbitrator.requestArbitration{value: forkFee}(removalQuestionId, 0); + l2ForkArbitrator.requestArbitration{value: forkFee}( + removalQuestionId, + 0 + ); // IMAGINE THE FORK HAPPENED HERE // There are now two L2s, each with a different chain ID @@ -366,42 +514,92 @@ contract AdjudicationIntegrationTest is Test { vm.chainId(newChainId1); // TODO: Adjust the forkingFee as the total supply has changed a bit - bytes memory fakeMessageData = abi.encode(uint64(newChainId1), address(l1ForkingManagerF1), uint256(forkingFee), false, address(l2ForkArbitrator), removalQuestionId, bytes32(uint256(1))); - l2Bridge.fakeClaimMessage(address(l1GlobalChainInfoPublisher), uint32(0), address(l2ChainInfo), fakeMessageData, uint256(0)); + bytes memory fakeMessageData = abi.encode( + uint64(newChainId1), + address(l1ForkingManagerF1), + uint256(forkingFee), + false, + address(l2ForkArbitrator), + removalQuestionId, + bytes32(uint256(1)) + ); + l2Bridge.fakeClaimMessage( + address(l1GlobalChainInfoPublisher), + uint32(0), + address(l2ChainInfo), + fakeMessageData, + uint256(0) + ); assertTrue(l2RealityEth.isPendingArbitration(removalQuestionId)); - l2ForkArbitrator.handleCompletedFork(removalQuestionId, lastHistoryHash, lastAnswer, lastAnswerer); + l2ForkArbitrator.handleCompletedFork( + removalQuestionId, + lastHistoryHash, + lastAnswer, + lastAnswerer + ); assertFalse(l2RealityEth.isPendingArbitration(removalQuestionId)); - assertEq(adjudicationFramework1.countArbitratorFreezePropositions(address(l2Arbitrator1)), 1); + assertEq( + adjudicationFramework1.countArbitratorFreezePropositions( + address(l2Arbitrator1) + ), + 1 + ); assertTrue(adjudicationFramework1.arbitrators(address(l2Arbitrator1))); - adjudicationFramework1.executeRemoveArbitratorFromAllowList(removalQuestionId); + adjudicationFramework1.executeRemoveArbitratorFromAllowList( + removalQuestionId + ); assertFalse(adjudicationFramework1.arbitrators(address(l2Arbitrator1))); - assertEq(adjudicationFramework1.countArbitratorFreezePropositions(address(l2Arbitrator1)), 0); + assertEq( + adjudicationFramework1.countArbitratorFreezePropositions( + address(l2Arbitrator1) + ), + 0 + ); // TODO: Retry the arbitration with a new arbitrator - } function testArbitrationContestRejectedWithFork() public { - //(bytes32 qid, bytes32 removalQuestionId, bytes32 lastHistoryHash, bytes32 lastAnswer, address lastAnswerer) = _setupContestedArbitration(); - (, bytes32 removalQuestionId, bytes32 lastHistoryHash, bytes32 lastAnswer, address lastAnswerer) = _setupContestedArbitration(); + ( + , + bytes32 removalQuestionId, + bytes32 lastHistoryHash, + bytes32 lastAnswer, + address lastAnswerer + ) = _setupContestedArbitration(); - // Currently in the "yes" state, so once it times out we can complete the removal + // 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"); bytes32 result = l2RealityEth.resultFor(removalQuestionId); assertEq(result, bytes32(uint256(0))); - assertEq(address(l2ForkArbitrator.realitio()), address(l2RealityEth), "l2ForkArbitrator expects to arbitrate our l2RealityEth"); - assertEq(address(adjudicationFramework1.realityETH()), address(l2RealityEth), "adjudicationFramework1 expects to use our l2RealityEth"); - assertEq(address(l2ForkArbitrator), l2RealityEth.getArbitrator(removalQuestionId), "Arbitrator of the removalQuestionId is l2ForkArbitrator"); + assertEq( + address(l2ForkArbitrator.realitio()), + address(l2RealityEth), + "l2ForkArbitrator expects to arbitrate our l2RealityEth" + ); + assertEq( + address(adjudicationFramework1.realityETH()), + address(l2RealityEth), + "adjudicationFramework1 expects to use our l2RealityEth" + ); + assertEq( + address(l2ForkArbitrator), + l2RealityEth.getArbitrator(removalQuestionId), + "Arbitrator of the removalQuestionId is l2ForkArbitrator" + ); uint256 forkFee = l2ForkArbitrator.getDisputeFee(removalQuestionId); - l2ForkArbitrator.requestArbitration{value: forkFee}(removalQuestionId, 0); + l2ForkArbitrator.requestArbitration{value: forkFee}( + removalQuestionId, + 0 + ); // IMAGINE THE FORK HAPPENED HERE // There are now two L2s, each with a different chain ID @@ -409,45 +607,89 @@ contract AdjudicationIntegrationTest is Test { vm.chainId(newChainId1); // TODO: Adjust the forkingFee as the total supply has changed a bit - bytes memory fakeMessageData = abi.encode(uint64(newChainId1), address(l1ForkingManagerF1), uint256(forkingFee), false, address(l2ForkArbitrator), removalQuestionId, bytes32(uint256(0))); - l2Bridge.fakeClaimMessage(address(l1GlobalChainInfoPublisher), uint32(0), address(l2ChainInfo), fakeMessageData, uint256(0)); + bytes memory fakeMessageData = abi.encode( + uint64(newChainId1), + address(l1ForkingManagerF1), + uint256(forkingFee), + false, + address(l2ForkArbitrator), + removalQuestionId, + bytes32(uint256(0)) + ); + l2Bridge.fakeClaimMessage( + address(l1GlobalChainInfoPublisher), + uint32(0), + address(l2ChainInfo), + fakeMessageData, + uint256(0) + ); assertTrue(l2RealityEth.isPendingArbitration(removalQuestionId)); - l2ForkArbitrator.handleCompletedFork(removalQuestionId, lastHistoryHash, lastAnswer, lastAnswerer); + l2ForkArbitrator.handleCompletedFork( + removalQuestionId, + lastHistoryHash, + lastAnswer, + lastAnswerer + ); assertFalse(l2RealityEth.isPendingArbitration(removalQuestionId)); - assertEq(adjudicationFramework1.countArbitratorFreezePropositions(address(l2Arbitrator1)), 1); + assertEq( + adjudicationFramework1.countArbitratorFreezePropositions( + address(l2Arbitrator1) + ), + 1 + ); assertTrue(adjudicationFramework1.arbitrators(address(l2Arbitrator1))); vm.expectRevert("Result was not 1"); - adjudicationFramework1.executeRemoveArbitratorFromAllowList(removalQuestionId); + adjudicationFramework1.executeRemoveArbitratorFromAllowList( + removalQuestionId + ); adjudicationFramework1.clearFailedProposition(removalQuestionId); assertTrue(adjudicationFramework1.arbitrators(address(l2Arbitrator1))); - assertEq(adjudicationFramework1.countArbitratorFreezePropositions(address(l2Arbitrator1)), 0); - + assertEq( + adjudicationFramework1.countArbitratorFreezePropositions( + address(l2Arbitrator1) + ), + 0 + ); } function testArbitrationContestForkFailed() public { - (, bytes32 removalQuestionId, , , ) = _setupContestedArbitration(); - // Currently in the "yes" state, so once it times out we can complete the removal + // 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"); bytes32 result = l2RealityEth.resultFor(removalQuestionId); assertEq(result, bytes32(uint256(0))); - assertEq(address(l2ForkArbitrator.realitio()), address(l2RealityEth), "l2ForkArbitrator expects to arbitrate our l2RealityEth"); - assertEq(address(adjudicationFramework1.realityETH()), address(l2RealityEth), "adjudicationFramework1 expects to use our l2RealityEth"); - assertEq(address(l2ForkArbitrator), l2RealityEth.getArbitrator(removalQuestionId), "Arbitrator of the removalQuestionId is l2ForkArbitrator"); + assertEq( + address(l2ForkArbitrator.realitio()), + address(l2RealityEth), + "l2ForkArbitrator expects to arbitrate our l2RealityEth" + ); + assertEq( + address(adjudicationFramework1.realityETH()), + address(l2RealityEth), + "adjudicationFramework1 expects to use our l2RealityEth" + ); + assertEq( + address(l2ForkArbitrator), + l2RealityEth.getArbitrator(removalQuestionId), + "Arbitrator of the removalQuestionId is l2ForkArbitrator" + ); uint256 forkFee = l2ForkArbitrator.getDisputeFee(removalQuestionId); vm.prank(user2); - l2ForkArbitrator.requestArbitration{value: forkFee}(removalQuestionId, 0); + l2ForkArbitrator.requestArbitration{value: forkFee}( + removalQuestionId, + 0 + ); assertTrue(l2ForkArbitrator.isForkInProgress(), "In forking state"); @@ -460,10 +702,19 @@ contract AdjudicationIntegrationTest is Test { assertEq(address(l2ForkArbitrator).balance, 0); payable(address(l2Bridge)).transfer(1000000); // Fund it so it can fund the L2ForkArbitrator bytes memory fakeMessageData = abi.encode(removalQuestionId); - l2Bridge.fakeClaimMessage(address(l1GlobalForkRequester), uint32(0), address(l2ForkArbitrator), fakeMessageData, forkFee); + l2Bridge.fakeClaimMessage( + address(l1GlobalForkRequester), + uint32(0), + address(l2ForkArbitrator), + fakeMessageData, + forkFee + ); assertEq(address(l2ForkArbitrator).balance, forkFee); - assertFalse(l2ForkArbitrator.isForkInProgress(), "Not in forking state"); + assertFalse( + l2ForkArbitrator.isForkInProgress(), + "Not in forking state" + ); l2ForkArbitrator.cancelArbitration(removalQuestionId); assertEq(forkFee, l2ForkArbitrator.refundsDue(user2)); @@ -473,7 +724,6 @@ contract AdjudicationIntegrationTest is Test { l2ForkArbitrator.claimRefund(); assertEq(address(l2ForkArbitrator).balance, 0); assertEq(user2.balance, user2Bal + forkFee); - } function testAdjudicationFrameworkTemplateCreation() public { @@ -482,22 +732,26 @@ contract AdjudicationIntegrationTest is Test { // Creates 2 templates, each with a log entry from reality.eth vm.prank(adjudictionDeployer); - new AdjudicationFramework(address(l2RealityEth), 123, address(l2ForkArbitrator), initialArbs); + new AdjudicationFramework( + address(l2RealityEth), + 123, + address(l2ForkArbitrator), + initialArbs + ); // NB The length and indexes of this may change if we add unrelated log entries to the AdjudicationFramework constructor Vm.Log[] memory entries = vm.getRecordedLogs(); assertEq(entries.length, 2); // We should always get the same contract address because we deploy only this with the same user so the address and nonce shouldn't change - string memory addLog = '{"title": "Should we add arbitrator %s to the framework 0xfed866a553d106378b828a2e1effb8bed9c9dc28?", "type": "bool", "category": "adjudication", "lang": "en"}'; - string memory removeLog = '{"title": "Should we remove arbitrator %s from the framework 0xfed866a553d106378b828a2e1effb8bed9c9dc28?", "type": "bool", "category": "adjudication", "lang": "en"}'; + string + memory addLog = '{"title": "Should we add arbitrator %s to the framework 0xfed866a553d106378b828a2e1effb8bed9c9dc28?", "type": "bool", "category": "adjudication", "lang": "en"}'; + string + memory removeLog = '{"title": "Should we remove arbitrator %s from the framework 0xfed866a553d106378b828a2e1effb8bed9c9dc28?", "type": "bool", "category": "adjudication", "lang": "en"}'; assertEq(abi.decode(entries[0].data, (string)), string(addLog)); assertEq(abi.decode(entries[1].data, (string)), string(removeLog)); - } - - /* function testL1RequestGovernanceArbitration() public { bytes32 questionId = keccak256(abi.encodePacked("Question 1")); // TODO: This should be in some wrapper contract @@ -511,7 +765,4 @@ contract AdjudicationIntegrationTest is Test { assertEq(govArb.arbitration_bounties(questionId), 50); } */ - - - } diff --git a/test/ChainIdManager.t.sol b/test/ChainIdManager.t.sol index 3ffea08c..f629770c 100644 --- a/test/ChainIdManager.t.sol +++ b/test/ChainIdManager.t.sol @@ -75,13 +75,10 @@ contract ChainIdManagerTest is Test { ); } - function testCheckGasBurn() view public { + function testCheckGasBurn() public view { uint256 initialGasLeft = gasleft(); chainIdManager.burnGas(); uint256 finalGasLeft = gasleft(); - assert( - initialGasLeft - finalGasLeft >= - chainIdManager.gasBurnAmount() - ); + assert(initialGasLeft - finalGasLeft >= chainIdManager.gasBurnAmount()); } } diff --git a/test/ForkingManager.t.sol b/test/ForkingManager.t.sol index 6bc3f742..b369f5d0 100644 --- a/test/ForkingManager.t.sol +++ b/test/ForkingManager.t.sol @@ -60,8 +60,8 @@ contract ForkingManagerTest is Test { bytes32[32] public depositTree; address public admin = address(0xad); uint64 public initialChainId = 1; - uint64 public firstChainId = initialChainId+1; - uint64 public secondChainId = initialChainId+2; + uint64 public firstChainId = initialChainId + 1; + uint64 public secondChainId = initialChainId + 2; // Setup new implementations for the fork address public newBridgeImplementation = address(new ForkableBridge()); @@ -134,7 +134,7 @@ contract ForkingManagerTest is Test { ) ) ); - ChainIdManager chainIdManager= new ChainIdManager(initialChainId); + ChainIdManager chainIdManager = new ChainIdManager(initialChainId); chainIdManagerAddress = address(chainIdManager); globalExitRoot.initialize( address(forkmanager), @@ -198,7 +198,6 @@ contract ForkingManagerTest is Test { } function testForkingStatusFunctions() public { - assertFalse(forkmanager.isForkingInitiated()); assertFalse(forkmanager.isForkingExecuted()); assertTrue(forkmanager.canFork()); @@ -242,7 +241,6 @@ contract ForkingManagerTest is Test { assertTrue(forkmanager.isForkingInitiated()); assertTrue(forkmanager.isForkingExecuted()); assertFalse(forkmanager.canFork()); - } function testInitiateForkChargesFees() public { @@ -252,7 +250,7 @@ contract ForkingManagerTest is Test { IForkingManager.DisputeData({ disputeContract: disputeContract, disputeContent: disputeContent, - isL1: isL1 + isL1: isL1 }), IForkingManager.NewImplementations({ bridgeImplementation: newBridgeImplementation, @@ -452,7 +450,9 @@ contract ForkingManagerTest is Test { } } - function testInitiateForkAndExecuteWorksWithoutChangingImplementations() public { + function testInitiateForkAndExecuteWorksWithoutChangingImplementations() + public + { // Mint and approve the arbitration fee for the test contract forkonomicToken.approve(address(forkmanager), arbitrationFee); vm.prank(address(this)); @@ -460,10 +460,7 @@ contract ForkingManagerTest is Test { IForkingManager.NewImplementations memory noNewImplementations; // Call the initiateFork function to create a new fork - forkmanager.initiateFork( - disputeData, - noNewImplementations - ); + forkmanager.initiateFork(disputeData, noNewImplementations); vm.warp(block.timestamp + forkmanager.forkPreparationTime() + 1); forkmanager.executeFork(); diff --git a/test/L1GlobalChainInfoPublisher.t.sol b/test/L1GlobalChainInfoPublisher.t.sol index 6d7496b6..e2ac212a 100644 --- a/test/L1GlobalChainInfoPublisher.t.sol +++ b/test/L1GlobalChainInfoPublisher.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; /* solhint-disable reentrancy */ /* solhint-disable quotes */ -import { Vm } from 'forge-std/Vm.sol'; +import {Vm} from "forge-std/Vm.sol"; import {Test} from "forge-std/Test.sol"; import {Arbitrator} from "../contracts/lib/reality-eth/Arbitrator.sol"; @@ -47,7 +47,6 @@ import {ChainIdManager} from "../contracts/ChainIdManager.sol"; import {ForkableZkEVM} from "../contracts/ForkableZkEVM.sol"; contract L1GlobalChainInfoPublisherTest is Test { - ForkableBridge public bridge; ForkonomicToken public forkonomicToken; ForkingManager public forkmanager; @@ -86,8 +85,8 @@ contract L1GlobalChainInfoPublisherTest is Test { bytes32[32] public depositTree; address public admin = address(0xad); uint64 public initialChainId = 1; - uint64 public firstChainId = initialChainId+1; - uint64 public secondChainId = initialChainId+2; + uint64 public firstChainId = initialChainId + 1; + uint64 public secondChainId = initialChainId + 2; // Setup new implementations for the fork address public newBridgeImplementation = address(new ForkableBridge()); @@ -104,8 +103,10 @@ contract L1GlobalChainInfoPublisherTest is Test { bytes32 public disputeContent = "0x34567890129"; bool public isL1 = true; - L1GlobalChainInfoPublisher public l1GlobalChainInfoPublisher = new L1GlobalChainInfoPublisher(); - L2ChainInfo public l2ChainInfo = new L2ChainInfo(address(l2Bridge), address(l1GlobalChainInfoPublisher)); + L1GlobalChainInfoPublisher public l1GlobalChainInfoPublisher = + new L1GlobalChainInfoPublisher(); + L2ChainInfo public l2ChainInfo = + new L2ChainInfo(address(l2Bridge), address(l1GlobalChainInfoPublisher)); ForkingManager.DisputeData public disputeData = IForkingManager.DisputeData({ @@ -121,7 +122,6 @@ contract L1GlobalChainInfoPublisherTest is Test { } function setUp() public { - bridgeImplementation = address(new ForkableBridge()); bridge = ForkableBridge( address( @@ -172,7 +172,7 @@ contract L1GlobalChainInfoPublisherTest is Test { ) ) ); - ChainIdManager chainIdManager= new ChainIdManager(initialChainId); + ChainIdManager chainIdManager = new ChainIdManager(initialChainId); chainIdManagerAddress = address(chainIdManager); globalExitRoot.initialize( address(forkmanager), @@ -236,24 +236,39 @@ contract L1GlobalChainInfoPublisherTest is Test { } function testChainInfoPublishedBeforeFork() public { - // vm.recordLogs(); - l1GlobalChainInfoPublisher.updateL2ChainInfo(address(bridge), address(l2ChainInfo), address(0), 10); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + address(bridge), + address(l2ChainInfo), + address(0), + 10 + ); // Vm.Log[] memory entries = vm.getRecordedLogs(); // TODO: Check the logs - } function testChainInfoPublishedBeforeForkBreaksWithBrokenBridge() public { address garbageAddress = address(0xabcd01); vm.expectRevert(); - l1GlobalChainInfoPublisher.updateL2ChainInfo(garbageAddress, address(l2ChainInfo), address(0), 10); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + garbageAddress, + address(l2ChainInfo), + address(0), + 10 + ); } - function testChainInfoPublishedBeforeForkRevertsWithBrokenAncestor() public { + function testChainInfoPublishedBeforeForkRevertsWithBrokenAncestor() + public + { address garbageAddress = address(0xabcd01); vm.expectRevert("Ancestor not found"); - l1GlobalChainInfoPublisher.updateL2ChainInfo(address(bridge), address(l2ChainInfo), garbageAddress, 10); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + address(bridge), + address(l2ChainInfo), + garbageAddress, + 10 + ); } function testChainInfoPublishedAfterForks() public { @@ -281,28 +296,57 @@ contract L1GlobalChainInfoPublisherTest is Test { // The current bridge should no longer work vm.expectRevert("No changes after forking"); - l1GlobalChainInfoPublisher.updateL2ChainInfo(address(bridge), address(l2ChainInfo), address(0), uint256(10)); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + address(bridge), + address(l2ChainInfo), + address(0), + uint256(10) + ); - (address forkmanager1Addrg, address forkmanager2Addr) = forkmanager.getChildren(); + (address forkmanager1Addrg, address forkmanager2Addr) = forkmanager + .getChildren(); address bridge1 = IForkingManager(forkmanager1Addrg).bridge(); address bridge2 = IForkingManager(forkmanager2Addr).bridge(); // The new bridges should work though - l1GlobalChainInfoPublisher.updateL2ChainInfo(bridge1, address(l2ChainInfo), address(0), uint256(10)); - l1GlobalChainInfoPublisher.updateL2ChainInfo(bridge2, address(l2ChainInfo), address(0), uint256(10)); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + bridge1, + address(l2ChainInfo), + address(0), + uint256(10) + ); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + bridge2, + address(l2ChainInfo), + address(0), + uint256(10) + ); - l1GlobalChainInfoPublisher.updateL2ChainInfo(bridge1, address(l2ChainInfo), address(forkmanager), uint256(10)); - l1GlobalChainInfoPublisher.updateL2ChainInfo(bridge2, address(l2ChainInfo), address(forkmanager), uint256(10)); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + bridge1, + address(l2ChainInfo), + address(forkmanager), + uint256(10) + ); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + bridge2, + address(l2ChainInfo), + address(forkmanager), + uint256(10) + ); ForkingManager forkmanager2 = ForkingManager(forkmanager2Addr); - ForkonomicToken forkonomicToken2 = ForkonomicToken(forkmanager2.forkonomicToken()); + ForkonomicToken forkonomicToken2 = ForkonomicToken( + forkmanager2.forkonomicToken() + ); // Next we'll fork with a dispute - ForkingManager.DisputeData memory disputeData2 = IForkingManager.DisputeData({ - disputeContract: address(0xabab), - disputeContent: bytes32("0xbaba"), - isL1: true - }); + ForkingManager.DisputeData memory disputeData2 = IForkingManager + .DisputeData({ + disputeContract: address(0xabab), + disputeContent: bytes32("0xbaba"), + isL1: true + }); IForkingManager.NewImplementations memory newImplementations2; // Empty one to simulate a question @@ -311,29 +355,49 @@ contract L1GlobalChainInfoPublisherTest is Test { vm.prank(address(this)); // Call the initiateFork function to create a new fork - forkmanager2.initiateFork( - disputeData2, - newImplementations2 - ); + forkmanager2.initiateFork(disputeData2, newImplementations2); skip(forkmanager.forkPreparationTime() + 1); forkmanager2.executeFork(); vm.expectRevert("No changes after forking"); - l1GlobalChainInfoPublisher.updateL2ChainInfo(bridge2, address(l2ChainInfo), address(forkmanager), uint256(10)); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + bridge2, + address(l2ChainInfo), + address(forkmanager), + uint256(10) + ); (, address forkmanager22Addr) = forkmanager2.getChildren(); // address bridge21 = IForkingManager(forkmanager21Addrg).bridge(); address bridge22 = IForkingManager(forkmanager22Addr).bridge(); - l1GlobalChainInfoPublisher.updateL2ChainInfo(bridge22, address(l2ChainInfo), address(forkmanager), uint256(10)); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + bridge22, + address(l2ChainInfo), + address(forkmanager), + uint256(10) + ); vm.expectRevert("Ancestor not found"); - l1GlobalChainInfoPublisher.updateL2ChainInfo(bridge22, address(l2ChainInfo), address(forkmanager), uint256(0)); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + bridge22, + address(l2ChainInfo), + address(forkmanager), + uint256(0) + ); vm.expectRevert("Ancestor not found"); - l1GlobalChainInfoPublisher.updateL2ChainInfo(bridge22, address(l2ChainInfo), address(forkmanager), uint256(1)); - - l1GlobalChainInfoPublisher.updateL2ChainInfo(bridge22, address(l2ChainInfo), address(forkmanager), uint256(2)); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + bridge22, + address(l2ChainInfo), + address(forkmanager), + uint256(1) + ); + l1GlobalChainInfoPublisher.updateL2ChainInfo( + bridge22, + address(l2ChainInfo), + address(forkmanager), + uint256(2) + ); } - } diff --git a/test/L1GlobalForkRequester.t.sol b/test/L1GlobalForkRequester.t.sol index ddee52c5..749f1f1a 100644 --- a/test/L1GlobalForkRequester.t.sol +++ b/test/L1GlobalForkRequester.t.sol @@ -23,8 +23,8 @@ import {L1GlobalForkRequester} from "../contracts/L1GlobalForkRequester.sol"; import {ExampleMoneyBoxUser} from "./testcontract/ExampleMoneyBoxUser.sol"; contract L1GlobalForkRequesterTest is Test { - - L1GlobalForkRequester public l1GlobalForkRequester = new L1GlobalForkRequester(); + L1GlobalForkRequester public l1GlobalForkRequester = + new L1GlobalForkRequester(); ForkableBridge public bridge; ForkonomicToken public forkonomicToken; @@ -32,7 +32,6 @@ contract L1GlobalForkRequesterTest is Test { ForkableZkEVM public zkevm; ForkableGlobalExitRoot public globalExitRoot; - address public bridgeImplementation; address public forkmanagerImplementation; address public zkevmImplementation; @@ -201,7 +200,6 @@ contract L1GlobalForkRequesterTest is Test { } function testReceivePayment() public { - uint256 fee = forkmanager.arbitrationFee(); ExampleMoneyBoxUser exampleMoneyBoxUser = new ExampleMoneyBoxUser(); @@ -210,7 +208,11 @@ contract L1GlobalForkRequesterTest is Test { address l2Requester = address(0xbabe01); bytes32 requestId = bytes32("0xc0ffee01"); bytes32 salt = keccak256(abi.encodePacked(l2Requester, requestId)); - address moneyBoxAddress = exampleMoneyBoxUser.calculateMoneyBoxAddress(address(l1GlobalForkRequester), salt, address(forkonomicToken)); + address moneyBoxAddress = exampleMoneyBoxUser.calculateMoneyBoxAddress( + address(l1GlobalForkRequester), + salt, + address(forkonomicToken) + ); vm.prank(address(this)); forkonomicToken.mint(address(this), fee); @@ -218,20 +220,25 @@ contract L1GlobalForkRequesterTest is Test { vm.prank(address(this)); forkonomicToken.transfer(moneyBoxAddress, fee); - assertEq(address(forkmanager.forkonomicToken()), address(forkonomicToken)); + assertEq( + address(forkmanager.forkonomicToken()), + address(forkonomicToken) + ); assertTrue(forkmanager.canFork()); assertFalse(forkmanager.isForkingInitiated()); assertFalse(forkmanager.isForkingExecuted()); - l1GlobalForkRequester.handlePayment(address(forkonomicToken), l2Requester, requestId); + l1GlobalForkRequester.handlePayment( + address(forkonomicToken), + l2Requester, + requestId + ); assertTrue(forkmanager.isForkingInitiated()); assertFalse(forkmanager.isForkingExecuted()); - } function testReceiveInsufficientPayment() public { - uint256 fee = forkmanager.arbitrationFee() - 1; ExampleMoneyBoxUser exampleMoneyBoxUser = new ExampleMoneyBoxUser(); @@ -240,7 +247,11 @@ contract L1GlobalForkRequesterTest is Test { address l2Requester = address(0xbabe01); bytes32 requestId = bytes32("0xc0ffee01"); bytes32 salt = keccak256(abi.encodePacked(l2Requester, requestId)); - address moneyBoxAddress = exampleMoneyBoxUser.calculateMoneyBoxAddress(address(l1GlobalForkRequester), salt, address(forkonomicToken)); + address moneyBoxAddress = exampleMoneyBoxUser.calculateMoneyBoxAddress( + address(l1GlobalForkRequester), + salt, + address(forkonomicToken) + ); vm.prank(address(this)); forkonomicToken.mint(address(this), fee); @@ -248,21 +259,34 @@ contract L1GlobalForkRequesterTest is Test { vm.prank(address(this)); forkonomicToken.transfer(moneyBoxAddress, fee); - assertEq(address(forkmanager.forkonomicToken()), address(forkonomicToken)); + assertEq( + address(forkmanager.forkonomicToken()), + address(forkonomicToken) + ); assertTrue(forkmanager.canFork()); - l1GlobalForkRequester.handlePayment(address(forkonomicToken), l2Requester, requestId); + l1GlobalForkRequester.handlePayment( + address(forkonomicToken), + l2Requester, + requestId + ); assertFalse(forkmanager.isForkingInitiated()); - (uint256 amount, uint256 amountRemainingY, uint256 amountRemainingN) = l1GlobalForkRequester.failedRequests(address(forkonomicToken), l2Requester, requestId); + ( + uint256 amount, + uint256 amountRemainingY, + uint256 amountRemainingN + ) = l1GlobalForkRequester.failedRequests( + address(forkonomicToken), + l2Requester, + requestId + ); assertEq(amount, fee); assertEq(amountRemainingY, 0); assertEq(amountRemainingN, 0); - } function testHandleOtherRequestForksFirst() public { - uint256 fee = forkmanager.arbitrationFee(); ExampleMoneyBoxUser exampleMoneyBoxUser = new ExampleMoneyBoxUser(); @@ -271,7 +295,11 @@ contract L1GlobalForkRequesterTest is Test { address l2Requester = address(0xbabe01); bytes32 requestId = bytes32("0xc0ffee01"); bytes32 salt = keccak256(abi.encodePacked(l2Requester, requestId)); - address moneyBoxAddress = exampleMoneyBoxUser.calculateMoneyBoxAddress(address(l1GlobalForkRequester), salt, address(forkonomicToken)); + address moneyBoxAddress = exampleMoneyBoxUser.calculateMoneyBoxAddress( + address(l1GlobalForkRequester), + salt, + address(forkonomicToken) + ); vm.prank(address(this)); forkonomicToken.mint(address(this), fee); @@ -279,7 +307,10 @@ contract L1GlobalForkRequesterTest is Test { vm.prank(address(this)); forkonomicToken.transfer(moneyBoxAddress, fee); - assertEq(address(forkmanager.forkonomicToken()), address(forkonomicToken)); + assertEq( + address(forkmanager.forkonomicToken()), + address(forkonomicToken) + ); assertTrue(forkmanager.canFork()); { @@ -290,47 +321,104 @@ contract L1GlobalForkRequesterTest is Test { forkonomicToken.approve(address(forkmanager), fee); // Assume the data contains the questionId and pass it directly to the forkmanager in the fork request IForkingManager.NewImplementations memory newImplementations; - IForkingManager.DisputeData memory disputeData = IForkingManager.DisputeData(false, address(this), requestId); + IForkingManager.DisputeData memory disputeData = IForkingManager + .DisputeData(false, address(this), requestId); forkmanager.initiateFork(disputeData, newImplementations); } // Our handlePayment will fail and leave our money sitting in failedRequests - uint256 balBeforeHandle = forkonomicToken.balanceOf(address(l1GlobalForkRequester)); + uint256 balBeforeHandle = forkonomicToken.balanceOf( + address(l1GlobalForkRequester) + ); - l1GlobalForkRequester.handlePayment(address(forkonomicToken), l2Requester, requestId); - (uint256 amount, uint256 amountRemainingY, uint256 amountRemainingN) = l1GlobalForkRequester.failedRequests(address(forkonomicToken), l2Requester, requestId); + l1GlobalForkRequester.handlePayment( + address(forkonomicToken), + l2Requester, + requestId + ); + ( + uint256 amount, + uint256 amountRemainingY, + uint256 amountRemainingN + ) = l1GlobalForkRequester.failedRequests( + address(forkonomicToken), + l2Requester, + requestId + ); assertEq(amount, fee); assertEq(amountRemainingY, 0); assertEq(amountRemainingN, 0); - uint256 balAfterHandle = forkonomicToken.balanceOf(address(l1GlobalForkRequester)); + uint256 balAfterHandle = forkonomicToken.balanceOf( + address(l1GlobalForkRequester) + ); assertEq(balBeforeHandle + amount, balAfterHandle); vm.expectRevert("Token not forked"); - l1GlobalForkRequester.splitTokensIntoChildTokens(address(forkonomicToken), l2Requester, requestId); + l1GlobalForkRequester.splitTokensIntoChildTokens( + address(forkonomicToken), + l2Requester, + requestId + ); // Execute the other guy's fork skip(forkmanager.forkPreparationTime() + 1); forkmanager.executeFork(); { - uint256 balBeforeSplit = forkonomicToken.balanceOf(address(l1GlobalForkRequester)); - l1GlobalForkRequester.splitTokensIntoChildTokens(address(forkonomicToken), l2Requester, requestId); - uint256 balAfterSplit = forkonomicToken.balanceOf(address(l1GlobalForkRequester)); + uint256 balBeforeSplit = forkonomicToken.balanceOf( + address(l1GlobalForkRequester) + ); + l1GlobalForkRequester.splitTokensIntoChildTokens( + address(forkonomicToken), + l2Requester, + requestId + ); + uint256 balAfterSplit = forkonomicToken.balanceOf( + address(l1GlobalForkRequester) + ); assertEq(balAfterSplit + amount, balBeforeSplit); } // The children should now both have the funds we split - (address childToken1, address childToken2) = forkonomicToken.getChildren(); - assertEq(ForkonomicToken(childToken1).balanceOf(address(l1GlobalForkRequester)), amount); - assertEq(ForkonomicToken(childToken2).balanceOf(address(l1GlobalForkRequester)), amount); + (address childToken1, address childToken2) = forkonomicToken + .getChildren(); + assertEq( + ForkonomicToken(childToken1).balanceOf( + address(l1GlobalForkRequester) + ), + amount + ); + assertEq( + ForkonomicToken(childToken2).balanceOf( + address(l1GlobalForkRequester) + ), + amount + ); // Now we should be able to return the tokens on the child chain - l1GlobalForkRequester.returnTokens(address(childToken1), l2Requester, requestId); - (uint256 amountChild1, , ) = l1GlobalForkRequester.failedRequests(childToken1, l2Requester, requestId); - (uint256 amountChild2, , ) = l1GlobalForkRequester.failedRequests(childToken2, l2Requester, requestId); + l1GlobalForkRequester.returnTokens( + address(childToken1), + l2Requester, + requestId + ); + (uint256 amountChild1, , ) = l1GlobalForkRequester.failedRequests( + childToken1, + l2Requester, + requestId + ); + (uint256 amountChild2, , ) = l1GlobalForkRequester.failedRequests( + childToken2, + l2Requester, + requestId + ); - assertEq(ForkonomicToken(childToken2).balanceOf(address(l1GlobalForkRequester)), amount); + assertEq( + ForkonomicToken(childToken2).balanceOf( + address(l1GlobalForkRequester) + ), + amount + ); assertEq(amountChild1, 0); assertEq(amountChild2, amount); @@ -341,7 +429,5 @@ contract L1GlobalForkRequesterTest is Test { // l1GlobalForkRequester.returnTokens(address(childToken2), l2Requester, requestId); // (amountChild2, , ) = l1GlobalForkRequester.failedRequests(childToken2, l2Requester, requestId); // assertEq(amountChild2, 0); - } - } diff --git a/test/MoneyBox.t.sol b/test/MoneyBox.t.sol index 6e2f7434..11ff6e00 100644 --- a/test/MoneyBox.t.sol +++ b/test/MoneyBox.t.sol @@ -10,7 +10,6 @@ import {ExampleToken} from "./testcontract/ExampleToken.sol"; import {ExampleMoneyBoxUser} from "./testcontract/ExampleMoneyBoxUser.sol"; contract MoneyBoxTest is Test { - ExampleToken internal token; bytes32 internal salt = bytes32("0xbabebabe"); @@ -30,14 +29,17 @@ contract MoneyBoxTest is Test { address approvedUser = address(uint160(uint256(entries[0].topics[2]))); assertEq(approvedUser, user2); uint256 approveAmount = abi.decode(entries[0].data, (uint256)); - assertEq(approveAmount, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); + assertEq( + approveAmount, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ); } function testCreatorCanTakeTokens() public { - // We'll credit the tokens before deploying the contract as this is the order it will be done in in real life ExampleMoneyBoxUser exampleMoneyBoxUser = new ExampleMoneyBoxUser(); - address calculatedAddress = exampleMoneyBoxUser.calculateMoneyBoxAddress(user1, salt, address(token)); + address calculatedAddress = exampleMoneyBoxUser + .calculateMoneyBoxAddress(user1, salt, address(token)); token.fakeMint(calculatedAddress, 10000123); vm.prank(user1); @@ -50,26 +52,30 @@ contract MoneyBoxTest is Test { assertEq(token.balanceOf(address(moneyBox)), 10000123 - 123); assertEq(token.balanceOf(user2), 123); - } function testAddressCalculation() public { - vm.prank(user2); MoneyBox moneyBox = new MoneyBox{salt: salt}(address(token)); ExampleMoneyBoxUser exampleMoneyBoxUser = new ExampleMoneyBoxUser(); - address calculatedAddress = exampleMoneyBoxUser.calculateMoneyBoxAddress(user2, salt, address(token)); + address calculatedAddress = exampleMoneyBoxUser + .calculateMoneyBoxAddress(user2, salt, address(token)); assertEq(calculatedAddress, address(moneyBox)); - address calculatedAddress2 = exampleMoneyBoxUser.calculateMoneyBoxAddress(user1, salt, address(token)); + address calculatedAddress2 = exampleMoneyBoxUser + .calculateMoneyBoxAddress(user1, salt, address(token)); assertNotEq(calculatedAddress2, address(moneyBox)); - address calculatedAddress3 = exampleMoneyBoxUser.calculateMoneyBoxAddress(user2, bytes32("0xee00bb"), address(token)); + address calculatedAddress3 = exampleMoneyBoxUser + .calculateMoneyBoxAddress( + user2, + bytes32("0xee00bb"), + address(token) + ); assertNotEq(calculatedAddress3, address(moneyBox)); - address calculatedAddress4 = exampleMoneyBoxUser.calculateMoneyBoxAddress(user2, salt, address(0xee00bb)); + address calculatedAddress4 = exampleMoneyBoxUser + .calculateMoneyBoxAddress(user2, salt, address(0xee00bb)); assertNotEq(calculatedAddress4, address(moneyBox)); - } - } diff --git a/test/RealityETH.t.sol b/test/RealityETH.t.sol index 8ce5741a..8d1f487a 100644 --- a/test/RealityETH.t.sol +++ b/test/RealityETH.t.sol @@ -10,14 +10,13 @@ Ultimately these will probably be moved to the reality.eth repo and included in /* solhint-disable reentrancy */ /* solhint-disable quotes */ -import { Vm } from 'forge-std/Vm.sol'; +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; @@ -26,8 +25,8 @@ contract RealityETHTest is Test { address payable internal user1 = payable(address(0xbabe01)); address payable internal user2 = payable(address(0xbabe02)); - bytes32 constant internal BYTES32_YES = bytes32(uint256(1)); - bytes32 constant internal BYTES32_NO = bytes32(uint256(0)); + bytes32 internal constant BYTES32_YES = bytes32(uint256(1)); + bytes32 internal constant BYTES32_NO = bytes32(uint256(0)); bytes32[] internal historyHashes; address[] internal addrs; @@ -36,17 +35,17 @@ contract RealityETHTest is Test { // 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--) { + 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 @@ -54,31 +53,36 @@ contract RealityETHTest is Test { continue; } - (bytes32 logAnswer, bytes32 logHistoryHash, uint256 logBond,,) = abi.decode(logs[i].data, (bytes32,bytes32,uint256,uint256,bool)); + ( + 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 (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]; + if (j < historyHashes.length - 1) { + historyHashes[j] = historyHashes[j + 1]; } else { historyHashes[j] = bytes32(0); } } - } - function _trimLogs() internal { - historyHashes.pop(); addrs.pop(); bonds.pop(); @@ -86,7 +90,6 @@ contract RealityETHTest is Test { } function setUp() public { - realityEth = new RealityETH_v3_0(); arb = new Arbitrator(); @@ -96,8 +99,22 @@ contract RealityETHTest is Test { 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); + 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(); @@ -115,7 +132,12 @@ contract RealityETHTest is Test { bytes32 answerHash1 = keccak256(abi.encodePacked(BYTES32_NO, nonce1)); uint256 bond1 = 1000; vm.prank(user1); - realityEth.submitAnswerCommitment{value: bond1}(q1, answerHash1, 0, user1); + realityEth.submitAnswerCommitment{value: bond1}( + q1, + answerHash1, + 0, + user1 + ); vm.prank(user2); realityEth.submitAnswer{value: 2500}(q1, BYTES32_NO, 0); @@ -125,7 +147,12 @@ contract RealityETHTest is Test { bytes32 answerHash2 = keccak256(abi.encodePacked(BYTES32_NO, nonce2)); uint256 bond2 = 5000; vm.prank(user2); - realityEth.submitAnswerCommitment{value: bond2}(q1, answerHash2, 0, user2); + realityEth.submitAnswerCommitment{value: bond2}( + q1, + answerHash2, + 0, + user2 + ); realityEth.submitAnswerReveal(q1, BYTES32_NO, nonce2, bond2); // Do a commit-reveal for yes @@ -133,7 +160,12 @@ contract RealityETHTest is Test { bytes32 answerHash3 = keccak256(abi.encodePacked(BYTES32_YES, nonce3)); uint256 bond3 = 10000; vm.prank(user1); - realityEth.submitAnswerCommitment{value: bond3}(q1, answerHash3, 0, user1); + realityEth.submitAnswerCommitment{value: bond3}( + q1, + answerHash3, + 0, + user1 + ); realityEth.submitAnswerReveal(q1, BYTES32_YES, nonce3, bond3); vm.prank(user1); @@ -143,26 +175,36 @@ contract RealityETHTest is Test { 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); + 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); - + realityEth.getEarliestAnswerFromSuppliedHistoryOrRevert( + q1, + historyHashes, + addrs, + bonds, + answers + ); } function testGetEarliestAnswerFromSuppliedHistoryOrRevert() public { - _checkSuppliedHistory(BYTES32_YES, 5); _trimLogs(); _checkSuppliedHistory(BYTES32_NO, 25); @@ -180,16 +222,22 @@ contract RealityETHTest is Test { _checkSuppliedHistory(BYTES32_YES, 20000); _trimLogs(); _checkSuppliedHistory(BYTES32_NO, 40000); - } - function testGetEarliestAnswerFromSuppliedHistoryOrRevertWrongHashReverts() public { - + 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); - + vm.expectRevert( + "History input provided did not match the expected hash" + ); + realityEth.getEarliestAnswerFromSuppliedHistoryOrRevert( + q1, + historyHashes, + addrs, + bonds, + answers + ); } - } diff --git a/test/testcontract/ExampleMoneyBoxUser.sol b/test/testcontract/ExampleMoneyBoxUser.sol index 75036e0b..aa7c5832 100644 --- a/test/testcontract/ExampleMoneyBoxUser.sol +++ b/test/testcontract/ExampleMoneyBoxUser.sol @@ -5,9 +5,16 @@ pragma solidity ^0.8.20; import {CalculateMoneyBoxAddress} from "../../contracts/lib/CalculateMoneyBoxAddress.sol"; contract ExampleMoneyBoxUser { - - function calculateMoneyBoxAddress(address _creator, bytes32 _salt, address _token) external pure returns (address) { - return CalculateMoneyBoxAddress._calculateMoneyBoxAddress(_creator, _salt, _token); + function calculateMoneyBoxAddress( + address _creator, + bytes32 _salt, + address _token + ) external pure returns (address) { + return + CalculateMoneyBoxAddress._calculateMoneyBoxAddress( + _creator, + _salt, + _token + ); } - } diff --git a/test/testcontract/ExampleToken.sol b/test/testcontract/ExampleToken.sol index 77bc11d0..1ae3f154 100644 --- a/test/testcontract/ExampleToken.sol +++ b/test/testcontract/ExampleToken.sol @@ -5,13 +5,10 @@ pragma solidity ^0.8.20; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract ExampleToken is ERC20 { - - constructor() ERC20("My Token", "MT") { - } + constructor() ERC20("My Token", "MT") {} // Permission-free mint for testing function fakeMint(address _to, uint256 _amount) external { _mint(_to, _amount); } - } diff --git a/test/testcontract/MockPolygonZkEVMBridge.sol b/test/testcontract/MockPolygonZkEVMBridge.sol index f9938822..bf95c4af 100644 --- a/test/testcontract/MockPolygonZkEVMBridge.sol +++ b/test/testcontract/MockPolygonZkEVMBridge.sol @@ -5,14 +5,12 @@ pragma solidity ^0.8.20; import {IBridgeMessageReceiver} from "@RealityETH/zkevm-contracts/contracts/interfaces/IBridgeMessageReceiver.sol"; contract MockPolygonZkEVMBridge { - function bridgeMessage( uint32 destinationNetwork, address destinationAddress, bool forceUpdateGlobalExitRoot, bytes calldata metadata - ) public virtual payable { - } + ) public payable virtual {} function bridgeAsset( uint32 destinationNetwork, @@ -21,14 +19,17 @@ contract MockPolygonZkEVMBridge { address token, bool forceUpdateGlobalExitRoot, bytes calldata permitData - ) - public - payable { - } + ) public payable {} receive() external payable {} - function fakeClaimMessage(address originAddress, uint32 originNetwork, address destinationAddress, bytes memory metadata, uint256 amount) external { + function fakeClaimMessage( + address originAddress, + uint32 originNetwork, + address destinationAddress, + bytes memory metadata, + uint256 amount + ) external { /* solhint-disable avoid-low-level-calls */ (bool success, ) = destinationAddress.call{value: amount}( abi.encodeCall( @@ -38,5 +39,4 @@ contract MockPolygonZkEVMBridge { ); require(success, "Call failed"); } - }