From f5d18c776eea7af92c7a1df85da6403050fb8ffe Mon Sep 17 00:00:00 2001 From: Gas <86567384+gas1cent@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:49:21 +0400 Subject: [PATCH] refactor: integration tests (#32) * feat: update the core package * refactor: clean up `IntegrationBase`, add helpers * refactor: clean up arbitration tests * refactor: clean up dispute escalation tests * refactor: clean up finalization tests * refactor: clean up payments tests * refactor: clean up response proposal tests * refactor: clean up request creation tests * refactor: clean up response dispute tests * refactor: clean up root verification tests * refactor: clean up accounting tests * refactor: clean up bond escalation tests * refactor: remove an unused helper * test: additional checks for the request creation * test: additional checks and comments for dispute tests * style: run linter --- package.json | 6 +- .../integration/AccountingExtension.t.sol | 142 +++++--- solidity/test/integration/Arbitration.t.sol | 116 ++----- .../test/integration/BondEscalation.t.sol | 327 ++++++++---------- .../test/integration/EscalateDispute.t.sol | 191 +++++----- solidity/test/integration/Finalization.t.sol | 272 ++++----------- solidity/test/integration/IntegrationBase.sol | 147 +++++++- solidity/test/integration/Payments.t.sol | 310 +++++++---------- .../test/integration/RequestCreation.t.sol | 322 +++++++---------- .../test/integration/ResponseDispute.t.sol | 212 ++++-------- .../test/integration/ResponseProposal.t.sol | 151 ++++---- .../test/integration/RootVerification.t.sol | 174 ++++------ solidity/test/utils/Helpers.sol | 19 +- yarn.lock | 8 +- 14 files changed, 1014 insertions(+), 1383 deletions(-) diff --git a/package.json b/package.json index 91b3245a..d7808a25 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,9 @@ "package.json": "sort-package-json" }, "dependencies": { - "@defi-wonderland/prophet-core-contracts": "0.0.0-ad870035", + "@defi-wonderland/prophet-core-contracts": "0.0.0-49fc8fa6", "@defi-wonderland/solidity-utils": "0.0.0-3e9c8e8b", "@openzeppelin/contracts": "^4.9.3", - "ds-test": "https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0", - "forge-std": "https://github.com/foundry-rs/forge-std.git#f73c73d2018eb6a111f35e4dae7b4f27401e9421", "solmate": "https://github.com/transmissions11/solmate.git#bfc9c25865a274a7827fea5abf6e4fb64fc64e6c" }, "devDependencies": { @@ -52,6 +50,8 @@ "@typechain/truffle-v5": "8.0.2", "@typechain/web3-v1": "6.0.2", "dotenv-cli": "7.2.1", + "ds-test": "https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0", + "forge-std": "https://github.com/foundry-rs/forge-std.git#f73c73d2018eb6a111f35e4dae7b4f27401e9421", "fs-extra": "10.1.0", "husky": "8.0.3", "lint-staged": "13.2.2", diff --git a/solidity/test/integration/AccountingExtension.t.sol b/solidity/test/integration/AccountingExtension.t.sol index 7590a7b1..3ea7348c 100644 --- a/solidity/test/integration/AccountingExtension.t.sol +++ b/solidity/test/integration/AccountingExtension.t.sol @@ -6,29 +6,34 @@ import './IntegrationBase.sol'; contract Integration_AccountingExtension is IntegrationBase { address public user = makeAddr('user'); - function test_depositERC20(uint256 _initialBalance, uint256 _depositAmount) public { - vm.assume(_initialBalance >= _depositAmount); - _forBondDepositERC20(_accountingExtension, user, usdc, _depositAmount, _initialBalance); - // Check: is virtual balance updated? - assertEq(_depositAmount, _accountingExtension.balanceOf(user, usdc)); - // Check: is token contract balance updated? - assertEq(_initialBalance - _depositAmount, usdc.balanceOf(user)); - } + function setUp() public override { + super.setUp(); - function test_withdrawERC20(uint256 _initialBalance, uint256 _depositAmount, uint256 _withdrawAmount) public { - vm.assume(_withdrawAmount <= _depositAmount); - // Deposit some USDC - _forBondDepositERC20(_accountingExtension, user, usdc, _depositAmount, _initialBalance); + // Full allowance for both tokens + vm.prank(user); + usdc.approve(address(_accountingExtension), type(uint256).max); vm.prank(user); - _accountingExtension.withdraw(usdc, _withdrawAmount); + weth.approve(address(_accountingExtension), type(uint256).max); + } + + /** + * @notice Depositing ERC20 should update the virtual balance and the token contract balance + */ + function test_depositERC20(uint256 _initialBalance, uint256 _depositAmount) public { + vm.assume(_initialBalance >= _depositAmount); + _deposit(_accountingExtension, user, usdc, _depositAmount, _initialBalance); // Check: is virtual balance updated? - assertEq(_depositAmount - _withdrawAmount, _accountingExtension.balanceOf(user, usdc)); + assertEq(_depositAmount, _accountingExtension.balanceOf(user, usdc)); + // Check: is token contract balance updated? - assertEq(_initialBalance - _depositAmount + _withdrawAmount, usdc.balanceOf(user)); + assertEq(_initialBalance - _depositAmount, usdc.balanceOf(user)); } + /** + * @notice Depositing more than the user's balance should revert + */ function test_depositERC20_invalidAmount(uint256 _initialBalance, uint256 _invalidDepositAmount) public { vm.assume(_invalidDepositAmount > _initialBalance); deal(address(usdc), user, _initialBalance); @@ -43,13 +48,33 @@ contract Integration_AccountingExtension is IntegrationBase { vm.stopPrank(); } + /** + * @notice Withdrawing ERC20 should update the virtual balance and the token contract balance + */ + function test_withdrawERC20(uint256 _initialBalance, uint256 _depositAmount, uint256 _withdrawAmount) public { + vm.assume(_withdrawAmount <= _depositAmount); + _deposit(_accountingExtension, user, usdc, _depositAmount, _initialBalance); + + vm.prank(user); + _accountingExtension.withdraw(usdc, _withdrawAmount); + + // Check: is virtual balance updated? + assertEq(_depositAmount - _withdrawAmount, _accountingExtension.balanceOf(user, usdc)); + + // Check: is token contract balance updated? + assertEq(_initialBalance - _depositAmount + _withdrawAmount, usdc.balanceOf(user)); + } + + /** + * @notice Withdrawing more than the user's virtual balance should revert + */ function test_withdrawERC20_insufficientFunds( uint256 _initialBalance, uint256 _depositAmount, uint256 _withdrawAmount ) public { vm.assume(_withdrawAmount > _depositAmount); - _forBondDepositERC20(_accountingExtension, user, usdc, _depositAmount, _initialBalance); + _deposit(_accountingExtension, user, usdc, _depositAmount, _initialBalance); // Check: does it revert if trying to withdraw an amount greater than virtual balance? vm.expectRevert(IAccountingExtension.AccountingExtension_InsufficientFunds.selector); @@ -57,13 +82,16 @@ contract Integration_AccountingExtension is IntegrationBase { _accountingExtension.withdraw(usdc, _withdrawAmount); } + /** + * @notice Withdrawing more WETH than was deposited by the user should revert + */ function test_withdrawETH_insufficientFunds( uint256 _initialBalance, uint256 _depositAmount, uint256 _withdrawAmount ) public { vm.assume(_withdrawAmount > _depositAmount); - _forBondDepositERC20(_accountingExtension, user, IERC20(address(weth)), _depositAmount, _initialBalance); + _deposit(_accountingExtension, user, weth, _depositAmount, _initialBalance); // Check: does it revert if trying to withdraw an amount greater than virtual balance? vm.expectRevert(IAccountingExtension.AccountingExtension_InsufficientFunds.selector); @@ -71,52 +99,56 @@ contract Integration_AccountingExtension is IntegrationBase { _accountingExtension.withdraw(weth, _withdrawAmount); } + /** + * @notice Withdrawing the bonded funds should revert + */ function test_withdrawBondedFunds(uint256 _initialBalance, uint256 _bondAmount) public { vm.assume(_bondAmount > 0); - _forBondDepositERC20(_accountingExtension, user, usdc, _bondAmount, _initialBalance); - - HttpRequestModule _requestModule = new HttpRequestModule(oracle); - BondedResponseModule _responseModule = new BondedResponseModule(oracle); - BondedDisputeModule _bondedDisputeModule = new BondedDisputeModule(oracle); - - IOracle.Request memory _request = IOracle.Request({ - nonce: 0, - requester: user, - requestModuleData: abi.encode( - IHttpRequestModule.RequestParameters({ - url: '', - method: IHttpRequestModule.HttpMethod.GET, - body: '', - accountingExtension: _accountingExtension, - paymentToken: usdc, - paymentAmount: _bondAmount - }) - ), - responseModuleData: abi.encode( - IBondedResponseModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: usdc, - bondSize: _bondAmount, - deadline: block.timestamp + BLOCK_TIME * 600, - disputeWindow: _baseDisputeWindow - }) - ), - disputeModuleData: abi.encode(), - resolutionModuleData: abi.encode(), - finalityModuleData: abi.encode(), - requestModule: address(_requestModule), - responseModule: address(_responseModule), - disputeModule: address(_bondedDisputeModule), - resolutionModule: address(0), - finalityModule: address(0) - }); + _deposit(_accountingExtension, user, usdc, _bondAmount, _initialBalance); + + mockRequest.requestModuleData = abi.encode( + IHttpRequestModule.RequestParameters({ + url: _expectedUrl, + body: _expectedBody, + method: _expectedMethod, + accountingExtension: _accountingExtension, + paymentToken: usdc, + paymentAmount: _bondAmount + }) + ); + + mockRequest.requester = user; vm.startPrank(user); _accountingExtension.approveModule(address(_requestModule)); - oracle.createRequest(_request, _ipfsHash); + oracle.createRequest(mockRequest, _ipfsHash); + // Check: does it revert if trying to withdraw an amount that is bonded to a request? vm.expectRevert(IAccountingExtension.AccountingExtension_InsufficientFunds.selector); _accountingExtension.withdraw(usdc, _bondAmount); vm.stopPrank(); } + + /** + * @notice Deposits the specified amount of tokens into the accounting extension + * + * @param _accounting The accounting extension + * @param _depositor The depositor + * @param _token The token to deposit + * @param _depositAmount The amount to deposit + * @param _balanceIncrease The amount to increase the depositor's initial balance by + */ + function _deposit( + IAccountingExtension _accounting, + address _depositor, + IERC20 _token, + uint256 _depositAmount, + uint256 _balanceIncrease + ) internal { + vm.assume(_balanceIncrease >= _depositAmount); + deal(address(_token), _depositor, _balanceIncrease); + + vm.prank(_depositor); + _accounting.deposit(_token, _depositAmount); + } } diff --git a/solidity/test/integration/Arbitration.t.sol b/solidity/test/integration/Arbitration.t.sol index 58a4f74b..6f9235e5 100644 --- a/solidity/test/integration/Arbitration.t.sol +++ b/solidity/test/integration/Arbitration.t.sol @@ -1,19 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import './IntegrationBase.sol'; import {IArbitrator} from '../../interfaces/IArbitrator.sol'; import {MockAtomicArbitrator} from '../mocks/MockAtomicArbitrator.sol'; +import './IntegrationBase.sol'; contract Integration_Arbitration is IntegrationBase { MockAtomicArbitrator internal _mockAtomicArbitrator; - IOracle.Request internal _request; - IOracle.Response internal _response; - IOracle.Dispute internal _dispute; - - bytes32 _requestId; - bytes32 _responseId; - bytes32 _disputeId; function setUp() public override { super.setUp(); @@ -21,28 +14,27 @@ contract Integration_Arbitration is IntegrationBase { vm.prank(governance); _mockAtomicArbitrator = new MockAtomicArbitrator(oracle); - _expectedDeadline = block.timestamp + BLOCK_TIME * 600; - - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedBondSize, _expectedBondSize); - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); - _forBondDepositERC20(_accountingExtension, disputer, usdc, _expectedBondSize, _expectedBondSize); + _deposit(_accountingExtension, requester, usdc, _expectedReward); + _deposit(_accountingExtension, proposer, usdc, _expectedBondSize); + _deposit(_accountingExtension, disputer, usdc, _expectedBondSize); } function test_resolveCorrectDispute_twoStep() public { - _setupDispute(address(_mockArbitrator)); + bytes32 _disputeId = _setupDispute(address(_mockArbitrator)); + // Check: is the dispute status unknown before starting the resolution? assertEq(uint256(_arbitratorModule.getStatus(_disputeId)), uint256(IArbitratorModule.ArbitrationStatus.Unknown)); // First step: escalating the dispute vm.prank(disputer); - oracle.escalateDispute(_request, _response, _dispute); + oracle.escalateDispute(mockRequest, mockResponse, mockDispute); // Check: is the dispute status active after starting the resolution? assertEq(uint256(_arbitratorModule.getStatus(_disputeId)), uint256(IArbitratorModule.ArbitrationStatus.Active)); // Second step: resolving the dispute vm.prank(disputer); - oracle.resolveDispute(_request, _response, _dispute); + oracle.resolveDispute(mockRequest, mockResponse, mockDispute); // Check: is the dispute status resolved after calling resolve? assertEq(uint256(_arbitratorModule.getStatus(_disputeId)), uint256(IArbitratorModule.ArbitrationStatus.Resolved)); @@ -56,19 +48,19 @@ contract Integration_Arbitration is IntegrationBase { assertEq(_disputerBalance, _expectedBondSize * 2); // Check: does the proposer get its bond slashed? - uint256 _proposerBondedAmount = _accountingExtension.bondedAmountOf(proposer, usdc, _requestId); + uint256 _proposerBondedAmount = _accountingExtension.bondedAmountOf(proposer, usdc, _getId(mockRequest)); assertEq(_proposerBondedAmount, 0); } function test_resolveCorrectDispute_atomically() public { - _setupDispute(address(_mockAtomicArbitrator)); + bytes32 _disputeId = _setupDispute(address(_mockAtomicArbitrator)); // Check: is the dispute status unknown before starting the resolution? assertEq(uint256(_arbitratorModule.getStatus(_disputeId)), uint256(IArbitratorModule.ArbitrationStatus.Unknown)); // First step: escalating the dispute vm.prank(disputer); - oracle.escalateDispute(_request, _response, _dispute); + oracle.escalateDispute(mockRequest, mockResponse, mockDispute); // Check: is the dispute status resolved after calling resolve? assertEq(uint256(_arbitratorModule.getStatus(_disputeId)), uint256(IArbitratorModule.ArbitrationStatus.Resolved)); @@ -82,18 +74,18 @@ contract Integration_Arbitration is IntegrationBase { assertEq(_disputerBalance, _expectedBondSize * 2); // Check: does the proposer get its bond slashed? - uint256 _proposerBondedAmount = _accountingExtension.bondedAmountOf(proposer, usdc, _requestId); + uint256 _proposerBondedAmount = _accountingExtension.bondedAmountOf(proposer, usdc, _getId(mockRequest)); assertEq(_proposerBondedAmount, 0); } function test_resolveIncorrectDispute_twoStep() public { - _setupDispute(address(_mockArbitrator)); + bytes32 _disputeId = _setupDispute(address(_mockArbitrator)); // Check: is the dispute status unknown before starting the resolution? assertEq(uint256(_arbitratorModule.getStatus(_disputeId)), uint256(IArbitratorModule.ArbitrationStatus.Unknown)); // First step: escalating the dispute vm.prank(disputer); - oracle.escalateDispute(_request, _response, _dispute); + oracle.escalateDispute(mockRequest, mockResponse, mockDispute); // Check: is the dispute status active after starting the resolution? assertEq(uint256(_arbitratorModule.getStatus(_disputeId)), uint256(IArbitratorModule.ArbitrationStatus.Active)); @@ -107,7 +99,7 @@ contract Integration_Arbitration is IntegrationBase { // Second step: resolving the dispute vm.prank(disputer); - oracle.resolveDispute(_request, _response, _dispute); + oracle.resolveDispute(mockRequest, mockResponse, mockDispute); // Check: is the dispute status resolved after calling resolve? assertEq(uint256(_arbitratorModule.getStatus(_disputeId)), uint256(IArbitratorModule.ArbitrationStatus.Resolved)); @@ -121,12 +113,12 @@ contract Integration_Arbitration is IntegrationBase { assertEq(_proposerBalance, _expectedBondSize * 2); // Check: does the disputer get its bond slashed? - uint256 _disputerBondedAmount = _accountingExtension.bondedAmountOf(disputer, usdc, _requestId); + uint256 _disputerBondedAmount = _accountingExtension.bondedAmountOf(disputer, usdc, _getId(mockRequest)); assertEq(_disputerBondedAmount, 0); } function test_resolveIncorrectDispute_atomically() public { - _setupDispute(address(_mockAtomicArbitrator)); + bytes32 _disputeId = _setupDispute(address(_mockAtomicArbitrator)); // Check: is the dispute status unknown before starting the resolution? assertEq(uint256(_arbitratorModule.getStatus(_disputeId)), uint256(IArbitratorModule.ArbitrationStatus.Unknown)); @@ -140,7 +132,7 @@ contract Integration_Arbitration is IntegrationBase { // First step: escalating and resolving the dispute vm.prank(disputer); - oracle.escalateDispute(_request, _response, _dispute); + oracle.escalateDispute(mockRequest, mockResponse, mockDispute); // Check: is the dispute status resolved after calling escalate? assertEq(uint256(_arbitratorModule.getStatus(_disputeId)), uint256(IArbitratorModule.ArbitrationStatus.Resolved)); @@ -154,68 +146,20 @@ contract Integration_Arbitration is IntegrationBase { assertEq(_proposerBalance, _expectedBondSize * 2); // Check: does the disputer get its bond slashed? - uint256 _disputerBondedAmount = _accountingExtension.bondedAmountOf(disputer, usdc, _requestId); + uint256 _disputerBondedAmount = _accountingExtension.bondedAmountOf(disputer, usdc, _getId(mockRequest)); assertEq(_disputerBondedAmount, 0); } - function _setupDispute(address _arbitrator) internal { - _request = IOracle.Request({ - nonce: 0, - requester: requester, - requestModuleData: abi.encode( - IHttpRequestModule.RequestParameters({ - url: _expectedUrl, - method: _expectedMethod, - body: _expectedBody, - accountingExtension: _accountingExtension, - paymentToken: IERC20(USDC_ADDRESS), - paymentAmount: _expectedReward - }) - ), - responseModuleData: abi.encode( - IBondedResponseModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize, - deadline: _expectedDeadline, - disputeWindow: _baseDisputeWindow - }) - ), - disputeModuleData: abi.encode( - IBondedDisputeModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize - }) - ), - resolutionModuleData: abi.encode(_arbitrator), - finalityModuleData: abi.encode( - ICallbackModule.RequestParameters({target: address(_mockCallback), data: abi.encode(_expectedCallbackValue)}) - ), - requestModule: address(_requestModule), - responseModule: address(_responseModule), - disputeModule: address(_bondedDisputeModule), - resolutionModule: address(_arbitratorModule), - finalityModule: address(_callbackModule) - }); - - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_request, _ipfsHash); - vm.stopPrank(); - - _response = IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode('response')}); - - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_request, _response); - vm.stopPrank(); - - _dispute = IOracle.Dispute({proposer: proposer, disputer: disputer, requestId: _requestId, responseId: _responseId}); - - vm.startPrank(disputer); - _accountingExtension.approveModule(address(_bondedDisputeModule)); - _disputeId = oracle.disputeResponse(_request, _response, _dispute); - vm.stopPrank(); + function _setArbitrator(address _arbitrator) internal { + mockRequest.resolutionModuleData = abi.encode(IArbitratorModule.RequestParameters({arbitrator: _arbitrator})); + } + + function _setupDispute(address _arbitrator) internal returns (bytes32 _disputeId) { + _setArbitrator(_arbitrator); + _resetMockIds(); + + _createRequest(); + _proposeResponse(); + _disputeId = _disputeResponse(); } } diff --git a/solidity/test/integration/BondEscalation.t.sol b/solidity/test/integration/BondEscalation.t.sol index c67436b4..555bcc56 100644 --- a/solidity/test/integration/BondEscalation.t.sol +++ b/solidity/test/integration/BondEscalation.t.sol @@ -4,118 +4,123 @@ pragma solidity ^0.8.19; import './IntegrationBase.sol'; contract Integration_BondEscalation is IntegrationBase { + address internal _secondDisputer = makeAddr('secondDisputer'); + address internal _secondProposer = makeAddr('secondProposer'); + address internal _thirdProposer = makeAddr('thirdProposer'); + bytes internal _responseData = abi.encode('response'); + bytes32 internal _requestId; bytes32 internal _responseId; bytes32 internal _disputeId; + uint256 internal _bondEscalationDeadline; uint256 internal _tyingBuffer = 1 days; uint256 internal _disputeWindow = 3 days; - IOracle.Request internal _request; - IOracle.Response internal _response; - IOracle.Dispute internal _dispute; - // TODO: There is a bug in the accounting, try with pledge size = 1 ether - // uint256 internal _pledgeSize = 5 ether; uint256 internal _pledgeSize = _expectedBondSize; function setUp() public override { super.setUp(); + _expectedDeadline = block.timestamp + 10 days; _bondEscalationDeadline = block.timestamp + 5 days; - _request = IOracle.Request({ - nonce: 0, - requester: requester, - requestModuleData: abi.encode( - IHttpRequestModule.RequestParameters({ - url: _expectedUrl, - method: _expectedMethod, - body: _expectedBody, - accountingExtension: _bondEscalationAccounting, - paymentToken: usdc, - paymentAmount: _expectedReward - }) - ), - responseModuleData: abi.encode( - IBondedResponseModule.RequestParameters({ - accountingExtension: _bondEscalationAccounting, - bondToken: usdc, - bondSize: _expectedBondSize, - deadline: _expectedDeadline, - disputeWindow: _baseDisputeWindow - }) - ), - disputeModuleData: abi.encode( - IBondEscalationModule.RequestParameters({ - accountingExtension: _bondEscalationAccounting, - bondToken: usdc, - bondSize: _pledgeSize, - maxNumberOfEscalations: 10, - bondEscalationDeadline: _bondEscalationDeadline, - tyingBuffer: _tyingBuffer, - disputeWindow: _disputeWindow - }) - ), - resolutionModuleData: abi.encode(_mockArbitrator), - finalityModuleData: abi.encode( - ICallbackModule.RequestParameters({target: address(_mockCallback), data: abi.encode(_expectedCallbackValue)}) - ), - requestModule: address(_requestModule), - responseModule: address(_responseModule), - disputeModule: address(_bondEscalationModule), - resolutionModule: address(_arbitratorModule), - finalityModule: address(_callbackModule) - }); + mockRequest.requestModuleData = abi.encode( + IHttpRequestModule.RequestParameters({ + url: _expectedUrl, + body: _expectedBody, + method: _expectedMethod, + accountingExtension: _bondEscalationAccounting, + paymentToken: usdc, + paymentAmount: _expectedReward + }) + ); - // Requester creates a request - _forBondDepositERC20(_bondEscalationAccounting, requester, usdc, _expectedReward, _expectedReward); - vm.startPrank(requester); + mockRequest.responseModuleData = abi.encode( + IBondedResponseModule.RequestParameters({ + accountingExtension: _bondEscalationAccounting, + bondToken: usdc, + bondSize: _expectedBondSize, + deadline: _expectedDeadline, + disputeWindow: _baseDisputeWindow + }) + ); + + mockRequest.disputeModuleData = abi.encode( + IBondEscalationModule.RequestParameters({ + accountingExtension: _bondEscalationAccounting, + bondToken: usdc, + bondSize: _pledgeSize, + maxNumberOfEscalations: 10, + bondEscalationDeadline: _bondEscalationDeadline, + tyingBuffer: _tyingBuffer, + disputeWindow: _disputeWindow + }) + ); + + mockRequest.disputeModule = address(_bondEscalationModule); + + _resetMockIds(); + + // Set up all approvals + vm.prank(requester); _bondEscalationAccounting.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_request, _ipfsHash); - vm.stopPrank(); - // Proposer proposes a response - _response = IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode('response')}); - _forBondDepositERC20(_bondEscalationAccounting, proposer, usdc, _expectedBondSize, _expectedBondSize); - vm.startPrank(proposer); + vm.prank(proposer); _bondEscalationAccounting.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_request, _response); - vm.stopPrank(); - // Disputer disputes the response - _dispute = IOracle.Dispute({proposer: proposer, responseId: _responseId, requestId: _requestId, disputer: disputer}); - _forBondDepositERC20(_bondEscalationAccounting, disputer, usdc, _expectedBondSize, _expectedBondSize); - vm.startPrank(disputer); + vm.prank(disputer); _bondEscalationAccounting.approveModule(address(_bondEscalationModule)); - _disputeId = oracle.disputeResponse(_request, _response, _dispute); - vm.stopPrank(); - } - function test_proposerWins() public { - // Step 1: Proposer pledges against the dispute - _forBondDepositERC20(_bondEscalationAccounting, proposer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(proposer); + vm.prank(proposer); _bondEscalationAccounting.approveModule(address(_bondEscalationModule)); - _bondEscalationModule.pledgeAgainstDispute(_request, _dispute); - vm.stopPrank(); - // Step 2: Disputer doubles down - _forBondDepositERC20(_bondEscalationAccounting, disputer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(disputer); - _bondEscalationModule.pledgeForDispute(_request, _dispute); - vm.stopPrank(); + vm.prank(_secondProposer); + _bondEscalationAccounting.approveModule(address(_responseModule)); - // Step 3: Proposer doubles down - _forBondDepositERC20(_bondEscalationAccounting, proposer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(proposer); - _bondEscalationModule.pledgeAgainstDispute(_request, _dispute); - vm.stopPrank(); + vm.prank(_thirdProposer); + _bondEscalationAccounting.approveModule(address(_responseModule)); - // Step 4: Disputer runs out of capital - // Step 5: External parties see that Disputer's dispute was wrong so they don't join to escalate - // Step 6: Proposer response's is deemed correct and final once the bond escalation window is over + vm.prank(_secondDisputer); + _bondEscalationAccounting.approveModule(address(_bondEscalationModule)); + + // Requester creates a request + _deposit(_bondEscalationAccounting, requester, usdc, _expectedReward); + vm.prank(requester); + _requestId = oracle.createRequest(mockRequest, _ipfsHash); + + // Proposer proposes a response + _deposit(_bondEscalationAccounting, proposer, usdc, _expectedBondSize); + vm.prank(proposer); + _responseId = oracle.proposeResponse(mockRequest, mockResponse); + + // Disputer disputes the response + _deposit(_bondEscalationAccounting, disputer, usdc, _pledgeSize); + vm.prank(disputer); + _disputeId = oracle.disputeResponse(mockRequest, mockResponse, mockDispute); + } + + function test_proposerWins() public { + // // Step 1: Proposer pledges against the dispute + _deposit(_bondEscalationAccounting, proposer, usdc, _pledgeSize); + vm.prank(proposer); + _bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); + + // // Step 2: Disputer doubles down + _deposit(_bondEscalationAccounting, disputer, usdc, _pledgeSize); + vm.prank(disputer); + _bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); + + // // Step 3: Proposer doubles down + _deposit(_bondEscalationAccounting, proposer, usdc, _pledgeSize); + vm.prank(proposer); + _bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); + + // // Step 4: Disputer runs out of capital + // // Step 5: External parties see that Disputer's dispute was wrong so they don't join to escalate + // // Step 6: Proposer response's is deemed correct and final once the bond escalation window is over vm.warp(_expectedDeadline + _tyingBuffer + 1); - _bondEscalationModule.settleBondEscalation(_request, _response, _dispute); + _bondEscalationModule.settleBondEscalation(mockRequest, mockResponse, mockDispute); IOracle.DisputeStatus _disputeStatus = oracle.disputeStatus(_disputeId); assertEq(uint256(_disputeStatus), uint256(IOracle.DisputeStatus.Lost), 'Mismatch: Dispute status'); @@ -139,7 +144,7 @@ contract Integration_BondEscalation is IntegrationBase { // Step 8: Finalize request and check balances again vm.roll(_expectedDeadline + 1 days); - oracle.finalize(_request, _response); + oracle.finalize(mockRequest, mockResponse); // Test: The requester has no balance because he has paid the proposer assertEq(_bondEscalationAccounting.balanceOf(requester, usdc), 0, 'Mismatch: Requester balance'); @@ -157,31 +162,26 @@ contract Integration_BondEscalation is IntegrationBase { function test_proposerLoses() public { // Step 1: Proposer pledges against the dispute - _forBondDepositERC20(_bondEscalationAccounting, proposer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(proposer); - _bondEscalationAccounting.approveModule(address(_bondEscalationModule)); - _bondEscalationModule.pledgeAgainstDispute(_request, _dispute); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, proposer, usdc, _pledgeSize); + vm.prank(proposer); + _bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); // Step 2: Disputer doubles down - _forBondDepositERC20(_bondEscalationAccounting, disputer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(disputer); - _bondEscalationModule.pledgeForDispute(_request, _dispute); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, disputer, usdc, _pledgeSize); + vm.prank(disputer); + _bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); // Step 3: Another party joins the dispute - address _secondDisputer = makeAddr('secondDisputer'); - _forBondDepositERC20(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(_secondDisputer); - _bondEscalationModule.pledgeForDispute(_request, _dispute); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize); + vm.prank(_secondDisputer); + _bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); // Step 4: Proposer runs out of capital and doesn't pledge anymore // External parties see that Proposer's proposal was wrong so they don't join to escalate // Step 5: Proposer response's is deemed incorrect. The bond escalation process along with the tying buffer is terminated vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); - _bondEscalationModule.settleBondEscalation(_request, _response, _dispute); + _bondEscalationModule.settleBondEscalation(mockRequest, mockResponse, mockDispute); IOracle.DisputeStatus _disputeStatus = oracle.disputeStatus(_disputeId); assertEq(uint256(_disputeStatus), uint256(IOracle.DisputeStatus.Won), 'Mismatch: Dispute status'); @@ -212,44 +212,35 @@ contract Integration_BondEscalation is IntegrationBase { ); // Step 7: Other parties can now propose different answers. Another proposer proposes a new answer - address _anotherProposer = makeAddr('anotherProposer'); IOracle.Response memory _secondResponse = - IOracle.Response({proposer: _anotherProposer, requestId: _requestId, response: abi.encode('second response')}); - _forBondDepositERC20(_bondEscalationAccounting, _anotherProposer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(_anotherProposer); - _bondEscalationAccounting.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_request, _secondResponse); - vm.stopPrank(); + IOracle.Response({proposer: _secondProposer, requestId: _requestId, response: abi.encode('second response')}); + _deposit(_bondEscalationAccounting, _secondProposer, usdc, _pledgeSize); + vm.prank(_secondProposer); + _responseId = oracle.proposeResponse(mockRequest, _secondResponse); // Step 8: Disputer disputes Another proposer's answer - // _forBondDepositERC20(_bondEscalationAccounting, disputer, usdc, _pledgeSize, _pledgeSize); + // _deposit(_bondEscalationAccounting, disputer, usdc, _pledgeSize); IOracle.Dispute memory _secondDispute = - IOracle.Dispute({disputer: disputer, responseId: _responseId, requestId: _requestId, proposer: _anotherProposer}); - vm.startPrank(disputer); - _bondEscalationAccounting.approveModule(address(_bondEscalationModule)); - _disputeId = oracle.disputeResponse(_request, _secondResponse, _secondDispute); - vm.stopPrank(); + IOracle.Dispute({disputer: disputer, responseId: _responseId, requestId: _requestId, proposer: _secondProposer}); + vm.prank(disputer); + _disputeId = oracle.disputeResponse(mockRequest, _secondResponse, _secondDispute); // Step 9: Shouldn't be able to pledge for or against the dispute due to the bond escalation deadline being over - _forBondDepositERC20(_bondEscalationAccounting, disputer, usdc, _pledgeSize, _pledgeSize); + _deposit(_bondEscalationAccounting, disputer, usdc, _pledgeSize); vm.expectRevert(IBondEscalationModule.BondEscalationModule_InvalidDispute.selector); - vm.startPrank(disputer); - _bondEscalationModule.pledgeForDispute(_request, _secondDispute); - vm.stopPrank(); + vm.prank(disputer); + _bondEscalationModule.pledgeForDispute(mockRequest, _secondDispute); // Step 10: Because Another proposer's answer is disputed, a third party can propose a new answer - address _thirdProposer = makeAddr('thirdProposer'); IOracle.Response memory _thirdResponse = IOracle.Response({proposer: _thirdProposer, requestId: _requestId, response: abi.encode('third response')}); - _forBondDepositERC20(_bondEscalationAccounting, _thirdProposer, usdc, _expectedBondSize, _expectedBondSize); - vm.startPrank(_thirdProposer); - _bondEscalationAccounting.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_request, _thirdResponse); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, _thirdProposer, usdc, _expectedBondSize); + vm.prank(_thirdProposer); + _responseId = oracle.proposeResponse(mockRequest, _thirdResponse); // Step 11: It goes undisputed for three days, therefore it's deemed correct and final vm.roll(_expectedDeadline + 1); - oracle.finalize(_request, _thirdResponse); + oracle.finalize(mockRequest, _thirdResponse); // Test: The requester has paid out the reward assertEq(_bondEscalationAccounting.balanceOf(requester, usdc), 0, 'Mismatch: Requester balance'); @@ -258,7 +249,7 @@ contract Integration_BondEscalation is IntegrationBase { assertEq(_bondEscalationAccounting.balanceOf(proposer, usdc), 0, 'Mismatch: Proposer balance'); // Test: The second proposer has received nothing - assertEq(_bondEscalationAccounting.balanceOf(_anotherProposer, usdc), 0, 'Mismatch: Another Proposer balance'); + assertEq(_bondEscalationAccounting.balanceOf(_secondProposer, usdc), 0, 'Mismatch: Another Proposer balance'); // Test: The third proposer has received the reward and this bond assertEq( @@ -285,7 +276,7 @@ contract Integration_BondEscalation is IntegrationBase { // So Another proposer gets paid Disputer's bond vm.roll(_expectedDeadline + 2 days); _mockArbitrator.setAnswer(IOracle.DisputeStatus.Lost); - oracle.resolveDispute(_request, _secondResponse, _secondDispute); + oracle.resolveDispute(mockRequest, _secondResponse, _secondDispute); // Test: The requester still has nothing assertEq(_bondEscalationAccounting.balanceOf(requester, usdc), 0, 'Mismatch: Requester balance'); @@ -295,7 +286,7 @@ contract Integration_BondEscalation is IntegrationBase { // Test: The second proposer has received the Disputer's bond assertEq( - _bondEscalationAccounting.balanceOf(_anotherProposer, usdc), + _bondEscalationAccounting.balanceOf(_secondProposer, usdc), _expectedBondSize, 'Mismatch: Another Proposer balance' ); @@ -324,51 +315,42 @@ contract Integration_BondEscalation is IntegrationBase { function test_bondEscalationTied() public { // Step 1: Proposer pledges against the dispute - _forBondDepositERC20(_bondEscalationAccounting, proposer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(proposer); - _bondEscalationAccounting.approveModule(address(_bondEscalationModule)); - _bondEscalationModule.pledgeAgainstDispute(_request, _dispute); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, proposer, usdc, _pledgeSize); + vm.prank(proposer); + _bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); // Step 2: Disputer doubles down - _forBondDepositERC20(_bondEscalationAccounting, disputer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(disputer); - _bondEscalationModule.pledgeForDispute(_request, _dispute); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, disputer, usdc, _pledgeSize); + vm.prank(disputer); + _bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); // Step 3: Proposer doubles down - _forBondDepositERC20(_bondEscalationAccounting, proposer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(proposer); - _bondEscalationModule.pledgeAgainstDispute(_request, _dispute); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, proposer, usdc, _pledgeSize); + vm.prank(proposer); + _bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); // Step 4: Disputer runs out of capital // Step 5: The tying buffer kicks in vm.warp(_bondEscalationDeadline + 1); // Step 6: An external party sees that Proposer's response is incorrect, so they bond the required WETH - address _secondDisputer = makeAddr('secondDisputer'); - _forBondDepositERC20(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(_secondDisputer); - _bondEscalationModule.pledgeForDispute(_request, _dispute); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize); + vm.prank(_secondDisputer); + _bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); // Step 7: They go into the dispute resolution module - oracle.escalateDispute(_request, _response, _dispute); + oracle.escalateDispute(mockRequest, mockResponse, mockDispute); // Step 8: At this point, new answers can be proposed - address _secondProposer = makeAddr('secondProposer'); IOracle.Response memory _secondResponse = IOracle.Response({proposer: _secondProposer, requestId: _requestId, response: abi.encode('second response')}); - _forBondDepositERC20(_bondEscalationAccounting, _secondProposer, usdc, _expectedBondSize, _expectedBondSize); - vm.startPrank(_secondProposer); - _bondEscalationAccounting.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_request, _secondResponse); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, _secondProposer, usdc, _expectedBondSize); + vm.prank(_secondProposer); + _responseId = oracle.proposeResponse(mockRequest, _secondResponse); // Step 9: After some time, the resolution module deems Disputer's dispute as correct _mineBlocks(100); - oracle.resolveDispute(_request, _response, _dispute); + oracle.resolveDispute(mockRequest, mockResponse, mockDispute); IOracle.DisputeStatus _disputeStatus = oracle.disputeStatus(_disputeId); assertEq(uint256(_disputeStatus), uint256(IOracle.DisputeStatus.Won), 'Mismatch: Dispute status'); @@ -407,38 +389,31 @@ contract Integration_BondEscalation is IntegrationBase { function test_externalParties() public { // Step 1: Proposer pledges against the dispute - _forBondDepositERC20(_bondEscalationAccounting, proposer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(proposer); - _bondEscalationAccounting.approveModule(address(_bondEscalationModule)); - _bondEscalationModule.pledgeAgainstDispute(_request, _dispute); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, proposer, usdc, _pledgeSize); + vm.prank(proposer); + _bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); // Step 2: Disputer doesn't have money // Step 3: External actor sees that Proposer's answer was incorrect so they pledge in favor of the dispute - address _secondDisputer = makeAddr('secondDisputer'); - _forBondDepositERC20(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(_secondDisputer); - _bondEscalationModule.pledgeForDispute(_request, _dispute); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, _secondDisputer, usdc, _pledgeSize); + vm.prank(_secondDisputer); + _bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); // Step 4: Proposer doubles down - _forBondDepositERC20(_bondEscalationAccounting, proposer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(proposer); - _bondEscalationAccounting.approveModule(address(_bondEscalationModule)); - _bondEscalationModule.pledgeAgainstDispute(_request, _dispute); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, proposer, usdc, _pledgeSize); + vm.prank(proposer); + _bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); // Step 5: External actor sees that Proposer's answer was incorrect so they pledge in favor of the dispute, tying the bond escalation address _thirdDisputer = makeAddr('thirdDisputer'); - _forBondDepositERC20(_bondEscalationAccounting, _thirdDisputer, usdc, _pledgeSize, _pledgeSize); - vm.startPrank(_thirdDisputer); - _bondEscalationModule.pledgeForDispute(_request, _dispute); - vm.stopPrank(); + _deposit(_bondEscalationAccounting, _thirdDisputer, usdc, _pledgeSize); + vm.prank(_thirdDisputer); + _bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); // Step 6: Proposer loses in resolution vm.warp(_bondEscalationDeadline + 1); - oracle.escalateDispute(_request, _response, _dispute); - oracle.resolveDispute(_request, _response, _dispute); + oracle.escalateDispute(mockRequest, mockResponse, mockDispute); + oracle.resolveDispute(mockRequest, mockResponse, mockDispute); IOracle.DisputeStatus _disputeStatus = oracle.disputeStatus(_disputeId); assertEq(uint256(_disputeStatus), uint256(IOracle.DisputeStatus.Won), 'Mismatch: Dispute status'); diff --git a/solidity/test/integration/EscalateDispute.t.sol b/solidity/test/integration/EscalateDispute.t.sol index eceec8b4..88d3165f 100644 --- a/solidity/test/integration/EscalateDispute.t.sol +++ b/solidity/test/integration/EscalateDispute.t.sol @@ -4,146 +4,113 @@ pragma solidity ^0.8.19; import './IntegrationBase.sol'; contract Integration_EscalateDispute is IntegrationBase { - bytes internal _responseData = abi.encode('response'); - - uint256 internal _blocksDeadline = 600; - - IOracle.Request internal _request; - IOracle.Response internal _response; - IOracle.Dispute internal _dispute; - bytes32 _requestId; - bytes32 _responseId; - bytes32 _disputeId; + bytes32 internal _requestId; + bytes32 internal _disputeId; function setUp() public override { super.setUp(); - _expectedDeadline = block.timestamp + BLOCK_TIME * _blocksDeadline; - } - function test_escalateDispute() public { - /// Create a dispute with bond escalation module and arbitrator module - _createRequestAndDispute( - _bondEscalationAccounting, - _bondEscalationModule, - abi.encode( - IBondEscalationModule.RequestParameters({ - accountingExtension: _bondEscalationAccounting, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize, - maxNumberOfEscalations: 1, - bondEscalationDeadline: _expectedDeadline, - tyingBuffer: 0, - disputeWindow: 0 - }) - ), - _arbitratorModule, - abi.encode(_mockArbitrator) + _deposit(_bondEscalationAccounting, requester, usdc, _expectedReward); + + // Create a request with bond escalation module and arbitrator module + mockRequest.requestModuleData = abi.encode( + IHttpRequestModule.RequestParameters({ + url: _expectedUrl, + method: _expectedMethod, + body: _expectedBody, + accountingExtension: _bondEscalationAccounting, + paymentToken: usdc, + paymentAmount: _expectedReward + }) ); - /// Escalate dispute reverts if dispute does not exist - _dispute.requestId = bytes32(0); + mockRequest.responseModuleData = abi.encode( + IBondedResponseModule.RequestParameters({ + accountingExtension: _bondEscalationAccounting, + bondToken: usdc, + bondSize: _expectedBondSize, + deadline: _expectedDeadline, + disputeWindow: _baseDisputeWindow + }) + ); + + mockRequest.disputeModuleData = abi.encode( + IBondEscalationModule.RequestParameters({ + accountingExtension: _bondEscalationAccounting, + bondToken: usdc, + bondSize: _expectedBondSize, + maxNumberOfEscalations: 1, + bondEscalationDeadline: _expectedDeadline, + tyingBuffer: 0, + disputeWindow: 0 + }) + ); + + mockRequest.disputeModule = address(_bondEscalationModule); + + vm.startPrank(requester); + _bondEscalationAccounting.approveModule(address(_requestModule)); + _requestId = oracle.createRequest(mockRequest, _ipfsHash); + vm.stopPrank(); + + _resetMockIds(); + + // Propose a response and dispute it + _deposit(_bondEscalationAccounting, proposer, usdc, _expectedBondSize); + vm.startPrank(proposer); + _bondEscalationAccounting.approveModule(address(_responseModule)); + oracle.proposeResponse(mockRequest, mockResponse); + vm.stopPrank(); + + _deposit(_bondEscalationAccounting, disputer, usdc, _expectedBondSize); + vm.startPrank(disputer); + _bondEscalationAccounting.approveModule(address(_bondEscalationModule)); + _disputeId = oracle.disputeResponse(mockRequest, mockResponse, mockDispute); + vm.stopPrank(); + } + + function test_escalateDispute() public { + // Escalate dispute reverts if dispute does not exist + mockDispute.requestId = bytes32(0); vm.expectRevert(IOracle.Oracle_InvalidDisputeBody.selector); - oracle.escalateDispute(_request, _response, _dispute); + oracle.escalateDispute(mockRequest, mockResponse, mockDispute); - _dispute.requestId = _requestId; + mockDispute.requestId = _requestId; - /// The oracle should call the dispute module + // The oracle should call the dispute module vm.expectCall( address(_bondEscalationModule), - abi.encodeCall(IDisputeModule.onDisputeStatusChange, (_disputeId, _request, _response, _dispute)) + abi.encodeCall(IDisputeModule.onDisputeStatusChange, (_disputeId, mockRequest, mockResponse, mockDispute)) ); - /// The oracle should call startResolution in the resolution module + // The oracle should call startResolution in the resolution module vm.expectCall( address(_arbitratorModule), - abi.encodeCall(IResolutionModule.startResolution, (_disputeId, _request, _response, _dispute)) + abi.encodeCall(IResolutionModule.startResolution, (_disputeId, mockRequest, mockResponse, mockDispute)) ); - /// The arbitrator module should call the arbitrator - vm.expectCall(address(_mockArbitrator), abi.encodeCall(MockArbitrator.resolve, (_request, _response, _dispute))); + // The arbitrator module should call the arbitrator + vm.expectCall( + address(_mockArbitrator), abi.encodeCall(MockArbitrator.resolve, (mockRequest, mockResponse, mockDispute)) + ); - /// We escalate the dispute + // We escalate the dispute _mineBlocks(_blocksDeadline + 1); - oracle.escalateDispute(_request, _response, _dispute); + oracle.escalateDispute(mockRequest, mockResponse, mockDispute); - /// We check that the dispute was escalated + // We check that the dispute was escalated IOracle.DisputeStatus _disputeStatus = oracle.disputeStatus(_disputeId); assertTrue(_disputeStatus == IOracle.DisputeStatus.Escalated); - /// The BondEscalationModule should now have the escalation status escalated + // The BondEscalationModule should now have the escalation status escalated IBondEscalationModule.BondEscalation memory _bondEscalation = _bondEscalationModule.getEscalation(_requestId); assertTrue(_bondEscalation.status == IBondEscalationModule.BondEscalationStatus.Escalated); - /// The ArbitratorModule should have updated the status of the dispute + // The ArbitratorModule should have updated the status of the dispute assertTrue(_arbitratorModule.getStatus(_disputeId) == IArbitratorModule.ArbitrationStatus.Active); - /// Escalate dispute reverts if dispute is not active + // Escalate dispute reverts if dispute is not active vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_CannotEscalate.selector, _disputeId)); - oracle.escalateDispute(_request, _response, _dispute); - } - - function _createRequestAndDispute( - IAccountingExtension _accounting, - IDisputeModule _disputeModule, - bytes memory _disputeModuleData, - IResolutionModule _resolutionModule, - bytes memory _resolutionModuleData - ) internal { - _forBondDepositERC20(_accounting, requester, usdc, _expectedBondSize, _expectedBondSize); - - _request = IOracle.Request({ - nonce: 0, - requester: requester, - requestModuleData: abi.encode( - IHttpRequestModule.RequestParameters({ - url: _expectedUrl, - method: _expectedMethod, - body: _expectedBody, - accountingExtension: _accounting, - paymentToken: IERC20(USDC_ADDRESS), - paymentAmount: _expectedReward - }) - ), - responseModuleData: abi.encode( - IBondedResponseModule.RequestParameters({ - accountingExtension: _accounting, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize, - deadline: _expectedDeadline, - disputeWindow: _baseDisputeWindow - }) - ), - disputeModuleData: _disputeModuleData, - resolutionModuleData: _resolutionModuleData, - finalityModuleData: abi.encode( - ICallbackModule.RequestParameters({target: address(_mockCallback), data: abi.encode(_expectedCallbackValue)}) - ), - requestModule: address(_requestModule), - responseModule: address(_responseModule), - disputeModule: address(_disputeModule), - resolutionModule: address(_resolutionModule), - finalityModule: address(_callbackModule) - }); - - vm.startPrank(requester); - _accounting.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_request, _ipfsHash); - vm.stopPrank(); - - _response = IOracle.Response({proposer: proposer, requestId: _requestId, response: _responseData}); - - _forBondDepositERC20(_accounting, proposer, usdc, _expectedBondSize, _expectedBondSize); - vm.startPrank(proposer); - _accounting.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_request, _response); - vm.stopPrank(); - - _dispute = IOracle.Dispute({disputer: disputer, proposer: proposer, responseId: _responseId, requestId: _requestId}); - - _forBondDepositERC20(_accounting, disputer, usdc, _expectedBondSize, _expectedBondSize); - vm.startPrank(disputer); - _accounting.approveModule(address(_disputeModule)); - _disputeId = oracle.disputeResponse(_request, _response, _dispute); - vm.stopPrank(); + oracle.escalateDispute(mockRequest, mockResponse, mockDispute); } } diff --git a/solidity/test/integration/Finalization.t.sol b/solidity/test/integration/Finalization.t.sol index 93acc112..76ec96d4 100644 --- a/solidity/test/integration/Finalization.t.sol +++ b/solidity/test/integration/Finalization.t.sol @@ -4,26 +4,29 @@ pragma solidity ^0.8.19; import './IntegrationBase.sol'; contract Integration_Finalization is IntegrationBase { - bytes internal _responseData; - address internal _finalizer = makeAddr('finalizer'); - IOracle.Request internal _request; - IOracle.Response internal _response; - bytes32 _requestId; - bytes32 _responseId; + address internal _callbackTarget = makeAddr('target'); function setUp() public override { super.setUp(); - _expectedDeadline = block.timestamp + BLOCK_TIME * 600; + + vm.etch(_callbackTarget, hex'069420'); + + _setFinalizationModule( + address(_callbackModule), + abi.encode(ICallbackModule.RequestParameters({target: _callbackTarget, data: bytes('')})) + ); + + _deposit(_accountingExtension, requester, usdc, _expectedReward); + _deposit(_accountingExtension, proposer, usdc, _expectedBondSize); + _deposit(_accountingExtension, disputer, usdc, _expectedBondSize); } /** * @notice Test to check if another module can be set as callback module. */ function test_targetIsAnotherModule() public { - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedBondSize, _expectedBondSize); - - _customFinalizationRequest( + _setFinalizationModule( address(_callbackModule), abi.encode( ICallbackModule.RequestParameters({ @@ -33,270 +36,139 @@ contract Integration_Finalization is IntegrationBase { ) ); - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_request, _ipfsHash); - vm.stopPrank(); + _createRequest(); + _proposeResponse(); - _setupFinalizationStage(_request); + // Traveling to the end of the dispute window + vm.roll(_expectedDeadline + 1 + _baseDisputeWindow); - vm.roll(block.number + _baseDisputeWindow); vm.prank(_finalizer); - oracle.finalize(_request, _response); + oracle.finalize(mockRequest, mockResponse); + + // Check: is response finalized? + bytes32 _finalizedResponseId = oracle.finalizedResponseId(_getId(mockRequest)); + assertEq(_finalizedResponseId, _getId(mockResponse)); } /** - * @notice Test to check that finalization data is set and callback calls are made. + * @notice Finalization data is set and callback calls are made. */ function test_makeAndIgnoreLowLevelCalls(bytes memory _calldata) public { - address _callbackTarget = makeAddr('target'); - vm.etch(_callbackTarget, hex'069420'); - - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedBondSize, _expectedBondSize); - - _customFinalizationRequest( + _setFinalizationModule( address(_callbackModule), abi.encode(ICallbackModule.RequestParameters({target: _callbackTarget, data: _calldata})) ); - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - oracle.createRequest(_request, _ipfsHash); - vm.stopPrank(); + _createRequest(); + _proposeResponse(); - _setupFinalizationStage(_request); + // Traveling to the end of the dispute window + vm.roll(_expectedDeadline + 1 + _baseDisputeWindow); // Check: all low-level calls are made? vm.expectCall(_callbackTarget, _calldata); - vm.roll(block.number + _baseDisputeWindow); vm.prank(_finalizer); - oracle.finalize(_request, _response); + oracle.finalize(mockRequest, mockResponse); - bytes32 _finalizedResponse = oracle.getFinalizedResponseId(_requestId); // Check: is response finalized? - assertEq(_finalizedResponse, _responseId); + bytes32 _finalizedResponseId = oracle.finalizedResponseId(_getId(mockRequest)); + assertEq(_finalizedResponseId, _getId(mockResponse)); } /** - * @notice Test to check that finalizing a request that has no response will revert. + * @notice Finalizing a request that has no response reverts. */ function test_revertFinalizeIfNoResponse() public { - address _callbackTarget = makeAddr('target'); - vm.etch(_callbackTarget, hex'069420'); - - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedBondSize, _expectedBondSize); - - _customFinalizationRequest( - address(_callbackModule), - abi.encode(ICallbackModule.RequestParameters({target: _callbackTarget, data: bytes('')})) - ); - - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - oracle.createRequest(_request, _ipfsHash); - vm.stopPrank(); - - IOracle.Response memory _nonExistentResponse = - IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode('repsonse')}); - - vm.prank(_finalizer); + _createRequest(); + mockResponse.response = abi.encode('nonexistent'); // Check: reverts if request has no response? vm.expectRevert(IOracle.Oracle_InvalidFinalizedResponse.selector); - oracle.finalize(_request, _nonExistentResponse); + vm.prank(_finalizer); + oracle.finalize(mockRequest, mockResponse); } /** - * @notice Test to check that finalizing a request with a ongoing dispute with revert. + * @notice Finalizing a request with a ongoing dispute reverts. */ function test_revertFinalizeWithDisputedResponse() public { - address _callbackTarget = makeAddr('target'); - vm.etch(_callbackTarget, hex'069420'); - - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedBondSize, _expectedBondSize); - - _customFinalizationRequest( - address(_callbackModule), - abi.encode(ICallbackModule.RequestParameters({target: _callbackTarget, data: bytes('')})) - ); - - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - oracle.createRequest(_request, _ipfsHash); - vm.stopPrank(); - - _response = IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode('response')}); - - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_request, _response); - vm.stopPrank(); - - _forBondDepositERC20(_accountingExtension, disputer, usdc, _expectedBondSize, _expectedBondSize); - IOracle.Dispute memory _dispute = - IOracle.Dispute({proposer: proposer, disputer: disputer, requestId: _requestId, responseId: _responseId}); - - vm.startPrank(disputer); - _accountingExtension.approveModule(address(_bondedDisputeModule)); - oracle.disputeResponse(_request, _response, _dispute); - vm.stopPrank(); + _createRequest(); + _proposeResponse(); + _disputeResponse(); vm.prank(_finalizer); vm.expectRevert(IOracle.Oracle_InvalidFinalizedResponse.selector); - oracle.finalize(_request, _response); + oracle.finalize(mockRequest, mockResponse); } /** - * @notice Test to check that finalizing a request with a ongoing dispute with revert. + * @notice Finalizing a request with a ongoing dispute reverts. */ function test_revertFinalizeInDisputeWindow(uint256 _block) public { _block = bound(_block, block.number, _expectedDeadline - _baseDisputeWindow - 1); - address _callbackTarget = makeAddr('target'); - vm.etch(_callbackTarget, hex'069420'); - - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedBondSize, _expectedBondSize); - - _customFinalizationRequest( - address(_callbackModule), - abi.encode(ICallbackModule.RequestParameters({target: _callbackTarget, data: bytes('')})) - ); - - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - oracle.createRequest(_request, _ipfsHash); - vm.stopPrank(); - - _response = IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode('response')}); - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - oracle.proposeResponse(_request, _response); - vm.stopPrank(); + _createRequest(); + _proposeResponse(); vm.roll(_block); - vm.prank(_finalizer); + + // Check: reverts if called during the dispute window? vm.expectRevert(IBondedResponseModule.BondedResponseModule_TooEarlyToFinalize.selector); - oracle.finalize(_request, _response); + vm.prank(_finalizer); + oracle.finalize(mockRequest, mockResponse); } + /** - * @notice Test to check that finalizing a request without disputes triggers callback calls and executes without reverting. + * @notice Finalizing a request without disputes triggers callback calls and executes without reverting. */ - function test_finalizeWithUndisputedResponse(bytes calldata _calldata) public { - address _callbackTarget = makeAddr('target'); - vm.etch(_callbackTarget, hex'069420'); - - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedBondSize, _expectedBondSize); - - _customFinalizationRequest( + _setFinalizationModule( address(_callbackModule), abi.encode(ICallbackModule.RequestParameters({target: _callbackTarget, data: _calldata})) ); - vm.expectCall(_callbackTarget, _calldata); - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - oracle.createRequest(_request, _ipfsHash); - vm.stopPrank(); + _createRequest(); + _proposeResponse(); - _setupFinalizationStage(_request); + // Traveling to the end of the dispute window + vm.roll(_expectedDeadline + 1 + _baseDisputeWindow); - vm.roll(block.number + _baseDisputeWindow); + vm.expectCall(_callbackTarget, _calldata); vm.prank(_finalizer); - oracle.finalize(_request, _response); + oracle.finalize(mockRequest, mockResponse); + + // Check: is response finalized? + bytes32 _finalizedResponseId = oracle.finalizedResponseId(_getId(mockRequest)); + assertEq(_finalizedResponseId, _getId(mockResponse)); } /** - * @notice Test to check that finalizing a request before the disputing deadline will revert. + * @notice Finalizing a request before the disputing deadline reverts. */ function test_revertFinalizeBeforeDeadline(bytes calldata _calldata) public { - address _callbackTarget = makeAddr('target'); - vm.etch(_callbackTarget, hex'069420'); - - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedBondSize, _expectedBondSize); - - _customFinalizationRequest( + _setFinalizationModule( address(_callbackModule), abi.encode(ICallbackModule.RequestParameters({target: _callbackTarget, data: _calldata})) ); vm.expectCall(_callbackTarget, _calldata); - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - oracle.createRequest(_request, _ipfsHash); - vm.stopPrank(); - - _response = IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode('response')}); + _createRequest(); + _proposeResponse(); - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - oracle.proposeResponse(_request, _response); - vm.stopPrank(); - - vm.prank(_finalizer); vm.expectRevert(IBondedResponseModule.BondedResponseModule_TooEarlyToFinalize.selector); - oracle.finalize(_request, _response); + vm.prank(_finalizer); + oracle.finalize(mockRequest, mockResponse); } /** - * @notice Internal helper function to setup the finalization stage of a request. + * @notice Updates the finalization module and its data. */ - function _setupFinalizationStage(IOracle.Request memory _requestToFinalize) internal { - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); - _response = IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode('response')}); - - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_requestToFinalize, _response); - vm.stopPrank(); - - vm.roll(_expectedDeadline + 1); - } - - function _customFinalizationRequest(address _finalityModule, bytes memory _finalityModuleData) internal { - _request = IOracle.Request({ - nonce: 0, - requester: requester, - requestModuleData: abi.encode( - IHttpRequestModule.RequestParameters({ - url: _expectedUrl, - method: _expectedMethod, - body: _expectedBody, - accountingExtension: _accountingExtension, - paymentToken: IERC20(USDC_ADDRESS), - paymentAmount: _expectedReward - }) - ), - responseModuleData: abi.encode( - IBondedResponseModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize, - deadline: _expectedDeadline, - disputeWindow: _baseDisputeWindow - }) - ), - disputeModuleData: abi.encode( - IBondedDisputeModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize - }) - ), - resolutionModuleData: abi.encode(_mockArbitrator), - finalityModuleData: _finalityModuleData, - requestModule: address(_requestModule), - responseModule: address(_responseModule), - disputeModule: address(_bondedDisputeModule), - resolutionModule: address(_arbitratorModule), - finalityModule: address(_finalityModule) - }); - _requestId = _getId(_request); + function _setFinalizationModule(address _finalityModule, bytes memory _finalityModuleData) internal { + mockRequest.finalityModule = _finalityModule; + mockRequest.finalityModuleData = _finalityModuleData; + _resetMockIds(); } } diff --git a/solidity/test/integration/IntegrationBase.sol b/solidity/test/integration/IntegrationBase.sol index 967b523e..728cbde3 100644 --- a/solidity/test/integration/IntegrationBase.sol +++ b/solidity/test/integration/IntegrationBase.sol @@ -5,38 +5,40 @@ pragma solidity ^0.8.19; // solhint-disable-next-line no-console import {console} from 'forge-std/console.sol'; -import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; -import {DSTestPlus} from '@defi-wonderland/solidity-utils/solidity/test/DSTestPlus.sol'; -import {Oracle, IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/contracts/Oracle.sol'; +import {IOracle, Oracle} from '@defi-wonderland/prophet-core-contracts/solidity/contracts/Oracle.sol'; import {IDisputeModule} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/modules/dispute/IDisputeModule.sol'; + +import {IFinalityModule} from + '@defi-wonderland/prophet-core-contracts/solidity/interfaces/modules/finality/IFinalityModule.sol'; import {IRequestModule} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/modules/request/IRequestModule.sol'; -import {IResponseModule} from - '@defi-wonderland/prophet-core-contracts/solidity/interfaces/modules/response/IResponseModule.sol'; import {IResolutionModule} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/modules/resolution/IResolutionModule.sol'; -import {IFinalityModule} from - '@defi-wonderland/prophet-core-contracts/solidity/interfaces/modules/finality/IFinalityModule.sol'; +import {IResponseModule} from + '@defi-wonderland/prophet-core-contracts/solidity/interfaces/modules/response/IResponseModule.sol'; +import {DSTestPlus} from '@defi-wonderland/solidity-utils/solidity/test/DSTestPlus.sol'; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {IWETH9} from '../utils/external/IWETH9.sol'; -import {HttpRequestModule, IHttpRequestModule} from '../../contracts/modules/request/HttpRequestModule.sol'; -import {BondedResponseModule, IBondedResponseModule} from '../../contracts/modules/response/BondedResponseModule.sol'; -import {BondedDisputeModule, IBondedDisputeModule} from '../../contracts/modules/dispute/BondedDisputeModule.sol'; -import {ArbitratorModule, IArbitratorModule} from '../../contracts/modules/resolution/ArbitratorModule.sol'; import {AccountingExtension, IAccountingExtension} from '../../contracts/extensions/AccountingExtension.sol'; -import {CallbackModule, ICallbackModule} from '../../contracts/modules/finality/CallbackModule.sol'; -import {BondEscalationModule, IBondEscalationModule} from '../../contracts/modules/dispute/BondEscalationModule.sol'; + import { BondEscalationAccounting, IBondEscalationAccounting } from '../../contracts/extensions/BondEscalationAccounting.sol'; +import {BondEscalationModule, IBondEscalationModule} from '../../contracts/modules/dispute/BondEscalationModule.sol'; +import {BondedDisputeModule, IBondedDisputeModule} from '../../contracts/modules/dispute/BondedDisputeModule.sol'; +import {CallbackModule, ICallbackModule} from '../../contracts/modules/finality/CallbackModule.sol'; +import {HttpRequestModule, IHttpRequestModule} from '../../contracts/modules/request/HttpRequestModule.sol'; +import {ArbitratorModule, IArbitratorModule} from '../../contracts/modules/resolution/ArbitratorModule.sol'; +import {BondedResponseModule, IBondedResponseModule} from '../../contracts/modules/response/BondedResponseModule.sol'; -import {MockCallback} from '../mocks/MockCallback.sol'; import {MockArbitrator} from '../mocks/MockArbitrator.sol'; +import {MockCallback} from '../mocks/MockCallback.sol'; -import {TestConstants} from '../utils/TestConstants.sol'; import {Helpers} from '../utils/Helpers.sol'; +import {TestConstants} from '../utils/TestConstants.sol'; // solhint-enable no-unused-import contract IntegrationBase is DSTestPlus, TestConstants, Helpers { @@ -73,6 +75,7 @@ contract IntegrationBase is DSTestPlus, TestConstants, Helpers { uint256 internal _expectedCallbackValue = 42; uint256 internal _baseDisputeWindow = 120; // blocks bytes32 internal _ipfsHash = bytes32('QmR4uiJH654k3Ta2uLLQ8r'); + uint256 internal _blocksDeadline = 600; function setUp() public virtual { vm.createSelectFork(vm.rpcUrl('optimism'), FORK_BLOCK); @@ -120,6 +123,56 @@ contract IntegrationBase is DSTestPlus, TestConstants, Helpers { _mockCallback = new MockCallback(); _mockArbitrator = new MockArbitrator(); vm.stopPrank(); + + // Set the expected deadline + _expectedDeadline = block.timestamp + BLOCK_TIME * _blocksDeadline; + + // Configure the mock request + mockRequest.requestModuleData = abi.encode( + IHttpRequestModule.RequestParameters({ + url: _expectedUrl, + body: _expectedBody, + method: _expectedMethod, + accountingExtension: _accountingExtension, + paymentToken: usdc, + paymentAmount: _expectedReward + }) + ); + + mockRequest.responseModuleData = abi.encode( + IBondedResponseModule.RequestParameters({ + accountingExtension: _accountingExtension, + bondToken: usdc, + bondSize: _expectedBondSize, + deadline: _expectedDeadline, + disputeWindow: _baseDisputeWindow + }) + ); + + mockRequest.disputeModuleData = abi.encode( + IBondedDisputeModule.RequestParameters({ + accountingExtension: _accountingExtension, + bondToken: usdc, + bondSize: _expectedBondSize + }) + ); + + mockRequest.resolutionModuleData = + abi.encode(IArbitratorModule.RequestParameters({arbitrator: address(_mockArbitrator)})); + + mockRequest.finalityModuleData = abi.encode( + ICallbackModule.RequestParameters({target: address(_mockCallback), data: abi.encode(_expectedCallbackValue)}) + ); + + mockRequest.requestModule = address(_requestModule); + mockRequest.responseModule = address(_responseModule); + mockRequest.disputeModule = address(_bondedDisputeModule); + mockRequest.resolutionModule = address(_arbitratorModule); + mockRequest.finalityModule = address(_callbackModule); + mockRequest.requester = requester; + mockRequest.nonce = uint96(oracle.totalRequestCount()); + + _resetMockIds(); } function _mineBlock() internal { @@ -130,4 +183,68 @@ contract IntegrationBase is DSTestPlus, TestConstants, Helpers { vm.warp(block.timestamp + _blocks * BLOCK_TIME); vm.roll(block.number + _blocks); } + + /** + * @notice Computes the IDs of the mock request, response and dispute and sets them in the mock objects + */ + function _resetMockIds() internal { + // Update the mock response + mockResponse.requestId = _getId(mockRequest); + + // Update the mock dispute + mockDispute.requestId = _getId(mockRequest); + mockDispute.responseId = _getId(mockResponse); + } + + /** + * @notice Pranks the requester and creates a request + * @dev The bond should be deposited into the accounting extension prior to calling this function + */ + function _createRequest() internal returns (bytes32 _requestId) { + vm.startPrank(requester); + _accountingExtension.approveModule(address(_requestModule)); + _requestId = oracle.createRequest(mockRequest, _ipfsHash); + vm.stopPrank(); + } + + /** + * @notice Pranks the proposer and proposes a response + * @dev The bond should be deposited into the accounting extension prior to calling this function + */ + function _proposeResponse() internal returns (bytes32 _responseId) { + vm.startPrank(proposer); + _accountingExtension.approveModule(address(_responseModule)); + _responseId = oracle.proposeResponse(mockRequest, mockResponse); + vm.stopPrank(); + } + + /** + * @notice Pranks the disputer and disputes a response + * @dev The bond should be deposited into the accounting extension prior to calling this function + */ + function _disputeResponse() internal returns (bytes32 _disputeId) { + vm.startPrank(disputer); + _accountingExtension.approveModule(address(_bondedDisputeModule)); + _disputeId = oracle.disputeResponse(mockRequest, mockResponse, mockDispute); + vm.stopPrank(); + } + + /** + * @notice Deposits the specified amount of tokens to the accounting extension + * + * @param _accounting The accounting extension + * @param _depositor The address of the depositor + * @param _token The token to deposit + * @param _amount The amount to deposit + */ + function _deposit(IAccountingExtension _accounting, address _depositor, IERC20 _token, uint256 _amount) internal { + if (_token.balanceOf(_depositor) < _amount) { + deal(address(_token), _depositor, _amount); + } + + vm.startPrank(_depositor); + _token.approve(address(_accounting), _amount); + _accounting.deposit(_token, _amount); + vm.stopPrank(); + } } diff --git a/solidity/test/integration/Payments.t.sol b/solidity/test/integration/Payments.t.sol index 03f72690..542233a9 100644 --- a/solidity/test/integration/Payments.t.sol +++ b/solidity/test/integration/Payments.t.sol @@ -4,112 +4,83 @@ pragma solidity ^0.8.19; import './IntegrationBase.sol'; contract Integration_Payments is IntegrationBase { - bytes32 internal _requestId; - bytes32 internal _responseId; - - function setUp() public override { - super.setUp(); - _expectedDeadline = block.timestamp + BLOCK_TIME * 600; - } - function test_releaseValidResponse_ERC20(uint256 _rewardSize, uint256 _bondSize) public { // Exception to avoid overflow when depositing. vm.assume(_rewardSize < type(uint256).max - _bondSize); - // Requester bonds and creates a request. - _forBondDepositERC20(_accountingExtension, requester, usdc, _rewardSize, _rewardSize); - IOracle.Request memory _erc20Request = _standardRequest(_rewardSize, _bondSize, usdc); - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_erc20Request, _ipfsHash); - vm.stopPrank(); + // Update the parameters of the request. + _setupRequest(_bondSize, _rewardSize, usdc); - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode('response')}); + // Requester bonds and creates a request. + _deposit(_accountingExtension, requester, usdc, _rewardSize); + bytes32 _requestId = _createRequest(); // Proposer bonds and proposes a response. - _forBondDepositERC20(_accountingExtension, proposer, usdc, _bondSize, _bondSize); - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_erc20Request, _response); - vm.stopPrank(); + _deposit(_accountingExtension, proposer, usdc, _bondSize); + _proposeResponse(); - // Check that both users have had their funds bonded. - uint256 _requesterBondedBalanceBefore = _accountingExtension.bondedAmountOf(requester, usdc, _requestId); - assertEq(_requesterBondedBalanceBefore, _rewardSize); + // Check: requester has placed the bond? + assertEq(_accountingExtension.bondedAmountOf(requester, usdc, _requestId), _rewardSize); - uint256 _proposerBondedBalanceBefore = _accountingExtension.bondedAmountOf(proposer, usdc, _requestId); - assertEq(_proposerBondedBalanceBefore, _bondSize); + // Check: proposer has placed the bond? + assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), _bondSize); // Warp to finalization time. vm.roll(_expectedDeadline + _baseDisputeWindow); - // Finalize request/response - oracle.finalize(_erc20Request, _response); - uint256 _requesterBalanceAfter = _accountingExtension.balanceOf(requester, usdc); - uint256 _proposerBalanceAfter = _accountingExtension.balanceOf(proposer, usdc); + // Finalize request/response + oracle.finalize(mockRequest, mockResponse); - // Check: requester paid for response? - assertEq(_requesterBalanceAfter, 0); - // Check: proposer got the reward + the bonded amount back? - assertEq(_proposerBalanceAfter, _rewardSize + _bondSize); + // Check: requester has paid for response? + assertEq(_accountingExtension.balanceOf(requester, usdc), 0); - uint256 _requesterBondedBalanceAfter = _accountingExtension.bondedAmountOf(requester, usdc, _requestId); + // Check: requester has no bonded balance left? + assertEq(_accountingExtension.bondedAmountOf(requester, usdc, _requestId), 0); - uint256 _proposerBondedBalanceAfter = _accountingExtension.bondedAmountOf(proposer, usdc, _requestId); + // Check: proposer got the reward? + assertEq(_accountingExtension.balanceOf(proposer, usdc), _rewardSize + _bondSize); - assertEq(_requesterBondedBalanceAfter, 0); - assertEq(_proposerBondedBalanceAfter, 0); + // Check: proposer has no bonded balance left? + assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), 0); } function test_releaseValidResponse_ETH(uint256 _rewardSize, uint256 _bondSize) public { // Exception to avoid overflow when depositing. vm.assume(_rewardSize < type(uint256).max - _bondSize); - // Requester bonds and creates request. - _forBondDepositERC20(_accountingExtension, requester, IERC20(address(weth)), _rewardSize, _rewardSize); - IOracle.Request memory _ethRequest = _standardRequest(_rewardSize, _bondSize, weth); - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_ethRequest, _ipfsHash); - vm.stopPrank(); + // Update the parameters of the request. + _setupRequest(_bondSize, _rewardSize, weth); - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode('response')}); + // Requester bonds and creates request. + _deposit(_accountingExtension, requester, weth, _rewardSize); + bytes32 _requestId = _createRequest(); // Proposer bonds and creates request. - _forBondDepositERC20(_accountingExtension, proposer, IERC20(address(weth)), _bondSize, _bondSize); - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_ethRequest, _response); - vm.stopPrank(); + _deposit(_accountingExtension, proposer, weth, _bondSize); + _proposeResponse(); - // Check that both users have had their funds bonded. - uint256 _requesterBondedBalanceBefore = _accountingExtension.bondedAmountOf(requester, weth, _requestId); - assertEq(_requesterBondedBalanceBefore, _rewardSize); + // Check: requester has placed the bond? + assertEq(_accountingExtension.bondedAmountOf(requester, weth, _requestId), _rewardSize); - uint256 _proposerBondedBalanceBefore = _accountingExtension.bondedAmountOf(proposer, weth, _requestId); - assertEq(_proposerBondedBalanceBefore, _bondSize); + // Check: proposer has placed the bond? + assertEq(_accountingExtension.bondedAmountOf(proposer, weth, _requestId), _bondSize); // Warp to finalization time. vm.roll(_expectedDeadline + _baseDisputeWindow); // Finalize request/response. - oracle.finalize(_ethRequest, _response); - - uint256 _requesterBalanceAfter = _accountingExtension.balanceOf(requester, weth); - uint256 _proposerBalanceAfter = _accountingExtension.balanceOf(proposer, weth); + oracle.finalize(mockRequest, mockResponse); - // Check: requester has no balance left? - assertEq(_requesterBalanceAfter, 0); - // Check: proposer got the reward + the bonded amount back? - assertEq(_proposerBalanceAfter, _rewardSize + _bondSize); + // Check: requester has paid for response? + assertEq(_accountingExtension.balanceOf(requester, weth), 0); - uint256 _requesterBondedBalanceAfter = _accountingExtension.bondedAmountOf(requester, weth, _requestId); + // Check: requester has no bonded balance left? + assertEq(_accountingExtension.bondedAmountOf(requester, weth, _requestId), 0); - uint256 _proposerBondedBalanceAfter = _accountingExtension.bondedAmountOf(proposer, weth, _requestId); + // Check: proposer got the reward? + assertEq(_accountingExtension.balanceOf(proposer, weth), _rewardSize + _bondSize); - assertEq(_requesterBondedBalanceAfter, 0); - assertEq(_proposerBondedBalanceAfter, 0); + // Check: proposer has no bonded balance left? + assertEq(_accountingExtension.bondedAmountOf(proposer, weth, _requestId), 0); } function test_releaseSuccessfulDispute_ERC20(uint256 _rewardSize, uint256 _bondSize) public { @@ -117,54 +88,35 @@ contract Integration_Payments is IntegrationBase { vm.assume(_bondSize < type(uint256).max / 2); vm.assume(_rewardSize < type(uint256).max - _bondSize * 2); - // Requester bonds and creates request. - _forBondDepositERC20(_accountingExtension, requester, usdc, _rewardSize, _rewardSize); - IOracle.Request memory _erc20Request = _standardRequest(_rewardSize, _bondSize, usdc); - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_erc20Request, _ipfsHash); - vm.stopPrank(); + // Update the parameters of the request. + _setupRequest(_bondSize, _rewardSize, usdc); - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode('response')}); + // Requester bonds and creates request. + _deposit(_accountingExtension, requester, usdc, _rewardSize); + bytes32 _requestId = _createRequest(); // Proposer bonds and proposes response. - _forBondDepositERC20(_accountingExtension, proposer, usdc, _bondSize, _bondSize); - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_erc20Request, _response); - vm.stopPrank(); - - IOracle.Dispute memory _dispute = - IOracle.Dispute({disputer: disputer, proposer: proposer, responseId: _responseId, requestId: _requestId}); + _deposit(_accountingExtension, proposer, usdc, _bondSize); + _proposeResponse(); + // Disputer bonds and disputes response. - _forBondDepositERC20(_accountingExtension, disputer, usdc, _bondSize, _bondSize); - vm.startPrank(disputer); - _accountingExtension.approveModule(address(_bondedDisputeModule)); - bytes32 _disputeId = oracle.disputeResponse(_erc20Request, _response, _dispute); - vm.stopPrank(); + _deposit(_accountingExtension, disputer, usdc, _bondSize); + bytes32 _disputeId = _disputeResponse(); // Overriding dispute status and finalizing. - vm.mockCall( - address(oracle), abi.encodeCall(IOracle.disputeStatus, _disputeId), abi.encode(IOracle.DisputeStatus.Won) - ); - vm.prank(address(oracle)); - _bondedDisputeModule.onDisputeStatusChange(_disputeId, _erc20Request, _response, _dispute); + _finishDispute(_disputeId, IOracle.DisputeStatus.Won); - uint256 _proposerBalanceAfter = _accountingExtension.balanceOf(proposer, usdc); - uint256 _disputerBalanceAfter = _accountingExtension.balanceOf(disputer, usdc); + // Check: proposer got slashed? + assertEq(_accountingExtension.balanceOf(proposer, usdc), 0); - // Check: proposer get slashed? - assertEq(_proposerBalanceAfter, 0); - // Check: disputer gets proposer's bond? - assertEq(_disputerBalanceAfter, _bondSize * 2); + // Check: proposer has no bonded balance left? + assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), 0); - uint256 _proposerBondedBalanceAfter = _accountingExtension.bondedAmountOf(proposer, usdc, _requestId); + // Check: disputer got proposer's bond? + assertEq(_accountingExtension.balanceOf(disputer, usdc), _bondSize * 2); - uint256 _disputerBondedBalanceAfter = _accountingExtension.bondedAmountOf(disputer, usdc, _requestId); - - assertEq(_proposerBondedBalanceAfter, 0); - assertEq(_disputerBondedBalanceAfter, 0); + // Check: disputer has no bonded balance left? + assertEq(_accountingExtension.bondedAmountOf(disputer, usdc, _requestId), 0); } function test_releaseSuccessfulDispute_ETH(uint256 _rewardSize, uint256 _bondSize) public { @@ -172,100 +124,80 @@ contract Integration_Payments is IntegrationBase { vm.assume(_bondSize < type(uint256).max / 2); vm.assume(_rewardSize < type(uint256).max - _bondSize * 2); - // Requester bonds and creates request. - _forBondDepositERC20(_accountingExtension, requester, weth, _rewardSize, _rewardSize); - IOracle.Request memory _erc20Request = _standardRequest(_rewardSize, _bondSize, weth); - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_erc20Request, _ipfsHash); - vm.stopPrank(); + // Update the parameters of the request. + _setupRequest(_bondSize, _rewardSize, weth); - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode('response')}); + // Requester bonds and creates request. + _deposit(_accountingExtension, requester, weth, _rewardSize); + bytes32 _requestId = _createRequest(); // Proposer bonds and proposes response. - _forBondDepositERC20(_accountingExtension, proposer, weth, _bondSize, _bondSize); - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_erc20Request, _response); - vm.stopPrank(); - - IOracle.Dispute memory _dispute = - IOracle.Dispute({disputer: disputer, proposer: proposer, responseId: _responseId, requestId: _requestId}); + _deposit(_accountingExtension, proposer, weth, _bondSize); + _proposeResponse(); // Disputer bonds and disputes response. - _forBondDepositERC20(_accountingExtension, disputer, weth, _bondSize, _bondSize); - vm.startPrank(disputer); - _accountingExtension.approveModule(address(_bondedDisputeModule)); - bytes32 _disputeId = oracle.disputeResponse(_erc20Request, _response, _dispute); - vm.stopPrank(); + _deposit(_accountingExtension, disputer, weth, _bondSize); + bytes32 _disputeId = _disputeResponse(); // Overriding dispute status and finalizing. - vm.mockCall( - address(oracle), abi.encodeCall(IOracle.disputeStatus, _disputeId), abi.encode(IOracle.DisputeStatus.Won) - ); - vm.prank(address(oracle)); - _bondedDisputeModule.onDisputeStatusChange(_disputeId, _erc20Request, _response, _dispute); + _finishDispute(_disputeId, IOracle.DisputeStatus.Won); - uint256 _proposerBalanceAfter = _accountingExtension.balanceOf(proposer, weth); - uint256 _disputerBalanceAfter = _accountingExtension.balanceOf(disputer, weth); + // Check: proposer got slashed? + assertEq(_accountingExtension.balanceOf(proposer, weth), 0); - // Check: proposer get slashed? - assertEq(_proposerBalanceAfter, 0); - // Check: disputer gets proposer's bond? - assertEq(_disputerBalanceAfter, _bondSize * 2); + // Check: proposer has no bonded balance left? + assertEq(_accountingExtension.bondedAmountOf(proposer, weth, _requestId), 0); - uint256 _proposerBondedBalanceAfter = _accountingExtension.bondedAmountOf(proposer, weth, _requestId); + // Check: disputer got proposer's bond? + assertEq(_accountingExtension.balanceOf(disputer, weth), _bondSize * 2); - uint256 _disputerBondedBalanceAfter = _accountingExtension.bondedAmountOf(disputer, weth, _requestId); + // Check: disputer has no bonded balance left? + assertEq(_accountingExtension.bondedAmountOf(disputer, weth, _requestId), 0); + } - assertEq(_proposerBondedBalanceAfter, 0); - assertEq(_disputerBondedBalanceAfter, 0); + /** + * @notice Updates the parameters of the mock request. + */ + function _setupRequest(uint256 _bondSize, uint256 _rewardSize, IERC20 _token) internal { + mockRequest.requestModuleData = abi.encode( + IHttpRequestModule.RequestParameters({ + url: _expectedUrl, + method: _expectedMethod, + body: _expectedBody, + accountingExtension: _accountingExtension, + paymentToken: _token, + paymentAmount: _rewardSize + }) + ); + + mockRequest.responseModuleData = abi.encode( + IBondedResponseModule.RequestParameters({ + accountingExtension: _accountingExtension, + bondToken: _token, + bondSize: _bondSize, + deadline: _expectedDeadline, + disputeWindow: _baseDisputeWindow + }) + ); + + mockRequest.disputeModuleData = abi.encode( + IBondedDisputeModule.RequestParameters({ + accountingExtension: _accountingExtension, + bondToken: _token, + bondSize: _bondSize + }) + ); + + _resetMockIds(); } - function _standardRequest( - uint256 _rewardSize, - uint256 _bondSize, - IERC20 _paymentToken - ) internal view returns (IOracle.Request memory _request) { - _request = IOracle.Request({ - nonce: 0, - requester: requester, - requestModuleData: abi.encode( - IHttpRequestModule.RequestParameters({ - url: _expectedUrl, - method: _expectedMethod, - body: _expectedBody, - accountingExtension: _accountingExtension, - paymentToken: _paymentToken, - paymentAmount: _rewardSize - }) - ), - responseModuleData: abi.encode( - IBondedResponseModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: _paymentToken, - bondSize: _bondSize, - deadline: _expectedDeadline, - disputeWindow: _baseDisputeWindow - }) - ), - disputeModuleData: abi.encode( - IBondedDisputeModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: _paymentToken, - bondSize: _bondSize - }) - ), - resolutionModuleData: abi.encode(_mockArbitrator), - finalityModuleData: abi.encode( - ICallbackModule.RequestParameters({target: address(_mockCallback), data: abi.encode(_expectedCallbackValue)}) - ), - requestModule: address(_requestModule), - responseModule: address(_responseModule), - disputeModule: address(_bondedDisputeModule), - resolutionModule: address(_arbitratorModule), - finalityModule: address(_callbackModule) - }); + /** + * @notice Simulates a dispute being resolved. + */ + function _finishDispute(bytes32 _disputeId, IOracle.DisputeStatus _disputeStatus) internal { + vm.mockCall(address(oracle), abi.encodeCall(IOracle.disputeStatus, _disputeId), abi.encode(_disputeStatus)); + + vm.prank(address(oracle)); + _bondedDisputeModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); } } diff --git a/solidity/test/integration/RequestCreation.t.sol b/solidity/test/integration/RequestCreation.t.sol index 32cb49db..6202ea13 100644 --- a/solidity/test/integration/RequestCreation.t.sol +++ b/solidity/test/integration/RequestCreation.t.sol @@ -4,200 +4,163 @@ pragma solidity ^0.8.19; import './IntegrationBase.sol'; contract Integration_RequestCreation is IntegrationBase { - bytes32 internal _requestId; - function setUp() public override { super.setUp(); - _expectedDeadline = block.timestamp + BLOCK_TIME * 600; - } - - function test_createRequestWithoutResolutionAndFinalityModules() public { - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedReward, _expectedReward); - // Request without resolution and finality modules. - IOracle.Request memory _request = _standardRequest(); - _request.resolutionModule = address(0); - _request.finalityModule = address(0); - _request.resolutionModuleData = bytes(''); - _request.finalityModuleData = bytes(''); - - vm.startPrank(requester); + vm.prank(requester); _accountingExtension.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_request, _ipfsHash); - - // Check: request data was stored in request module? - IHttpRequestModule.RequestParameters memory _reqParams = - _requestModule.decodeRequestData(_request.requestModuleData); - - assertEq(_reqParams.url, _expectedUrl); - assertEq(uint256(_reqParams.method), uint256(_expectedMethod)); - assertEq(_reqParams.body, _expectedBody); - assertEq(address(_reqParams.accountingExtension), address(_accountingExtension)); - assertEq(address(_reqParams.paymentToken), address(usdc)); - assertEq(_reqParams.paymentAmount, _expectedReward); - - // Check: request data was stored in response module? - IBondedResponseModule.RequestParameters memory _params = - _responseModule.decodeRequestData(_request.responseModuleData); - assertEq(address(_accountingExtension), address(_params.accountingExtension)); - assertEq(address(_params.bondToken), address(usdc)); - assertEq(_expectedBondSize, _params.bondSize); - assertEq(_expectedDeadline, _params.deadline); - - // Check: request data was stored in dispute module? - IBondedDisputeModule.RequestParameters memory _params2 = - _bondedDisputeModule.decodeRequestData(_request.disputeModuleData); - - assertEq(address(_accountingExtension), address(_params2.accountingExtension)); - assertEq(address(_params.bondToken), address(_params2.bondToken)); - assertEq(_expectedBondSize, _params2.bondSize); + // Deposit the bond + _deposit(_accountingExtension, requester, usdc, _expectedReward); } - function test_createRequestWithAllModules() public { - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedReward, _expectedReward); + /** + * @notice Test that the request is created correctly with only 3 modules + */ + function test_createRequest_withoutResolutionAndFinalityModules() public { + // Request without resolution and finality modules. + mockRequest.resolutionModule = address(0); + mockRequest.finalityModule = address(0); + mockRequest.resolutionModuleData = bytes(''); + mockRequest.finalityModuleData = bytes(''); - // Request with all modules. - IOracle.Request memory _request = _standardRequest(); + // Create the request + vm.prank(requester); + bytes32 _requestId = oracle.createRequest(mockRequest, _ipfsHash); - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_request, _ipfsHash); - - // Check: request data was stored in request module? - IHttpRequestModule.RequestParameters memory _reqParams = - _requestModule.decodeRequestData(_request.requestModuleData); - - assertEq(_reqParams.url, _expectedUrl); - assertEq(uint256(_reqParams.method), uint256(_expectedMethod)); - assertEq(_reqParams.body, _expectedBody); - assertEq(address(_reqParams.accountingExtension), address(_accountingExtension)); - assertEq(address(_reqParams.paymentToken), address(usdc)); - assertEq(_reqParams.paymentAmount, _expectedReward); - - // Check: request data was stored in response module? - IBondedResponseModule.RequestParameters memory _params = - _responseModule.decodeRequestData(_request.responseModuleData); - - assertEq(address(_accountingExtension), address(_params.accountingExtension)); - assertEq(address(_params.bondToken), address(usdc)); - assertEq(_expectedBondSize, _params.bondSize); - assertEq(_expectedDeadline, _params.deadline); - - // Check: request data was stored in dispute module? - IBondedDisputeModule.RequestParameters memory _params2 = - _bondedDisputeModule.decodeRequestData(_request.disputeModuleData); - - assertEq(address(_accountingExtension), address(_params2.accountingExtension)); - assertEq(address(_params.bondToken), address(_params2.bondToken)); - assertEq(_expectedBondSize, _params2.bondSize); - - // Check: request data was stored in resolution module? - IArbitratorModule.RequestParameters memory _params3 = - _arbitratorModule.decodeRequestData(_request.resolutionModuleData); - assertEq(_params3.arbitrator, address(_mockArbitrator)); - - // Check: request data was stored in finality module? - ICallbackModule.RequestParameters memory _callbackParams = - _callbackModule.decodeRequestData(_request.finalityModuleData); - assertEq(_callbackParams.target, address(_mockCallback)); - assertEq(_callbackParams.data, abi.encode(_expectedCallbackValue)); - } + // Check: saved the correct id? + assertEq(_requestId, _getId(mockRequest)); - function test_createRequestWithReward_UserHasBonded() public { - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedReward, _expectedReward); + // Check: saved the correct nonce? + assertEq(oracle.nonceToRequestId(mockRequest.nonce), _requestId); - // Request with rewards. - IOracle.Request memory _request = _standardRequest(); + // Check: saved the correct creation block? + assertEq(oracle.createdAt(_requestId), block.number); - // Check: should not revert as user has bonded. - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); + // Check: saved the allowed modules? + assertTrue(oracle.allowedModule(_requestId, mockRequest.requestModule)); + assertTrue(oracle.allowedModule(_requestId, mockRequest.responseModule)); + assertTrue(oracle.allowedModule(_requestId, mockRequest.disputeModule)); - oracle.createRequest(_request, _ipfsHash); + // Check: saved the participants? + assertTrue(oracle.isParticipant(_requestId, requester)); } - function test_createRequestWithReward_UserHasNotBonded() public { - // Request with rewards. - IOracle.Request memory _request = _standardRequest(); + /** + * @notice Test that the request is created correctly with all modules + */ + function test_createRequest_withAllModules() public { + // Create the request + vm.prank(requester); + bytes32 _requestId = oracle.createRequest(mockRequest, _ipfsHash); - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); + // Check: saved the correct id? + assertEq(_requestId, _getId(mockRequest)); - // Check: should revert with `InsufficientFunds` as user has not deposited. - vm.expectRevert(IAccountingExtension.AccountingExtension_InsufficientFunds.selector); - _requestId = oracle.createRequest(_request, _ipfsHash); - } + // Check: saved the correct nonce? + assertEq(oracle.nonceToRequestId(mockRequest.nonce), _requestId); + + // Check: saved the correct creation block? + assertEq(oracle.createdAt(_requestId), block.number); + + // Check: saved the allowed modules? + assertTrue(oracle.allowedModule(_requestId, mockRequest.requestModule)); + assertTrue(oracle.allowedModule(_requestId, mockRequest.responseModule)); + assertTrue(oracle.allowedModule(_requestId, mockRequest.disputeModule)); + assertTrue(oracle.allowedModule(_requestId, mockRequest.resolutionModule)); + assertTrue(oracle.allowedModule(_requestId, mockRequest.finalityModule)); - function test_createRequestWithoutReward_UserHasBonded() public { - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedReward, _expectedReward); + // Check: saved the participants? + assertTrue(oracle.isParticipant(_requestId, requester)); + } - // Request without rewards. - IOracle.Request memory _request = _standardRequest(); - _request.requestModuleData = abi.encode( + /** + * @notice Creating a request without a reward after depositing the bond + */ + function test_createRequest_withoutReward_UserHasBonded() public { + // Request without rewards + mockRequest.requestModuleData = abi.encode( IHttpRequestModule.RequestParameters({ url: _expectedUrl, method: _expectedMethod, body: _expectedBody, accountingExtension: _accountingExtension, - paymentToken: IERC20(USDC_ADDRESS), + paymentToken: usdc, paymentAmount: 0 }) ); - // Check: should not revert as user has set no rewards and bonded. - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - oracle.createRequest(_request, _ipfsHash); + // Check: should not revert as user has set no rewards and bonded? + vm.prank(requester); + oracle.createRequest(mockRequest, _ipfsHash); } - function test_createRequestWithoutReward_UserHasNotBonded() public { + /** + * @notice Creating a request without a reward and not depositing a bond should not revert + */ + function test_createRequest_withoutReward_UserHasNotBonded() public { // Request without rewards - IOracle.Request memory _request = _standardRequest(); - _request.requestModuleData = abi.encode( + mockRequest.requestModuleData = abi.encode( IHttpRequestModule.RequestParameters({ url: _expectedUrl, method: _expectedMethod, body: _expectedBody, accountingExtension: _accountingExtension, - paymentToken: IERC20(USDC_ADDRESS), + paymentToken: weth, paymentAmount: 0 }) ); - vm.startPrank(requester); - // Approving the request module to bond the requester tokens - _accountingExtension.approveModule(address(_requestModule)); - - // Check: should not revert as user has set no rewards. - oracle.createRequest(_request, _ipfsHash); + // Check: doesn't revert if the reward is 0 and the user has not bonded? + vm.prank(requester); + oracle.createRequest(mockRequest, _ipfsHash); } - function test_createRequestDuplicate() public { - // Double token amount as each request is a unique bond. - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedReward * 2, _expectedReward * 2); + /** + * @notice Creating a request without any funds deposited to the accounting extension + */ + function test_createRequest_withReward_UserHasNotBonded() public { + // Using WETH as the payment token and not depositing into the accounting extension + mockRequest.requestModuleData = abi.encode( + IHttpRequestModule.RequestParameters({ + url: _expectedUrl, + method: _expectedMethod, + body: _expectedBody, + accountingExtension: _accountingExtension, + paymentToken: weth, + paymentAmount: _expectedReward + }) + ); - IOracle.Request memory _firstRequest = _standardRequest(); - IOracle.Request memory _secondRequest = _standardRequest(); + // Check: should revert with `InsufficientFunds` as user has not deposited? + vm.expectRevert(IAccountingExtension.AccountingExtension_InsufficientFunds.selector); + vm.prank(requester); + oracle.createRequest(mockRequest, _ipfsHash); + } - _secondRequest.nonce = 1; + /** + * @notice Creating 2 request with the same parameters + */ + function test_createRequest_duplicate() public { + // Double token amount as each request is a unique bond. + _deposit(_accountingExtension, requester, usdc, _expectedReward * 2); + // Create the first request vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); + bytes32 _firstRequestId = oracle.createRequest(mockRequest, _ipfsHash); - bytes32 _firstRequestId = oracle.createRequest(_firstRequest, _ipfsHash); - bytes32 _secondRequestId = oracle.createRequest(_secondRequest, _ipfsHash); + // Set the new nonce and create the second request + mockRequest.nonce += 1; + bytes32 _secondRequestId = oracle.createRequest(mockRequest, _ipfsHash); vm.stopPrank(); + // Check: saved different ids? assertTrue(_firstRequestId != _secondRequestId, 'Request IDs should not be equal'); } - function test_createRequestWithInvalidParameters() public { - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedReward, _expectedReward); - + function test_createRequest_withInvalidParameters() public { // Request with invalid token address. - IOracle.Request memory _invalidTokenRequest = _standardRequest(); - _invalidTokenRequest.requestModuleData = abi.encode( + mockRequest.requestModuleData = abi.encode( IHttpRequestModule.RequestParameters({ url: _expectedUrl, method: _expectedMethod, @@ -208,74 +171,33 @@ contract Integration_RequestCreation is IntegrationBase { }) ); - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - + // Check: reverts due to the invalid token address? vm.expectRevert(IAccountingExtension.AccountingExtension_InsufficientFunds.selector); - oracle.createRequest(_invalidTokenRequest, _ipfsHash); + vm.prank(requester); + oracle.createRequest(mockRequest, _ipfsHash); } - function test_createRequestWithDisallowedModule() public { - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedReward, _expectedReward); - - IOracle.Request memory _request = _standardRequest(); - _request.requestModule = address(_responseModule); - _request.responseModule = address(_requestModule); + /** + * @notice Reverts if the request module cannot be called + */ + function test_createRequest_withDisallowedModule() public { + mockRequest.requestModule = address(_responseModule); + mockRequest.responseModule = address(_requestModule); vm.startPrank(requester); + // Check: reverts with `EVM error`? vm.expectRevert(); - oracle.createRequest(_request, _ipfsHash); + oracle.createRequest(mockRequest, _ipfsHash); - // Check: switch modules back and give a non-existent module. Reverts? - vm.expectRevert(); - _request.requestModule = address(_requestModule); - _request.responseModule = address(_responseModule); - _request.disputeModule = makeAddr('NON-EXISTENT DISPUTE MODULE'); - oracle.createRequest(_request, _ipfsHash); + // Reset the modules back and configure an invalid dispute module. + mockRequest.requestModule = address(_requestModule); + mockRequest.responseModule = address(_responseModule); + mockRequest.disputeModule = makeAddr('NON-EXISTENT DISPUTE MODULE'); - vm.stopPrank(); - } + // Check: doesn't revert if any module but the request module is invalid? + oracle.createRequest(mockRequest, _ipfsHash); - function _standardRequest() internal view returns (IOracle.Request memory _request) { - _request = IOracle.Request({ - nonce: 0, - requester: requester, - requestModuleData: abi.encode( - IHttpRequestModule.RequestParameters({ - url: _expectedUrl, - method: _expectedMethod, - body: _expectedBody, - accountingExtension: _accountingExtension, - paymentToken: IERC20(USDC_ADDRESS), - paymentAmount: _expectedReward - }) - ), - responseModuleData: abi.encode( - IBondedResponseModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize, - deadline: _expectedDeadline, - disputeWindow: _baseDisputeWindow - }) - ), - disputeModuleData: abi.encode( - IBondedDisputeModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize - }) - ), - resolutionModuleData: abi.encode(_mockArbitrator), - finalityModuleData: abi.encode( - ICallbackModule.RequestParameters({target: address(_mockCallback), data: abi.encode(_expectedCallbackValue)}) - ), - requestModule: address(_requestModule), - responseModule: address(_responseModule), - disputeModule: address(_bondedDisputeModule), - resolutionModule: address(_arbitratorModule), - finalityModule: address(_callbackModule) - }); + vm.stopPrank(); } } diff --git a/solidity/test/integration/ResponseDispute.t.sol b/solidity/test/integration/ResponseDispute.t.sol index 797d8b95..1ff17bf0 100644 --- a/solidity/test/integration/ResponseDispute.t.sol +++ b/solidity/test/integration/ResponseDispute.t.sol @@ -4,179 +4,105 @@ pragma solidity ^0.8.19; import './IntegrationBase.sol'; contract Integration_ResponseDispute is IntegrationBase { - bytes internal _responseData; - bytes32 internal _requestId; - bytes32 internal _responseId; - - IOracle.Request internal _request; - IOracle.Response internal _response; - IOracle.Dispute internal _dispute; - function setUp() public override { super.setUp(); - _expectedDeadline = block.timestamp + BLOCK_TIME * 600; - _responseData = abi.encode('response'); - - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedBondSize, _expectedBondSize); - - _request = IOracle.Request({ - nonce: 0, - requester: requester, - requestModuleData: abi.encode( - IHttpRequestModule.RequestParameters({ - url: _expectedUrl, - method: _expectedMethod, - body: _expectedBody, - accountingExtension: _accountingExtension, - paymentToken: IERC20(USDC_ADDRESS), - paymentAmount: _expectedReward - }) - ), - responseModuleData: abi.encode( - IBondedResponseModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize, - deadline: _expectedDeadline, - disputeWindow: _baseDisputeWindow - }) - ), - disputeModuleData: abi.encode( - IBondedDisputeModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize - }) - ), - resolutionModuleData: abi.encode(_mockArbitrator), - finalityModuleData: abi.encode( - ICallbackModule.RequestParameters({target: address(_mockCallback), data: abi.encode(_expectedCallbackValue)}) - ), - requestModule: address(_requestModule), - responseModule: address(_responseModule), - disputeModule: address(_bondedDisputeModule), - resolutionModule: address(_arbitratorModule), - finalityModule: address(_callbackModule) - }); - - vm.startPrank(requester); - _accountingExtension.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_request, _ipfsHash); - vm.stopPrank(); - - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); - - _response = IOracle.Response({proposer: proposer, requestId: _requestId, response: _responseData}); - - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - _responseId = oracle.proposeResponse(_request, _response); - vm.stopPrank(); - - _dispute = IOracle.Dispute({disputer: disputer, proposer: proposer, responseId: _responseId, requestId: _requestId}); - } + // Create request + _deposit(_accountingExtension, requester, usdc, _expectedBondSize); + _createRequest(); - // check that the dispute id is stored in the response struct - function test_disputeResponse_disputeIdStoredInResponse() public { - _forBondDepositERC20(_accountingExtension, disputer, usdc, _expectedBondSize, _expectedBondSize); + // Propose a response + _deposit(_accountingExtension, proposer, usdc, _expectedBondSize); + _proposeResponse(); - vm.startPrank(disputer); + // Disputer approves the dispute module + vm.prank(disputer); _accountingExtension.approveModule(address(_bondedDisputeModule)); - oracle.disputeResponse(_request, _response, _dispute); - vm.stopPrank(); } - // dispute a non-existent response - function test_disputeResponse_nonExistentResponse(bytes32 _nonExistentResponseId) public { - vm.assume(_nonExistentResponseId != _responseId); - _dispute.responseId = _nonExistentResponseId; + /** + * @notice Disputing a response should be reflected the oracle's state + */ + function test_disputeResponse() public { + _deposit(_accountingExtension, disputer, usdc, _expectedBondSize); vm.prank(disputer); + bytes32 _disputeId = oracle.disputeResponse(mockRequest, mockResponse, mockDispute); - vm.expectRevert(IOracle.Oracle_InvalidDisputeBody.selector); - oracle.disputeResponse(_request, _response, _dispute); + // Check: the disputer is a participant now? + assertTrue(oracle.isParticipant(_getId(mockRequest), disputer)); + + // Check: the dispute status is Active? + assertEq(uint256(oracle.disputeStatus(_disputeId)), uint256(IOracle.DisputeStatus.Active)); + + // Check: dispute id is stored? + assertEq(oracle.disputeOf(_getId(mockResponse)), _disputeId); + + // Check: creation time is correct? + assertEq(oracle.createdAt(_disputeId), block.number); } - function test_disputeResponse_requestAndResponseMismatch() public { - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedBondSize, _expectedBondSize); - IOracle.Request memory _secondRequest = IOracle.Request({ - nonce: 1, - requester: requester, - requestModuleData: abi.encode( - IHttpRequestModule.RequestParameters({ - url: _expectedUrl, - method: _expectedMethod, - body: _expectedBody, - accountingExtension: _accountingExtension, - paymentToken: IERC20(USDC_ADDRESS), - paymentAmount: _expectedReward - }) - ), - responseModuleData: abi.encode( - _accountingExtension, USDC_ADDRESS, _expectedBondSize, _expectedDeadline, _baseDisputeWindow - ), - disputeModuleData: abi.encode( - _accountingExtension, USDC_ADDRESS, _expectedBondSize, _expectedDeadline, _mockArbitrator - ), - resolutionModuleData: abi.encode(_mockArbitrator), - finalityModuleData: abi.encode( - ICallbackModule.RequestParameters({target: address(_mockCallback), data: abi.encode(_expectedCallbackValue)}) - ), - requestModule: address(_requestModule), - responseModule: address(_responseModule), - disputeModule: address(_bondedDisputeModule), - resolutionModule: address(_arbitratorModule), - finalityModule: address(_callbackModule) - }); - vm.prank(requester); - bytes32 _secondRequestId = oracle.createRequest(_secondRequest, _ipfsHash); - - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); - _response.requestId = _secondRequestId; - vm.prank(proposer); - oracle.proposeResponse(_secondRequest, _response); + /** + * @notice Disputing a non-existent response should revert + */ + function test_disputeResponse_nonExistentResponse(bytes32 _nonExistentResponseId) public { + vm.assume(_nonExistentResponseId != _getId(mockResponse)); + mockDispute.responseId = _nonExistentResponseId; + + vm.expectRevert(IOracle.Oracle_InvalidDisputeBody.selector); vm.prank(disputer); + oracle.disputeResponse(mockRequest, mockResponse, mockDispute); + } + + /** + * @notice Sending an an invalid dispute in should revert + */ + function test_disputeResponse_requestAndResponseMismatch(bytes32 _requestId) public { + vm.assume(_requestId != _getId(mockRequest)); + + mockDispute.requestId = _requestId; + vm.expectRevert(IOracle.Oracle_InvalidDisputeBody.selector); - oracle.disputeResponse(_request, _response, _dispute); + + vm.prank(disputer); + oracle.disputeResponse(mockRequest, mockResponse, mockDispute); } + /** + * @notice Revert if the disputer has no funds to bond + */ function test_disputeResponse_noBondedFunds() public { - vm.startPrank(disputer); - _accountingExtension.approveModule(address(_bondedDisputeModule)); vm.expectRevert(IAccountingExtension.AccountingExtension_InsufficientFunds.selector); - oracle.disputeResponse(_request, _response, _dispute); + + vm.prank(disputer); + oracle.disputeResponse(mockRequest, mockResponse, mockDispute); } + /** + * @notice Disputing a finalized response should revert + */ function test_disputeResponse_alreadyFinalized() public { vm.roll(_expectedDeadline + _baseDisputeWindow); - oracle.finalize(_request, _response); + oracle.finalize(mockRequest, mockResponse); + + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, _getId(mockRequest))); vm.prank(disputer); - vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_AlreadyFinalized.selector, _requestId)); - oracle.disputeResponse(_request, _response, _dispute); + oracle.disputeResponse(mockRequest, mockResponse, mockDispute); } + /** + * @notice Disputing a response that has already been disputed should revert + */ function test_disputeResponse_alreadyDisputed() public { - _forBondDepositERC20(_accountingExtension, disputer, usdc, _expectedBondSize, _expectedBondSize); - vm.startPrank(disputer); - _accountingExtension.approveModule(address(_bondedDisputeModule)); - oracle.disputeResponse(_request, _response, _dispute); - vm.stopPrank(); + _deposit(_accountingExtension, disputer, usdc, _expectedBondSize); vm.prank(disputer); - vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_ResponseAlreadyDisputed.selector, _responseId)); - oracle.disputeResponse(_request, _response, _dispute); - } + oracle.disputeResponse(mockRequest, mockResponse, mockDispute); - // TODO: discuss and decide on the implementation of a dispute deadline - // function test_disputeResponse_afterDeadline(uint256 _timestamp) public { - // vm.assume(_timestamp > _expectedDeadline); - // _bondDisputerFunds(); - // vm.warp(_timestamp); - // vm.prank(disputer); - // vm.expectRevert(abi.encodeWithSelector(IBondedDisputeModule.BondedDisputeModule_TooLateToDispute.selector, _responseId)); - // oracle.disputeResponse(_requestId, _responseId); - // } + vm.prank(disputer); + vm.expectRevert(abi.encodeWithSelector(IOracle.Oracle_ResponseAlreadyDisputed.selector, _getId(mockResponse))); + oracle.disputeResponse(mockRequest, mockResponse, mockDispute); + } } diff --git a/solidity/test/integration/ResponseProposal.t.sol b/solidity/test/integration/ResponseProposal.t.sol index 81b46cda..6798eb70 100644 --- a/solidity/test/integration/ResponseProposal.t.sol +++ b/solidity/test/integration/ResponseProposal.t.sol @@ -5,132 +5,129 @@ import './IntegrationBase.sol'; contract Integration_ResponseProposal is IntegrationBase { bytes32 internal _requestId; - IOracle.Request internal _request; function setUp() public override { super.setUp(); - _expectedDeadline = block.timestamp + BLOCK_TIME * 600; - - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedReward, _expectedReward); - - _request = IOracle.Request({ - nonce: 0, - requester: requester, - requestModuleData: abi.encode( - IHttpRequestModule.RequestParameters({ - url: _expectedUrl, - method: _expectedMethod, - body: _expectedBody, - accountingExtension: _accountingExtension, - paymentToken: IERC20(USDC_ADDRESS), - paymentAmount: _expectedReward - }) - ), - responseModuleData: abi.encode( - IBondedResponseModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize, - deadline: _expectedDeadline, - disputeWindow: _baseDisputeWindow - }) - ), - disputeModuleData: abi.encode( - IBondedDisputeModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize - }) - ), - resolutionModuleData: abi.encode(_mockArbitrator), - finalityModuleData: abi.encode( - ICallbackModule.RequestParameters({target: address(_mockCallback), data: abi.encode(_expectedCallbackValue)}) - ), - requestModule: address(_requestModule), - responseModule: address(_responseModule), - disputeModule: address(_bondedDisputeModule), - resolutionModule: address(_arbitratorModule), - finalityModule: address(_callbackModule) - }); + // Requester and proposer deposit funds + _deposit(_accountingExtension, requester, usdc, _expectedReward); + _deposit(_accountingExtension, proposer, usdc, _expectedBondSize); + // Create the request vm.startPrank(requester); _accountingExtension.approveModule(address(_requestModule)); - _requestId = oracle.createRequest(_request, _ipfsHash); + _requestId = oracle.createRequest(mockRequest, _ipfsHash); + vm.stopPrank(); + + // Approve the response module on behalf of the proposer + vm.prank(proposer); + _accountingExtension.approveModule(address(_responseModule)); } + /** + * @notice Proposing a response updates the state of the oracle, including the list of participants and the response's creation time + */ function test_proposeResponse_validResponse(bytes memory _responseBytes) public { - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); + mockResponse.response = _responseBytes; - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _requestId, response: _responseBytes}); + vm.prank(proposer); + oracle.proposeResponse(mockRequest, mockResponse); - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - oracle.proposeResponse(_request, _response); - vm.stopPrank(); + // Check: the proposer is a participant now? + assertTrue(oracle.isParticipant(_requestId, proposer)); + + // Check: the response id was added to the list? + bytes32[] memory _getResponseIds = oracle.getResponseIds(_requestId); + assertEq(_getResponseIds[0], _getId(mockResponse)); + + // Check: the creation block is correct? + assertEq(oracle.createdAt(_getId(mockResponse)), block.number); } + /** + * @notice Proposing a response after the deadline reverts + */ function test_proposeResponse_afterDeadline(uint256 _timestamp, bytes memory _responseBytes) public { vm.assume(_timestamp > _expectedDeadline); - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); // Warp to timestamp after deadline vm.warp(_timestamp); + + mockResponse.response = _responseBytes; + // Check: does revert if deadline is passed? vm.expectRevert(IBondedResponseModule.BondedResponseModule_TooLateToPropose.selector); - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _requestId, response: _responseBytes}); - vm.prank(proposer); - oracle.proposeResponse(_request, _response); + oracle.proposeResponse(mockRequest, mockResponse); } + /** + * @notice Proposing a response to an already answered request reverts + */ function test_proposeResponse_alreadyResponded(bytes memory _responseBytes) public { - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); - - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _requestId, response: _responseBytes}); + mockResponse.response = _responseBytes; // First response - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - oracle.proposeResponse(_request, _response); - vm.stopPrank(); + vm.prank(proposer); + oracle.proposeResponse(mockRequest, mockResponse); + + mockResponse.response = abi.encode('second response'); // Check: does revert if already responded? vm.expectRevert(IBondedResponseModule.BondedResponseModule_AlreadyResponded.selector); // Second response vm.prank(proposer); - oracle.proposeResponse(_request, _response); + oracle.proposeResponse(mockRequest, mockResponse); } + /** + * @notice Proposing a response with an invalid request id reverts + */ function test_proposeResponse_nonExistentRequest(bytes memory _responseBytes, bytes32 _nonExistentRequestId) public { vm.assume(_nonExistentRequestId != _requestId); - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _nonExistentRequestId, response: _responseBytes}); + mockResponse.response = _responseBytes; + mockResponse.requestId = _nonExistentRequestId; // Check: does revert if request does not exist? vm.expectRevert(IOracle.Oracle_InvalidResponseBody.selector); vm.prank(proposer); - oracle.proposeResponse(_request, _response); + oracle.proposeResponse(mockRequest, mockResponse); } - // Proposing without enough funds bonded (should revert insufficient funds) + /** + * @notice Proposing without enough funds bonded reverts + */ function test_proposeResponse_insufficientFunds(bytes memory _responseBytes) public { - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _requestId, response: _responseBytes}); + // Using WETH as the bond token + mockRequest.nonce += 1; + mockRequest.responseModuleData = abi.encode( + IBondedResponseModule.RequestParameters({ + accountingExtension: _accountingExtension, + bondToken: weth, + bondSize: _expectedBondSize, + deadline: _expectedDeadline, + disputeWindow: _baseDisputeWindow + }) + ); + + // Requester deposit funds + _deposit(_accountingExtension, requester, usdc, _expectedReward); + + // Creates the request + vm.prank(requester); + oracle.createRequest(mockRequest, _ipfsHash); + + mockResponse.response = _responseBytes; + _resetMockIds(); // Check: does revert if proposer does not have enough funds bonded? - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - vm.expectRevert(IAccountingExtension.AccountingExtension_InsufficientFunds.selector); - oracle.proposeResponse(_request, _response); + + vm.prank(proposer); + oracle.proposeResponse(mockRequest, mockResponse); } } diff --git a/solidity/test/integration/RootVerification.t.sol b/solidity/test/integration/RootVerification.t.sol index 9eeb717b..b84b32b5 100644 --- a/solidity/test/integration/RootVerification.t.sol +++ b/solidity/test/integration/RootVerification.t.sol @@ -1,24 +1,20 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; - -import './IntegrationBase.sol'; import { - SparseMerkleTreeRequestModule, + IRootVerificationModule, RootVerificationModule +} from '../../contracts/modules/dispute/RootVerificationModule.sol'; +import { ISparseMerkleTreeRequestModule, - IOracle + SparseMerkleTreeRequestModule } from '../../contracts/modules/request/SparseMerkleTreeRequestModule.sol'; import {SparseMerkleTreeL32Verifier} from '../../contracts/periphery/SparseMerkleTreeL32Verifier.sol'; -import { - RootVerificationModule, IRootVerificationModule -} from '../../contracts/modules/dispute/RootVerificationModule.sol'; import {ITreeVerifier} from '../../interfaces/ITreeVerifier.sol'; +import './IntegrationBase.sol'; contract Integration_RootVerification is IntegrationBase { SparseMerkleTreeL32Verifier internal _treeVerifier; - IOracle.Request internal _request; bytes32 internal _requestId; bytes32[32] internal _treeBranches = [ bytes32('branch1'), @@ -57,10 +53,10 @@ contract Integration_RootVerification is IntegrationBase { uint256 internal _treeCount = 1; bytes internal _treeData = abi.encode(_treeBranches, _treeCount); bytes32[] internal _leavesToInsert = [bytes32('leave1'), bytes32('leave2')]; + bytes32 internal _correctRoot; function setUp() public override { super.setUp(); - _expectedDeadline = block.timestamp + BLOCK_TIME * 600; SparseMerkleTreeRequestModule _sparseMerkleTreeModule = new SparseMerkleTreeRequestModule(oracle); label(address(_sparseMerkleTreeModule), 'SparseMerkleTreeModule'); @@ -71,142 +67,108 @@ contract Integration_RootVerification is IntegrationBase { _treeVerifier = new SparseMerkleTreeL32Verifier(); label(address(_treeVerifier), 'TreeVerifier'); - _request = IOracle.Request({ - nonce: 0, - requester: requester, - requestModuleData: abi.encode( - ISparseMerkleTreeRequestModule.RequestParameters({ - treeData: _treeData, - leavesToInsert: _leavesToInsert, - treeVerifier: ITreeVerifier(_treeVerifier), - accountingExtension: _accountingExtension, - paymentToken: IERC20(USDC_ADDRESS), - paymentAmount: _expectedReward - }) - ), - responseModuleData: abi.encode( - IBondedResponseModule.RequestParameters({ - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize, - deadline: _expectedDeadline, - disputeWindow: _baseDisputeWindow - }) - ), - disputeModuleData: abi.encode( - IRootVerificationModule.RequestParameters({ - treeData: _treeData, - leavesToInsert: _leavesToInsert, - treeVerifier: ITreeVerifier(_treeVerifier), - accountingExtension: _accountingExtension, - bondToken: IERC20(USDC_ADDRESS), - bondSize: _expectedBondSize - }) - ), - resolutionModuleData: abi.encode(_mockArbitrator), - finalityModuleData: abi.encode( - ICallbackModule.RequestParameters({target: address(_mockCallback), data: abi.encode(_expectedCallbackValue)}) - ), - requestModule: address(_sparseMerkleTreeModule), - responseModule: address(_responseModule), - disputeModule: address(_rootVerificationModule), - resolutionModule: address(_arbitratorModule), - finalityModule: address(_callbackModule) - }); - - _forBondDepositERC20(_accountingExtension, requester, usdc, _expectedReward, _expectedReward); + mockRequest.requestModuleData = abi.encode( + ISparseMerkleTreeRequestModule.RequestParameters({ + treeData: _treeData, + leavesToInsert: _leavesToInsert, + treeVerifier: ITreeVerifier(_treeVerifier), + accountingExtension: _accountingExtension, + paymentToken: usdc, + paymentAmount: _expectedReward + }) + ); + + mockRequest.disputeModuleData = abi.encode( + IRootVerificationModule.RequestParameters({ + treeData: _treeData, + leavesToInsert: _leavesToInsert, + treeVerifier: ITreeVerifier(_treeVerifier), + accountingExtension: _accountingExtension, + bondToken: usdc, + bondSize: _expectedBondSize + }) + ); + + mockRequest.requestModule = address(_sparseMerkleTreeModule); + mockRequest.disputeModule = address(_rootVerificationModule); + + _resetMockIds(); + + _deposit(_accountingExtension, requester, usdc, _expectedReward); vm.startPrank(requester); _accountingExtension.approveModule(address(_sparseMerkleTreeModule)); - _requestId = oracle.createRequest(_request, _ipfsHash); + _requestId = oracle.createRequest(mockRequest, _ipfsHash); vm.stopPrank(); + + vm.prank(proposer); + _accountingExtension.approveModule(address(_responseModule)); + + _correctRoot = ITreeVerifier(_treeVerifier).calculateRoot(_treeData, _leavesToInsert); } function test_validResponse() public { - bytes32 _correctRoot = ITreeVerifier(_treeVerifier).calculateRoot(_treeData, _leavesToInsert); + _deposit(_accountingExtension, proposer, usdc, _expectedBondSize); - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); + mockResponse.response = abi.encode(_correctRoot); - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode(_correctRoot)}); - - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - oracle.proposeResponse(_request, _response); - vm.stopPrank(); + vm.prank(proposer); + oracle.proposeResponse(mockRequest, mockResponse); vm.roll(_expectedDeadline + _baseDisputeWindow); - oracle.finalize(_request, _response); + oracle.finalize(mockRequest, mockResponse); } function test_disputeResponse_incorrectResponse(bytes32 _invalidRoot) public { - bytes32 _correctRoot = ITreeVerifier(_treeVerifier).calculateRoot(_treeData, _leavesToInsert); vm.assume(_correctRoot != _invalidRoot); - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); - - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode(_invalidRoot)}); + _deposit(_accountingExtension, proposer, usdc, _expectedBondSize); - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - bytes32 _responseId = oracle.proposeResponse(_request, _response); - vm.stopPrank(); + mockResponse.response = abi.encode(_invalidRoot); - IOracle.Dispute memory _dispute = - IOracle.Dispute({proposer: proposer, disputer: disputer, requestId: _requestId, responseId: _responseId}); + vm.prank(proposer); + oracle.proposeResponse(mockRequest, mockResponse); + _resetMockIds(); vm.startPrank(disputer); _accountingExtension.approveModule(address(_responseModule)); - oracle.disputeResponse(_request, _response, _dispute); + oracle.disputeResponse(mockRequest, mockResponse, mockDispute); vm.stopPrank(); uint256 _requesterBondedBalance = _accountingExtension.bondedAmountOf(requester, usdc, _requestId); - uint256 _proposerBondedBalance = _accountingExtension.bondedAmountOf(proposer, usdc, _requestId); - - uint256 _requesterVirtualBalance = _accountingExtension.balanceOf(requester, usdc); - uint256 _proposerVirtualBalance = _accountingExtension.balanceOf(proposer, usdc); - uint256 _disputerVirtualBalance = _accountingExtension.balanceOf(disputer, usdc); - assertEq(_requesterBondedBalance, 0); + + uint256 _proposerBondedBalance = _accountingExtension.bondedAmountOf(proposer, usdc, _requestId); assertEq(_proposerBondedBalance, 0); + uint256 _requesterVirtualBalance = _accountingExtension.balanceOf(requester, usdc); assertEq(_requesterVirtualBalance, 0); + + uint256 _proposerVirtualBalance = _accountingExtension.balanceOf(proposer, usdc); assertEq(_proposerVirtualBalance, 0); + + uint256 _disputerVirtualBalance = _accountingExtension.balanceOf(disputer, usdc); assertEq(_disputerVirtualBalance, _expectedBondSize + _expectedReward); } function test_disputeResponse_correctResponse() public { - bytes32 _correctRoot = ITreeVerifier(_treeVerifier).calculateRoot(_treeData, _leavesToInsert); - - _forBondDepositERC20(_accountingExtension, proposer, usdc, _expectedBondSize, _expectedBondSize); + _deposit(_accountingExtension, proposer, usdc, _expectedBondSize); - IOracle.Response memory _response = - IOracle.Response({proposer: proposer, requestId: _requestId, response: abi.encode(_correctRoot)}); + mockResponse.response = abi.encode(_correctRoot); - vm.startPrank(proposer); - _accountingExtension.approveModule(address(_responseModule)); - bytes32 _responseId = oracle.proposeResponse(_request, _response); - vm.stopPrank(); - - IOracle.Dispute memory _dispute = - IOracle.Dispute({proposer: proposer, disputer: disputer, requestId: _requestId, responseId: _responseId}); + vm.prank(proposer); + oracle.proposeResponse(mockRequest, mockResponse); + _resetMockIds(); vm.startPrank(disputer); _accountingExtension.approveModule(address(_responseModule)); - oracle.disputeResponse(_request, _response, _dispute); + oracle.disputeResponse(mockRequest, mockResponse, mockDispute); vm.stopPrank(); - uint256 _requesterBondedBalance = _accountingExtension.bondedAmountOf(requester, usdc, _requestId); - uint256 _proposerBondedBalance = _accountingExtension.bondedAmountOf(proposer, usdc, _requestId); - - uint256 _requesterVirtualBalance = _accountingExtension.balanceOf(requester, usdc); - uint256 _proposerVirtualBalance = _accountingExtension.balanceOf(proposer, usdc); - assertEq(_requesterBondedBalance, 0); - assertEq(_proposerBondedBalance, 0); - - assertEq(_requesterVirtualBalance, 0); - assertEq(_proposerVirtualBalance, _expectedBondSize + _expectedReward); + assertEq(_accountingExtension.bondedAmountOf(requester, usdc, _requestId), 0); + assertEq(_accountingExtension.bondedAmountOf(proposer, usdc, _requestId), 0); + assertEq(_accountingExtension.balanceOf(requester, usdc), 0); + assertEq(_accountingExtension.balanceOf(proposer, usdc), _expectedBondSize + _expectedReward); } } diff --git a/solidity/test/utils/Helpers.sol b/solidity/test/utils/Helpers.sol index 81b580dd..9e7beab2 100644 --- a/solidity/test/utils/Helpers.sol +++ b/solidity/test/utils/Helpers.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; -import {DSTestPlus} from '@defi-wonderland/solidity-utils/solidity/test/DSTestPlus.sol'; import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol'; +import {DSTestPlus} from '@defi-wonderland/solidity-utils/solidity/test/DSTestPlus.sol'; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {IAccountingExtension} from '../../interfaces/extensions/IAccountingExtension.sol'; import {TestConstants} from './TestConstants.sol'; @@ -53,21 +53,6 @@ contract Helpers is DSTestPlus, TestConstants { vm.expectCall(_receiver, _calldata); } - function _forBondDepositERC20( - IAccountingExtension _accountingExtension, - address _depositor, - IERC20 _token, - uint256 _depositAmount, - uint256 _balanceIncrease - ) internal { - vm.assume(_balanceIncrease >= _depositAmount); - deal(address(_token), _depositor, _balanceIncrease); - vm.startPrank(_depositor); - _token.approve(address(_accountingExtension), _depositAmount); - _accountingExtension.deposit(_token, _depositAmount); - vm.stopPrank(); - } - /** * @notice Computes the ID of a given request as it's done in the Oracle * diff --git a/yarn.lock b/yarn.lock index 3fcb4654..a0be5325 100644 --- a/yarn.lock +++ b/yarn.lock @@ -192,10 +192,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@defi-wonderland/prophet-core-contracts@0.0.0-ad870035": - version "0.0.0-ad870035" - resolved "https://registry.yarnpkg.com/@defi-wonderland/prophet-core-contracts/-/prophet-core-contracts-0.0.0-ad870035.tgz#b3ed33f46310bbac137f4c5952bd75c5cd608507" - integrity sha512-tfGPXUYB8zF7+NSGzTNKZfYDJ4/yihmf2ZQevTmHn8gzkNEzQeBYf3qi/hslxXQ0HwnHfJd7VftCQDy/kVcr8g== +"@defi-wonderland/prophet-core-contracts@0.0.0-49fc8fa6": + version "0.0.0-49fc8fa6" + resolved "https://registry.yarnpkg.com/@defi-wonderland/prophet-core-contracts/-/prophet-core-contracts-0.0.0-49fc8fa6.tgz#53d3488a1f0c1513d7887ae5647210900b4d7dd9" + integrity sha512-FAmCxlnLGJk7pK1R0PYSNBay7Fqk4CUBJPoua6dvUw/q/RsZJeK8IVAoYII2OOXIn0XqFRysXIMAK/SV71wQQw== "@defi-wonderland/solidity-utils@0.0.0-3e9c8e8b": version "0.0.0-3e9c8e8b"