From 8151d8cd8fe4b1973f6455bd57909f3fc8af8301 Mon Sep 17 00:00:00 2001 From: shaito <106555513+0xShaito@users.noreply.github.com> Date: Wed, 9 Aug 2023 19:53:24 +0300 Subject: [PATCH] feat: sequential resolution module (#55) --- solidity/contracts/Module.sol | 5 +- solidity/contracts/ModuleData.sol | 18 + .../modules/SequentialResolutionModule.sol | 148 ++++++ solidity/interfaces/IModule.sol | 6 +- solidity/interfaces/IModuleData.sol | 7 + .../modules/ISequentialResolutionModule.sol | 22 + solidity/test/unit/Oracle.t.sol | 11 +- .../unit/SequentialResolutionModule.t.sol | 470 ++++++++++++++++++ 8 files changed, 677 insertions(+), 10 deletions(-) create mode 100644 solidity/contracts/ModuleData.sol create mode 100644 solidity/contracts/modules/SequentialResolutionModule.sol create mode 100644 solidity/interfaces/IModuleData.sol create mode 100644 solidity/interfaces/modules/ISequentialResolutionModule.sol create mode 100644 solidity/test/unit/SequentialResolutionModule.t.sol diff --git a/solidity/contracts/Module.sol b/solidity/contracts/Module.sol index ee777dc6..fd02fbff 100644 --- a/solidity/contracts/Module.sol +++ b/solidity/contracts/Module.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import {IOracle} from '../interfaces/IOracle.sol'; import {IModule} from '../interfaces/IModule.sol'; +import {ModuleData} from './ModuleData.sol'; +import {IOracle} from '../interfaces/IOracle.sol'; abstract contract Module is IModule { IOracle public immutable ORACLE; @@ -18,7 +19,7 @@ abstract contract Module is IModule { _; } - function setupRequest(bytes32 _requestId, bytes calldata _data) external onlyOracle { + function setupRequest(bytes32 _requestId, bytes calldata _data) public virtual onlyOracle { requestData[_requestId] = _data; _afterSetupRequest(_requestId, _data); } diff --git a/solidity/contracts/ModuleData.sol b/solidity/contracts/ModuleData.sol new file mode 100644 index 00000000..5c1bdd0f --- /dev/null +++ b/solidity/contracts/ModuleData.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {IOracle} from '../interfaces/IOracle.sol'; +import {IModuleData} from '../interfaces/IModuleData.sol'; + +abstract contract ModuleData is IModuleData { + mapping(bytes32 _requestId => bytes _requestData) public requestData; + + function _setupRequest(bytes32 _requestId, bytes calldata _data) internal virtual { + requestData[_requestId] = _data; + _afterSetupRequest(_requestId, _data); + } + + function _finalizeRequest(bytes32 _requestId) internal virtual; + + function _afterSetupRequest(bytes32 _requestId, bytes calldata _data) internal virtual {} +} diff --git a/solidity/contracts/modules/SequentialResolutionModule.sol b/solidity/contracts/modules/SequentialResolutionModule.sol new file mode 100644 index 00000000..f792b477 --- /dev/null +++ b/solidity/contracts/modules/SequentialResolutionModule.sol @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; + +import {IAccountingExtension} from '../../interfaces/extensions/IAccountingExtension.sol'; +import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; +import {Module} from '../Module.sol'; +import {IResolutionModule} from '../../interfaces/modules/IResolutionModule.sol'; +import {ISequentialResolutionModule, IOracle} from '../../interfaces/modules/ISequentialResolutionModule.sol'; + +contract SequentialResolutionModule is Module, ISequentialResolutionModule { + IResolutionModule[] public resolutionModules; + uint256 public currentModuleIndex; + + constructor(IOracle _oracle, IResolutionModule[] memory _resolutionModules) Module(_oracle) { + resolutionModules = _resolutionModules; + } + + modifier onlySubmodule() { + if (msg.sender != address(resolutionModules[currentModuleIndex])) revert SequentialResolutionModule_OnlySubmodule(); + _; + } + + function moduleName() external view returns (string memory _moduleName) { + string memory _submodules = resolutionModules[0].moduleName(); + for (uint256 i = 1; i < resolutionModules.length; ++i) { + _submodules = string.concat(_submodules, ', ', resolutionModules[i].moduleName()); + } + return string.concat('SequentialResolutionModule', '[', _submodules, ']'); + } + + function setupRequest(bytes32 _requestId, bytes calldata _data) public override onlyOracle { + super.setupRequest(_requestId, _data); + bytes[] memory _submoduleData = abi.decode(_data, (bytes[])); + for (uint256 i; i < resolutionModules.length; ++i) { + resolutionModules[i].setupRequest(_requestId, _submoduleData[i]); + } + } + + function startResolution(bytes32 _disputeId) external onlyOracle { + resolutionModules[0].startResolution(_disputeId); + } + + function resolveDispute(bytes32 _disputeId) external onlyOracle { + resolutionModules[currentModuleIndex].resolveDispute(_disputeId); + } + + function updateDisputeStatus(bytes32 _disputeId, DisputeStatus _status) external onlySubmodule { + if (_status == DisputeStatus.NoResolution && currentModuleIndex < resolutionModules.length - 1) { + resolutionModules[++currentModuleIndex].startResolution(_disputeId); + } else { + ORACLE.updateDisputeStatus(_disputeId, _status); + } + } + + // TODO: finalizeRequest function. None of the resolution modules use it. How should we implement it? + function finalizeRequest(bytes32 _requestId) external virtual override onlyOracle { + for (uint256 i; i < resolutionModules.length; ++i) { + resolutionModules[i].finalizeRequest(_requestId); + } + } + + function listSubmodules( + uint256 _startFrom, + uint256 _batchSize + ) external view returns (IResolutionModule[] memory _list) { + uint256 _length = resolutionModules.length; + uint256 _count = (_batchSize > _length - _startFrom) ? _length - _startFrom : _batchSize; + _list = new IResolutionModule[](_count); + for (uint256 i; i < _count; ++i) { + _list[i] = resolutionModules[_startFrom + i]; + } + } + + // ============ ORACLE Proxy ============= + + function validModule(bytes32 _requestId, address _module) external view returns (bool _validModule) { + _validModule = ORACLE.validModule(_requestId, _module); + } + + function getDispute(bytes32 _disputeId) external view returns (IOracle.Dispute memory _dispute) { + _dispute = ORACLE.getDispute(_disputeId); + } + + function getResponse(bytes32 _responseId) external view returns (IOracle.Response memory _response) { + _response = ORACLE.getResponse(_responseId); + } + + function getRequest(bytes32 _requestId) external view returns (IOracle.Request memory _request) { + _request = ORACLE.getRequest(_requestId); + } + + function getFullRequest(bytes32 _requestId) external view returns (IOracle.FullRequest memory _request) { + _request = ORACLE.getFullRequest(_requestId); + } + + function disputeOf(bytes32 _requestId) external view returns (bytes32 _disputeId) { + _disputeId = ORACLE.disputeOf(_requestId); + } + + function getFinalizedResponse(bytes32 _requestId) external view returns (IOracle.Response memory _response) { + _response = ORACLE.getFinalizedResponse(_requestId); + } + + function getResponseIds(bytes32 _requestId) external view returns (bytes32[] memory _ids) { + _ids = ORACLE.getResponseIds(_requestId); + } + + function listRequests(uint256 _startFrom, uint256 _amount) external view returns (IOracle.FullRequest[] memory _list) { + _list = ORACLE.listRequests(_startFrom, _amount); + } + + function listRequestIds(uint256 _startFrom, uint256 _batchSize) external view returns (bytes32[] memory _list) { + _list = ORACLE.listRequestIds(_startFrom, _batchSize); + } + + function finalize(bytes32 _requestId, bytes32 _finalizedResponseId) external onlySubmodule { + ORACLE.finalize(_requestId, _finalizedResponseId); + } + + function escalateDispute(bytes32 _disputeId) external onlySubmodule { + ORACLE.escalateDispute(_disputeId); + } + + // ============ ORACLE Proxy not implemented ============= + // This functions use msg.sender in the oracle implementation and cannot be called from a the sequential resolution module + + function createRequest(IOracle.NewRequest memory) external payable onlySubmodule returns (bytes32) { + revert SequentialResolutionModule_NotImplemented(); + } + + function createRequests(IOracle.NewRequest[] calldata) external view onlySubmodule returns (bytes32[] memory) { + revert SequentialResolutionModule_NotImplemented(); + } + + function disputeResponse(bytes32, bytes32) external view onlySubmodule returns (bytes32) { + revert SequentialResolutionModule_NotImplemented(); + } + + function proposeResponse(bytes32, bytes calldata) external view onlySubmodule returns (bytes32) { + revert SequentialResolutionModule_NotImplemented(); + } + + function proposeResponse(address, bytes32, bytes calldata) external view onlySubmodule returns (bytes32) { + revert SequentialResolutionModule_NotImplemented(); + } +} diff --git a/solidity/interfaces/IModule.sol b/solidity/interfaces/IModule.sol index d3d775f6..09f0d372 100644 --- a/solidity/interfaces/IModule.sol +++ b/solidity/interfaces/IModule.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -interface IModule { +import {IModuleData} from '../interfaces/IModuleData.sol'; + +interface IModule is IModuleData { error Module_OnlyOracle(); error Module_InvalidCaller(); - function requestData(bytes32 _requestId) external view returns (bytes memory _data); function setupRequest(bytes32 _requestId, bytes calldata _data) external; function finalizeRequest(bytes32 _requestId) external; - function moduleName() external pure returns (string memory _moduleName); } diff --git a/solidity/interfaces/IModuleData.sol b/solidity/interfaces/IModuleData.sol new file mode 100644 index 00000000..ae31b51b --- /dev/null +++ b/solidity/interfaces/IModuleData.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +interface IModuleData { + function requestData(bytes32 _requestId) external view returns (bytes memory _data); + function moduleName() external view returns (string memory _moduleName); +} diff --git a/solidity/interfaces/modules/ISequentialResolutionModule.sol b/solidity/interfaces/modules/ISequentialResolutionModule.sol new file mode 100644 index 00000000..4a79bd00 --- /dev/null +++ b/solidity/interfaces/modules/ISequentialResolutionModule.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {IOracle} from '../../interfaces/IOracle.sol'; +import {IResolutionModule} from './IResolutionModule.sol'; + +interface ISequentialResolutionModule is IOracle { + /// @notice Thrown when the caller is not a valid sub-module + error SequentialResolutionModule_OnlySubmodule(); + + /// @notice Thrown when the function called is not implemented + error SequentialResolutionModule_NotImplemented(); + + /// @notice Returns the list of submodules + /// @param _startFrom The index to start from + /// @param _batchSize The number of submodules to return + /// @return _list The list of submodules + function listSubmodules( + uint256 _startFrom, + uint256 _batchSize + ) external view returns (IResolutionModule[] memory _list); +} diff --git a/solidity/test/unit/Oracle.t.sol b/solidity/test/unit/Oracle.t.sol index 58ca2852..0bddc5b3 100644 --- a/solidity/test/unit/Oracle.t.sol +++ b/solidity/test/unit/Oracle.t.sol @@ -16,6 +16,7 @@ import { } from '../../interfaces/IOracle.sol'; import {IModule} from '../../interfaces/IModule.sol'; +import {IModuleData} from '../../interfaces/IModuleData.sol'; /** * @dev Harness to deploy and test Oracle @@ -924,31 +925,31 @@ contract Oracle_UnitTest is Test { vm.mockCall( address(requestModule), - abi.encodeCall(IModule.requestData, (_requestIds[i])), + abi.encodeCall(IModuleData.requestData, (_requestIds[i])), abi.encode(_request.requestModuleData) ); vm.mockCall( address(responseModule), - abi.encodeCall(IModule.requestData, (_requestIds[i])), + abi.encodeCall(IModuleData.requestData, (_requestIds[i])), abi.encode(_request.responseModuleData) ); vm.mockCall( address(disputeModule), - abi.encodeCall(IModule.requestData, (_requestIds[i])), + abi.encodeCall(IModuleData.requestData, (_requestIds[i])), abi.encode(_request.disputeModuleData) ); vm.mockCall( address(resolutionModule), - abi.encodeCall(IModule.requestData, (_requestIds[i])), + abi.encodeCall(IModuleData.requestData, (_requestIds[i])), abi.encode(_request.resolutionModuleData) ); vm.mockCall( address(finalityModule), - abi.encodeCall(IModule.requestData, (_requestIds[i])), + abi.encodeCall(IModuleData.requestData, (_requestIds[i])), abi.encode(_request.finalityModuleData) ); } diff --git a/solidity/test/unit/SequentialResolutionModule.t.sol b/solidity/test/unit/SequentialResolutionModule.t.sol new file mode 100644 index 00000000..7cbafa3c --- /dev/null +++ b/solidity/test/unit/SequentialResolutionModule.t.sol @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +// solhint-disable-next-line +import 'forge-std/Test.sol'; + +import { + SequentialResolutionModule, + Module, + IOracle, + IResolutionModule, + ISequentialResolutionModule +} from '../../contracts/modules/SequentialResolutionModule.sol'; + +import {IModule} from '../../interfaces/IModule.sol'; + +contract ForTest_ResolutionModule is Module { + string public name; + IOracle.DisputeStatus internal _responseStatus; + + constructor(IOracle _oracle, string memory _name) payable Module(_oracle) { + name = _name; + } + + function resolveDispute(bytes32 _disputeId) external { + ORACLE.updateDisputeStatus(_disputeId, _responseStatus); + } + + function startResolution(bytes32 _disputeId) external {} + + function moduleName() external view returns (string memory _moduleName) { + return name; + } + + function forTest_setResponseStatus(IOracle.DisputeStatus _status) external { + _responseStatus = _status; + } +} + +contract Base is Test { + SequentialResolutionModule public module; + IOracle public oracle; + bytes32 public disputeId = bytes32(uint256(1)); + bytes32 public responseId = bytes32(uint256(2)); + bytes32 public requestId = bytes32(uint256(3)); + address public proposer = makeAddr('proposer'); + address public disputer = makeAddr('disputer'); + bytes public responseData = abi.encode('responseData'); + + ForTest_ResolutionModule public submodule1; + ForTest_ResolutionModule public submodule2; + ForTest_ResolutionModule public submodule3; + + function setUp() public { + oracle = IOracle(makeAddr('Oracle')); + vm.etch(address(oracle), hex'069420'); + + vm.mockCall( + address(oracle), + abi.encodeWithSelector(IOracle.getDispute.selector), + abi.encode( + IOracle.Dispute(block.timestamp, disputer, proposer, responseId, requestId, IOracle.DisputeStatus.Escalated) + ) + ); + + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.updateDisputeStatus.selector), abi.encode()); + + uint256 _nonce = vm.getNonce(address(this)); + module = SequentialResolutionModule(computeCreateAddress(address(this), _nonce + 3)); + + submodule1 = new ForTest_ResolutionModule(module, 'module1'); + submodule2 = new ForTest_ResolutionModule(module, 'module2'); + submodule3 = new ForTest_ResolutionModule(module, 'module3'); + + IResolutionModule[] memory _resolutionModules = new IResolutionModule[](3); + _resolutionModules[0] = IResolutionModule(address(submodule1)); + _resolutionModules[1] = IResolutionModule(address(submodule2)); + _resolutionModules[2] = IResolutionModule(address(submodule3)); + + module = new SequentialResolutionModule(oracle, _resolutionModules); + } +} + +/** + * @title SequentialResolutionModule Unit tests + */ +contract SequentialResolutionModule_UnitTest is Base { + function test_setupRequestCallsAllSubmodules() public { + bytes memory _submodule1Data = abi.encode('submodule1Data'); + bytes memory _submodule2Data = abi.encode('submodule2Data'); + bytes memory _submodule3Data = abi.encode('submodule3Data'); + + bytes[] memory _data = new bytes[](3); + _data[0] = _submodule1Data; + _data[1] = _submodule2Data; + _data[2] = _submodule3Data; + + vm.expectCall( + address(submodule1), abi.encodeWithSelector(IModule.setupRequest.selector, requestId, _submodule1Data) + ); + vm.expectCall( + address(submodule2), abi.encodeWithSelector(IModule.setupRequest.selector, requestId, _submodule2Data) + ); + vm.expectCall( + address(submodule3), abi.encodeWithSelector(IModule.setupRequest.selector, requestId, _submodule3Data) + ); + + vm.prank(address(oracle)); + module.setupRequest(requestId, abi.encode(_data)); + } + + function test_setupRequestRevertsIfNotOracle() public { + vm.prank(makeAddr('other_sender')); + vm.expectRevert(abi.encodeWithSelector(IModule.Module_OnlyOracle.selector)); + module.setupRequest(requestId, abi.encode()); + } + + function test_moduleName() public { + assertEq(module.moduleName(), 'SequentialResolutionModule[module1, module2, module3]'); + } + + function test_getDisputeCallsManager(bytes32 _disputeId) public { + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.getDispute.selector, _disputeId)); + module.getDispute(_disputeId); + } + + function testReverts_startResolutionIfNotOracle() public { + vm.prank(makeAddr('other_sender')); + vm.expectRevert(abi.encodeWithSelector(IModule.Module_OnlyOracle.selector)); + module.startResolution(disputeId); + } + + function test_startResolutionCallsFirstModule() public { + vm.expectCall(address(submodule1), abi.encodeWithSelector(IResolutionModule.startResolution.selector, disputeId)); + + vm.prank(address(oracle)); + module.startResolution(disputeId); + } + + function testReverts_resolveDisputeIfNotOracle() public { + vm.prank(makeAddr('other_sender')); + vm.expectRevert(abi.encodeWithSelector(IModule.Module_OnlyOracle.selector)); + module.resolveDispute(disputeId); + } + + function test_resolveDisputeCallsFirstModuleAndResolvesIfWon() public { + submodule1.forTest_setResponseStatus(IOracle.DisputeStatus.Won); + + vm.expectCall( + address(module), + abi.encodeWithSelector(IOracle.updateDisputeStatus.selector, disputeId, IOracle.DisputeStatus.Won) + ); + + vm.expectCall( + address(oracle), + abi.encodeWithSelector(IOracle.updateDisputeStatus.selector, disputeId, IOracle.DisputeStatus.Won) + ); + + vm.prank(address(oracle)); + module.resolveDispute(disputeId); + } + + function test_resolveDisputeCallsFirstModuleAndResolvesIfLost() public { + submodule1.forTest_setResponseStatus(IOracle.DisputeStatus.Lost); + + vm.expectCall( + address(module), + abi.encodeWithSelector(IOracle.updateDisputeStatus.selector, disputeId, IOracle.DisputeStatus.Lost) + ); + + vm.expectCall( + address(oracle), + abi.encodeWithSelector(IOracle.updateDisputeStatus.selector, disputeId, IOracle.DisputeStatus.Lost) + ); + + vm.prank(address(oracle)); + module.resolveDispute(disputeId); + } + + function test_resolveDisputeGoesToTheNextResolutionModule() public { + submodule1.forTest_setResponseStatus(IOracle.DisputeStatus.NoResolution); + + vm.expectCall(address(submodule2), abi.encodeWithSelector(IResolutionModule.startResolution.selector, disputeId)); + + assertEq(module.currentModuleIndex(), 0); + + vm.prank(address(oracle)); + module.resolveDispute(disputeId); + + assertEq(module.currentModuleIndex(), 1); + + vm.expectCall(address(submodule2), abi.encodeWithSelector(IResolutionModule.resolveDispute.selector, disputeId)); + vm.prank(address(oracle)); + module.resolveDispute(disputeId); + } + + function test_resolveDisputeCallsTheManagerWhenThereAreNoMoreSubmodulesLeft() public { + submodule1.forTest_setResponseStatus(IOracle.DisputeStatus.NoResolution); + vm.prank(address(oracle)); + module.resolveDispute(disputeId); + + submodule2.forTest_setResponseStatus(IOracle.DisputeStatus.NoResolution); + vm.prank(address(oracle)); + module.resolveDispute(disputeId); + + vm.expectCall( + address(oracle), + abi.encodeWithSelector(IOracle.updateDisputeStatus.selector, disputeId, IOracle.DisputeStatus.NoResolution) + ); + + submodule3.forTest_setResponseStatus(IOracle.DisputeStatus.NoResolution); + vm.prank(address(oracle)); + module.resolveDispute(disputeId); + } + + function testReverts_updateDisputeStatusNotValidSubmodule() public { + vm.prank(makeAddr('other_sender')); + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_OnlySubmodule.selector) + ); + module.updateDisputeStatus(disputeId, IOracle.DisputeStatus.NoResolution); + } + + function testReverts_updateDisputeStatusNotCurrentSubmodule() public { + vm.prank(address(submodule2)); + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_OnlySubmodule.selector) + ); + module.updateDisputeStatus(disputeId, IOracle.DisputeStatus.NoResolution); + } + + function test_updateDisputeStatusChangesCurrentIndex() public { + vm.expectCall(address(submodule2), abi.encodeWithSelector(IResolutionModule.startResolution.selector, disputeId)); + assertEq(module.currentModuleIndex(), 0); + vm.prank(address(submodule1)); + module.updateDisputeStatus(disputeId, IOracle.DisputeStatus.NoResolution); + assertEq(module.currentModuleIndex(), 1); + } + + function test_updateDisputeStatusCallsManagerWhenResolved() public { + vm.expectCall( + address(oracle), + abi.encodeWithSelector(IOracle.updateDisputeStatus.selector, disputeId, IOracle.DisputeStatus.Won) + ); + vm.prank(address(submodule1)); + module.updateDisputeStatus(disputeId, IOracle.DisputeStatus.Won); + } + + function test_finalizeRequestFinalizesAllSubmodules() public { + vm.expectCall(address(submodule1), abi.encodeWithSelector(IModule.finalizeRequest.selector, requestId)); + vm.expectCall(address(submodule2), abi.encodeWithSelector(IModule.finalizeRequest.selector, requestId)); + vm.expectCall(address(submodule3), abi.encodeWithSelector(IModule.finalizeRequest.selector, requestId)); + vm.prank(address(oracle)); + module.finalizeRequest(requestId); + } + + function testReverts_finalizeRequestCalledByNonOracle() public { + vm.prank(makeAddr('other_sender')); + vm.expectRevert(abi.encodeWithSelector(IModule.Module_OnlyOracle.selector)); + module.finalizeRequest(requestId); + } + + function test_listSubmodulesFullList() public { + IResolutionModule[] memory submodules = module.listSubmodules(0, 3); + assertEq(submodules.length, 3); + assertEq(address(submodules[0]), address(submodule1)); + assertEq(address(submodules[1]), address(submodule2)); + assertEq(address(submodules[2]), address(submodule3)); + } + + function test_listSubmodulesMoreThanExist() public { + IResolutionModule[] memory submodules = module.listSubmodules(0, 200); + assertEq(submodules.length, 3); + assertEq(address(submodules[0]), address(submodule1)); + assertEq(address(submodules[1]), address(submodule2)); + assertEq(address(submodules[2]), address(submodule3)); + } + + function test_listSubmodulesPartialListMiddle() public { + IResolutionModule[] memory submodules = module.listSubmodules(1, 2); + assertEq(submodules.length, 2); + assertEq(address(submodules[0]), address(submodule2)); + assertEq(address(submodules[1]), address(submodule3)); + } + + function test_listSubmodulesPartialListStart() public { + IResolutionModule[] memory submodules = module.listSubmodules(0, 2); + assertEq(submodules.length, 2); + assertEq(address(submodules[0]), address(submodule1)); + assertEq(address(submodules[1]), address(submodule2)); + } +} + +contract SequentialResolutionModuleOracleProxy_UnitTest is Base { + function test_validModuleCallsOracle() public { + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.validModule.selector), abi.encode(true)); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.validModule.selector, requestId, module)); + module.validModule(requestId, address(module)); + } + + function test_getDisputeCallsOracle() public { + IOracle.Dispute memory _dispute; + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.getDispute.selector), abi.encode(_dispute)); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.getDispute.selector, disputeId)); + module.getDispute(disputeId); + } + + function test_getResponseCallsOracle() public { + IOracle.Response memory _response; + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.getResponse.selector), abi.encode(_response)); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.getResponse.selector, responseId)); + module.getResponse(responseId); + } + + function test_getRequestCallsOracle() public { + IOracle.Request memory _request; + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.getRequest.selector), abi.encode(_request)); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.getRequest.selector, requestId)); + module.getRequest(requestId); + } + + function test_getFullRequestCallsOracle() public { + IOracle.FullRequest memory _request; + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.getFullRequest.selector), abi.encode(_request)); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.getFullRequest.selector, requestId)); + module.getFullRequest(requestId); + } + + function test_disputeOfCallsOracle() public { + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.disputeOf.selector), abi.encode(disputeId)); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.disputeOf.selector, requestId)); + module.disputeOf(requestId); + } + + function test_getFinalizedResponseCallsOracle() public { + IOracle.Response memory _response; + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.getFinalizedResponse.selector), abi.encode(_response)); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.getFinalizedResponse.selector, requestId)); + module.getFinalizedResponse(requestId); + } + + function test_getResponseIdsCallsOracle() public { + bytes32[] memory _ids; + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.getResponseIds.selector), abi.encode(_ids)); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.getResponseIds.selector, requestId)); + module.getResponseIds(requestId); + } + + function test_listRequestsCallsOracle() public { + IOracle.FullRequest[] memory _list; + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.listRequests.selector), abi.encode(_list)); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.listRequests.selector, 0, 10)); + module.listRequests(0, 10); + } + + function test_listRequestIdsCallsOracle() public { + bytes32[] memory _list; + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.listRequestIds.selector), abi.encode(_list)); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.listRequestIds.selector, 0, 10)); + module.listRequestIds(0, 10); + } + + function testReverts_createRequestNotSubmodule() public { + IOracle.NewRequest memory _request; + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_OnlySubmodule.selector) + ); + module.createRequest(_request); + } + + function testReverts_createRequestsNotSubmodule() public { + IOracle.NewRequest[] memory _requests; + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_OnlySubmodule.selector) + ); + module.createRequests(_requests); + } + + function test_finalizeCallsOracle() public { + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.finalize.selector), abi.encode()); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.finalize.selector, requestId, responseId)); + vm.prank(address(submodule1)); + module.finalize(requestId, responseId); + } + + function testReverts_finalizeNotSubmodule() public { + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_OnlySubmodule.selector) + ); + module.finalize(requestId, responseId); + } + + function test_escalateDisputeCallsOracle() public { + vm.mockCall(address(oracle), abi.encodeWithSelector(IOracle.escalateDispute.selector), abi.encode()); + vm.expectCall(address(oracle), abi.encodeWithSelector(IOracle.escalateDispute.selector, disputeId)); + vm.prank(address(submodule1)); + module.escalateDispute(disputeId); + } + + function testReverts_escalateDisputeNotSubmodule() public { + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_OnlySubmodule.selector) + ); + module.escalateDispute(disputeId); + } + + function testReverts_disputeResponseNotSubmodule() public { + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_OnlySubmodule.selector) + ); + module.disputeResponse(requestId, responseId); + } + + function testReverts_proposeResponseNotSubmodule() public { + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_OnlySubmodule.selector) + ); + module.proposeResponse(requestId, responseData); + } + + function testReverts_proposeResponseWithProposerNotSubmodule() public { + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_OnlySubmodule.selector) + ); + module.proposeResponse(proposer, requestId, responseData); + } + + function testReverts_disputeResponseNotImplemented() public { + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_NotImplemented.selector) + ); + vm.prank(address(submodule1)); + module.disputeResponse(requestId, responseId); + } + + function testReverts_proposeResponseNotImplemented() public { + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_NotImplemented.selector) + ); + vm.prank(address(submodule1)); + module.proposeResponse(requestId, responseData); + } + + function testReverts_proposeResponseWithProposerNotImplemented() public { + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_NotImplemented.selector) + ); + vm.prank(address(submodule1)); + module.proposeResponse(proposer, requestId, responseData); + } + + function testReverts_createRequestNotImplemented() public { + IOracle.NewRequest memory _request; + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_NotImplemented.selector) + ); + vm.prank(address(submodule1)); + module.createRequest(_request); + } + + function testReverts_createRequestsNotImplemented() public { + IOracle.NewRequest[] memory _requests; + bytes32[] memory _ids; + vm.expectRevert( + abi.encodeWithSelector(ISequentialResolutionModule.SequentialResolutionModule_NotImplemented.selector) + ); + vm.prank(address(submodule1)); + module.createRequests(_requests); + } +}