Skip to content

Commit

Permalink
perf: optimize BondedResponseModule (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
gas1cent authored Nov 11, 2023
1 parent 7486383 commit c82e476
Show file tree
Hide file tree
Showing 9 changed files with 851 additions and 1,003 deletions.
13 changes: 5 additions & 8 deletions docs/src/content/modules/response/bonded_response_module.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ The Bonded Response Module is a contract that allows users to propose a response

### Key Methods

- `decodeRequestData(bytes32 _requestId)`: Returns the decoded data for a request.
- `propose(bytes32 _requestId, address _proposer, bytes calldata _responseData)`: Proposes a response for a request, bonding the proposer's tokens.
- `deleteResponse(bytes32 _requestId, bytes32 _responseId, address _proposer)`: Allows a user to delete an undisputed response they proposed before the deadline, releasing the bond.
- `finalizeRequest(bytes32 _requestId, address _finalizer)`: Finalizes the request.
- `decodeRequestData`: Returns the decoded data for a request.
- `propose`: Proposes a response for a request, bonding the proposer's tokens.
- `finalizeRequest`: Finalizes the request.

### Request Parameters

Expand All @@ -24,14 +23,12 @@ The Bonded Response Module is a contract that allows users to propose a response

## 3. Key Mechanisms & Concepts

- Deleting a response: If a proposer realizes the response they've submitted is incorrect, they can delete it. Note that disputed responses cannot be taken back.

- Early finalization: It is possible for pre-dispute modules to atomically calculate the correct response on-chain, decide on the result of a dispute and finalize the request before its deadline.

- Dispute window: Prevents proposers from submitting a response 1 block before the deadline and finalizing it in the next block, leaving disputers no time to dispute the response.

## 4. Gotchas

- In case of no valid responses, a request can be finalized after the deadline and the requester will get back their tokens.
- A proposer might submit a response 1 block before the deadline and finalize it in the next block, making it impossible to dispute.
- Users cannot propose a response after the deadline for a request.
- Users cannot propose a response if an undisputed response has already been proposed.
- Users cannot delete a response after the proposing deadline.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"package.json": "sort-package-json"
},
"dependencies": {
"@defi-wonderland/prophet-core-contracts": "0.0.0-d05a00d0",
"@defi-wonderland/prophet-core-contracts": "0.0.0-a1d2cc55",
"@defi-wonderland/solidity-utils": "0.0.0-3e9c8e8b",
"@openzeppelin/contracts": "^4.9.3",
"ds-test": "https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0",
Expand Down
209 changes: 91 additions & 118 deletions solidity/contracts/modules/response/BondedResponseModule.sol
Original file line number Diff line number Diff line change
@@ -1,118 +1,91 @@
// // SPDX-License-Identifier: MIT
// pragma solidity ^0.8.19;

// // solhint-disable-next-line no-unused-import
// import {Module, IModule} from '@defi-wonderland/prophet-core-contracts/solidity/contracts/Module.sol';
// import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol';
// import {IBondedResponseModule} from '../../../interfaces/modules/response/IBondedResponseModule.sol';

// contract BondedResponseModule is Module, IBondedResponseModule {
// constructor(IOracle _oracle) Module(_oracle) {}

// /// @inheritdoc IModule
// function moduleName() public pure returns (string memory _moduleName) {
// _moduleName = 'BondedResponseModule';
// }

// /// @inheritdoc IBondedResponseModule
// function decodeRequestData(bytes32 _requestId) public view returns (RequestParameters memory _params) {
// _params = abi.decode(requestData[_requestId], (RequestParameters));
// }

// /// @inheritdoc IBondedResponseModule
// function propose(
// bytes32 _requestId,
// address _proposer,
// bytes calldata _responseData,
// address _sender
// ) external onlyOracle returns (IOracle.Response memory _response) {
// RequestParameters memory _params = decodeRequestData(_requestId);

// // Cannot propose after the deadline
// if (block.timestamp >= _params.deadline) revert BondedResponseModule_TooLateToPropose();

// // Cannot propose to a request with a response, unless the response is being disputed
// bytes32[] memory _responseIds = ORACLE.getResponseIds(_requestId);
// uint256 _responsesLength = _responseIds.length;

// if (_responsesLength != 0) {
// bytes32 _disputeId = ORACLE.getResponse(_responseIds[_responsesLength - 1]).disputeId;

// // Allowing one undisputed response at a time
// if (_disputeId == bytes32(0)) revert BondedResponseModule_AlreadyResponded();
// IOracle.Dispute memory _dispute = ORACLE.getDispute(_disputeId);
// // TODO: leaving a note here to re-check this check if a new status is added
// // If the dispute was lost, we assume the proposed answer was correct. DisputeStatus.None should not be reachable due to the previous check.
// if (_dispute.status == IOracle.DisputeStatus.Lost) revert BondedResponseModule_AlreadyResponded();
// }

// _response = IOracle.Response({
// requestId: _requestId,
// disputeId: bytes32(0),
// proposer: _proposer,
// response: _responseData,
// createdAt: block.timestamp
// });

// _params.accountingExtension.bond({
// _bonder: _response.proposer,
// _requestId: _requestId,
// _token: _params.bondToken,
// _amount: _params.bondSize,
// _sender: _sender
// });

// emit ProposeResponse(_requestId, _proposer, _responseData);
// }

// /// @inheritdoc IBondedResponseModule
// function deleteResponse(bytes32 _requestId, bytes32, address _proposer) external onlyOracle {
// RequestParameters memory _params = decodeRequestData(_requestId);

// if (block.timestamp > _params.deadline) revert BondedResponseModule_TooLateToDelete();

// _params.accountingExtension.release({
// _bonder: _proposer,
// _requestId: _requestId,
// _token: _params.bondToken,
// _amount: _params.bondSize
// });
// }

// /// @inheritdoc IBondedResponseModule
// function finalizeRequest(
// bytes32 _requestId,
// address _finalizer
// ) external override(IBondedResponseModule, Module) onlyOracle {
// RequestParameters memory _params = decodeRequestData(_requestId);

// bool _isModule = ORACLE.allowedModule(_requestId, _finalizer);

// if (!_isModule && block.timestamp < _params.deadline) {
// revert BondedResponseModule_TooEarlyToFinalize();
// }

// IOracle.Response memory _response = ORACLE.getFinalizedResponse(_requestId);
// if (_response.createdAt != 0) {
// if (!_isModule && block.timestamp < _response.createdAt + _params.disputeWindow) {
// revert BondedResponseModule_TooEarlyToFinalize();
// }

// _params.accountingExtension.release({
// _bonder: _response.proposer,
// _requestId: _requestId,
// _token: _params.bondToken,
// _amount: _params.bondSize
// });
// }
// emit RequestFinalized(_requestId, _finalizer);
// }

// /// @inheritdoc Module
// function _afterSetupRequest(bytes32, bytes calldata _data) internal view override {
// RequestParameters memory _params = abi.decode(_data, (RequestParameters));
// if (_params.deadline <= block.timestamp) {
// revert BondedResponseModule_InvalidRequest();
// }
// }
// }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// solhint-disable-next-line no-unused-import
import {Module, IModule} from '@defi-wonderland/prophet-core-contracts/solidity/contracts/Module.sol';
import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol';
import {IBondedResponseModule} from '../../../interfaces/modules/response/IBondedResponseModule.sol';

contract BondedResponseModule is Module, IBondedResponseModule {
constructor(IOracle _oracle) Module(_oracle) {}

/// @inheritdoc IModule
function moduleName() public pure returns (string memory _moduleName) {
_moduleName = 'BondedResponseModule';
}

/// @inheritdoc IBondedResponseModule
function decodeRequestData(bytes calldata _data) public pure returns (RequestParameters memory _params) {
_params = abi.decode(_data, (RequestParameters));
}

/// @inheritdoc IBondedResponseModule
function propose(
IOracle.Request calldata _request,
IOracle.Response calldata _response,
address _sender
) external onlyOracle {
RequestParameters memory _params = decodeRequestData(_request.responseModuleData);

// Cannot propose after the deadline
if (block.timestamp >= _params.deadline) revert BondedResponseModule_TooLateToPropose();

// Cannot propose to a request with a response, unless the response is being disputed
bytes32[] memory _responseIds = ORACLE.getResponseIds(_response.requestId);
uint256 _responsesLength = _responseIds.length;

if (_responsesLength != 0) {
bytes32 _disputeId = ORACLE.disputeOf(_responseIds[_responsesLength - 1]);

// Allowing one undisputed response at a time
if (_disputeId == bytes32(0)) revert BondedResponseModule_AlreadyResponded();
IOracle.DisputeStatus _status = ORACLE.disputeStatus(_disputeId);
// TODO: leaving a note here to re-check this check if a new status is added
// If the dispute was lost, we assume the proposed answer was correct. DisputeStatus.None should not be reachable due to the previous check.
if (_status == IOracle.DisputeStatus.Lost) revert BondedResponseModule_AlreadyResponded();
}

_params.accountingExtension.bond({
_bonder: _response.proposer,
_requestId: _response.requestId,
_token: _params.bondToken,
_amount: _params.bondSize,
_sender: _sender
});

emit ResponseProposed(_response.requestId, _response, block.number);
}

/// @inheritdoc IBondedResponseModule
function finalizeRequest(
IOracle.Request calldata _request,
IOracle.Response calldata _response,
address _finalizer
) external override(IBondedResponseModule, Module) onlyOracle {
RequestParameters memory _params = decodeRequestData(_request.responseModuleData);

// TODO: If deadline has passed, we can skip the caller validation
bool _isModule = ORACLE.allowedModule(_response.requestId, _finalizer);

if (!_isModule && block.timestamp < _params.deadline) {
revert BondedResponseModule_TooEarlyToFinalize();
}

uint256 _responseCreatedAt = ORACLE.createdAt(_getId(_response));

if (_responseCreatedAt != 0) {
if (!_isModule && block.timestamp < _responseCreatedAt + _params.disputeWindow) {
revert BondedResponseModule_TooEarlyToFinalize();
}

_params.accountingExtension.release({
_bonder: _response.proposer,
_requestId: _response.requestId,
_token: _params.bondToken,
_amount: _params.bondSize
});
}

emit RequestFinalized(_response.requestId, _response, _finalizer);
}
}
Loading

0 comments on commit c82e476

Please sign in to comment.