Skip to content

Commit

Permalink
feat: sequential resolution module (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xShaito authored Aug 9, 2023
1 parent 3fe3d5a commit 8151d8c
Show file tree
Hide file tree
Showing 8 changed files with 677 additions and 10 deletions.
5 changes: 3 additions & 2 deletions solidity/contracts/Module.sol
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
}
Expand Down
18 changes: 18 additions & 0 deletions solidity/contracts/ModuleData.sol
Original file line number Diff line number Diff line change
@@ -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 {}
}
148 changes: 148 additions & 0 deletions solidity/contracts/modules/SequentialResolutionModule.sol
Original file line number Diff line number Diff line change
@@ -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();
}
}
6 changes: 3 additions & 3 deletions solidity/interfaces/IModule.sol
Original file line number Diff line number Diff line change
@@ -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);
}
7 changes: 7 additions & 0 deletions solidity/interfaces/IModuleData.sol
Original file line number Diff line number Diff line change
@@ -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);
}
22 changes: 22 additions & 0 deletions solidity/interfaces/modules/ISequentialResolutionModule.sol
Original file line number Diff line number Diff line change
@@ -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);
}
11 changes: 6 additions & 5 deletions solidity/test/unit/Oracle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
);
}
Expand Down
Loading

0 comments on commit 8151d8c

Please sign in to comment.