From 157a624e7c1863344d65ef1721be7b12e1d45911 Mon Sep 17 00:00:00 2001 From: Gas One Cent <86567384+gas1cent@users.noreply.github.com> Date: Tue, 7 Nov 2023 22:54:18 +0400 Subject: [PATCH 1/6] feat: uncomment helpers --- .../extensions/IAccountingExtension.sol | 380 +++++++------- .../extensions/IBondEscalationAccounting.sol | 480 +++++++++--------- solidity/test/utils/Helpers.sol | 122 +++-- 3 files changed, 496 insertions(+), 486 deletions(-) diff --git a/solidity/interfaces/extensions/IAccountingExtension.sol b/solidity/interfaces/extensions/IAccountingExtension.sol index 5a42243e..74c9c9cd 100644 --- a/solidity/interfaces/extensions/IAccountingExtension.sol +++ b/solidity/interfaces/extensions/IAccountingExtension.sol @@ -1,190 +1,190 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.19; - -// import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; -// import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol'; - -// /* -// * @title AccountingExtension -// * @notice Extension allowing users to deposit and bond funds -// * to be used for payments and disputes. -// */ -// interface IAccountingExtension { -// /*/////////////////////////////////////////////////////////////// -// EVENTS -// //////////////////////////////////////////////////////////////*/ - -// /** -// * @notice A user deposited tokens into the accounting extension -// * @param _depositor The user who deposited the tokens -// * @param _token The address of the token deposited by the user -// * @param _amount The amount of `_token` deposited -// */ -// event Deposited(address indexed _depositor, IERC20 indexed _token, uint256 _amount); - -// /** -// * @notice A user withdrew tokens from the accounting extension -// * @param _withdrawer The user who withdrew the tokens -// * @param _token The address of the token withdrawn by the user -// * @param _amount The amount of `_token` withdrawn -// */ -// event Withdrew(address indexed _withdrawer, IERC20 indexed _token, uint256 _amount); - -// /** -// * @notice A payment between users has been made -// * @param _beneficiary The user receiving the tokens -// * @param _payer The user who is getting its tokens transferred -// * @param _token The address of the token being transferred -// * @param _amount The amount of `_token` transferred -// */ -// event Paid( -// bytes32 indexed _requestId, address indexed _beneficiary, address indexed _payer, IERC20 _token, uint256 _amount -// ); - -// /** -// * @notice User's funds have been bonded -// * @param _bonder The user who is getting its tokens bonded -// * @param _token The address of the token being bonded -// * @param _amount The amount of `_token` bonded -// */ -// event Bonded(bytes32 indexed _requestId, address indexed _bonder, IERC20 indexed _token, uint256 _amount); - -// /** -// * @notice User's funds have been released -// * @param _beneficiary The user who is getting its tokens released -// * @param _token The address of the token being released -// * @param _amount The amount of `_token` released -// */ -// event Released(bytes32 indexed _requestId, address indexed _beneficiary, IERC20 indexed _token, uint256 _amount); - -// /*/////////////////////////////////////////////////////////////// -// ERRORS -// //////////////////////////////////////////////////////////////*/ - -// /** -// * @notice Thrown when the account doesn't have enough balance to bond/withdraw -// * or not enough bonded to release/pay -// */ -// error AccountingExtension_InsufficientFunds(); - -// /** -// * @notice Thrown when the module bonding user tokens hasn't been approved by the user. -// */ -// error AccountingExtension_InsufficientAllowance(); - -// /** -// * @notice Thrown when an `onlyAllowedModule` function is called by something -// * else than a module being used in the corresponding request -// */ -// error AccountingExtension_UnauthorizedModule(); - -// /** -// * @notice Thrown when an `onlyParticipant` function is called with an address -// * that is not part of the request. -// */ -// error AccountingExtension_UnauthorizedUser(); - -// /*/////////////////////////////////////////////////////////////// -// VARIABLES -// //////////////////////////////////////////////////////////////*/ - -// /** -// * @notice Returns the interface for the Oracle contract -// */ -// function ORACLE() external view returns (IOracle _oracle); - -// /** -// * @notice Returns the amount of a token a user has bonded -// * @param _user The address of the user with bonded tokens -// * @param _bondToken The token bonded -// * @param _requestId The id of the request the user bonded for -// * @return _amount The amount of `_bondToken` bonded -// */ -// function bondedAmountOf(address _user, IERC20 _bondToken, bytes32 _requestId) external returns (uint256 _amount); - -// /** -// * @notice Returns the amount of a token a user has deposited -// * @param _user The address of the user with deposited tokens -// * @param _token The token deposited -// * @return _amount The amount of `_token` deposited -// */ -// function balanceOf(address _user, IERC20 _token) external view returns (uint256 _amount); - -// /*/////////////////////////////////////////////////////////////// -// LOGIC -// //////////////////////////////////////////////////////////////*/ - -// /** -// * @notice Transfers tokens from a user and updates his virtual balance -// * @dev The user must have approved the accounting extension to transfer the tokens. -// * @param _token The address of the token being deposited -// * @param _amount The amount of `_token` to deposit -// */ -// function deposit(IERC20 _token, uint256 _amount) external; - -// /** -// * @notice Allows an user to withdraw deposited tokens -// * @param _token The address of the token being withdrawn -// * @param _amount The amount of `_token` to withdraw -// */ -// function withdraw(IERC20 _token, uint256 _amount) external; - -// /** -// * @notice Allows a allowed module to transfer bonded tokens from one user to another -// * @dev Only the virtual balances in the accounting extension are modified. The token contract -// * is not called nor its balances modified. -// * @param _requestId The id of the request handling the user's tokens -// * @param _payer The address of the user paying the tokens -// * @param _receiver The address of the user receiving the tokens -// * @param _token The address of the token being transferred -// * @param _amount The amount of `_token` being transferred -// */ -// function pay(bytes32 _requestId, address _payer, address _receiver, IERC20 _token, uint256 _amount) external; - -// /** -// * @notice Allows a allowed module to bond a user's tokens for a request -// * @param _bonder The address of the user to bond tokens for -// * @param _requestId The id of the request the user is bonding for -// * @param _token The address of the token being bonded -// * @param _amount The amount of `_token` to bond -// */ -// function bond(address _bonder, bytes32 _requestId, IERC20 _token, uint256 _amount) external; - -// /** -// * @notice Allows a valid module to bond a user's tokens for a request -// * @param _bonder The address of the user to bond tokens for -// * @param _requestId The id of the request the user is bonding for -// * @param _token The address of the token being bonded -// * @param _amount The amount of `_token` to bond -// * @param _sender The address starting the propose call on the Oracle -// */ -// function bond(address _bonder, bytes32 _requestId, IERC20 _token, uint256 _amount, address _sender) external; - -// /** -// * @notice Allows a valid module to release a user's tokens -// * @param _bonder The address of the user to release tokens for -// * @param _requestId The id of the request where the tokens were bonded -// * @param _token The address of the token being released -// * @param _amount The amount of `_token` to release -// */ -// function release(address _bonder, bytes32 _requestId, IERC20 _token, uint256 _amount) external; - -// /** -// * @notice Allows a user to approve a module for bonding tokens -// * @param _module The address of the module to be approved -// */ -// function approveModule(address _module) external; - -// /** -// * @notice Allows a user to revoke a module's approval for bonding tokens -// * @param _module The address of the module to be revoked -// */ -// function revokeModule(address _module) external; - -// /** -// * @notice Returns a list of all modules a user has approved -// * @param _user The address of the user -// * @return _approvedModules The array of all modules approved by the user -// */ -// function approvedModules(address _user) external view returns (address[] memory _approvedModules); -// } +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol'; + +/* + * @title AccountingExtension + * @notice Extension allowing users to deposit and bond funds + * to be used for payments and disputes. + */ +interface IAccountingExtension { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice A user deposited tokens into the accounting extension + * @param _depositor The user who deposited the tokens + * @param _token The address of the token deposited by the user + * @param _amount The amount of `_token` deposited + */ + event Deposited(address indexed _depositor, IERC20 indexed _token, uint256 _amount); + + /** + * @notice A user withdrew tokens from the accounting extension + * @param _withdrawer The user who withdrew the tokens + * @param _token The address of the token withdrawn by the user + * @param _amount The amount of `_token` withdrawn + */ + event Withdrew(address indexed _withdrawer, IERC20 indexed _token, uint256 _amount); + + /** + * @notice A payment between users has been made + * @param _beneficiary The user receiving the tokens + * @param _payer The user who is getting its tokens transferred + * @param _token The address of the token being transferred + * @param _amount The amount of `_token` transferred + */ + event Paid( + bytes32 indexed _requestId, address indexed _beneficiary, address indexed _payer, IERC20 _token, uint256 _amount + ); + + /** + * @notice User's funds have been bonded + * @param _bonder The user who is getting its tokens bonded + * @param _token The address of the token being bonded + * @param _amount The amount of `_token` bonded + */ + event Bonded(bytes32 indexed _requestId, address indexed _bonder, IERC20 indexed _token, uint256 _amount); + + /** + * @notice User's funds have been released + * @param _beneficiary The user who is getting its tokens released + * @param _token The address of the token being released + * @param _amount The amount of `_token` released + */ + event Released(bytes32 indexed _requestId, address indexed _beneficiary, IERC20 indexed _token, uint256 _amount); + + /*/////////////////////////////////////////////////////////////// + ERRORS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Thrown when the account doesn't have enough balance to bond/withdraw + * or not enough bonded to release/pay + */ + error AccountingExtension_InsufficientFunds(); + + /** + * @notice Thrown when the module bonding user tokens hasn't been approved by the user. + */ + error AccountingExtension_InsufficientAllowance(); + + /** + * @notice Thrown when an `onlyAllowedModule` function is called by something + * else than a module being used in the corresponding request + */ + error AccountingExtension_UnauthorizedModule(); + + /** + * @notice Thrown when an `onlyParticipant` function is called with an address + * that is not part of the request. + */ + error AccountingExtension_UnauthorizedUser(); + + /*/////////////////////////////////////////////////////////////// + VARIABLES + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Returns the interface for the Oracle contract + */ + function ORACLE() external view returns (IOracle _oracle); + + /** + * @notice Returns the amount of a token a user has bonded + * @param _user The address of the user with bonded tokens + * @param _bondToken The token bonded + * @param _requestId The id of the request the user bonded for + * @return _amount The amount of `_bondToken` bonded + */ + function bondedAmountOf(address _user, IERC20 _bondToken, bytes32 _requestId) external returns (uint256 _amount); + + /** + * @notice Returns the amount of a token a user has deposited + * @param _user The address of the user with deposited tokens + * @param _token The token deposited + * @return _amount The amount of `_token` deposited + */ + function balanceOf(address _user, IERC20 _token) external view returns (uint256 _amount); + + /*/////////////////////////////////////////////////////////////// + LOGIC + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Transfers tokens from a user and updates his virtual balance + * @dev The user must have approved the accounting extension to transfer the tokens. + * @param _token The address of the token being deposited + * @param _amount The amount of `_token` to deposit + */ + function deposit(IERC20 _token, uint256 _amount) external; + + /** + * @notice Allows an user to withdraw deposited tokens + * @param _token The address of the token being withdrawn + * @param _amount The amount of `_token` to withdraw + */ + function withdraw(IERC20 _token, uint256 _amount) external; + + /** + * @notice Allows a allowed module to transfer bonded tokens from one user to another + * @dev Only the virtual balances in the accounting extension are modified. The token contract + * is not called nor its balances modified. + * @param _requestId The id of the request handling the user's tokens + * @param _payer The address of the user paying the tokens + * @param _receiver The address of the user receiving the tokens + * @param _token The address of the token being transferred + * @param _amount The amount of `_token` being transferred + */ + function pay(bytes32 _requestId, address _payer, address _receiver, IERC20 _token, uint256 _amount) external; + + /** + * @notice Allows a allowed module to bond a user's tokens for a request + * @param _bonder The address of the user to bond tokens for + * @param _requestId The id of the request the user is bonding for + * @param _token The address of the token being bonded + * @param _amount The amount of `_token` to bond + */ + function bond(address _bonder, bytes32 _requestId, IERC20 _token, uint256 _amount) external; + + /** + * @notice Allows a valid module to bond a user's tokens for a request + * @param _bonder The address of the user to bond tokens for + * @param _requestId The id of the request the user is bonding for + * @param _token The address of the token being bonded + * @param _amount The amount of `_token` to bond + * @param _sender The address starting the propose call on the Oracle + */ + function bond(address _bonder, bytes32 _requestId, IERC20 _token, uint256 _amount, address _sender) external; + + /** + * @notice Allows a valid module to release a user's tokens + * @param _bonder The address of the user to release tokens for + * @param _requestId The id of the request where the tokens were bonded + * @param _token The address of the token being released + * @param _amount The amount of `_token` to release + */ + function release(address _bonder, bytes32 _requestId, IERC20 _token, uint256 _amount) external; + + /** + * @notice Allows a user to approve a module for bonding tokens + * @param _module The address of the module to be approved + */ + function approveModule(address _module) external; + + /** + * @notice Allows a user to revoke a module's approval for bonding tokens + * @param _module The address of the module to be revoked + */ + function revokeModule(address _module) external; + + /** + * @notice Returns a list of all modules a user has approved + * @param _user The address of the user + * @return _approvedModules The array of all modules approved by the user + */ + function approvedModules(address _user) external view returns (address[] memory _approvedModules); +} diff --git a/solidity/interfaces/extensions/IBondEscalationAccounting.sol b/solidity/interfaces/extensions/IBondEscalationAccounting.sol index 6dbec948..ce7728c9 100644 --- a/solidity/interfaces/extensions/IBondEscalationAccounting.sol +++ b/solidity/interfaces/extensions/IBondEscalationAccounting.sol @@ -1,240 +1,240 @@ -// // SPDX-License-Identifier: AGPL-3.0-only -// pragma solidity ^0.8.19; - -// import {IAccountingExtension} from './IAccountingExtension.sol'; -// import {IBondEscalationModule} from '../modules/dispute/IBondEscalationModule.sol'; -// import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; - -// /** -// * @title BondEscalationAccounting -// * @notice Extension allowing users to deposit and pledge funds to be used for bond escalation -// */ -// interface IBondEscalationAccounting is IAccountingExtension { -// /*/////////////////////////////////////////////////////////////// -// EVENTS -// //////////////////////////////////////////////////////////////*/ - -// /** -// * @notice A user pledged tokens for one of the sides of a dispute -// * -// * @param _pledger The user who pledged the tokens -// * @param _requestId The ID of the bond-escalated request -// * @param _disputeId The ID of the bond-escalated dispute -// * @param _token The address of the token being pledged -// * @param _amount The amount of `_token` pledged by the user -// */ -// event Pledged( -// address indexed _pledger, bytes32 indexed _requestId, bytes32 indexed _disputeId, IERC20 _token, uint256 _amount -// ); - -// /** -// * @notice The pledgers of the winning side of a dispute have been paid -// * -// * @param _requestId The ID of the bond-escalated request -// * @param _disputeId The ID of the bond-escalated dispute -// * @param _winningPledgers The users who got paid for pledging for the winning side -// * @param _token The address of the token being paid out -// * @param _amountPerPledger The amount of `_token` paid to each of the winning pledgers -// */ -// event WinningPledgersPaid( -// bytes32 indexed _requestId, -// bytes32 indexed _disputeId, -// address[] indexed _winningPledgers, -// IERC20 _token, -// uint256 _amountPerPledger -// ); - -// /** -// * @notice A bond escalation has been settled -// * -// * @param _requestId The ID of the bond-escalated request -// * @param _disputeId The ID of the bond-escalated dispute -// * @param _forVotesWon True if the winning side were the for votes -// * @param _token The address of the token being paid out -// * @param _amountPerPledger The amount of `_token` to be paid for each winning pledgers -// * @param _winningPledgersLength The number of winning pledgers -// */ -// event BondEscalationSettled( -// bytes32 _requestId, -// bytes32 _disputeId, -// bool _forVotesWon, -// IERC20 _token, -// uint256 _amountPerPledger, -// uint256 _winningPledgersLength -// ); - -// /** -// * @notice A pledge has been released back to the user -// * -// * @param _requestId The ID of the bond-escalated request -// * @param _disputeId The ID of the bond-escalated dispute -// * @param _pledger The user who is getting their tokens released -// * @param _token The address of the token being released -// * @param _amount The amount of `_token` released -// */ -// event PledgeReleased( -// bytes32 indexed _requestId, bytes32 indexed _disputeId, address indexed _pledger, IERC20 _token, uint256 _amount -// ); - -// /** -// * @notice A user claimed their reward for pledging for the winning side of a dispute -// * -// * @param _requestId The ID of the bond-escalated request -// * @param _disputeId The ID of the bond-escalated dispute -// * @param _pledger The user who claimed their reward -// * @param _token The address of the token being paid out -// * @param _amount The amount of `_token` paid to the pledger -// */ -// event EscalationRewardClaimed( -// bytes32 indexed _requestId, bytes32 indexed _disputeId, address indexed _pledger, IERC20 _token, uint256 _amount -// ); - -// /*/////////////////////////////////////////////////////////////// -// STRUCTS -// //////////////////////////////////////////////////////////////*/ - -// /** -// * @notice Contains the data of the result of an escalation. Is used by users to claim their pledges -// * @param requestId The ID of the bond-escalated request -// * @param forVotesWon Whether the for votes won the dispute -// * @param token The address of the token being paid out -// * @param amountPerPledger The amount of token paid to each of the winning pledgers -// * @param bondEscalationModule The address of the bond escalation module that was used -// */ -// struct EscalationResult { -// bytes32 requestId; -// bool forVotesWon; -// IERC20 token; -// uint256 amountPerPledger; -// IBondEscalationModule bondEscalationModule; -// } - -// /*/////////////////////////////////////////////////////////////// -// ERRORS -// //////////////////////////////////////////////////////////////*/ - -// /** -// * @notice Thrown when the user tries to claim their pledge for an escalation that was already claimed -// */ -// error BondEscalationAccounting_AlreadyClaimed(); - -// /** -// * @notice Thrown when the user tries to claim their pledge for an escalation that wasn't finished yet -// */ -// error BondEscalationAccounting_NoEscalationResult(); - -// /** -// * @notice Thrown when the user doesn't have enough funds to pledge -// */ -// error BondEscalationAccounting_InsufficientFunds(); - -// /** -// * @notice Thrown when trying to settle an already settled escalation -// */ -// error BondEscalationAccounting_AlreadySettled(); - -// /*/////////////////////////////////////////////////////////////// -// VARIABLES -// //////////////////////////////////////////////////////////////*/ - -// /** -// * @notice The amount pledged by the given pledger in the given dispute of the given request -// * -// * @param _disputeId The ID of the bond-escalated dispute -// * @param _token Address of the token being pledged -// * @return _amountPledged The amount of pledged tokens -// */ -// function pledges(bytes32 _disputeId, IERC20 _token) external returns (uint256 _amountPledged); - -// /** -// * @notice The result of the given dispute -// * -// * @param _disputeId The ID of the bond-escalated dispute -// * @return _requestId The ID of the bond-escalated request -// * @return _forVotesWon True if the for votes won the dispute -// * @return _token Address of the token being paid as a reward for winning the bond escalation -// * @return _amountPerPledger Amount of `_token` to be rewarded to each of the winning pledgers -// * @return _bondEscalationModule The address of the bond escalation module that was used -// */ -// function escalationResults(bytes32 _disputeId) -// external -// returns ( -// bytes32 _requestId, -// bool _forVotesWon, -// IERC20 _token, -// uint256 _amountPerPledger, -// IBondEscalationModule _bondEscalationModule -// ); - -// /** -// * @notice True if the given pledger has claimed their reward for the given dispute -// * -// * @param _requestId The ID of the bond-escalated request -// * @param _pledger Address of the pledger -// * @return _claimed True if the pledger has claimed their reward -// */ -// function pledgerClaimed(bytes32 _requestId, address _pledger) external returns (bool _claimed); - -// /*/////////////////////////////////////////////////////////////// -// LOGIC -// //////////////////////////////////////////////////////////////*/ - -// /** -// * @notice Pledges the given amount of token to the provided dispute id of the provided request id -// * -// * @dev This function must be called by an allowed module -// * -// * @param _pledger Address of the pledger -// * @param _requestId The ID of the bond-escalated request -// * @param _disputeId The ID of the bond-escalated dispute -// * @param _token Address of the token being paid as a reward for winning the bond escalation -// * @param _amount Amount of token to pledge -// */ -// function pledge(address _pledger, bytes32 _requestId, bytes32 _disputeId, IERC20 _token, uint256 _amount) external; - -// /** -// * @notice Updates the accounting of the given dispute to reflect the result of the bond escalation -// * @dev This function must be called by an allowed module -// * -// * @param _requestId The ID of the bond-escalated request -// * @param _disputeId The ID of the bond-escalated dispute -// * @param _forVotesWon True if the for votes won the dispute -// * @param _token Address of the token being paid as a reward for winning the bond escalation -// * @param _amountPerPledger Amount of `_token` to be rewarded to each of the winning pledgers -// * @param _winningPledgersLength Amount of pledges that won the dispute -// */ -// function onSettleBondEscalation( -// bytes32 _requestId, -// bytes32 _disputeId, -// bool _forVotesWon, -// IERC20 _token, -// uint256 _amountPerPledger, -// uint256 _winningPledgersLength -// ) external; - -// /** -// * @notice Releases a given amount of funds to the pledger -// * -// * @dev This function must be called by an allowed module -// * -// * @param _requestId The ID of the bond-escalated request -// * @param _disputeId The ID of the bond-escalated dispute -// * @param _pledger Address of the pledger -// * @param _token Address of the token to be released -// * @param _amount Amount of `_token` to be released to the pledger -// */ -// function releasePledge( -// bytes32 _requestId, -// bytes32 _disputeId, -// address _pledger, -// IERC20 _token, -// uint256 _amount -// ) external; - -// /** -// * @notice Claims the reward for the pledger the given dispute -// * @param _disputeId The ID of the bond-escalated dispute -// * @param _pledger Address of the pledger to claim the rewards -// */ -// function claimEscalationReward(bytes32 _disputeId, address _pledger) external; -// } +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import {IAccountingExtension} from './IAccountingExtension.sol'; +import {IBondEscalationModule} from '../modules/dispute/IBondEscalationModule.sol'; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; + +/** + * @title BondEscalationAccounting + * @notice Extension allowing users to deposit and pledge funds to be used for bond escalation + */ +interface IBondEscalationAccounting is IAccountingExtension { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice A user pledged tokens for one of the sides of a dispute + * + * @param _pledger The user who pledged the tokens + * @param _requestId The ID of the bond-escalated request + * @param _disputeId The ID of the bond-escalated dispute + * @param _token The address of the token being pledged + * @param _amount The amount of `_token` pledged by the user + */ + event Pledged( + address indexed _pledger, bytes32 indexed _requestId, bytes32 indexed _disputeId, IERC20 _token, uint256 _amount + ); + + /** + * @notice The pledgers of the winning side of a dispute have been paid + * + * @param _requestId The ID of the bond-escalated request + * @param _disputeId The ID of the bond-escalated dispute + * @param _winningPledgers The users who got paid for pledging for the winning side + * @param _token The address of the token being paid out + * @param _amountPerPledger The amount of `_token` paid to each of the winning pledgers + */ + event WinningPledgersPaid( + bytes32 indexed _requestId, + bytes32 indexed _disputeId, + address[] indexed _winningPledgers, + IERC20 _token, + uint256 _amountPerPledger + ); + + /** + * @notice A bond escalation has been settled + * + * @param _requestId The ID of the bond-escalated request + * @param _disputeId The ID of the bond-escalated dispute + * @param _forVotesWon True if the winning side were the for votes + * @param _token The address of the token being paid out + * @param _amountPerPledger The amount of `_token` to be paid for each winning pledgers + * @param _winningPledgersLength The number of winning pledgers + */ + event BondEscalationSettled( + bytes32 _requestId, + bytes32 _disputeId, + bool _forVotesWon, + IERC20 _token, + uint256 _amountPerPledger, + uint256 _winningPledgersLength + ); + + /** + * @notice A pledge has been released back to the user + * + * @param _requestId The ID of the bond-escalated request + * @param _disputeId The ID of the bond-escalated dispute + * @param _pledger The user who is getting their tokens released + * @param _token The address of the token being released + * @param _amount The amount of `_token` released + */ + event PledgeReleased( + bytes32 indexed _requestId, bytes32 indexed _disputeId, address indexed _pledger, IERC20 _token, uint256 _amount + ); + + /** + * @notice A user claimed their reward for pledging for the winning side of a dispute + * + * @param _requestId The ID of the bond-escalated request + * @param _disputeId The ID of the bond-escalated dispute + * @param _pledger The user who claimed their reward + * @param _token The address of the token being paid out + * @param _amount The amount of `_token` paid to the pledger + */ + event EscalationRewardClaimed( + bytes32 indexed _requestId, bytes32 indexed _disputeId, address indexed _pledger, IERC20 _token, uint256 _amount + ); + + /*/////////////////////////////////////////////////////////////// + STRUCTS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Contains the data of the result of an escalation. Is used by users to claim their pledges + * @param requestId The ID of the bond-escalated request + * @param forVotesWon Whether the for votes won the dispute + * @param token The address of the token being paid out + * @param amountPerPledger The amount of token paid to each of the winning pledgers + * @param bondEscalationModule The address of the bond escalation module that was used + */ + struct EscalationResult { + bytes32 requestId; + bool forVotesWon; + IERC20 token; + uint256 amountPerPledger; + IBondEscalationModule bondEscalationModule; + } + + /*/////////////////////////////////////////////////////////////// + ERRORS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Thrown when the user tries to claim their pledge for an escalation that was already claimed + */ + error BondEscalationAccounting_AlreadyClaimed(); + + /** + * @notice Thrown when the user tries to claim their pledge for an escalation that wasn't finished yet + */ + error BondEscalationAccounting_NoEscalationResult(); + + /** + * @notice Thrown when the user doesn't have enough funds to pledge + */ + error BondEscalationAccounting_InsufficientFunds(); + + /** + * @notice Thrown when trying to settle an already settled escalation + */ + error BondEscalationAccounting_AlreadySettled(); + + /*/////////////////////////////////////////////////////////////// + VARIABLES + //////////////////////////////////////////////////////////////*/ + + /** + * @notice The amount pledged by the given pledger in the given dispute of the given request + * + * @param _disputeId The ID of the bond-escalated dispute + * @param _token Address of the token being pledged + * @return _amountPledged The amount of pledged tokens + */ + function pledges(bytes32 _disputeId, IERC20 _token) external returns (uint256 _amountPledged); + + /** + * @notice The result of the given dispute + * + * @param _disputeId The ID of the bond-escalated dispute + * @return _requestId The ID of the bond-escalated request + * @return _forVotesWon True if the for votes won the dispute + * @return _token Address of the token being paid as a reward for winning the bond escalation + * @return _amountPerPledger Amount of `_token` to be rewarded to each of the winning pledgers + * @return _bondEscalationModule The address of the bond escalation module that was used + */ + function escalationResults(bytes32 _disputeId) + external + returns ( + bytes32 _requestId, + bool _forVotesWon, + IERC20 _token, + uint256 _amountPerPledger, + IBondEscalationModule _bondEscalationModule + ); + + /** + * @notice True if the given pledger has claimed their reward for the given dispute + * + * @param _requestId The ID of the bond-escalated request + * @param _pledger Address of the pledger + * @return _claimed True if the pledger has claimed their reward + */ + function pledgerClaimed(bytes32 _requestId, address _pledger) external returns (bool _claimed); + + /*/////////////////////////////////////////////////////////////// + LOGIC + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Pledges the given amount of token to the provided dispute id of the provided request id + * + * @dev This function must be called by an allowed module + * + * @param _pledger Address of the pledger + * @param _requestId The ID of the bond-escalated request + * @param _disputeId The ID of the bond-escalated dispute + * @param _token Address of the token being paid as a reward for winning the bond escalation + * @param _amount Amount of token to pledge + */ + function pledge(address _pledger, bytes32 _requestId, bytes32 _disputeId, IERC20 _token, uint256 _amount) external; + + /** + * @notice Updates the accounting of the given dispute to reflect the result of the bond escalation + * @dev This function must be called by an allowed module + * + * @param _requestId The ID of the bond-escalated request + * @param _disputeId The ID of the bond-escalated dispute + * @param _forVotesWon True if the for votes won the dispute + * @param _token Address of the token being paid as a reward for winning the bond escalation + * @param _amountPerPledger Amount of `_token` to be rewarded to each of the winning pledgers + * @param _winningPledgersLength Amount of pledges that won the dispute + */ + function onSettleBondEscalation( + bytes32 _requestId, + bytes32 _disputeId, + bool _forVotesWon, + IERC20 _token, + uint256 _amountPerPledger, + uint256 _winningPledgersLength + ) external; + + /** + * @notice Releases a given amount of funds to the pledger + * + * @dev This function must be called by an allowed module + * + * @param _requestId The ID of the bond-escalated request + * @param _disputeId The ID of the bond-escalated dispute + * @param _pledger Address of the pledger + * @param _token Address of the token to be released + * @param _amount Amount of `_token` to be released to the pledger + */ + function releasePledge( + bytes32 _requestId, + bytes32 _disputeId, + address _pledger, + IERC20 _token, + uint256 _amount + ) external; + + /** + * @notice Claims the reward for the pledger the given dispute + * @param _disputeId The ID of the bond-escalated dispute + * @param _pledger Address of the pledger to claim the rewards + */ + function claimEscalationReward(bytes32 _disputeId, address _pledger) external; +} diff --git a/solidity/test/utils/Helpers.sol b/solidity/test/utils/Helpers.sol index 46874489..af32e439 100644 --- a/solidity/test/utils/Helpers.sol +++ b/solidity/test/utils/Helpers.sol @@ -1,56 +1,66 @@ -// // 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 {IAccountingExtension} from '../../interfaces/extensions/IAccountingExtension.sol'; - -// contract Helpers is DSTestPlus { -// modifier assumeFuzzable(address _address) { -// _assumeFuzzable(_address); -// _; -// } - -// function _assumeFuzzable(address _address) internal pure { -// assumeNotForgeAddress(_address); -// assumeNotZeroAddress(_address); -// assumeNotPrecompile(_address); -// } - -// function _mockAndExpect(address _receiver, bytes memory _calldata, bytes memory _returned) internal { -// vm.mockCall(_receiver, _calldata, _returned); -// vm.expectCall(_receiver, _calldata); -// } - -// function _getMockDispute( -// bytes32 _requestId, -// address _disputer, -// address _proposer -// ) internal view returns (IOracle.Dispute memory _dispute) { -// _dispute = IOracle.Dispute({ -// disputer: _disputer, -// responseId: bytes32('response'), -// proposer: _proposer, -// requestId: _requestId, -// status: IOracle.DisputeStatus.None, -// createdAt: block.timestamp -// }); -// } - -// 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(); -// } -// } +// 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 {IAccountingExtension} from '../../interfaces/extensions/IAccountingExtension.sol'; + +contract Helpers is DSTestPlus { + modifier assumeFuzzable(address _address) { + _assumeFuzzable(_address); + _; + } + + function _assumeFuzzable(address _address) internal pure { + assumeNotForgeAddress(_address); + assumeNotZeroAddress(_address); + assumeNotPrecompile(_address); + } + + function _mockAndExpect(address _receiver, bytes memory _calldata, bytes memory _returned) internal { + vm.mockCall(_receiver, _calldata, _returned); + vm.expectCall(_receiver, _calldata); + } + + function _getMockDispute( + bytes32 _requestId, + address _disputer, + address _proposer + ) internal view returns (IOracle.Dispute memory _dispute) { + _dispute = IOracle.Dispute({ + disputer: _disputer, + responseId: bytes32('response'), + proposer: _proposer, + requestId: _requestId + }); + } + + 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(); + } + + function _getId(IOracle.Response memory _response) internal pure returns (bytes32 _id) { + _id = keccak256(abi.encode(_response)); + } + + function _getId(IOracle.Request memory _request) internal pure returns (bytes32 _id) { + _id = keccak256(abi.encode(_request)); + } + + function _getId(IOracle.Dispute memory _dispute) internal pure returns (bytes32 _id) { + _id = keccak256(abi.encode(_dispute)); + } +} From feede2c2e13ebf63d81730c684c816b2f980a492 Mon Sep 17 00:00:00 2001 From: Gas One Cent <86567384+gas1cent@users.noreply.github.com> Date: Tue, 7 Nov 2023 22:54:43 +0400 Subject: [PATCH 2/6] perf: optimize `BondEscalationModule` --- .../modules/dispute/BondEscalationModule.sol | 620 ++-- .../modules/dispute/IBondEscalationModule.sol | 491 ++- .../dispute/BondEscalationModule.t.sol | 2712 ++++++++--------- 3 files changed, 1831 insertions(+), 1992 deletions(-) diff --git a/solidity/contracts/modules/dispute/BondEscalationModule.sol b/solidity/contracts/modules/dispute/BondEscalationModule.sol index ce44ec97..cf0dbedd 100644 --- a/solidity/contracts/modules/dispute/BondEscalationModule.sol +++ b/solidity/contracts/modules/dispute/BondEscalationModule.sol @@ -1,314 +1,306 @@ -// // SPDX-License-Identifier: AGPL-3.0-only -// pragma solidity ^0.8.19; - -// // solhint-disable-next-line no-unused-import -// import {Module, IModule} from '@defi-wonderland/prophet-core-contracts/solidity/contracts/Module.sol'; -// import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol'; -// import {FixedPointMathLib} from 'solmate/utils/FixedPointMathLib.sol'; - -// import {IBondEscalationModule} from '../../../interfaces/modules/dispute/IBondEscalationModule.sol'; - -// contract BondEscalationModule is Module, IBondEscalationModule { -// /// @inheritdoc IBondEscalationModule -// mapping(bytes32 _requestId => mapping(address _pledger => uint256 pledges)) public pledgesForDispute; - -// /// @inheritdoc IBondEscalationModule -// mapping(bytes32 _requestId => mapping(address _pledger => uint256 pledges)) public pledgesAgainstDispute; - -// /** -// * @notice Struct containing all the data for a given escalation. -// */ -// mapping(bytes32 _requestId => BondEscalation) internal _escalations; - -// constructor(IOracle _oracle) Module(_oracle) {} - -// /// @inheritdoc IModule -// function moduleName() external pure returns (string memory _moduleName) { -// return 'BondEscalationModule'; -// } - -// /** -// * @notice Checks if the escalation parameters are valid -// * @param _data The encoded data for the request -// */ -// function _afterSetupRequest(bytes32, bytes calldata _data) internal pure override { -// RequestParameters memory _params = abi.decode(_data, (RequestParameters)); -// if (_params.maxNumberOfEscalations == 0 || _params.bondSize == 0) { -// revert BondEscalationModule_InvalidEscalationParameters(); -// } -// } - -// /// @inheritdoc IBondEscalationModule -// function disputeEscalated(bytes32 _disputeId) external onlyOracle { -// IOracle.Dispute memory _dispute = ORACLE.getDispute(_disputeId); -// bytes32 _requestId = _dispute.requestId; -// BondEscalation storage _escalation = _escalations[_requestId]; - -// if (_requestId == bytes32(0)) revert BondEscalationModule_DisputeDoesNotExist(); - -// if (_disputeId == _escalation.disputeId) { -// RequestParameters memory _params = decodeRequestData(_requestId); -// if (block.timestamp <= _params.bondEscalationDeadline) revert BondEscalationModule_BondEscalationNotOver(); - -// if ( -// _escalation.status != BondEscalationStatus.Active -// || _escalation.amountOfPledgesForDispute != _escalation.amountOfPledgesAgainstDispute -// ) { -// revert BondEscalationModule_NotEscalatable(); -// } - -// _escalation.status = BondEscalationStatus.Escalated; -// emit BondEscalationStatusUpdated(_requestId, _disputeId, BondEscalationStatus.Escalated); -// } -// } - -// /// @inheritdoc IBondEscalationModule -// function disputeResponse( -// bytes32 _requestId, -// bytes32 _responseId, -// address _disputer, -// address _proposer -// ) external onlyOracle returns (IOracle.Dispute memory _dispute) { -// RequestParameters memory _params = decodeRequestData(_requestId); -// IOracle.Response memory _response = ORACLE.getResponse(_responseId); - -// if (block.timestamp > _response.createdAt + _params.disputeWindow) { -// revert BondEscalationModule_DisputeWindowOver(); -// } - -// BondEscalation storage _escalation = _escalations[_requestId]; - -// // Only the first dispute of a request should go through the bond escalation -// // Consecutive disputes should be handled by the resolution module -// if (_escalation.status == BondEscalationStatus.None) { -// if (block.timestamp > _params.bondEscalationDeadline) revert BondEscalationModule_BondEscalationOver(); - -// // Note: this imitates the way _disputeId is calculated on the Oracle, it must always match -// bytes32 _disputeId = keccak256(abi.encodePacked(_disputer, _requestId, _responseId)); -// _escalation.status = BondEscalationStatus.Active; -// _escalation.disputeId = _disputeId; -// emit BondEscalationStatusUpdated(_requestId, _disputeId, BondEscalationStatus.Active); -// } - -// _dispute = IOracle.Dispute({ -// disputer: _disputer, -// responseId: _responseId, -// proposer: _proposer, -// requestId: _requestId, -// status: IOracle.DisputeStatus.Active, -// createdAt: block.timestamp -// }); - -// _params.accountingExtension.bond({ -// _bonder: _disputer, -// _requestId: _requestId, -// _token: _params.bondToken, -// _amount: _params.bondSize -// }); - -// emit ResponseDisputed(_requestId, _responseId, _disputer, _proposer); -// } - -// /// @inheritdoc IBondEscalationModule -// function onDisputeStatusChange(bytes32 _disputeId, IOracle.Dispute memory _dispute) external onlyOracle { -// RequestParameters memory _params = decodeRequestData(_dispute.requestId); - -// bool _won = _dispute.status == IOracle.DisputeStatus.Won; - -// _params.accountingExtension.pay({ -// _requestId: _dispute.requestId, -// _payer: _won ? _dispute.proposer : _dispute.disputer, -// _receiver: _won ? _dispute.disputer : _dispute.proposer, -// _token: _params.bondToken, -// _amount: _params.bondSize -// }); - -// if (_won) { -// _params.accountingExtension.release({ -// _requestId: _dispute.requestId, -// _bonder: _dispute.disputer, -// _token: _params.bondToken, -// _amount: _params.bondSize -// }); -// } - -// BondEscalation storage _escalation = _escalations[_dispute.requestId]; - -// if (_disputeId == _escalation.disputeId) { -// // The dispute has been escalated to the Resolution module -// if (_escalation.status == BondEscalationStatus.Escalated) { -// if (_escalation.amountOfPledgesAgainstDispute == 0) { -// return; -// } - -// BondEscalationStatus _newStatus = _won ? BondEscalationStatus.DisputerWon : BondEscalationStatus.DisputerLost; -// _escalation.status = _newStatus; - -// emit BondEscalationStatusUpdated(_dispute.requestId, _disputeId, _newStatus); - -// _params.accountingExtension.onSettleBondEscalation({ -// _requestId: _dispute.requestId, -// _disputeId: _disputeId, -// _forVotesWon: _won, -// _token: _params.bondToken, -// _amountPerPledger: _params.bondSize << 1, -// _winningPledgersLength: _won ? _escalation.amountOfPledgesForDispute : _escalation.amountOfPledgesAgainstDispute -// }); -// } else { -// // The status has changed to Won or Lost -// uint256 _pledgesForDispute = _escalation.amountOfPledgesForDispute; -// uint256 _pledgesAgainstDispute = _escalation.amountOfPledgesAgainstDispute; -// bool _disputersWon = _pledgesForDispute > _pledgesAgainstDispute; - -// uint256 _amountToPay = _disputersWon -// ? _params.bondSize + FixedPointMathLib.mulDivDown(_pledgesAgainstDispute, _params.bondSize, _pledgesForDispute) -// : _params.bondSize + FixedPointMathLib.mulDivDown(_pledgesForDispute, _params.bondSize, _pledgesAgainstDispute); - -// _params.accountingExtension.onSettleBondEscalation({ -// _requestId: _dispute.requestId, -// _disputeId: _escalation.disputeId, -// _forVotesWon: _disputersWon, -// _token: _params.bondToken, -// _amountPerPledger: _amountToPay, -// _winningPledgersLength: _disputersWon ? _pledgesForDispute : _pledgesAgainstDispute -// }); -// } -// } - -// emit DisputeStatusChanged({ -// _requestId: _dispute.requestId, -// _responseId: _dispute.responseId, -// _disputer: _dispute.disputer, -// _proposer: _dispute.proposer, -// _status: _dispute.status -// }); -// } - -// //////////////////////////////////////////////////////////////////// -// // Bond Escalation Exclusive Functions -// //////////////////////////////////////////////////////////////////// - -// /// @inheritdoc IBondEscalationModule -// function pledgeForDispute(bytes32 _disputeId) external { -// (bytes32 _requestId, RequestParameters memory _params) = _pledgeChecks(_disputeId, true); - -// _escalations[_requestId].amountOfPledgesForDispute += 1; -// pledgesForDispute[_requestId][msg.sender] += 1; -// _params.accountingExtension.pledge({ -// _pledger: msg.sender, -// _requestId: _requestId, -// _disputeId: _disputeId, -// _token: _params.bondToken, -// _amount: _params.bondSize -// }); - -// emit PledgedForDispute(_disputeId, msg.sender, _params.bondSize); -// } - -// /// @inheritdoc IBondEscalationModule -// function pledgeAgainstDispute(bytes32 _disputeId) external { -// (bytes32 _requestId, RequestParameters memory _params) = _pledgeChecks(_disputeId, false); - -// _escalations[_requestId].amountOfPledgesAgainstDispute += 1; -// pledgesAgainstDispute[_requestId][msg.sender] += 1; -// _params.accountingExtension.pledge({ -// _pledger: msg.sender, -// _requestId: _requestId, -// _disputeId: _disputeId, -// _token: _params.bondToken, -// _amount: _params.bondSize -// }); - -// emit PledgedAgainstDispute(_disputeId, msg.sender, _params.bondSize); -// } - -// /// @inheritdoc IBondEscalationModule -// function settleBondEscalation(bytes32 _requestId) external { -// RequestParameters memory _params = decodeRequestData(_requestId); -// BondEscalation storage _escalation = _escalations[_requestId]; - -// if (block.timestamp <= _params.bondEscalationDeadline + _params.tyingBuffer) { -// revert BondEscalationModule_BondEscalationNotOver(); -// } - -// if (_escalation.status != BondEscalationStatus.Active) { -// revert BondEscalationModule_BondEscalationCantBeSettled(); -// } - -// uint256 _pledgesForDispute = _escalation.amountOfPledgesForDispute; -// uint256 _pledgesAgainstDispute = _escalation.amountOfPledgesAgainstDispute; - -// if (_pledgesForDispute == _pledgesAgainstDispute) { -// revert BondEscalationModule_ShouldBeEscalated(); -// } - -// bool _disputersWon = _pledgesForDispute > _pledgesAgainstDispute; -// _escalation.status = _disputersWon ? BondEscalationStatus.DisputerWon : BondEscalationStatus.DisputerLost; - -// emit BondEscalationStatusUpdated(_requestId, _escalation.disputeId, _escalation.status); - -// ORACLE.updateDisputeStatus( -// _escalation.disputeId, _disputersWon ? IOracle.DisputeStatus.Won : IOracle.DisputeStatus.Lost -// ); -// } - -// /** -// * @notice Checks the necessary conditions for pledging -// * @param _disputeId The encoded data for the request -// * @return _requestId The ID of the request being disputed on -// * @return _params The decoded parameters for the request -// */ -// function _pledgeChecks( -// bytes32 _disputeId, -// bool _forDispute -// ) internal view returns (bytes32 _requestId, RequestParameters memory _params) { -// if (_disputeId == 0) revert BondEscalationModule_DisputeDoesNotExist(); - -// IOracle.Dispute memory _dispute = ORACLE.getDispute(_disputeId); -// _requestId = _dispute.requestId; -// BondEscalation memory _escalation = _escalations[_requestId]; - -// if (_disputeId != _escalation.disputeId) { -// revert BondEscalationModule_InvalidDispute(); -// } - -// _params = decodeRequestData(_requestId); - -// if (block.timestamp > _params.bondEscalationDeadline + _params.tyingBuffer) { -// revert BondEscalationModule_BondEscalationOver(); -// } - -// uint256 _numPledgersForDispute = _escalation.amountOfPledgesForDispute; -// uint256 _numPledgersAgainstDispute = _escalation.amountOfPledgesAgainstDispute; - -// if (_forDispute) { -// if (_numPledgersForDispute == _params.maxNumberOfEscalations) { -// revert BondEscalationModule_MaxNumberOfEscalationsReached(); -// } -// if (_numPledgersForDispute > _numPledgersAgainstDispute) revert BondEscalationModule_CanOnlySurpassByOnePledge(); -// } else { -// if (_numPledgersAgainstDispute == _params.maxNumberOfEscalations) { -// revert BondEscalationModule_MaxNumberOfEscalationsReached(); -// } -// if (_numPledgersAgainstDispute > _numPledgersForDispute) revert BondEscalationModule_CanOnlySurpassByOnePledge(); -// } - -// if (block.timestamp > _params.bondEscalationDeadline && _numPledgersForDispute == _numPledgersAgainstDispute) { -// revert BondEscalationModule_CannotBreakTieDuringTyingBuffer(); -// } -// } - -// //////////////////////////////////////////////////////////////////// -// // View Functions -// //////////////////////////////////////////////////////////////////// - -// /// @inheritdoc IBondEscalationModule -// function decodeRequestData(bytes32 _requestId) public view returns (RequestParameters memory _params) { -// _params = abi.decode(requestData[_requestId], (RequestParameters)); -// } - -// /// @inheritdoc IBondEscalationModule -// function getEscalation(bytes32 _requestId) public view returns (BondEscalation memory _escalation) { -// _escalation = _escalations[_requestId]; -// } -// } +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +// solhint-disable-next-line no-unused-import +import {Module, IModule} from '@defi-wonderland/prophet-core-contracts/solidity/contracts/Module.sol'; +import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol'; +import {FixedPointMathLib} from 'solmate/utils/FixedPointMathLib.sol'; + +import {IBondEscalationModule} from '../../../interfaces/modules/dispute/IBondEscalationModule.sol'; + +contract BondEscalationModule is Module, IBondEscalationModule { + /// @inheritdoc IBondEscalationModule + mapping(bytes32 _requestId => mapping(address _pledger => uint256 pledges)) public pledgesForDispute; + + /// @inheritdoc IBondEscalationModule + mapping(bytes32 _requestId => mapping(address _pledger => uint256 pledges)) public pledgesAgainstDispute; + + /** + * @notice Struct containing all the data for a given escalation. + */ + mapping(bytes32 _requestId => BondEscalation) internal _escalations; + + constructor(IOracle _oracle) Module(_oracle) {} + + /// @inheritdoc IModule + function moduleName() external pure returns (string memory _moduleName) { + return 'BondEscalationModule'; + } + + // TODO: Remove and instead use onDisputeStatusChanged + /// @inheritdoc IBondEscalationModule + // function disputeEscalated(bytes32 _disputeId, IOracle.Dispute calldata _dispute) external onlyOracle { + // bytes32 _requestId = _dispute.requestId; + // BondEscalation storage _escalation = _escalations[_dispute.requestId]; + + // if (_requestId == bytes32(0)) revert BondEscalationModule_DisputeDoesNotExist(); + + // if (_disputeId == _escalation.disputeId) { + // RequestParameters memory _params = decodeRequestData(_request.disputeModuleData); + // if (block.timestamp <= _params.bondEscalationDeadline) revert BondEscalationModule_BondEscalationNotOver(); + + // if ( + // _escalation.status != BondEscalationStatus.Active + // || _escalation.amountOfPledgesForDispute != _escalation.amountOfPledgesAgainstDispute + // ) { + // revert BondEscalationModule_NotEscalatable(); + // } + + // _escalation.status = BondEscalationStatus.Escalated; + // emit BondEscalationStatusUpdated(_requestId, _disputeId, BondEscalationStatus.Escalated); + // } + // } + + /// @inheritdoc IBondEscalationModule + function disputeResponse( + IOracle.Request calldata _request, + IOracle.Response calldata _response, + IOracle.Dispute calldata _dispute + ) external onlyOracle { + RequestParameters memory _params = decodeRequestData(_request.disputeModuleData); + + if (block.timestamp > ORACLE.createdAt(_dispute.responseId) + _params.disputeWindow) { + revert BondEscalationModule_DisputeWindowOver(); + } + + BondEscalation storage _escalation = _escalations[_dispute.requestId]; + bytes32 _disputeId = _getId(_dispute); + + // Only the first dispute of a request should go through the bond escalation + // Consecutive disputes should be handled by the resolution module + if (_escalation.status == BondEscalationStatus.None) { + if (block.timestamp > _params.bondEscalationDeadline) revert BondEscalationModule_BondEscalationOver(); + + // Note: this imitates the way _disputeId is calculated on the Oracle, it must always match + _escalation.status = BondEscalationStatus.Active; + _escalation.disputeId = _disputeId; + emit BondEscalationStatusUpdated(_dispute.requestId, _disputeId, BondEscalationStatus.Active); + } + + _params.accountingExtension.bond({ + _bonder: _dispute.disputer, + _requestId: _dispute.requestId, + _token: _params.bondToken, + _amount: _params.bondSize + }); + + emit ResponseDisputed({ + _responseId: _dispute.responseId, + _disputeId: _disputeId, + _dispute: _dispute, + _blockNumber: block.number + }); + } + + /// @inheritdoc IBondEscalationModule + function onDisputeStatusChange( + bytes32 _disputeId, + IOracle.Request calldata _request, + IOracle.Response calldata _response, + IOracle.Dispute calldata _dispute + ) external onlyOracle { + RequestParameters memory _params = decodeRequestData(_request.disputeModuleData); + + bool _won = ORACLE.disputeStatus(_disputeId) == IOracle.DisputeStatus.Won; + + _params.accountingExtension.pay({ + _requestId: _dispute.requestId, + _payer: _won ? _dispute.proposer : _dispute.disputer, + _receiver: _won ? _dispute.disputer : _dispute.proposer, + _token: _params.bondToken, + _amount: _params.bondSize + }); + + if (_won) { + _params.accountingExtension.release({ + _requestId: _dispute.requestId, + _bonder: _dispute.disputer, + _token: _params.bondToken, + _amount: _params.bondSize + }); + } + + BondEscalation storage _escalation = _escalations[_dispute.requestId]; + + if (_disputeId == _escalation.disputeId) { + // The dispute has been escalated to the Resolution module + if (_escalation.status == BondEscalationStatus.Escalated) { + if (_escalation.amountOfPledgesAgainstDispute == 0) { + return; + } + + BondEscalationStatus _newStatus = _won ? BondEscalationStatus.DisputerWon : BondEscalationStatus.DisputerLost; + _escalation.status = _newStatus; + + emit BondEscalationStatusUpdated(_dispute.requestId, _disputeId, _newStatus); + + _params.accountingExtension.onSettleBondEscalation({ + _requestId: _dispute.requestId, + _disputeId: _disputeId, + _forVotesWon: _won, + _token: _params.bondToken, + _amountPerPledger: _params.bondSize << 1, + _winningPledgersLength: _won ? _escalation.amountOfPledgesForDispute : _escalation.amountOfPledgesAgainstDispute + }); + } else { + // The status has changed to Won or Lost + uint256 _pledgesForDispute = _escalation.amountOfPledgesForDispute; + uint256 _pledgesAgainstDispute = _escalation.amountOfPledgesAgainstDispute; + bool _disputersWon = _pledgesForDispute > _pledgesAgainstDispute; + + uint256 _amountToPay = _disputersWon + ? _params.bondSize + FixedPointMathLib.mulDivDown(_pledgesAgainstDispute, _params.bondSize, _pledgesForDispute) + : _params.bondSize + FixedPointMathLib.mulDivDown(_pledgesForDispute, _params.bondSize, _pledgesAgainstDispute); + + _params.accountingExtension.onSettleBondEscalation({ + _requestId: _dispute.requestId, + _disputeId: _escalation.disputeId, + _forVotesWon: _disputersWon, + _token: _params.bondToken, + _amountPerPledger: _amountToPay, + _winningPledgersLength: _disputersWon ? _pledgesForDispute : _pledgesAgainstDispute + }); + } + } + + // TODO: Emit event + // emit DisputeStatusChanged({ + // _disputeId: _disputeId, + // _dispute: _dispute, + // _status: _status + // }); + } + + //////////////////////////////////////////////////////////////////// + // Bond Escalation Exclusive Functions + //////////////////////////////////////////////////////////////////// + + /// @inheritdoc IBondEscalationModule + function pledgeForDispute(IOracle.Request calldata _request, IOracle.Dispute calldata _dispute) external { + bytes32 _disputeId = _getId(_dispute); + RequestParameters memory _params = _pledgeChecks(_disputeId, _request, _dispute, true); + + _escalations[_dispute.requestId].amountOfPledgesForDispute += 1; + pledgesForDispute[_dispute.requestId][msg.sender] += 1; + _params.accountingExtension.pledge({ + _pledger: msg.sender, + _requestId: _dispute.requestId, + _disputeId: _disputeId, + _token: _params.bondToken, + _amount: _params.bondSize + }); + + emit PledgedForDispute(_disputeId, msg.sender, _params.bondSize); + } + + /// @inheritdoc IBondEscalationModule + function pledgeAgainstDispute(IOracle.Request calldata _request, IOracle.Dispute calldata _dispute) external { + bytes32 _disputeId = _getId(_dispute); + RequestParameters memory _params = _pledgeChecks(_disputeId, _request, _dispute, false); + + _escalations[_dispute.requestId].amountOfPledgesAgainstDispute += 1; + pledgesAgainstDispute[_dispute.requestId][msg.sender] += 1; + _params.accountingExtension.pledge({ + _pledger: msg.sender, + _requestId: _dispute.requestId, + _disputeId: _disputeId, + _token: _params.bondToken, + _amount: _params.bondSize + }); + + emit PledgedAgainstDispute(_disputeId, msg.sender, _params.bondSize); + } + + /// @inheritdoc IBondEscalationModule + function settleBondEscalation( + IOracle.Request calldata _request, + IOracle.Response calldata _response, + IOracle.Dispute calldata _dispute + ) external { + bytes32 _requestId = _getId(_request); + RequestParameters memory _params = decodeRequestData(_request.disputeModuleData); + BondEscalation storage _escalation = _escalations[_requestId]; + + if (block.timestamp <= _params.bondEscalationDeadline + _params.tyingBuffer) { + revert BondEscalationModule_BondEscalationNotOver(); + } + + if (_escalation.status != BondEscalationStatus.Active) { + revert BondEscalationModule_BondEscalationCantBeSettled(); + } + + uint256 _pledgesForDispute = _escalation.amountOfPledgesForDispute; + uint256 _pledgesAgainstDispute = _escalation.amountOfPledgesAgainstDispute; + + if (_pledgesForDispute == _pledgesAgainstDispute) { + revert BondEscalationModule_ShouldBeEscalated(); + } + + bool _disputersWon = _pledgesForDispute > _pledgesAgainstDispute; + _escalation.status = _disputersWon ? BondEscalationStatus.DisputerWon : BondEscalationStatus.DisputerLost; + + emit BondEscalationStatusUpdated(_requestId, _escalation.disputeId, _escalation.status); + + ORACLE.updateDisputeStatus( + _request, _response, _dispute, _disputersWon ? IOracle.DisputeStatus.Won : IOracle.DisputeStatus.Lost + ); + } + + /** + * @notice Checks the necessary conditions for pledging + * @return _params The decoded parameters for the request + */ + function _pledgeChecks( + bytes32 _disputeId, + IOracle.Request calldata _request, + IOracle.Dispute calldata _dispute, + bool _forDispute + ) internal view returns (RequestParameters memory _params) { + if (_disputeId == 0) revert BondEscalationModule_DisputeDoesNotExist(); + + BondEscalation memory _escalation = _escalations[_dispute.requestId]; + + if (_disputeId != _escalation.disputeId) { + revert BondEscalationModule_InvalidDispute(); + } + + _params = decodeRequestData(_request.disputeModuleData); + + if (block.timestamp > _params.bondEscalationDeadline + _params.tyingBuffer) { + revert BondEscalationModule_BondEscalationOver(); + } + + uint256 _numPledgersForDispute = _escalation.amountOfPledgesForDispute; + uint256 _numPledgersAgainstDispute = _escalation.amountOfPledgesAgainstDispute; + + if (_forDispute) { + if (_numPledgersForDispute == _params.maxNumberOfEscalations) { + revert BondEscalationModule_MaxNumberOfEscalationsReached(); + } + if (_numPledgersForDispute > _numPledgersAgainstDispute) revert BondEscalationModule_CanOnlySurpassByOnePledge(); + } else { + if (_numPledgersAgainstDispute == _params.maxNumberOfEscalations) { + revert BondEscalationModule_MaxNumberOfEscalationsReached(); + } + if (_numPledgersAgainstDispute > _numPledgersForDispute) revert BondEscalationModule_CanOnlySurpassByOnePledge(); + } + + if (block.timestamp > _params.bondEscalationDeadline && _numPledgersForDispute == _numPledgersAgainstDispute) { + revert BondEscalationModule_CannotBreakTieDuringTyingBuffer(); + } + } + + //////////////////////////////////////////////////////////////////// + // View Functions + //////////////////////////////////////////////////////////////////// + + /// @inheritdoc IBondEscalationModule + function decodeRequestData(bytes calldata _data) public pure returns (RequestParameters memory _params) { + _params = abi.decode(_data, (RequestParameters)); + } + + /// @inheritdoc IBondEscalationModule + function getEscalation(bytes32 _requestId) public view returns (BondEscalation memory _escalation) { + _escalation = _escalations[_requestId]; + } +} diff --git a/solidity/interfaces/modules/dispute/IBondEscalationModule.sol b/solidity/interfaces/modules/dispute/IBondEscalationModule.sol index 4b511fc8..20463e01 100644 --- a/solidity/interfaces/modules/dispute/IBondEscalationModule.sol +++ b/solidity/interfaces/modules/dispute/IBondEscalationModule.sol @@ -1,275 +1,264 @@ -// // SPDX-License-Identifier: AGPL-3.0-only -// pragma solidity ^0.8.19; +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; -// import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; -// import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol'; -// import {IDisputeModule} from -// '@defi-wonderland/prophet-core-contracts/solidity/interfaces/modules/dispute/IDisputeModule.sol'; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol'; +import {IDisputeModule} from + '@defi-wonderland/prophet-core-contracts/solidity/interfaces/modules/dispute/IDisputeModule.sol'; -// import {IBondEscalationAccounting} from '../../extensions/IBondEscalationAccounting.sol'; +import {IBondEscalationAccounting} from '../../extensions/IBondEscalationAccounting.sol'; -// /** -// * @title BondEscalationModule -// * @notice Module allowing users to have the first dispute of a request go through the bond escalation mechanism. -// */ -// interface IBondEscalationModule is IDisputeModule { -// /*/////////////////////////////////////////////////////////////// -// EVENTS -// //////////////////////////////////////////////////////////////*/ +/** + * @title BondEscalationModule + * @notice Module allowing users to have the first dispute of a request go through the bond escalation mechanism. + */ +interface IBondEscalationModule is IDisputeModule { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ -// /** -// * @notice A pledge has been made in favor of a dispute. -// * -// * @param _disputeId The id of the dispute the pledger is pledging in favor of. -// * @param _pledger The address of the pledger. -// * @param _amount The amount pledged. -// */ -// event PledgedForDispute(bytes32 indexed _disputeId, address indexed _pledger, uint256 indexed _amount); + /** + * @notice A pledge has been made in favor of a dispute. + * + * @param _disputeId The id of the dispute the pledger is pledging in favor of. + * @param _pledger The address of the pledger. + * @param _amount The amount pledged. + */ + event PledgedForDispute(bytes32 indexed _disputeId, address indexed _pledger, uint256 indexed _amount); -// /** -// * @notice A pledge has been made against a dispute. -// * -// * @param _disputeId The id of the dispute the pledger is pledging against. -// * @param _pledger The address of the pledger. -// * @param _amount The amount pledged. -// */ -// event PledgedAgainstDispute(bytes32 indexed _disputeId, address indexed _pledger, uint256 indexed _amount); + /** + * @notice A pledge has been made against a dispute. + * + * @param _disputeId The id of the dispute the pledger is pledging against. + * @param _pledger The address of the pledger. + * @param _amount The amount pledged. + */ + event PledgedAgainstDispute(bytes32 indexed _disputeId, address indexed _pledger, uint256 indexed _amount); -// /** -// * @notice The status of the bond escalation mechanism has been updated. -// * -// * @param _requestId The id of the request associated with the bond escalation mechanism. -// * @param _disputeId The id of the dispute going through the bond escalation mechanism. -// * @param _status The new status. -// */ -// event BondEscalationStatusUpdated( -// bytes32 indexed _requestId, bytes32 indexed _disputeId, BondEscalationStatus _status -// ); + /** + * @notice The status of the bond escalation mechanism has been updated. + * + * @param _requestId The id of the request associated with the bond escalation mechanism. + * @param _disputeId The id of the dispute going through the bond escalation mechanism. + * @param _status The new status. + */ + event BondEscalationStatusUpdated( + bytes32 indexed _requestId, bytes32 indexed _disputeId, BondEscalationStatus _status + ); -// /*/////////////////////////////////////////////////////////////// -// ERRORS -// //////////////////////////////////////////////////////////////*/ + /*/////////////////////////////////////////////////////////////// + ERRORS + //////////////////////////////////////////////////////////////*/ -// /** -// * @notice Thrown when trying to escalate a dispute going through the bond escalation module before its deadline. -// */ -// error BondEscalationModule_BondEscalationNotOver(); -// /** -// * @notice Thrown when trying to pledge for a dispute that is not going through the bond escalation mechanism. -// */ -// error BondEscalationModule_InvalidDispute(); -// /** -// * @notice Thrown when the number of escalation pledges of a given dispute has reached its maximum. -// */ -// error BondEscalationModule_MaxNumberOfEscalationsReached(); -// /** -// * @notice Thrown when trying to settle a dispute that went through the bond escalation when it's not active. -// */ -// error BondEscalationModule_BondEscalationCantBeSettled(); -// /** -// * @notice Thrown when trying to settle a bond escalation process that is not tied. -// */ -// error BondEscalationModule_ShouldBeEscalated(); -// /** -// * @notice Thrown when trying to break a tie after the tying buffer has started. -// */ -// error BondEscalationModule_CannotBreakTieDuringTyingBuffer(); -// /** -// * @notice Thrown when the max number of escalations or the bond size is set to 0. -// */ -// error BondEscalationModule_ZeroValue(); -// /** -// * @notice Thrown when trying to pledge after the bond escalation deadline. -// */ -// error BondEscalationModule_BondEscalationOver(); -// /** -// * @notice Thrown when trying to escalate a dispute going through the bond escalation process that is not tied -// * or that is not active. -// */ -// error BondEscalationModule_NotEscalatable(); -// /** -// * @notice Thrown when trying to pledge for a dispute that does not exist -// */ -// error BondEscalationModule_DisputeDoesNotExist(); -// /** -// * @notice Thrown when trying to surpass the number of pledges of the other side by more than 1 in the bond escalation mechanism. -// */ -// error BondEscalationModule_CanOnlySurpassByOnePledge(); -// /** -// * @notice Thrown when trying to dispute a response after the dispute period expired. -// */ -// error BondEscalationModule_DisputeWindowOver(); -// /** -// * @notice Thrown when trying to set up a request with invalid bond size or maximum amount of escalations. -// */ -// error BondEscalationModule_InvalidEscalationParameters(); + /** + * @notice Thrown when trying to escalate a dispute going through the bond escalation module before its deadline. + */ + error BondEscalationModule_BondEscalationNotOver(); + /** + * @notice Thrown when trying to pledge for a dispute that is not going through the bond escalation mechanism. + */ + error BondEscalationModule_InvalidDispute(); + /** + * @notice Thrown when the number of escalation pledges of a given dispute has reached its maximum. + */ + error BondEscalationModule_MaxNumberOfEscalationsReached(); + /** + * @notice Thrown when trying to settle a dispute that went through the bond escalation when it's not active. + */ + error BondEscalationModule_BondEscalationCantBeSettled(); + /** + * @notice Thrown when trying to settle a bond escalation process that is not tied. + */ + error BondEscalationModule_ShouldBeEscalated(); + /** + * @notice Thrown when trying to break a tie after the tying buffer has started. + */ + error BondEscalationModule_CannotBreakTieDuringTyingBuffer(); + /** + * @notice Thrown when the max number of escalations or the bond size is set to 0. + */ + error BondEscalationModule_ZeroValue(); + /** + * @notice Thrown when trying to pledge after the bond escalation deadline. + */ + error BondEscalationModule_BondEscalationOver(); + /** + * @notice Thrown when trying to escalate a dispute going through the bond escalation process that is not tied + * or that is not active. + */ + error BondEscalationModule_NotEscalatable(); + /** + * @notice Thrown when trying to pledge for a dispute that does not exist + */ + error BondEscalationModule_DisputeDoesNotExist(); + /** + * @notice Thrown when trying to surpass the number of pledges of the other side by more than 1 in the bond escalation mechanism. + */ + error BondEscalationModule_CanOnlySurpassByOnePledge(); + /** + * @notice Thrown when trying to dispute a response after the dispute period expired. + */ + error BondEscalationModule_DisputeWindowOver(); + /** + * @notice Thrown when trying to set up a request with invalid bond size or maximum amount of escalations. + */ + error BondEscalationModule_InvalidEscalationParameters(); -// /*/////////////////////////////////////////////////////////////// -// ENUMS -// //////////////////////////////////////////////////////////////*/ + /*/////////////////////////////////////////////////////////////// + ENUMS + //////////////////////////////////////////////////////////////*/ -// /** -// * @notice Enum holding all the possible statuses of a dispute going through the bond escalation mechanism. -// */ -// enum BondEscalationStatus { -// None, // Dispute is not going through the bond escalation mechanism. -// Active, // Dispute is going through the bond escalation mechanism. -// Escalated, // Dispute is going through the bond escalation mechanism and has been escalated. -// DisputerLost, // An escalated dispute has been settled and the disputer lost. -// DisputerWon // An escalated dispute has been settled and the disputer won. -// } + /** + * @notice Enum holding all the possible statuses of a dispute going through the bond escalation mechanism. + */ + enum BondEscalationStatus { + None, // Dispute is not going through the bond escalation mechanism. + Active, // Dispute is going through the bond escalation mechanism. + Escalated, // Dispute is going through the bond escalation mechanism and has been escalated. + DisputerLost, // An escalated dispute has been settled and the disputer lost. + DisputerWon // An escalated dispute has been settled and the disputer won. + } -// /*/////////////////////////////////////////////////////////////// -// STRUCTS -// //////////////////////////////////////////////////////////////*/ + /*/////////////////////////////////////////////////////////////// + STRUCTS + //////////////////////////////////////////////////////////////*/ -// /** -// * @notice Parameters of the request as stored in the module -// * -// * @param _accountingExtension Address of the accounting extension associated with the given request -// * @param _bondToken Address of the token associated with the given request -// * @param _bondSize Amount to bond to dispute or propose an answer for the given request -// * @param _numberOfEscalations Maximum allowed escalations or pledges for each side during the bond escalation process -// * @param _bondEscalationDeadline Timestamp at which bond escalation process finishes when pledges are not tied -// * @param _tyingBuffer Number of seconds to extend the bond escalation process to allow the losing -// * party to tie if at the end of the initial deadline the pledges weren't tied. -// * @param _disputeWindow Number of seconds disputers have to challenge the proposed response since its creation. -// */ -// struct RequestParameters { -// IBondEscalationAccounting accountingExtension; -// IERC20 bondToken; -// uint256 bondSize; -// uint256 maxNumberOfEscalations; -// uint256 bondEscalationDeadline; -// uint256 tyingBuffer; -// uint256 disputeWindow; -// } + /** + * @notice Parameters of the request as stored in the module + * + * @param _accountingExtension Address of the accounting extension associated with the given request + * @param _bondToken Address of the token associated with the given request + * @param _bondSize Amount to bond to dispute or propose an answer for the given request + * @param _numberOfEscalations Maximum allowed escalations or pledges for each side during the bond escalation process + * @param _bondEscalationDeadline Timestamp at which bond escalation process finishes when pledges are not tied + * @param _tyingBuffer Number of seconds to extend the bond escalation process to allow the losing + * party to tie if at the end of the initial deadline the pledges weren't tied. + * @param _disputeWindow Number of seconds disputers have to challenge the proposed response since its creation. + */ + struct RequestParameters { + IBondEscalationAccounting accountingExtension; + IERC20 bondToken; + uint256 bondSize; + uint256 maxNumberOfEscalations; + uint256 bondEscalationDeadline; + uint256 tyingBuffer; + uint256 disputeWindow; + } -// /** -// * @notice Data of a dispute going through the bond escalation. -// * -// * @param disputeId The id of the dispute being bond-escalated. -// * @param status The status of the bond escalation. -// * @param amountOfPledgesForDispute The amount of pledges made in favor of the dispute. -// * @param amountOfPledgesAgainstDispute The amount of pledges made against the dispute. -// */ -// struct BondEscalation { -// bytes32 disputeId; -// BondEscalationStatus status; -// uint256 amountOfPledgesForDispute; -// uint256 amountOfPledgesAgainstDispute; -// } + /** + * @notice Data of a dispute going through the bond escalation. + * + * @param disputeId The id of the dispute being bond-escalated. + * @param status The status of the bond escalation. + * @param amountOfPledgesForDispute The amount of pledges made in favor of the dispute. + * @param amountOfPledgesAgainstDispute The amount of pledges made against the dispute. + */ + struct BondEscalation { + bytes32 disputeId; + BondEscalationStatus status; + uint256 amountOfPledgesForDispute; + uint256 amountOfPledgesAgainstDispute; + } -// /*/////////////////////////////////////////////////////////////// -// VARIABLES -// //////////////////////////////////////////////////////////////*/ + /*/////////////////////////////////////////////////////////////// + VARIABLES + //////////////////////////////////////////////////////////////*/ -// /** -// * @notice Returns the escalation data for a request. -// * @param _requestId The id of the request to get its escalation data. -// * @return _escalation The struct containing the escalation data. -// */ -// function getEscalation(bytes32 _requestId) external view returns (BondEscalation memory _escalation); + /** + * @notice Returns the escalation data for a request. + * @param _requestId The id of the request to get its escalation data. + * @return _escalation The struct containing the escalation data. + */ + function getEscalation(bytes32 _requestId) external view returns (BondEscalation memory _escalation); -// /** -// * @notice Decodes the request data associated with a request id. -// * @param _requestId The id of the request to decode. -// * @return _params The struct containing the parameters for the request -// */ -// function decodeRequestData(bytes32 _requestId) external view returns (RequestParameters memory _params); + /** + * @notice Returns the decoded data for a request + * @param _data The encoded request parameters + * @return _params The struct containing the parameters for the request + */ + function decodeRequestData(bytes calldata _data) external view returns (RequestParameters memory _params); -// /** -// * @notice Returns the amount of pledges that a particular pledger has made for a given dispute. -// * @param _requestId The id of the request to get the pledges for. -// * @param _pledger The address of the pledger to get the pledges for. -// * @return _numPledges The number of pledges made by the pledger for the dispute. -// */ -// function pledgesForDispute(bytes32 _requestId, address _pledger) external view returns (uint256 _numPledges); + /** + * @notice Returns the amount of pledges that a particular pledger has made for a given dispute. + * @param _requestId The id of the request to get the pledges for. + * @param _pledger The address of the pledger to get the pledges for. + * @return _numPledges The number of pledges made by the pledger for the dispute. + */ + function pledgesForDispute(bytes32 _requestId, address _pledger) external view returns (uint256 _numPledges); -// /** -// * @notice Returns the amount of pledges that a particular pledger has made against a given dispute. -// * @param _requestId The id of the request to get the pledges for. -// * @param _pledger The address of the pledger to get the pledges for. -// * @return _numPledges The number of pledges made by the pledger against the dispute. -// */ -// function pledgesAgainstDispute(bytes32 _requestId, address _pledger) external view returns (uint256 _numPledges); + /** + * @notice Returns the amount of pledges that a particular pledger has made against a given dispute. + * @param _requestId The id of the request to get the pledges for. + * @param _pledger The address of the pledger to get the pledges for. + * @return _numPledges The number of pledges made by the pledger against the dispute. + */ + function pledgesAgainstDispute(bytes32 _requestId, address _pledger) external view returns (uint256 _numPledges); -// /*/////////////////////////////////////////////////////////////// -// LOGIC -// //////////////////////////////////////////////////////////////*/ + /*/////////////////////////////////////////////////////////////// + LOGIC + //////////////////////////////////////////////////////////////*/ -// /** -// * @notice Disputes a response -// * -// * @dev If this is the first dispute of the request and the bond escalation window is not over, -// * it will start the bond escalation process. This function must be called through the Oracle. -// * -// * @param _requestId The ID of the request containing the response to dispute. -// * @param _responseId The ID of the request to dispute. -// * @param _disputer The address of the disputer. -// * @param _proposer The address of the proposer of the response. -// * -// * @return _dispute The data of the created dispute. -// */ -// function disputeResponse( -// bytes32 _requestId, -// bytes32 _responseId, -// address _disputer, -// address _proposer -// ) external override returns (IOracle.Dispute memory _dispute); + /** + * @notice Disputes a response + * + * @dev If this is the first dispute of the request and the bond escalation window is not over, + * it will start the bond escalation process. This function must be called through the Oracle. + * + * @param _response The response being disputed. + */ + function disputeResponse( + IOracle.Request calldata _request, + IOracle.Response calldata _response, + IOracle.Dispute calldata _dispute + ) external; -// /** -// * @notice Updates the status of a given disputeId and pays the proposer and disputer accordingly. If this -// * dispute has gone through the bond escalation mechanism, then it will pay the winning pledgers as well. -// * -// * @param _disputeId The ID of the dispute to update the status for. -// * @param _dispute The full dispute object. -// * -// */ -// function onDisputeStatusChange(bytes32 _disputeId, IOracle.Dispute memory _dispute) external override; + /** + * @notice Updates the status of a given disputeId and pays the proposer and disputer accordingly. If this + * dispute has gone through the bond escalation mechanism, then it will pay the winning pledgers as well. + * + * @param _disputeId The ID of the dispute to update the status for. + * @param _dispute The full dispute object. + * + */ + function onDisputeStatusChange( + bytes32 _disputeId, + IOracle.Request calldata _request, + IOracle.Response calldata _response, + IOracle.Dispute calldata _dispute + ) external; -// /** -// * @notice Verifies whether the dispute going through the bond escalation mechanism has reached a tie and -// * updates its escalation status accordingly. -// * -// * @param _disputeId The ID of the dispute to escalate. -// */ -// function disputeEscalated(bytes32 _disputeId) external; + /** + * @notice Bonds funds in favor of a given dispute during the bond escalation process. + * + * @dev This function must be called directly through this contract. + * @dev If the bond escalation is not tied at the end of its deadline, a tying buffer is added + * to avoid scenarios where one of the parties breaks the tie very last second. + * During the tying buffer, the losing party can only tie, and once the escalation is tied + * no further funds can be pledged. + */ + function pledgeForDispute(IOracle.Request calldata _request, IOracle.Dispute calldata _dispute) external; -// /** -// * @notice Bonds funds in favor of a given dispute during the bond escalation process. -// * -// * @dev This function must be called directly through this contract. -// * @dev If the bond escalation is not tied at the end of its deadline, a tying buffer is added -// * to avoid scenarios where one of the parties breaks the tie very last second. -// * During the tying buffer, the losing party can only tie, and once the escalation is tied -// * no further funds can be pledged. -// * -// * @param _disputeId The ID of the dispute to pledge for. -// */ -// function pledgeForDispute(bytes32 _disputeId) external; + /** + * @notice Pledges funds against a given disputeId during its bond escalation process. + * + * @dev Must be called directly through this contract. Will revert if the disputeId is not going through + * the bond escalation process. + * @dev If the bond escalation is not tied at the end of its deadline, a tying buffer is added + * to avoid scenarios where one of the parties breaks the tie very last second. + * During the tying buffer, the losing party can only tie, and once the escalation is tied + * no further funds can be pledged. + */ + function pledgeAgainstDispute(IOracle.Request calldata _request, IOracle.Dispute calldata _dispute) external; -// /** -// * @notice Pledges funds against a given disputeId during its bond escalation process. -// * -// * @dev Must be called directly through this contract. Will revert if the disputeId is not going through -// * the bond escalation process. -// * @dev If the bond escalation is not tied at the end of its deadline, a tying buffer is added -// * to avoid scenarios where one of the parties breaks the tie very last second. -// * During the tying buffer, the losing party can only tie, and once the escalation is tied -// * no further funds can be pledged. -// * -// * @param _disputeId ID of the dispute id to pledge against. -// */ -// function pledgeAgainstDispute(bytes32 _disputeId) external; - -// /** -// * @notice Settles the bond escalation process of a given requestId. -// * -// * @dev Must be called directly through this contract. -// * @dev Can only be called if after the deadline + tyingBuffer window is over, the pledges weren't tied -// * -// * @param _requestId requestId of the request to settle the bond escalation process for. -// */ -// function settleBondEscalation(bytes32 _requestId) external; -// } + /** + * @notice Settles the bond escalation process of a given requestId. + * + * @dev Must be called directly through this contract. + * @dev Can only be called if after the deadline + tyingBuffer window is over, the pledges weren't tied + */ + function settleBondEscalation( + IOracle.Request calldata _request, + IOracle.Response calldata _response, + IOracle.Dispute calldata _dispute + ) external; +} diff --git a/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol b/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol index bcf3c02f..8c0eb08c 100644 --- a/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol +++ b/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol @@ -1,1437 +1,1295 @@ -// // SPDX-License-Identifier: AGPL-3.0-only -// pragma solidity ^0.8.19; - -// import 'forge-std/Test.sol'; - -// import {Helpers} from '../../../utils/Helpers.sol'; - -// import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; -// import {Strings} from '@openzeppelin/contracts/utils/Strings.sol'; -// import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol'; -// import {IModule} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IModule.sol'; - -// import { -// BondEscalationModule, IBondEscalationModule -// } from '../../../../contracts/modules/dispute/BondEscalationModule.sol'; - -// import {IAccountingExtension} from '../../../../interfaces/extensions/IAccountingExtension.sol'; -// import {IBondEscalationAccounting} from '../../../../interfaces/extensions/IBondEscalationAccounting.sol'; - -// /** -// * @dev Harness to set an entry in the requestData mapping, without triggering setup request hooks -// */ -// contract ForTest_BondEscalationModule is BondEscalationModule { -// constructor(IOracle _oracle) BondEscalationModule(_oracle) {} - -// function forTest_setRequestData(bytes32 _requestId, bytes memory _data) public { -// requestData[_requestId] = _data; -// } - -// function forTest_setBondEscalation( -// bytes32 _requestId, -// address[] memory _pledgersForDispute, -// address[] memory _pledgersAgainstDispute -// ) public { -// for (uint256 _i; _i < _pledgersForDispute.length; _i++) { -// pledgesForDispute[_requestId][_pledgersForDispute[_i]] += 1; -// } - -// for (uint256 _i; _i < _pledgersAgainstDispute.length; _i++) { -// pledgesAgainstDispute[_requestId][_pledgersAgainstDispute[_i]] += 1; -// } - -// _escalations[_requestId].amountOfPledgesForDispute += _pledgersForDispute.length; -// _escalations[_requestId].amountOfPledgesAgainstDispute += _pledgersAgainstDispute.length; -// } - -// function forTest_setBondEscalationStatus( -// bytes32 _requestId, -// BondEscalationModule.BondEscalationStatus _bondEscalationStatus -// ) public { -// _escalations[_requestId].status = _bondEscalationStatus; -// } - -// function forTest_setEscalatedDispute(bytes32 _requestId, bytes32 _disputeId) public { -// _escalations[_requestId].disputeId = _disputeId; -// } -// } - -// /** -// * @title Bonded Response Module Unit tests -// */ - -// contract BaseTest is Test, Helpers { -// // The target contract -// ForTest_BondEscalationModule public bondEscalationModule; -// // A mock oracle -// IOracle public oracle; -// // A mock accounting extension -// IBondEscalationAccounting public accounting; -// // A mock token -// IERC20 public token; -// // Mock EOA proposer -// address public proposer = makeAddr('proposer'); -// // Mock EOA disputer -// address public disputer = makeAddr('disputer'); -// // Mock bondSize -// uint256 public bondSize; -// // Mock max number of escalations -// uint256 public maxEscalations; -// // Mock bond escalation deadline -// uint256 public bondEscalationDeadline; -// // Mock tyingBuffer -// uint256 public tyingBuffer; -// // Mock dispute window -// uint256 public disputeWindow; -// // Mock dispute -// IOracle.Dispute internal _mockDispute; -// // Mock response -// IOracle.Response internal _mockResponse; - -// // Events -// event PledgedForDispute(bytes32 indexed _disputeId, address indexed _pledger, uint256 indexed _amount); -// event PledgedAgainstDispute(bytes32 indexed _disputeId, address indexed _pledger, uint256 indexed _amount); -// event BondEscalationStatusUpdated( -// bytes32 indexed _requestId, bytes32 indexed _disputeId, IBondEscalationModule.BondEscalationStatus _status -// ); -// event ResponseDisputed(bytes32 indexed _requestId, bytes32 _responseId, address _disputer, address _proposer); -// event DisputeStatusChanged( -// bytes32 indexed _requestId, bytes32 _responseId, address _disputer, address _proposer, IOracle.DisputeStatus _status -// ); - -// /** -// * @notice Deploy the target and mock oracle+accounting extension -// */ -// function setUp() public { -// oracle = IOracle(makeAddr('Oracle')); -// vm.etch(address(oracle), hex'069420'); - -// accounting = IBondEscalationAccounting(makeAddr('BondEscalationAccounting')); -// vm.etch(address(accounting), hex'069420'); - -// token = IERC20(makeAddr('ERC20')); -// vm.etch(address(token), hex'069420'); - -// // Set to an arbitrary large value to avoid unintended reverts -// disputeWindow = type(uint128).max; - -// // Avoid starting at 0 for time sensitive tests -// vm.warp(123_456); - -// _mockDispute = IOracle.Dispute({ -// disputer: disputer, -// responseId: bytes32('response'), -// proposer: proposer, -// requestId: bytes32('69'), -// status: IOracle.DisputeStatus.Active, -// createdAt: block.timestamp -// }); - -// _mockResponse = IOracle.Response({ -// createdAt: block.timestamp, -// proposer: proposer, -// requestId: bytes32('69'), -// disputeId: 0, -// response: abi.encode(bytes32('response')) -// }); - -// bondEscalationModule = new ForTest_BondEscalationModule(oracle); -// } - -// function _setRequestData( -// bytes32 _requestId, -// uint256 _bondSize, -// uint256 _maxNumberOfEscalations, -// uint256 _bondEscalationDeadline, -// uint256 _tyingBuffer, -// uint256 _disputeWindow -// ) internal { -// bytes memory _data = abi.encode( -// IBondEscalationModule.RequestParameters({ -// accountingExtension: accounting, -// bondToken: token, -// bondSize: _bondSize, -// maxNumberOfEscalations: _maxNumberOfEscalations, -// bondEscalationDeadline: _bondEscalationDeadline, -// tyingBuffer: _tyingBuffer, -// disputeWindow: _disputeWindow -// }) -// ); -// bondEscalationModule.forTest_setRequestData(_requestId, _data); -// } - -// function _getRandomDispute( -// bytes32 _requestId, -// IOracle.DisputeStatus _status -// ) internal view returns (IOracle.Dispute memory _dispute) { -// _dispute = IOracle.Dispute({ -// disputer: disputer, -// responseId: bytes32('response'), -// proposer: proposer, -// requestId: _requestId, -// status: _status, -// createdAt: block.timestamp -// }); -// } - -// function _setBondEscalation( -// bytes32 _requestId, -// uint256 _numForPledgers, -// uint256 _numAgainstPledgers -// ) internal returns (address[] memory _forPledgers, address[] memory _againstPledgers) { -// _forPledgers = new address[](_numForPledgers); -// _againstPledgers = new address[](_numAgainstPledgers); -// address _forPledger; -// address _againstPledger; - -// for (uint256 _i; _i < _numForPledgers; _i++) { -// _forPledger = makeAddr(string.concat('forPledger', Strings.toString(_i))); -// _forPledgers[_i] = _forPledger; -// } - -// for (uint256 _j; _j < _numAgainstPledgers; _j++) { -// _againstPledger = makeAddr(string.concat('againstPledger', Strings.toString(_j))); -// _againstPledgers[_j] = _againstPledger; -// } - -// bondEscalationModule.forTest_setBondEscalation(_requestId, _forPledgers, _againstPledgers); - -// return (_forPledgers, _againstPledgers); -// } -// } - -// contract BondEscalationModule_Unit_ModuleData is BaseTest { -// /** -// * @notice Test that the moduleName function returns the correct name -// */ -// function test_moduleName() public { -// assertEq(bondEscalationModule.moduleName(), 'BondEscalationModule'); -// } - -// /** -// * @notice Tests that decodeRequestData decodes the data correctly -// */ -// function test_decodeRequestDataReturnTheCorrectData( -// bytes32 _requestId, -// uint256 _bondSize, -// uint256 _maxNumberOfEscalations, -// uint256 _bondEscalationDeadline, -// uint256 _tyingBuffer, -// uint256 _disputeWindow -// ) public { -// _setRequestData( -// _requestId, _bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, _tyingBuffer, _disputeWindow -// ); -// IBondEscalationModule.RequestParameters memory _params = bondEscalationModule.decodeRequestData(_requestId); - -// // Check: does the stored data match the provided one? -// assertEq(address(accounting), address(_params.accountingExtension)); -// assertEq(address(token), address(_params.bondToken)); -// assertEq(_bondSize, _params.bondSize); -// assertEq(_maxNumberOfEscalations, _params.maxNumberOfEscalations); -// assertEq(_bondEscalationDeadline, _params.bondEscalationDeadline); -// assertEq(_tyingBuffer, _params.tyingBuffer); -// assertEq(_disputeWindow, _params.disputeWindow); -// } -// } - -// contract BondEscalationModule_Unit_EscalateDispute is BaseTest { -// /** -// * @notice Tests that escalateDispute reverts if the _disputeId doesn't match any existing disputes. -// */ -// function test_revertOnInvalidDispute(bytes32 _disputeId) public { -// _mockDispute.requestId = bytes32(0); -// // Mock and expect Oracle.getDispute to be called. -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// // Check: does it revert if the dispute does not exist? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeDoesNotExist.selector); -// vm.prank(address(oracle)); -// bondEscalationModule.disputeEscalated(_disputeId); -// } - -// /** -// * @notice Tests that escalateDispute reverts if the _disputeId doesn't match any existing disputes. -// */ -// function test_revertOnInvalidParameters( -// bytes32 _requestId, -// uint256 _maxNumberOfEscalations, -// uint256 _bondSize, -// uint256 _bondEscalationDeadline, -// uint256 _tyingBuffer, -// uint256 _disputeWindow -// ) public { -// bytes memory _requestData = abi.encode( -// IBondEscalationModule.RequestParameters({ -// accountingExtension: accounting, -// bondToken: token, -// bondSize: _bondSize, -// maxNumberOfEscalations: _maxNumberOfEscalations, -// bondEscalationDeadline: _bondEscalationDeadline, -// tyingBuffer: _tyingBuffer, -// disputeWindow: _disputeWindow -// }) -// ); - -// if (_maxNumberOfEscalations == 0 || _bondSize == 0) { -// // Check: does it revert if _maxNumberOfEscalations or _bondSize is 0? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_InvalidEscalationParameters.selector); -// } - -// vm.prank(address(oracle)); -// bondEscalationModule.setupRequest(_requestId, _requestData); -// } - -// /** -// * @notice Tests that escalateDispute reverts if a dispute is escalated before the bond escalation deadline is over. -// * Conditions to reach this check: -// * - The _requestId tied to the dispute tied to _disputeId must be valid (non-zero) -// * - The block.timestamp has to be <= bond escalation deadline -// */ -// function test_revertEscalationDuringBondEscalation(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_requestId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect Oracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// // Set _bondEscalationDeadline to be the current timestamp to reach the second condition. -// uint256 _bondEscalationDeadline = block.timestamp; - -// // Populate the requestData for the given requestId -// _setRequestData(_requestId, bondSize, maxEscalations, _bondEscalationDeadline, tyingBuffer, disputeWindow); - -// // Setting this dispute as the one going through the bond escalation process, as the user can only -// // dispute once before the bond escalation deadline is over, and that dispute goes through the escalation module. -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// // Check: does it revert if the bond escalation is not over yet? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationNotOver.selector); -// vm.prank(address(oracle)); -// bondEscalationModule.disputeEscalated(_disputeId); -// } - -// /** -// * @notice Tests that escalateDispute reverts if a dispute that went through the bond escalation mechanism but isn't active -// * anymore is escalated. -// * Conditions to reach this check: -// * - The _requestId tied to the dispute tied to _disputeId must be valid (non-zero) -// * - The block.timestamp has to be > bond escalation deadline -// * - The dispute has to have gone through the bond escalation process before -// * - The status of the bond escalation mechanism has to be different from Active -// */ -// function test_revertIfEscalatingNonActiveDispute(bytes32 _disputeId, bytes32 _requestId, uint8 _status) public { -// // Assume _requestId is not zero -// vm.assume(_requestId > 0); -// // Assume the status will be any available other but Active -// vm.assume(_status != uint8(IBondEscalationModule.BondEscalationStatus.Active) && _status < 4); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect Oracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// // Set a tying buffer to show that this can happen even in the tying buffer if the dispute was settled -// uint256 _tyingBuffer = 1000; - -// // Make the current timestamp be greater than the bond escalation deadline -// uint256 _bondEscalationDeadline = block.timestamp - 1; - -// // Populate the requestData for the given requestId -// _setRequestData(_requestId, bondSize, maxEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow); - -// // Set the bond escalation status of the given requestId to something different than Active -// bondEscalationModule.forTest_setBondEscalationStatus( -// _requestId, IBondEscalationModule.BondEscalationStatus(_status) -// ); - -// // Set the dispute to be the one that went through the bond escalation process for the given requestId -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// // Check: does it revert if the dispute is not escalatable? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_NotEscalatable.selector); -// vm.prank(address(oracle)); -// bondEscalationModule.disputeEscalated(_disputeId); -// } - -// /** -// * @notice Tests that escalateDispute reverts if a dispute that went through the bond escalation mechanism and is still active -// * but its pledges are not tied even after the tying buffer is escalated. -// * Conditions to reach this check: -// * - The _requestId tied to the dispute tied to _disputeId must be valid (non-zero) -// * - The block.timestamp has to be > bond escalation deadline + tying buffer -// * - The dispute has to have gone or be going through the bond escalation process -// * - The pledges must not be tied -// */ -// function test_revertIfEscalatingDisputeIsNotTied(bytes32 _disputeId, bytes32 _requestId) public { -// // Assume _requestId is not zero -// vm.assume(_requestId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect Oracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// // Set a tying buffer to make the test more explicit -// uint256 _tyingBuffer = 1000; - -// // Set bond escalation deadline to be the current timestamp. We will warp this. -// uint256 _bondEscalationDeadline = block.timestamp; - -// // Set the number of pledgers to be different -// uint256 _numForPledgers = 1; -// uint256 _numAgainstPledgers = 2; - -// // Warp the current timestamp so we are past the tyingBuffer -// vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); - -// // Populate the requestData for the given requestId -// _setRequestData(_requestId, bondSize, maxEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow); - -// // Set the bond escalation status of the given requestId to Active -// bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); - -// // Set the dispute to be the one that went through the bond escalation process for the given requestId -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// // Set the number of pledgers for both sides -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Check: does it revert if the dispute is not escalatable? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_NotEscalatable.selector); -// vm.prank(address(oracle)); -// bondEscalationModule.disputeEscalated(_disputeId); -// } - -// /** -// * @notice Tests that escalateDispute escalates the dispute going through the bond escalation mechanism correctly when the -// * pledges are tied and the dispute is still active. -// * Conditions for the function to succeed: -// * - The _requestId tied to the dispute tied to _disputeId must be valid (non-zero) -// * - The block.timestamp has to be > bond escalation deadline -// * - The dispute has to have gone or be going through the bond escalation process -// * - The pledges must be tied -// */ -// function test_escalateTiedDispute(bytes32 _disputeId, bytes32 _requestId) public { -// // Assume _requestId is not zero -// vm.assume(_requestId > 0); -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect Oracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.19; + +import 'forge-std/Test.sol'; + +import {Helpers} from '../../../utils/Helpers.sol'; + +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {Strings} from '@openzeppelin/contracts/utils/Strings.sol'; +import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol'; +import {IModule} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IModule.sol'; + +import { + BondEscalationModule, IBondEscalationModule +} from '../../../../contracts/modules/dispute/BondEscalationModule.sol'; + +import {IAccountingExtension} from '../../../../interfaces/extensions/IAccountingExtension.sol'; +import {IBondEscalationAccounting} from '../../../../interfaces/extensions/IBondEscalationAccounting.sol'; + +/** + * @dev Harness to set an entry in the requestData mapping, without triggering setup request hooks + */ +contract ForTest_BondEscalationModule is BondEscalationModule { + constructor(IOracle _oracle) BondEscalationModule(_oracle) {} + + function forTest_setBondEscalation( + bytes32 _requestId, + address[] memory _pledgersForDispute, + address[] memory _pledgersAgainstDispute + ) public { + for (uint256 _i; _i < _pledgersForDispute.length; _i++) { + pledgesForDispute[_requestId][_pledgersForDispute[_i]] += 1; + } + + for (uint256 _i; _i < _pledgersAgainstDispute.length; _i++) { + pledgesAgainstDispute[_requestId][_pledgersAgainstDispute[_i]] += 1; + } + + _escalations[_requestId].amountOfPledgesForDispute += _pledgersForDispute.length; + _escalations[_requestId].amountOfPledgesAgainstDispute += _pledgersAgainstDispute.length; + } + + function forTest_setBondEscalationStatus( + bytes32 _requestId, + BondEscalationModule.BondEscalationStatus _bondEscalationStatus + ) public { + _escalations[_requestId].status = _bondEscalationStatus; + } + + function forTest_setEscalatedDispute(bytes32 _requestId, bytes32 _disputeId) public { + _escalations[_requestId].disputeId = _disputeId; + } +} + +/** + * @title Bonded Response Module Unit tests + */ + +contract BaseTest is Test, Helpers { + // The target contract + ForTest_BondEscalationModule public bondEscalationModule; + // A mock oracle + IOracle public oracle; + // A mock accounting extension + IBondEscalationAccounting public accounting; + // A mock token + IERC20 public token; + // Mock EOA proposer + address public proposer = makeAddr('proposer'); + // Mock EOA disputer + address public disputer = makeAddr('disputer'); + // Mock bondSize + uint256 public bondSize; + // Mock max number of escalations + uint256 public maxEscalations; + // Mock bond escalation deadline + uint256 public bondEscalationDeadline; + // Mock tyingBuffer + uint256 public tyingBuffer; + // Mock dispute window + uint256 public disputeWindow; + // Mock dispute + IOracle.Dispute public mockDispute; + // Mock response + IOracle.Response public mockResponse; + + // Events + event PledgedForDispute(bytes32 indexed _disputeId, address indexed _pledger, uint256 indexed _amount); + event PledgedAgainstDispute(bytes32 indexed _disputeId, address indexed _pledger, uint256 indexed _amount); + event BondEscalationStatusUpdated( + bytes32 indexed _requestId, bytes32 indexed _disputeId, IBondEscalationModule.BondEscalationStatus _status + ); + event ResponseDisputed(bytes32 indexed _requestId, bytes32 _responseId, address _disputer, address _proposer); + event DisputeStatusChanged(bytes32 indexed _disputeId, IOracle.Dispute _dispute, IOracle.DisputeStatus _status); + + /** + * @notice Deploy the target and mock oracle+accounting extension + */ + function setUp() public { + oracle = IOracle(makeAddr('Oracle')); + vm.etch(address(oracle), hex'069420'); + + accounting = IBondEscalationAccounting(makeAddr('BondEscalationAccounting')); + vm.etch(address(accounting), hex'069420'); + + token = IERC20(makeAddr('ERC20')); + vm.etch(address(token), hex'069420'); + + // Set to an arbitrary large value to avoid unintended reverts + disputeWindow = type(uint128).max; + + // Avoid starting at 0 for time sensitive tests + vm.warp(123_456); + + mockDispute = IOracle.Dispute({ + disputer: disputer, + responseId: bytes32('response'), + proposer: proposer, + requestId: bytes32('69') + }); + + mockResponse = + IOracle.Response({proposer: proposer, requestId: bytes32('69'), response: abi.encode(bytes32('response'))}); + + bondEscalationModule = new ForTest_BondEscalationModule(oracle); + } + + function _getRandomDispute( + bytes32 _requestId, + IOracle.DisputeStatus _status + ) internal view returns (IOracle.Dispute memory _dispute) { + _dispute = + IOracle.Dispute({disputer: disputer, responseId: bytes32('response'), proposer: proposer, requestId: _requestId}); + } + + function _setBondEscalation( + bytes32 _requestId, + uint256 _numForPledgers, + uint256 _numAgainstPledgers + ) internal returns (address[] memory _forPledgers, address[] memory _againstPledgers) { + _forPledgers = new address[](_numForPledgers); + _againstPledgers = new address[](_numAgainstPledgers); + address _forPledger; + address _againstPledger; + + for (uint256 _i; _i < _numForPledgers; _i++) { + _forPledger = makeAddr(string.concat('forPledger', Strings.toString(_i))); + _forPledgers[_i] = _forPledger; + } + + for (uint256 _j; _j < _numAgainstPledgers; _j++) { + _againstPledger = makeAddr(string.concat('againstPledger', Strings.toString(_j))); + _againstPledgers[_j] = _againstPledger; + } + + bondEscalationModule.forTest_setBondEscalation(_requestId, _forPledgers, _againstPledgers); + + return (_forPledgers, _againstPledgers); + } +} + +contract BondEscalationModule_Unit_ModuleData is BaseTest { + /** + * @notice Test that the moduleName function returns the correct name + */ + function test_moduleName() public { + assertEq(bondEscalationModule.moduleName(), 'BondEscalationModule'); + } + + /** + * @notice Tests that decodeRequestData decodes the data correctly + */ + function test_decodeRequestDataReturnTheCorrectData( + bytes32 _requestId, + uint256 _bondSize, + uint256 _maxNumberOfEscalations, + uint256 _bondEscalationDeadline, + uint256 _tyingBuffer, + uint256 _disputeWindow + ) public { + IBondEscalationModule.RequestParameters memory _params = bondEscalationModule.decodeRequestData( + abi.encode(_bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, _tyingBuffer, _disputeWindow) + ); + + // Check: does the stored data match the provided one? + assertEq(address(accounting), address(_params.accountingExtension)); + assertEq(address(token), address(_params.bondToken)); + assertEq(_bondSize, _params.bondSize); + assertEq(_maxNumberOfEscalations, _params.maxNumberOfEscalations); + assertEq(_bondEscalationDeadline, _params.bondEscalationDeadline); + assertEq(_tyingBuffer, _params.tyingBuffer); + assertEq(_disputeWindow, _params.disputeWindow); + } +} + +contract BondEscalationModule_Unit_EscalateDispute is BaseTest { + /** + * @notice Tests that escalateDispute reverts if the _disputeId doesn't match any existing disputes. + */ + function test_revertOnInvalidDispute(bytes32 _disputeId, IOracle.Request calldata _request) public { + mockDispute.requestId = bytes32(0); + + // Check: does it revert if the dispute does not exist? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeDoesNotExist.selector); + vm.prank(address(oracle)); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + } + + /** + * @notice Tests that escalateDispute reverts if a dispute is escalated before the bond escalation deadline is over. + * Conditions to reach this check: + * - The _requestId tied to the dispute tied to _disputeId must be valid (non-zero) + * - The block.timestamp has to be <= bond escalation deadline + */ + function test_revertEscalationDuringBondEscalation( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_requestId > 0); + + mockDispute.requestId = _requestId; + + // Set _bondEscalationDeadline to be the current timestamp to reach the second condition. + uint256 _bondEscalationDeadline = block.timestamp; + + // Setting this dispute as the one going through the bond escalation process, as the user can only + // dispute once before the bond escalation deadline is over, and that dispute goes through the escalation module. + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + // Check: does it revert if the bond escalation is not over yet? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationNotOver.selector); + vm.prank(address(oracle)); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + } + + /** + * @notice Tests that escalateDispute reverts if a dispute that went through the bond escalation mechanism but isn't active + * anymore is escalated. + * Conditions to reach this check: + * - The _requestId tied to the dispute tied to _disputeId must be valid (non-zero) + * - The block.timestamp has to be > bond escalation deadline + * - The dispute has to have gone through the bond escalation process before + * - The status of the bond escalation mechanism has to be different from Active + */ + function test_revertIfEscalatingNonActiveDispute( + bytes32 _disputeId, + bytes32 _requestId, + uint8 _status, + IOracle.Request calldata _request + ) public { + // Assume _requestId is not zero + vm.assume(_requestId > 0); + // Assume the status will be any available other but Active + vm.assume(_status != uint8(IBondEscalationModule.BondEscalationStatus.Active) && _status < 4); + + mockDispute.requestId = _requestId; + + // Set a tying buffer to show that this can happen even in the tying buffer if the dispute was settled + uint256 _tyingBuffer = 1000; + + // Make the current timestamp be greater than the bond escalation deadline + uint256 _bondEscalationDeadline = block.timestamp - 1; + + // Set the bond escalation status of the given requestId to something different than Active + bondEscalationModule.forTest_setBondEscalationStatus( + _requestId, IBondEscalationModule.BondEscalationStatus(_status) + ); + + // Set the dispute to be the one that went through the bond escalation process for the given requestId + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + // Check: does it revert if the dispute is not escalatable? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_NotEscalatable.selector); + vm.prank(address(oracle)); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + } + + /** + * @notice Tests that escalateDispute reverts if a dispute that went through the bond escalation mechanism and is still active + * but its pledges are not tied even after the tying buffer is escalated. + * Conditions to reach this check: + * - The _requestId tied to the dispute tied to _disputeId must be valid (non-zero) + * - The block.timestamp has to be > bond escalation deadline + tying buffer + * - The dispute has to have gone or be going through the bond escalation process + * - The pledges must not be tied + */ + function test_revertIfEscalatingDisputeIsNotTied( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + // Assume _requestId is not zero + vm.assume(_requestId > 0); + + mockDispute.requestId = _requestId; + + // Set a tying buffer to make the test more explicit + uint256 _tyingBuffer = 1000; + + // Set bond escalation deadline to be the current timestamp. We will warp this. + uint256 _bondEscalationDeadline = block.timestamp; + + // Set the number of pledgers to be different + uint256 _numForPledgers = 1; + uint256 _numAgainstPledgers = 2; + + // Warp the current timestamp so we are past the tyingBuffer + vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + + // Set the bond escalation status of the given requestId to Active + bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); + + // Set the dispute to be the one that went through the bond escalation process for the given requestId + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + // Set the number of pledgers for both sides + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Check: does it revert if the dispute is not escalatable? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_NotEscalatable.selector); + vm.prank(address(oracle)); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + } + + /** + * @notice Tests that escalateDispute escalates the dispute going through the bond escalation mechanism correctly when the + * pledges are tied and the dispute is still active. + * Conditions for the function to succeed: + * - The _requestId tied to the dispute tied to _disputeId must be valid (non-zero) + * - The block.timestamp has to be > bond escalation deadline + * - The dispute has to have gone or be going through the bond escalation process + * - The pledges must be tied + */ + function test_escalateTiedDispute(bytes32 _disputeId, bytes32 _requestId, IOracle.Request calldata _request) public { + // Assume _requestId is not zero + vm.assume(_requestId > 0); + vm.assume(_disputeId > 0); + + mockDispute.requestId = _requestId; + + // Set a tying buffer + uint256 _tyingBuffer = 1000; + + // Set bond escalation deadline to be the current timestamp. We will warp this. + uint256 _bondEscalationDeadline = block.timestamp; + + // Set the number of pledgers to be the same. This means the pledges are tied. + uint256 _numForPledgers = 2; + uint256 _numAgainstPledgers = 2; + + // Warp so we are still in the tying buffer period. This is to show a dispute can be escalated during the buffer if the pledges are tied. + vm.warp(_bondEscalationDeadline + _tyingBuffer); + + // Set the bond escalation status of the given requestId to Active + bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); + + // Set the dispute to be the one that went through the bond escalation process for the given requestId + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + // Set the number of pledgers for both sides + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Check: is the event emitted? + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.Escalated); + + vm.prank(address(oracle)); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + + IBondEscalationModule.BondEscalation memory _escalation = bondEscalationModule.getEscalation(_requestId); + // Check: is the bond escalation status properly updated? + assertEq(uint256(_escalation.status), uint256(IBondEscalationModule.BondEscalationStatus.Escalated)); + } +} + +contract BondEscalationModule_Unit_DisputeResponse is BaseTest { + /** + * @notice Tests that disputeResponse reverts the caller is not the oracle address. + */ + function test_revertIfCallerIsNotOracle( + bytes32 _requestId, + bytes32 _responseId, + address _caller, + IOracle.Request calldata _request + ) public { + vm.assume(_caller != address(oracle)); + + // Check: does it revert if not called by the Oracle? + vm.expectRevert(IModule.Module_OnlyOracle.selector); + vm.prank(_caller); + bondEscalationModule.disputeResponse(_request, mockResponse, mockDispute); + } + + /** + * @notice Tests that disputeResponse reverts if the challenge period for the response is over. + */ + function test_revertIfDisputeWindowIsOver( + bytes32 _requestId, + bytes32 _responseId, + IOracle.Request calldata _request + ) public { + uint256 _disputeWindow = 1; + + mockResponse.requestId = _requestId; + + // Warp to a time after the disputeWindow is over. + vm.warp(block.timestamp + _disputeWindow + 1); + + // Check: does it revert if the dispute window is over? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeWindowOver.selector); + vm.prank(address(oracle)); + bondEscalationModule.disputeResponse(_request, mockResponse, mockDispute); + } + + /** + * @notice Tests that disputeResponse succeeds if someone dispute after the bond escalation deadline is over + */ + function test_succeedIfDisputeAfterBondingEscalationDeadline( + bytes32 _requestId, + bytes32 _responseId, + IOracle.Request calldata _request + ) public { + // Set deadline to timestamp so we are still in the bond escalation period + uint256 _bondEscalationDeadline = block.timestamp - 1; + + mockResponse.requestId = _requestId; + + // Check: does it revert if the bond escalation is over? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); + vm.prank(address(oracle)); + bondEscalationModule.disputeResponse(_request, mockResponse, mockDispute); + } + + /** + * @notice Tests that disputeResponse succeeds in starting the bond escalation mechanism when someone disputes + * the first propose before the bond escalation deadline is over. + */ + function test_firstDisputeThroughBondMechanism( + bytes32 _requestId, + bytes32 _responseId, + IOracle.Request calldata _request + ) public { + // Set deadline to timestamp so we are still in the bond escalation period + uint256 _bondEscalationDeadline = block.timestamp; + + mockResponse.requestId = _requestId; + + // Mock and expect the accounting extension to be called + _mockAndExpect( + address(accounting), + abi.encodeWithSignature('bond(address,bytes32,address,uint256)', disputer, _requestId, token, bondSize), + abi.encode(true) + ); + + bytes32 _expectedDisputeId = keccak256(abi.encodePacked(disputer, _requestId, _responseId)); + + // Check: is the event emitted? + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit BondEscalationStatusUpdated(_requestId, _expectedDisputeId, IBondEscalationModule.BondEscalationStatus.Active); + + vm.prank(address(oracle)); + bondEscalationModule.disputeResponse(_request, mockResponse, mockDispute); + + // Check: is the bond escalation status now active? + assertEq( + uint256(bondEscalationModule.getEscalation(_requestId).status), + uint256(IBondEscalationModule.BondEscalationStatus.Active) + ); + + // Check: is the dispute assigned to the bond escalation process? + assertEq(bondEscalationModule.getEscalation(_requestId).disputeId, _expectedDisputeId); + } + + function test_emitsEvent(bytes32 _requestId, bytes32 _responseId, IOracle.Request calldata _request) public { + // Set deadline to timestamp so we are still in the bond escalation period + uint256 _bondEscalationDeadline = block.timestamp; + + mockResponse.requestId = _requestId; + + // Mock and expect the accounting extension to be called + _mockAndExpect( + address(accounting), + abi.encodeWithSignature('bond(address,bytes32,address,uint256)', disputer, _requestId, token, bondSize), + abi.encode(true) + ); + + // Check: is the event emitted? + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit ResponseDisputed(_requestId, _responseId, disputer, proposer); + + vm.prank(address(oracle)); + bondEscalationModule.disputeResponse(_request, mockResponse, mockDispute); + } +} + +contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { + /** + * @notice Tests that onDisputeStatusChange reverts + */ + function test_revertIfCallerIsNotOracle( + bytes32 _disputeId, + bytes32 _requestId, + address _caller, + uint8 _status, + IOracle.Request calldata _request + ) public { + vm.assume(_caller != address(oracle)); + vm.assume(_status < 4); + + IOracle.DisputeStatus _disputeStatus = IOracle.DisputeStatus(_status); + IOracle.Dispute memory _dispute = _getRandomDispute(_requestId, _disputeStatus); + + // Check: does it revert if not called by the Oracle? + vm.expectRevert(IModule.Module_OnlyOracle.selector); + vm.prank(_caller); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + } + + /** + * @notice Tests that onDisputeStatusChange pays the proposer if the disputer lost + */ + function test_callPayIfNormalDisputeLost( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_disputeId > 0 && _requestId > 0); + + IOracle.DisputeStatus _status = IOracle.DisputeStatus.Lost; + + // Mock and expect IAccountingExtension.pay to be called + _mockAndExpect( + address(accounting), + abi.encodeCall( + IAccountingExtension.pay, (_requestId, mockDispute.disputer, mockDispute.proposer, token, bondSize) + ), + abi.encode(true) + ); + + vm.prank(address(oracle)); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + } + + /** + * @notice Tests that onDisputeStatusChange pays the disputer if the disputer won + */ + function test_callPayIfNormalDisputeWon( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; + + // Mock and expect IAccountingExtension.pay to be called + _mockAndExpect( + address(accounting), + abi.encodeCall( + IAccountingExtension.pay, (_requestId, mockDispute.proposer, mockDispute.disputer, token, bondSize) + ), + abi.encode(true) + ); + + // Mock and expect IAccountingExtension.release to be called + _mockAndExpect( + address(accounting), + abi.encodeCall(IAccountingExtension.release, (mockDispute.disputer, _requestId, token, bondSize)), + abi.encode(true) + ); + + vm.prank(address(oracle)); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + } + + function test_emitsEvent(bytes32 _disputeId, bytes32 _requestId, IOracle.Request calldata _request) public { + IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; + + // Mock and expect IAccountingExtension.pay to be called + _mockAndExpect( + address(accounting), + abi.encodeCall( + IAccountingExtension.pay, (_requestId, mockDispute.proposer, mockDispute.disputer, token, bondSize) + ), + abi.encode(true) + ); + + // Mock and expect IAccountingExtension.release to be called + _mockAndExpect( + address(accounting), + abi.encodeCall(IAccountingExtension.release, (mockDispute.disputer, _requestId, token, bondSize)), + abi.encode(true) + ); + + // Check: is the event emitted? + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit DisputeStatusChanged(_disputeId, mockDispute, IOracle.DisputeStatus.Won); + + vm.prank(address(oracle)); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + } + + /** + * @notice Tests that onDisputeStatusChange returns early if the dispute has gone through the bond + * escalation mechanism but no one pledged + */ + function test_earlyReturnIfBondEscalatedDisputeHashNoPledgers( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; + + uint256 _numForPledgers = 0; + uint256 _numAgainstPledgers = 0; + + // Set bond escalation data to have no pledgers + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Set this dispute to have gone through the bond escalation process + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + // Set the bond escalation status to Escalated, which is the only possible one for this function + bondEscalationModule.forTest_setBondEscalationStatus( + _requestId, IBondEscalationModule.BondEscalationStatus.Escalated + ); + + // Mock and expect IAccountingExtension.pay to be called + _mockAndExpect( + address(accounting), + abi.encodeCall( + IAccountingExtension.pay, (_requestId, mockDispute.proposer, mockDispute.disputer, token, bondSize) + ), + abi.encode(true) + ); + + // Mock and expect IAccountingExtension.release to be called + _mockAndExpect( + address(accounting), + abi.encodeCall(IAccountingExtension.release, (mockDispute.disputer, _requestId, token, bondSize)), + abi.encode(true) + ); + + vm.prank(address(oracle)); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + + // Check: is the bond escalation status properly updated? + assertEq( + uint256(bondEscalationModule.getEscalation(_requestId).status), + uint256(IBondEscalationModule.BondEscalationStatus.Escalated) + ); + } + + /** + * @notice Tests that onDisputeStatusChange changes the status of the bond escalation if the + * dispute went through the bond escalation process, as well as testing that it calls + * payPledgersWon with the correct arguments. In the Won case, this would be, passing + * the users that pledged in favor of the dispute, as they have won. + */ + function test_shouldChangeBondEscalationStatusAndCallPayPledgersWon( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; + + uint256 _numForPledgers = 2; + uint256 _numAgainstPledgers = 2; + + // Set bond escalation data to have pledgers and to return the winning for pledgers as in this case they won the escalation + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Set this dispute to have gone through the bond escalation process + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + // Set the bond escalation status to Escalated, which is the only possible one for this function + bondEscalationModule.forTest_setBondEscalationStatus( + _requestId, IBondEscalationModule.BondEscalationStatus.Escalated + ); + + // Mock and expect IAccountingExtension.pay to be called + _mockAndExpect( + address(accounting), + abi.encodeCall( + IAccountingExtension.pay, (_requestId, mockDispute.proposer, mockDispute.disputer, token, bondSize) + ), + abi.encode(true) + ); + + // Mock and expect IAccountingExtension.release to be called + _mockAndExpect( + address(accounting), + abi.encodeCall(IAccountingExtension.release, (mockDispute.disputer, _requestId, token, bondSize)), + abi.encode(true) + ); + + // Mock and expect IBondEscalationAccounting.onSettleBondEscalation to be called + _mockAndExpect( + address(accounting), + abi.encodeCall( + IBondEscalationAccounting.onSettleBondEscalation, + (_requestId, _disputeId, true, token, bondSize << 1, _numForPledgers) + ), + abi.encode() + ); + + // Check: is the event emitted? + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerWon); + + vm.prank(address(oracle)); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + + // Check: is the bond escalation status properly updated? + assertEq( + uint256(bondEscalationModule.getEscalation(_requestId).status), + uint256(IBondEscalationModule.BondEscalationStatus.DisputerWon) + ); + } + + /** + * @notice Tests that onDisputeStatusChange changes the status of the bond escalation if the + * dispute went through the bond escalation process, as well as testing that it calls + * payPledgersWon with the correct arguments. In the Lost case, this would be, passing + * the users that pledged against the dispute, as those that pledged in favor have lost . + */ + function test_shouldChangeBondEscalationStatusAndCallPayPledgersLost( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + // Set to Lost so the proposer and againstDisputePledgers win + IOracle.DisputeStatus _status = IOracle.DisputeStatus.Lost; + + uint256 _bondSize = 1000; + + uint256 _numForPledgers = 2; + uint256 _numAgainstPledgers = 2; + + // Set bond escalation data to have pledgers and to return the winning for pledgers as in this case they won the escalation + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Set this dispute to have gone through the bond escalation process + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + // Set the bond escalation status to Escalated, which is the only possible one for this function + bondEscalationModule.forTest_setBondEscalationStatus( + _requestId, IBondEscalationModule.BondEscalationStatus.Escalated + ); + + // Mock and expect IAccountingExtension.pay to be called + _mockAndExpect( + address(accounting), + abi.encodeCall( + IAccountingExtension.pay, (_requestId, mockDispute.disputer, mockDispute.proposer, token, _bondSize) + ), + abi.encode(true) + ); + + // Mock and expect IBondEscalationAccounting.onSettleBondEscalation to be called + vm.mockCall( + address(accounting), + abi.encodeCall( + IBondEscalationAccounting.onSettleBondEscalation, + (_requestId, _disputeId, false, token, _bondSize << 1, _numAgainstPledgers) + ), + abi.encode(true) + ); + + // Check: is th event emitted? + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerLost); + + vm.prank(address(oracle)); + bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + + // Check: is the bond escalation status properly updated? + assertEq( + uint256(bondEscalationModule.getEscalation(_requestId).status), + uint256(IBondEscalationModule.BondEscalationStatus.DisputerLost) + ); + } +} + +contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { + /** + * @notice Tests that pledgeForDispute reverts if the dispute does not exist. + */ + function test_revertIfDisputeIsZero(IOracle.Request calldata _request) public { + bytes32 _disputeId = 0; + + // Check: does it revert if the dispute does not exist? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeDoesNotExist.selector); + bondEscalationModule.pledgeForDispute(_request, mockDispute); + } + + /** + * @notice Tests that pledgeForDispute reverts if the dispute is not going through the bond escalation mechanism. + */ + function test_revertIfTheDisputeIsNotGoingThroughTheBondEscalationProcess( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_disputeId > 0); + + mockDispute.requestId = _requestId; + + // Check: does it revert if the dispute is not escalated yet? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_InvalidDispute.selector); + bondEscalationModule.pledgeForDispute(_request, mockDispute); + } + + /** + * @notice Tests that pledgeForDispute reverts if someone tries to pledge after the tying buffer. + */ + function test_revertIfTimestampBeyondTyingBuffer( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_disputeId > 0); + + mockDispute.requestId = _requestId; + + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _bondSize = 1; + uint256 _maxNumberOfEscalations = 1; + uint256 _bondEscalationDeadline = block.timestamp; + uint256 _tyingBuffer = 1000; + + vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + + // Check: does it revert if the bond escalation is over? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); + bondEscalationModule.pledgeForDispute(_request, mockDispute); + } + + /** + * @notice Tests that pledgeForDispute reverts if the maximum number of escalations has been reached. + */ + function test_revertIfMaxNumberOfEscalationsReached( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_disputeId > 0); + + mockDispute.requestId = _requestId; + + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _bondSize = 1; + uint256 _maxNumberOfEscalations = 2; + uint256 _bondEscalationDeadline = block.timestamp - 1; + uint256 _tyingBuffer = 1000; + + uint256 _numForPledgers = 2; + uint256 _numAgainstPledgers = _numForPledgers; + + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Check: does it revert if the maximum number of escalations is reached? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_MaxNumberOfEscalationsReached.selector); + bondEscalationModule.pledgeForDispute(_request, mockDispute); + } + + /** + * @notice Tests that pledgeForDispute reverts if someone tries to pledge in favor of the dispute when there are + * more pledges in favor of the dispute than against + */ + function test_revertIfThereIsMorePledgedForForDisputeThanAgainst( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_disputeId > 0); + + mockDispute.requestId = _requestId; + + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _bondSize = 1; + uint256 _maxNumberOfEscalations = 3; + uint256 _bondEscalationDeadline = block.timestamp + 1; + + uint256 _numForPledgers = 2; + uint256 _numAgainstPledgers = _numForPledgers - 1; + + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Check: does it revert if trying to pledge in a dispute that is already surpassed? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_CanOnlySurpassByOnePledge.selector); + bondEscalationModule.pledgeForDispute(_request, mockDispute); + } + + /** + * @notice Tests that pledgeForDispute reverts if the timestamp is within the tying buffer and someone attempts + * to pledge when the funds are tied, effectively breaking the tie + */ + function test_revertIfAttemptToBreakTieDuringTyingBuffer( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_disputeId > 0); + + mockDispute.requestId = _requestId; + + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _bondSize = 1; + uint256 _maxNumberOfEscalations = 3; + uint256 _bondEscalationDeadline = block.timestamp - 1; + uint256 _tyingBuffer = 1000; + + uint256 _numForPledgers = 2; + uint256 _numAgainstPledgers = _numForPledgers; + + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Check: does it revert if trying to tie outside of the tying buffer? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_CannotBreakTieDuringTyingBuffer.selector); + bondEscalationModule.pledgeForDispute(_request, mockDispute); + } + + /** + * @notice Tests that pledgeForDispute is called successfully + */ + function test_successfulCall(bytes32 _disputeId, bytes32 _requestId, IOracle.Request calldata _request) public { + vm.assume(_disputeId > 0); + + mockDispute.requestId = _requestId; + + // Mock and expect + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _bondSize = 1000; + uint256 _maxNumberOfEscalations = 3; + uint256 _bondEscalationDeadline = block.timestamp - 1; + uint256 _tyingBuffer = 1000; + + uint256 _numForPledgers = 2; + uint256 _numAgainstPledgers = _numForPledgers + 1; + + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Mock and expect IBondEscalationAccounting.pledge to be called + _mockAndExpect( + address(accounting), + abi.encodeCall(IBondEscalationAccounting.pledge, (address(this), _requestId, _disputeId, token, _bondSize)), + abi.encode(true) + ); + + // Check: is event emitted? + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit PledgedForDispute(_disputeId, address(this), _bondSize); + + bondEscalationModule.pledgeForDispute(_request, mockDispute); + + uint256 _pledgesForDispute = bondEscalationModule.getEscalation(_requestId).amountOfPledgesForDispute; + // Check: is the number of pledges for the dispute properly updated? + assertEq(_pledgesForDispute, _numForPledgers + 1); + + uint256 _userPledges = bondEscalationModule.pledgesForDispute(_requestId, address(this)); + // Check: is the number of pledges for the user properly updated? + assertEq(_userPledges, 1); + } +} + +contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { + /** + * @notice Tests that pledgeAgainstDispute reverts if the dispute does not exist. + */ + function test_revertIfDisputeIsZero(IOracle.Request calldata _request) public { + bytes32 _disputeId = 0; + + // Check: does it revert if the dispute does not exist? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeDoesNotExist.selector); + bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + } + + /** + * @notice Tests that pledgeAgainstDispute reverts if the dispute is not going through the bond escalation mechanism. + */ + function test_revertIfTheDisputeIsNotGoingThroughTheBondEscalationProcess( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_disputeId > 0); + + mockDispute.requestId = _requestId; + + // Check: does it revert if the dispute is not escalated yet? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_InvalidDispute.selector); + bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + } + + /** + * @notice Tests that pledgeAgainstDispute reverts if someone tries to pledge after the tying buffer. + */ + function test_revertIfTimestampBeyondTyingBuffer( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_disputeId > 0); -// // Set a tying buffer -// uint256 _tyingBuffer = 1000; + mockDispute.requestId = _requestId; -// // Set bond escalation deadline to be the current timestamp. We will warp this. -// uint256 _bondEscalationDeadline = block.timestamp; + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _bondSize = 1; + uint256 _maxNumberOfEscalations = 1; + uint256 _bondEscalationDeadline = block.timestamp; + uint256 _tyingBuffer = 1000; + + vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + + // Check: does it revert if the bond escalation is over? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); + + bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + } -// // Set the number of pledgers to be the same. This means the pledges are tied. -// uint256 _numForPledgers = 2; -// uint256 _numAgainstPledgers = 2; - -// // Warp so we are still in the tying buffer period. This is to show a dispute can be escalated during the buffer if the pledges are tied. -// vm.warp(_bondEscalationDeadline + _tyingBuffer); - -// // Populate the requestData for the given requestId -// _setRequestData(_requestId, bondSize, maxEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow); - -// // Set the bond escalation status of the given requestId to Active -// bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); - -// // Set the dispute to be the one that went through the bond escalation process for the given requestId -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// // Set the number of pledgers for both sides -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Check: is the event emitted? -// vm.expectEmit(true, true, true, true, address(bondEscalationModule)); -// emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.Escalated); - -// vm.prank(address(oracle)); -// bondEscalationModule.disputeEscalated(_disputeId); - -// IBondEscalationModule.BondEscalation memory _escalation = bondEscalationModule.getEscalation(_requestId); -// // Check: is the bond escalation status properly updated? -// assertEq(uint256(_escalation.status), uint256(IBondEscalationModule.BondEscalationStatus.Escalated)); -// } - -// /** -// * @notice Tests that escalateDispute escalates a dispute not going through the bond escalation mechanism correctly after -// * the bond mechanism deadline has gone by. -// * Conditions for the function to succeed: -// * - The _requestId tied to the dispute tied to _disputeId must be valid (non-zero) -// * - The block.timestamp has to be > bond escalation deadline -// */ -// function test_escalateNormalDispute(bytes32 _disputeId, bytes32 _requestId) public { -// // Assume _requestId and _disputeId are not zero -// vm.assume(_requestId > 0); -// vm.assume(_disputeId > 0); + /** + * @notice Tests that pledgeAgainstDispute reverts if the maximum number of escalations has been reached. + */ + function test_revertIfMaxNumberOfEscalationsReached( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_disputeId > 0); -// uint256 _tyingBuffer = 1000; + mockDispute.requestId = _requestId; + + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _bondSize = 1; + uint256 _maxNumberOfEscalations = 2; + uint256 _bondEscalationDeadline = block.timestamp - 1; + uint256 _tyingBuffer = 1000; + + uint256 _numForPledgers = 2; + uint256 _numAgainstPledgers = _numForPledgers; + + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Check: does it revert if the maximum number of escalations is reached? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_MaxNumberOfEscalationsReached.selector); -// _mockDispute.requestId = _requestId; + bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + } + + /** + * @notice Tests that pledgeAgainstDispute reverts if someone tries to pledge in favor of the dispute when there are + * more pledges against of the dispute than in favor of it + */ + function test_revertIfThereIsMorePledgedAgainstDisputeThanFor( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_disputeId > 0); + + mockDispute.requestId = _requestId; + + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _bondSize = 1; + uint256 _maxNumberOfEscalations = 3; + uint256 _bondEscalationDeadline = block.timestamp + 1; + + uint256 _numAgainstPledgers = 2; + uint256 _numForPledgers = _numAgainstPledgers - 1; + + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Check: does it revert if trying to pledge in a dispute that is already surpassed? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_CanOnlySurpassByOnePledge.selector); + + bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + } + + /** + * @notice Tests that pledgeAgainstDispute reverts if the timestamp is within the tying buffer and someone attempts + * to pledge when the funds are tied, effectively breaking the tie + */ + function test_revertIfAttemptToBreakTieDuringTyingBuffer( + bytes32 _disputeId, + bytes32 _requestId, + IOracle.Request calldata _request + ) public { + vm.assume(_disputeId > 0); + + mockDispute.requestId = _requestId; + + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _bondSize = 1; + uint256 _maxNumberOfEscalations = 3; + uint256 _bondEscalationDeadline = block.timestamp - 1; + uint256 _tyingBuffer = 1000; -// // Mock and expect Oracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); + uint256 _numForPledgers = 2; + uint256 _numAgainstPledgers = _numForPledgers; -// // Set bond escalation deadline to be the current timestamp. We will warp this. -// uint256 _bondEscalationDeadline = block.timestamp; + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); -// // Warp so we are past the tying buffer period -// vm.warp(_bondEscalationDeadline + 1); - -// // Populate the requestData for the given requestId -// _setRequestData(_requestId, bondSize, maxEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow); - -// vm.prank(address(oracle)); -// bondEscalationModule.disputeEscalated(_disputeId); -// } -// } - -// contract BondEscalationModule_Unit_DisputeResponse is BaseTest { -// /** -// * @notice Tests that disputeResponse reverts the caller is not the oracle address. -// */ -// function test_revertIfCallerIsNotOracle(bytes32 _requestId, bytes32 _responseId, address _caller) public { -// vm.assume(_caller != address(oracle)); - -// // Check: does it revert if not called by the Oracle? -// vm.expectRevert(IModule.Module_OnlyOracle.selector); -// vm.prank(_caller); -// bondEscalationModule.disputeResponse(_requestId, _responseId, disputer, proposer); -// } - -// /** -// * @notice Tests that disputeResponse reverts if the challenge period for the response is over. -// */ -// function test_revertIfDisputeWindowIsOver(bytes32 _requestId, bytes32 _responseId) public { -// uint256 _disputeWindow = 1; + // Check: does it revert if trying to tie outside of the tying buffer? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_CannotBreakTieDuringTyingBuffer.selector); + bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + } -// _setRequestData(_requestId, bondSize, maxEscalations, bondEscalationDeadline, tyingBuffer, _disputeWindow); + /** + * @notice Tests that pledgeAgainstDispute is called successfully + */ + function test_successfulCall(bytes32 _disputeId, bytes32 _requestId, IOracle.Request calldata _request) public { + vm.assume(_disputeId > 0); + + mockDispute.requestId = _requestId; + + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _bondSize = 1000; + uint256 _maxNumberOfEscalations = 3; + uint256 _bondEscalationDeadline = block.timestamp - 1; + uint256 _tyingBuffer = 1000; + + uint256 _numAgainstPledgers = 2; + uint256 _numForPledgers = _numAgainstPledgers + 1; + + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); -// _mockResponse.requestId = _requestId; - -// // Mock and expect Oracle.getResponse to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getResponse, (_responseId)), abi.encode(_mockResponse)); - -// // Warp to a time after the disputeWindow is over. -// vm.warp(block.timestamp + _disputeWindow + 1); - -// // Check: does it revert if the dispute window is over? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeWindowOver.selector); -// vm.prank(address(oracle)); -// bondEscalationModule.disputeResponse(_requestId, _responseId, disputer, proposer); -// } - -// /** -// * @notice Tests that disputeResponse succeeds if someone dispute after the bond escalation deadline is over -// */ -// function test_succeedIfDisputeAfterBondingEscalationDeadline(bytes32 _requestId, bytes32 _responseId) public { -// // Set deadline to timestamp so we are still in the bond escalation period -// uint256 _bondEscalationDeadline = block.timestamp - 1; - -// // Set the request data for the given requestId -// _setRequestData(_requestId, bondSize, maxEscalations, _bondEscalationDeadline, tyingBuffer, disputeWindow); - -// _mockResponse.requestId = _requestId; - -// // Mock and expect Oracle.getResponse to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getResponse, (_responseId)), abi.encode(_mockResponse)); - -// // Check: does it revert if the bond escalation is over? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); -// vm.prank(address(oracle)); -// bondEscalationModule.disputeResponse(_requestId, _responseId, disputer, proposer); -// } - -// /** -// * @notice Tests that disputeResponse succeeds in starting the bond escalation mechanism when someone disputes -// * the first propose before the bond escalation deadline is over. -// */ -// function test_firstDisputeThroughBondMechanism(bytes32 _requestId, bytes32 _responseId) public { -// // Set deadline to timestamp so we are still in the bond escalation period -// uint256 _bondEscalationDeadline = block.timestamp; - -// // Set the request data for the given requestId -// _setRequestData(_requestId, bondSize, maxEscalations, _bondEscalationDeadline, tyingBuffer, disputeWindow); - -// _mockResponse.requestId = _requestId; - -// // Mock and expect Oracle.getResponse to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getResponse, (_responseId)), abi.encode(_mockResponse)); - -// // Mock and expect the accounting extension to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeWithSignature('bond(address,bytes32,address,uint256)', disputer, _requestId, token, bondSize), -// abi.encode(true) -// ); - -// bytes32 _expectedDisputeId = keccak256(abi.encodePacked(disputer, _requestId, _responseId)); - -// // Check: is the event emitted? -// vm.expectEmit(true, true, true, true, address(bondEscalationModule)); -// emit BondEscalationStatusUpdated(_requestId, _expectedDisputeId, IBondEscalationModule.BondEscalationStatus.Active); - -// vm.prank(address(oracle)); -// bondEscalationModule.disputeResponse(_requestId, _responseId, disputer, proposer); - -// // Check: is the bond escalation status now active? -// assertEq( -// uint256(bondEscalationModule.getEscalation(_requestId).status), -// uint256(IBondEscalationModule.BondEscalationStatus.Active) -// ); - -// // Check: is the dispute assigned to the bond escalation process? -// assertEq(bondEscalationModule.getEscalation(_requestId).disputeId, _expectedDisputeId); -// } - -// function test_emitsEvent(bytes32 _requestId, bytes32 _responseId) public { -// // Set deadline to timestamp so we are still in the bond escalation period -// uint256 _bondEscalationDeadline = block.timestamp; - -// // Set the request data for the given requestId -// _setRequestData(_requestId, bondSize, maxEscalations, _bondEscalationDeadline, tyingBuffer, disputeWindow); - -// _mockResponse.requestId = _requestId; - -// // Mock and expect Oracle.getResponse to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getResponse, (_responseId)), abi.encode(_mockResponse)); - -// // Mock and expect the accounting extension to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeWithSignature('bond(address,bytes32,address,uint256)', disputer, _requestId, token, bondSize), -// abi.encode(true) -// ); - -// // Check: is the event emitted? -// vm.expectEmit(true, true, true, true, address(bondEscalationModule)); -// emit ResponseDisputed(_requestId, _responseId, disputer, proposer); - -// vm.prank(address(oracle)); -// bondEscalationModule.disputeResponse(_requestId, _responseId, disputer, proposer); -// } -// } - -// contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { -// /** -// * @notice Tests that onDisputeStatusChange reverts -// */ -// function test_revertIfCallerIsNotOracle( -// bytes32 _disputeId, -// bytes32 _requestId, -// address _caller, -// uint8 _status -// ) public { -// vm.assume(_caller != address(oracle)); -// vm.assume(_status < 4); - -// IOracle.DisputeStatus _disputeStatus = IOracle.DisputeStatus(_status); -// IOracle.Dispute memory _dispute = _getRandomDispute(_requestId, _disputeStatus); - -// // Check: does it revert if not called by the Oracle? -// vm.expectRevert(IModule.Module_OnlyOracle.selector); -// vm.prank(_caller); -// bondEscalationModule.onDisputeStatusChange(_disputeId, _dispute); -// } - -// /** -// * @notice Tests that onDisputeStatusChange pays the proposer if the disputer lost -// */ -// function test_callPayIfNormalDisputeLost(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_disputeId > 0 && _requestId > 0); - -// IOracle.DisputeStatus _status = IOracle.DisputeStatus.Lost; -// IOracle.Dispute memory _dispute = _getRandomDispute(_requestId, _status); - -// _setRequestData(_requestId, bondSize, maxEscalations, bondEscalationDeadline, tyingBuffer, disputeWindow); - -// // Mock and expect IAccountingExtension.pay to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IAccountingExtension.pay, (_requestId, _dispute.disputer, _dispute.proposer, token, bondSize)), -// abi.encode(true) -// ); - -// vm.prank(address(oracle)); -// bondEscalationModule.onDisputeStatusChange(_disputeId, _dispute); -// } - -// /** -// * @notice Tests that onDisputeStatusChange pays the disputer if the disputer won -// */ -// function test_callPayIfNormalDisputeWon(bytes32 _disputeId, bytes32 _requestId) public { -// IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; -// IOracle.Dispute memory _dispute = _getRandomDispute(_requestId, _status); - -// _setRequestData(_requestId, bondSize, maxEscalations, bondEscalationDeadline, tyingBuffer, disputeWindow); - -// // Mock and expect IAccountingExtension.pay to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IAccountingExtension.pay, (_requestId, _dispute.proposer, _dispute.disputer, token, bondSize)), -// abi.encode(true) -// ); - -// // Mock and expect IAccountingExtension.release to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IAccountingExtension.release, (_dispute.disputer, _requestId, token, bondSize)), -// abi.encode(true) -// ); - -// vm.prank(address(oracle)); -// bondEscalationModule.onDisputeStatusChange(_disputeId, _dispute); -// } - -// function test_emitsEvent(bytes32 _disputeId, bytes32 _requestId) public { -// IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; -// IOracle.Dispute memory _dispute = _getRandomDispute(_requestId, _status); - -// _setRequestData(_requestId, bondSize, maxEscalations, bondEscalationDeadline, tyingBuffer, disputeWindow); - -// // Mock and expect IAccountingExtension.pay to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IAccountingExtension.pay, (_requestId, _dispute.proposer, _dispute.disputer, token, bondSize)), -// abi.encode(true) -// ); - -// // Mock and expect IAccountingExtension.release to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IAccountingExtension.release, (_dispute.disputer, _requestId, token, bondSize)), -// abi.encode(true) -// ); - -// // Check: is the event emitted? -// vm.expectEmit(true, true, true, true, address(bondEscalationModule)); -// emit DisputeStatusChanged( -// _requestId, _dispute.responseId, _dispute.disputer, _dispute.proposer, IOracle.DisputeStatus.Won -// ); - -// vm.prank(address(oracle)); -// bondEscalationModule.onDisputeStatusChange(_disputeId, _dispute); -// } - -// /** -// * @notice Tests that onDisputeStatusChange returns early if the dispute has gone through the bond -// * escalation mechanism but no one pledged -// */ -// function test_earlyReturnIfBondEscalatedDisputeHashNoPledgers(bytes32 _disputeId, bytes32 _requestId) public { -// IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; -// IOracle.Dispute memory _dispute = _getRandomDispute(_requestId, _status); - -// _setRequestData(_requestId, bondSize, maxEscalations, bondEscalationDeadline, tyingBuffer, disputeWindow); - -// uint256 _numForPledgers = 0; -// uint256 _numAgainstPledgers = 0; - -// // Set bond escalation data to have no pledgers -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Set this dispute to have gone through the bond escalation process -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// // Set the bond escalation status to Escalated, which is the only possible one for this function -// bondEscalationModule.forTest_setBondEscalationStatus( -// _requestId, IBondEscalationModule.BondEscalationStatus.Escalated -// ); - -// // Mock and expect IAccountingExtension.pay to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IAccountingExtension.pay, (_requestId, _dispute.proposer, _dispute.disputer, token, bondSize)), -// abi.encode(true) -// ); - -// // Mock and expect IAccountingExtension.release to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IAccountingExtension.release, (_dispute.disputer, _requestId, token, bondSize)), -// abi.encode(true) -// ); - -// vm.prank(address(oracle)); -// bondEscalationModule.onDisputeStatusChange(_disputeId, _dispute); - -// // Check: is the bond escalation status properly updated? -// assertEq( -// uint256(bondEscalationModule.getEscalation(_requestId).status), -// uint256(IBondEscalationModule.BondEscalationStatus.Escalated) -// ); -// } - -// /** -// * @notice Tests that onDisputeStatusChange changes the status of the bond escalation if the -// * dispute went through the bond escalation process, as well as testing that it calls -// * payPledgersWon with the correct arguments. In the Won case, this would be, passing -// * the users that pledged in favor of the dispute, as they have won. -// */ -// function test_shouldChangeBondEscalationStatusAndCallPayPledgersWon(bytes32 _disputeId, bytes32 _requestId) public { -// IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; -// IOracle.Dispute memory _dispute = _getRandomDispute(_requestId, _status); - -// _setRequestData(_requestId, bondSize, maxEscalations, bondEscalationDeadline, tyingBuffer, disputeWindow); - -// uint256 _numForPledgers = 2; -// uint256 _numAgainstPledgers = 2; - -// // Set bond escalation data to have pledgers and to return the winning for pledgers as in this case they won the escalation -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Set this dispute to have gone through the bond escalation process -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// // Set the bond escalation status to Escalated, which is the only possible one for this function -// bondEscalationModule.forTest_setBondEscalationStatus( -// _requestId, IBondEscalationModule.BondEscalationStatus.Escalated -// ); - -// // Mock and expect IAccountingExtension.pay to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IAccountingExtension.pay, (_requestId, _dispute.proposer, _dispute.disputer, token, bondSize)), -// abi.encode(true) -// ); - -// // Mock and expect IAccountingExtension.release to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IAccountingExtension.release, (_dispute.disputer, _requestId, token, bondSize)), -// abi.encode(true) -// ); - -// // Mock and expect IBondEscalationAccounting.onSettleBondEscalation to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall( -// IBondEscalationAccounting.onSettleBondEscalation, -// (_requestId, _disputeId, true, token, bondSize << 1, _numForPledgers) -// ), -// abi.encode() -// ); - -// // Check: is the event emitted? -// vm.expectEmit(true, true, true, true, address(bondEscalationModule)); -// emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerWon); - -// vm.prank(address(oracle)); -// bondEscalationModule.onDisputeStatusChange(_disputeId, _dispute); - -// // Check: is the bond escalation status properly updated? -// assertEq( -// uint256(bondEscalationModule.getEscalation(_requestId).status), -// uint256(IBondEscalationModule.BondEscalationStatus.DisputerWon) -// ); -// } - -// /** -// * @notice Tests that onDisputeStatusChange changes the status of the bond escalation if the -// * dispute went through the bond escalation process, as well as testing that it calls -// * payPledgersWon with the correct arguments. In the Lost case, this would be, passing -// * the users that pledged against the dispute, as those that pledged in favor have lost . -// */ -// function test_shouldChangeBondEscalationStatusAndCallPayPledgersLost(bytes32 _disputeId, bytes32 _requestId) public { -// // Set to Lost so the proposer and againstDisputePledgers win -// IOracle.DisputeStatus _status = IOracle.DisputeStatus.Lost; -// IOracle.Dispute memory _dispute = _getRandomDispute(_requestId, _status); - -// uint256 _bondSize = 1000; - -// _setRequestData(_requestId, _bondSize, maxEscalations, bondEscalationDeadline, tyingBuffer, disputeWindow); - -// uint256 _numForPledgers = 2; -// uint256 _numAgainstPledgers = 2; - -// // Set bond escalation data to have pledgers and to return the winning for pledgers as in this case they won the escalation -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Set this dispute to have gone through the bond escalation process -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// // Set the bond escalation status to Escalated, which is the only possible one for this function -// bondEscalationModule.forTest_setBondEscalationStatus( -// _requestId, IBondEscalationModule.BondEscalationStatus.Escalated -// ); - -// // Mock and expect IAccountingExtension.pay to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IAccountingExtension.pay, (_requestId, _dispute.disputer, _dispute.proposer, token, _bondSize)), -// abi.encode(true) -// ); - -// // Mock and expect IBondEscalationAccounting.onSettleBondEscalation to be called -// vm.mockCall( -// address(accounting), -// abi.encodeCall( -// IBondEscalationAccounting.onSettleBondEscalation, -// (_requestId, _disputeId, false, token, _bondSize << 1, _numAgainstPledgers) -// ), -// abi.encode(true) -// ); - -// // Check: is th event emitted? -// vm.expectEmit(true, true, true, true, address(bondEscalationModule)); -// emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerLost); - -// vm.prank(address(oracle)); -// bondEscalationModule.onDisputeStatusChange(_disputeId, _dispute); - -// // Check: is the bond escalation status properly updated? -// assertEq( -// uint256(bondEscalationModule.getEscalation(_requestId).status), -// uint256(IBondEscalationModule.BondEscalationStatus.DisputerLost) -// ); -// } -// } - -// contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { -// /** -// * @notice Tests that pledgeForDispute reverts if the dispute does not exist. -// */ -// function test_revertIfDisputeIsZero() public { -// bytes32 _disputeId = 0; - -// // Check: does it revert if the dispute does not exist? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeDoesNotExist.selector); -// bondEscalationModule.pledgeForDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeForDispute reverts if the dispute is not going through the bond escalation mechanism. -// */ -// function test_revertIfTheDisputeIsNotGoingThroughTheBondEscalationProcess( -// bytes32 _disputeId, -// bytes32 _requestId -// ) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect IOracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// // Check: does it revert if the dispute is not escalated yet? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_InvalidDispute.selector); -// bondEscalationModule.pledgeForDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeForDispute reverts if someone tries to pledge after the tying buffer. -// */ -// function test_revertIfTimestampBeyondTyingBuffer(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect IOracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _bondSize = 1; -// uint256 _maxNumberOfEscalations = 1; -// uint256 _bondEscalationDeadline = block.timestamp; -// uint256 _tyingBuffer = 1000; - -// vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); - -// _setRequestData( -// _requestId, _bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow -// ); - -// // Check: does it revert if the bond escalation is over? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); -// bondEscalationModule.pledgeForDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeForDispute reverts if the maximum number of escalations has been reached. -// */ -// function test_revertIfMaxNumberOfEscalationsReached(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect IOracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _bondSize = 1; -// uint256 _maxNumberOfEscalations = 2; -// uint256 _bondEscalationDeadline = block.timestamp - 1; -// uint256 _tyingBuffer = 1000; - -// _setRequestData( -// _requestId, _bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow -// ); - -// uint256 _numForPledgers = 2; -// uint256 _numAgainstPledgers = _numForPledgers; - -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Check: does it revert if the maximum number of escalations is reached? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_MaxNumberOfEscalationsReached.selector); -// bondEscalationModule.pledgeForDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeForDispute reverts if someone tries to pledge in favor of the dispute when there are -// * more pledges in favor of the dispute than against -// */ -// function test_revertIfThereIsMorePledgedForForDisputeThanAgainst(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect IOracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _bondSize = 1; -// uint256 _maxNumberOfEscalations = 3; -// uint256 _bondEscalationDeadline = block.timestamp + 1; - -// _setRequestData(_requestId, _bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, tyingBuffer, disputeWindow); - -// uint256 _numForPledgers = 2; -// uint256 _numAgainstPledgers = _numForPledgers - 1; - -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Check: does it revert if trying to pledge in a dispute that is already surpassed? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_CanOnlySurpassByOnePledge.selector); -// bondEscalationModule.pledgeForDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeForDispute reverts if the timestamp is within the tying buffer and someone attempts -// * to pledge when the funds are tied, effectively breaking the tie -// */ -// function test_revertIfAttemptToBreakTieDuringTyingBuffer(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect IOracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _bondSize = 1; -// uint256 _maxNumberOfEscalations = 3; -// uint256 _bondEscalationDeadline = block.timestamp - 1; -// uint256 _tyingBuffer = 1000; - -// _setRequestData( -// _requestId, _bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow -// ); - -// uint256 _numForPledgers = 2; -// uint256 _numAgainstPledgers = _numForPledgers; - -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Check: does it revert if trying to tie outside of the tying buffer? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_CannotBreakTieDuringTyingBuffer.selector); -// bondEscalationModule.pledgeForDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeForDispute is called successfully -// */ -// function test_successfulCall(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _bondSize = 1000; -// uint256 _maxNumberOfEscalations = 3; -// uint256 _bondEscalationDeadline = block.timestamp - 1; -// uint256 _tyingBuffer = 1000; - -// _setRequestData( -// _requestId, _bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow -// ); - -// uint256 _numForPledgers = 2; -// uint256 _numAgainstPledgers = _numForPledgers + 1; - -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Mock and expect IBondEscalationAccounting.pledge to be called -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IBondEscalationAccounting.pledge, (address(this), _requestId, _disputeId, token, _bondSize)), -// abi.encode(true) -// ); - -// // Check: is event emitted? -// vm.expectEmit(true, true, true, true, address(bondEscalationModule)); -// emit PledgedForDispute(_disputeId, address(this), _bondSize); - -// bondEscalationModule.pledgeForDispute(_disputeId); - -// uint256 _pledgesForDispute = bondEscalationModule.getEscalation(_requestId).amountOfPledgesForDispute; -// // Check: is the number of pledges for the dispute properly updated? -// assertEq(_pledgesForDispute, _numForPledgers + 1); - -// uint256 _userPledges = bondEscalationModule.pledgesForDispute(_requestId, address(this)); -// // Check: is the number of pledges for the user properly updated? -// assertEq(_userPledges, 1); -// } -// } - -// contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { -// /** -// * @notice Tests that pledgeAgainstDispute reverts if the dispute does not exist. -// */ -// function test_revertIfDisputeIsZero() public { -// bytes32 _disputeId = 0; - -// // Check: does it revert if the dispute does not exist? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeDoesNotExist.selector); -// bondEscalationModule.pledgeAgainstDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeAgainstDispute reverts if the dispute is not going through the bond escalation mechanism. -// */ -// function test_revertIfTheDisputeIsNotGoingThroughTheBondEscalationProcess( -// bytes32 _disputeId, -// bytes32 _requestId -// ) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect IOracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// // Check: does it revert if the dispute is not escalated yet? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_InvalidDispute.selector); -// bondEscalationModule.pledgeAgainstDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeAgainstDispute reverts if someone tries to pledge after the tying buffer. -// */ -// function test_revertIfTimestampBeyondTyingBuffer(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect IOracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _bondSize = 1; -// uint256 _maxNumberOfEscalations = 1; -// uint256 _bondEscalationDeadline = block.timestamp; -// uint256 _tyingBuffer = 1000; - -// vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); - -// _setRequestData( -// _requestId, _bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow -// ); - -// // Check: does it revert if the bond escalation is over? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); - -// bondEscalationModule.pledgeAgainstDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeAgainstDispute reverts if the maximum number of escalations has been reached. -// */ -// function test_revertIfMaxNumberOfEscalationsReached(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect IOracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _bondSize = 1; -// uint256 _maxNumberOfEscalations = 2; -// uint256 _bondEscalationDeadline = block.timestamp - 1; -// uint256 _tyingBuffer = 1000; - -// _setRequestData( -// _requestId, _bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow -// ); - -// uint256 _numForPledgers = 2; -// uint256 _numAgainstPledgers = _numForPledgers; - -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Check: does it revert if the maximum number of escalations is reached? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_MaxNumberOfEscalationsReached.selector); - -// bondEscalationModule.pledgeAgainstDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeAgainstDispute reverts if someone tries to pledge in favor of the dispute when there are -// * more pledges against of the dispute than in favor of it -// */ -// function test_revertIfThereIsMorePledgedAgainstDisputeThanFor(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect IOracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _bondSize = 1; -// uint256 _maxNumberOfEscalations = 3; -// uint256 _bondEscalationDeadline = block.timestamp + 1; - -// _setRequestData(_requestId, _bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, tyingBuffer, disputeWindow); - -// uint256 _numAgainstPledgers = 2; -// uint256 _numForPledgers = _numAgainstPledgers - 1; - -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Check: does it revert if trying to pledge in a dispute that is already surpassed? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_CanOnlySurpassByOnePledge.selector); - -// bondEscalationModule.pledgeAgainstDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeAgainstDispute reverts if the timestamp is within the tying buffer and someone attempts -// * to pledge when the funds are tied, effectively breaking the tie -// */ -// function test_revertIfAttemptToBreakTieDuringTyingBuffer(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect IOracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _bondSize = 1; -// uint256 _maxNumberOfEscalations = 3; -// uint256 _bondEscalationDeadline = block.timestamp - 1; -// uint256 _tyingBuffer = 1000; - -// _setRequestData( -// _requestId, _bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow -// ); - -// uint256 _numForPledgers = 2; -// uint256 _numAgainstPledgers = _numForPledgers; - -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Check: does it revert if trying to tie outside of the tying buffer? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_CannotBreakTieDuringTyingBuffer.selector); -// bondEscalationModule.pledgeAgainstDispute(_disputeId); -// } - -// /** -// * @notice Tests that pledgeAgainstDispute is called successfully -// */ -// function test_successfulCall(bytes32 _disputeId, bytes32 _requestId) public { -// vm.assume(_disputeId > 0); - -// _mockDispute.requestId = _requestId; - -// // Mock and expect IOracle.getDispute to be called -// _mockAndExpect(address(oracle), abi.encodeCall(IOracle.getDispute, (_disputeId)), abi.encode(_mockDispute)); - -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _bondSize = 1000; -// uint256 _maxNumberOfEscalations = 3; -// uint256 _bondEscalationDeadline = block.timestamp - 1; -// uint256 _tyingBuffer = 1000; - -// _setRequestData( -// _requestId, _bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow -// ); - -// uint256 _numAgainstPledgers = 2; -// uint256 _numForPledgers = _numAgainstPledgers + 1; - -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// _mockAndExpect( -// address(accounting), -// abi.encodeCall(IBondEscalationAccounting.pledge, (address(this), _requestId, _disputeId, token, _bondSize)), -// abi.encode(true) -// ); - -// // Check: is the event emitted? -// vm.expectEmit(true, true, true, true, address(bondEscalationModule)); -// emit PledgedAgainstDispute(_disputeId, address(this), _bondSize); - -// bondEscalationModule.pledgeAgainstDispute(_disputeId); - -// uint256 _pledgesForDispute = bondEscalationModule.getEscalation(_requestId).amountOfPledgesAgainstDispute; -// // Check: is the number of pledges for the dispute properly updated? -// assertEq(_pledgesForDispute, _numAgainstPledgers + 1); - -// uint256 _userPledges = bondEscalationModule.pledgesAgainstDispute(_requestId, address(this)); -// // Check: is the number of pledges for the user properly updated? -// assertEq(_userPledges, 1); -// } -// } - -// contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { -// /** -// * @notice Tests that settleBondEscalation reverts if someone tries to settle the escalation before the tying buffer -// * has elapsed. -// */ -// function test_revertIfTimestampLessThanEndOfTyingBuffer(bytes32 _requestId) public { -// uint256 _bondEscalationDeadline = block.timestamp; -// _setRequestData(_requestId, bondSize, maxEscalations, _bondEscalationDeadline, tyingBuffer, disputeWindow); - -// // Check: does it revert if the bond escalation is not over? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationNotOver.selector); -// bondEscalationModule.settleBondEscalation(_requestId); -// } - -// /** -// * @notice Tests that settleBondEscalation reverts if someone tries to settle a bond-escalated dispute that -// * is not active. -// */ -// function test_revertIfStatusOfBondEscalationIsNotActive(bytes32 _requestId) public { -// uint256 _bondEscalationDeadline = block.timestamp; -// uint256 _tyingBuffer = 1000; - -// vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); - -// _setRequestData(_requestId, bondSize, maxEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow); - -// bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.None); - -// // Check: does it revert if the bond escalation is not active? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationCantBeSettled.selector); -// bondEscalationModule.settleBondEscalation(_requestId); -// } - -// /** -// * @notice Tests that settleBondEscalation reverts if someone tries to settle a bond-escalated dispute that -// * has the same number of pledgers. -// */ -// function test_revertIfSameNumberOfPledgers(bytes32 _requestId, bytes32 _disputeId) public { -// uint256 _bondEscalationDeadline = block.timestamp; -// uint256 _tyingBuffer = 1000; - -// vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); - -// _setRequestData(_requestId, bondSize, maxEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow); -// bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _numForPledgers = 5; -// uint256 _numAgainstPledgers = _numForPledgers; - -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// // Check: does it revert if the number of pledgers is the same? -// vm.expectRevert(IBondEscalationModule.BondEscalationModule_ShouldBeEscalated.selector); -// bondEscalationModule.settleBondEscalation(_requestId); -// } - -// /** -// * @notice Tests that settleBondEscalation is called successfully. -// */ -// function test_successfulCallDisputerWon(bytes32 _requestId, bytes32 _disputeId) public { -// uint256 _bondSize = 1000; -// uint256 _bondEscalationDeadline = block.timestamp; -// uint256 _tyingBuffer = 1000; - -// vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); - -// _setRequestData(_requestId, _bondSize, maxEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow); -// bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _numForPledgers = 2; -// uint256 _numAgainstPledgers = _numForPledgers - 1; - -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// _mockAndExpect( -// address(oracle), -// abi.encodeCall(IOracle.updateDisputeStatus, (_disputeId, IOracle.DisputeStatus.Won)), -// abi.encode(true) -// ); - -// // Check: is the event emitted? -// vm.expectEmit(true, true, true, true, address(bondEscalationModule)); -// emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerWon); - -// bondEscalationModule.settleBondEscalation(_requestId); -// // Check: is the bond escalation status properly updated? -// assertEq( -// uint256(bondEscalationModule.getEscalation(_requestId).status), -// uint256(IBondEscalationModule.BondEscalationStatus.DisputerWon) -// ); -// } - -// /** -// * @notice Tests that settleBondEscalation is called successfully. -// */ -// function test_successfulCallDisputerLost(bytes32 _requestId, bytes32 _disputeId) public { -// uint256 _bondSize = 1000; -// uint256 _bondEscalationDeadline = block.timestamp; -// uint256 _tyingBuffer = 1000; - -// vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); - -// _setRequestData(_requestId, _bondSize, maxEscalations, _bondEscalationDeadline, _tyingBuffer, disputeWindow); -// bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); -// bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - -// uint256 _numAgainstPledgers = 2; -// uint256 _numForPledgers = _numAgainstPledgers - 1; - -// _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); - -// _mockAndExpect( -// address(oracle), -// abi.encodeCall(IOracle.updateDisputeStatus, (_disputeId, IOracle.DisputeStatus.Lost)), -// abi.encode(true) -// ); - -// // Check: is the event emitted? -// vm.expectEmit(true, true, true, true, address(bondEscalationModule)); -// emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerLost); - -// bondEscalationModule.settleBondEscalation(_requestId); -// // Check: is the bond escalation status properly updated? -// assertEq( -// uint256(bondEscalationModule.getEscalation(_requestId).status), -// uint256(IBondEscalationModule.BondEscalationStatus.DisputerLost) -// ); -// } -// } + _mockAndExpect( + address(accounting), + abi.encodeCall(IBondEscalationAccounting.pledge, (address(this), _requestId, _disputeId, token, _bondSize)), + abi.encode(true) + ); + + // Check: is the event emitted? + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit PledgedAgainstDispute(_disputeId, address(this), _bondSize); + + bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + + uint256 _pledgesForDispute = bondEscalationModule.getEscalation(_requestId).amountOfPledgesAgainstDispute; + // Check: is the number of pledges for the dispute properly updated? + assertEq(_pledgesForDispute, _numAgainstPledgers + 1); + + uint256 _userPledges = bondEscalationModule.pledgesAgainstDispute(_requestId, address(this)); + // Check: is the number of pledges for the user properly updated? + assertEq(_userPledges, 1); + } +} + +contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { + /** + * @notice Tests that settleBondEscalation reverts if someone tries to settle the escalation before the tying buffer + * has elapsed. + */ + function test_revertIfTimestampLessThanEndOfTyingBuffer(bytes32 _requestId, IOracle.Request calldata _request) public { + uint256 _bondEscalationDeadline = block.timestamp; + + // Check: does it revert if the bond escalation is not over? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationNotOver.selector); + bondEscalationModule.settleBondEscalation(_request, mockResponse, mockDispute); + } + + /** + * @notice Tests that settleBondEscalation reverts if someone tries to settle a bond-escalated dispute that + * is not active. + */ + function test_revertIfStatusOfBondEscalationIsNotActive(bytes32 _requestId, IOracle.Request calldata _request) public { + uint256 _bondEscalationDeadline = block.timestamp; + uint256 _tyingBuffer = 1000; + + vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + + bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.None); + + // Check: does it revert if the bond escalation is not active? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationCantBeSettled.selector); + bondEscalationModule.settleBondEscalation(_request, mockResponse, mockDispute); + } + + /** + * @notice Tests that settleBondEscalation reverts if someone tries to settle a bond-escalated dispute that + * has the same number of pledgers. + */ + function test_revertIfSameNumberOfPledgers( + bytes32 _requestId, + bytes32 _disputeId, + IOracle.Request calldata _request + ) public { + uint256 _bondEscalationDeadline = block.timestamp; + uint256 _tyingBuffer = 1000; + + vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + + bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _numForPledgers = 5; + uint256 _numAgainstPledgers = _numForPledgers; + + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + // Check: does it revert if the number of pledgers is the same? + vm.expectRevert(IBondEscalationModule.BondEscalationModule_ShouldBeEscalated.selector); + bondEscalationModule.settleBondEscalation(_request, mockResponse, mockDispute); + } + + /** + * @notice Tests that settleBondEscalation is called successfully. + */ + function test_successfulCallDisputerWon( + bytes32 _requestId, + bytes32 _disputeId, + IOracle.Request calldata _request + ) public { + uint256 _bondSize = 1000; + uint256 _bondEscalationDeadline = block.timestamp; + uint256 _tyingBuffer = 1000; + + vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + + bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _numForPledgers = 2; + uint256 _numAgainstPledgers = _numForPledgers - 1; + + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + _mockAndExpect( + address(oracle), + abi.encodeCall(IOracle.updateDisputeStatus, (_request, mockResponse, mockDispute, IOracle.DisputeStatus.Won)), + abi.encode(true) + ); + + // Check: is the event emitted? + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerWon); + + bondEscalationModule.settleBondEscalation(_request, mockResponse, mockDispute); + // Check: is the bond escalation status properly updated? + assertEq( + uint256(bondEscalationModule.getEscalation(_requestId).status), + uint256(IBondEscalationModule.BondEscalationStatus.DisputerWon) + ); + } + + /** + * @notice Tests that settleBondEscalation is called successfully. + */ + function test_successfulCallDisputerLost( + bytes32 _requestId, + bytes32 _disputeId, + IOracle.Request calldata _request + ) public { + uint256 _bondSize = 1000; + uint256 _bondEscalationDeadline = block.timestamp; + uint256 _tyingBuffer = 1000; + + vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + + bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); + bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); + + uint256 _numAgainstPledgers = 2; + uint256 _numForPledgers = _numAgainstPledgers - 1; + + _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + + _mockAndExpect( + address(oracle), + abi.encodeCall(IOracle.updateDisputeStatus, (_request, mockResponse, mockDispute, IOracle.DisputeStatus.Lost)), + abi.encode(true) + ); + + // Check: is the event emitted? + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerLost); + + bondEscalationModule.settleBondEscalation(_request, mockResponse, mockDispute); + // Check: is the bond escalation status properly updated? + assertEq( + uint256(bondEscalationModule.getEscalation(_requestId).status), + uint256(IBondEscalationModule.BondEscalationStatus.DisputerLost) + ); + } +} From 4cdfe06db23b972c32e05002d6a8ac73792ec0ec Mon Sep 17 00:00:00 2001 From: moebius <0xmoebius@tutanota.com> Date: Tue, 21 Nov 2023 12:38:43 -0300 Subject: [PATCH 3/6] test: fix (most) unit tests --- package.json | 2 +- .../modules/dispute/BondEscalationModule.sol | 10 +- .../dispute/BondEscalationModule.t.sol | 809 ++++++++++-------- solidity/test/utils/Helpers.sol | 70 +- yarn.lock | 12 +- 5 files changed, 522 insertions(+), 381 deletions(-) diff --git a/package.json b/package.json index b7700bc1..d3eac0c9 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "package.json": "sort-package-json" }, "dependencies": { - "@defi-wonderland/prophet-core-contracts": "0.0.0-d05a00d0", + "@defi-wonderland/prophet-core-contracts": "0.0.0-1ae08a81", "@defi-wonderland/solidity-utils": "0.0.0-3e9c8e8b", "@openzeppelin/contracts": "^4.9.3", "ds-test": "https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0", diff --git a/solidity/contracts/modules/dispute/BondEscalationModule.sol b/solidity/contracts/modules/dispute/BondEscalationModule.sol index cf0dbedd..bd0d01cf 100644 --- a/solidity/contracts/modules/dispute/BondEscalationModule.sol +++ b/solidity/contracts/modules/dispute/BondEscalationModule.sol @@ -71,7 +71,6 @@ contract BondEscalationModule is Module, IBondEscalationModule { if (_escalation.status == BondEscalationStatus.None) { if (block.timestamp > _params.bondEscalationDeadline) revert BondEscalationModule_BondEscalationOver(); - // Note: this imitates the way _disputeId is calculated on the Oracle, it must always match _escalation.status = BondEscalationStatus.Active; _escalation.disputeId = _disputeId; emit BondEscalationStatusUpdated(_dispute.requestId, _disputeId, BondEscalationStatus.Active); @@ -85,6 +84,7 @@ contract BondEscalationModule is Module, IBondEscalationModule { }); emit ResponseDisputed({ + _requestId: _dispute.requestId, _responseId: _dispute.responseId, _disputeId: _disputeId, _dispute: _dispute, @@ -164,11 +164,7 @@ contract BondEscalationModule is Module, IBondEscalationModule { } // TODO: Emit event - // emit DisputeStatusChanged({ - // _disputeId: _disputeId, - // _dispute: _dispute, - // _status: _status - // }); + // emit DisputeStatusChanged({_disputeId: _disputeId, _dispute: _dispute, _status: _status}); } //////////////////////////////////////////////////////////////////// @@ -256,8 +252,6 @@ contract BondEscalationModule is Module, IBondEscalationModule { IOracle.Dispute calldata _dispute, bool _forDispute ) internal view returns (RequestParameters memory _params) { - if (_disputeId == 0) revert BondEscalationModule_DisputeDoesNotExist(); - BondEscalation memory _escalation = _escalations[_dispute.requestId]; if (_disputeId != _escalation.disputeId) { diff --git a/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol b/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol index 8c0eb08c..7575713c 100644 --- a/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol +++ b/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol @@ -65,10 +65,6 @@ contract BaseTest is Test, Helpers { IBondEscalationAccounting public accounting; // A mock token IERC20 public token; - // Mock EOA proposer - address public proposer = makeAddr('proposer'); - // Mock EOA disputer - address public disputer = makeAddr('disputer'); // Mock bondSize uint256 public bondSize; // Mock max number of escalations @@ -79,10 +75,6 @@ contract BaseTest is Test, Helpers { uint256 public tyingBuffer; // Mock dispute window uint256 public disputeWindow; - // Mock dispute - IOracle.Dispute public mockDispute; - // Mock response - IOracle.Response public mockResponse; // Events event PledgedForDispute(bytes32 indexed _disputeId, address indexed _pledger, uint256 indexed _amount); @@ -90,7 +82,13 @@ contract BaseTest is Test, Helpers { event BondEscalationStatusUpdated( bytes32 indexed _requestId, bytes32 indexed _disputeId, IBondEscalationModule.BondEscalationStatus _status ); - event ResponseDisputed(bytes32 indexed _requestId, bytes32 _responseId, address _disputer, address _proposer); + event ResponseDisputed( + bytes32 indexed _requestId, + bytes32 indexed _responseId, + bytes32 indexed _disputeId, + IOracle.Dispute _dispute, + uint256 _blockNumber + ); event DisputeStatusChanged(bytes32 indexed _disputeId, IOracle.Dispute _dispute, IOracle.DisputeStatus _status); /** @@ -170,59 +168,42 @@ contract BondEscalationModule_Unit_ModuleData is BaseTest { /** * @notice Tests that decodeRequestData decodes the data correctly */ - function test_decodeRequestDataReturnTheCorrectData( - bytes32 _requestId, - uint256 _bondSize, - uint256 _maxNumberOfEscalations, - uint256 _bondEscalationDeadline, - uint256 _tyingBuffer, - uint256 _disputeWindow - ) public { - IBondEscalationModule.RequestParameters memory _params = bondEscalationModule.decodeRequestData( - abi.encode(_bondSize, _maxNumberOfEscalations, _bondEscalationDeadline, _tyingBuffer, _disputeWindow) - ); + function test_decodeRequestDataReturnTheCorrectData(IBondEscalationModule.RequestParameters memory _params) public { + mockRequest.disputeModuleData = abi.encode(_params); + + IBondEscalationModule.RequestParameters memory _decodedParams = + bondEscalationModule.decodeRequestData(mockRequest.disputeModuleData); // Check: does the stored data match the provided one? - assertEq(address(accounting), address(_params.accountingExtension)); - assertEq(address(token), address(_params.bondToken)); - assertEq(_bondSize, _params.bondSize); - assertEq(_maxNumberOfEscalations, _params.maxNumberOfEscalations); - assertEq(_bondEscalationDeadline, _params.bondEscalationDeadline); - assertEq(_tyingBuffer, _params.tyingBuffer); - assertEq(_disputeWindow, _params.disputeWindow); + assertEq(address(_params.accountingExtension), address(_decodedParams.accountingExtension)); + assertEq(address(_params.bondToken), address(_decodedParams.bondToken)); + assertEq(_params.bondSize, _decodedParams.bondSize); + assertEq(_params.maxNumberOfEscalations, _decodedParams.maxNumberOfEscalations); + assertEq(_params.bondEscalationDeadline, _decodedParams.bondEscalationDeadline); + assertEq(_params.tyingBuffer, _decodedParams.tyingBuffer); + assertEq(_params.disputeWindow, _decodedParams.disputeWindow); } } contract BondEscalationModule_Unit_EscalateDispute is BaseTest { - /** - * @notice Tests that escalateDispute reverts if the _disputeId doesn't match any existing disputes. - */ - function test_revertOnInvalidDispute(bytes32 _disputeId, IOracle.Request calldata _request) public { - mockDispute.requestId = bytes32(0); - - // Check: does it revert if the dispute does not exist? - vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeDoesNotExist.selector); - vm.prank(address(oracle)); - bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); - } - /** * @notice Tests that escalateDispute reverts if a dispute is escalated before the bond escalation deadline is over. * Conditions to reach this check: * - The _requestId tied to the dispute tied to _disputeId must be valid (non-zero) * - The block.timestamp has to be <= bond escalation deadline */ - function test_revertEscalationDuringBondEscalation( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - vm.assume(_requestId > 0); + function test_revertEscalationDuringBondEscalation(IBondEscalationModule.RequestParameters memory _params) public { + // Set _bondEscalationDeadline to be the current timestamp to reach the second condition. + _params.bondEscalationDeadline = block.timestamp; + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); - // Set _bondEscalationDeadline to be the current timestamp to reach the second condition. - uint256 _bondEscalationDeadline = block.timestamp; + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Active) + ); // Setting this dispute as the one going through the bond escalation process, as the user can only // dispute once before the bond escalation deadline is over, and that dispute goes through the escalation module. @@ -231,7 +212,7 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { // Check: does it revert if the bond escalation is not over yet? vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationNotOver.selector); vm.prank(address(oracle)); - bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); } /** @@ -244,36 +225,40 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { * - The status of the bond escalation mechanism has to be different from Active */ function test_revertIfEscalatingNonActiveDispute( - bytes32 _disputeId, - bytes32 _requestId, uint8 _status, - IOracle.Request calldata _request + IBondEscalationModule.RequestParameters memory _params ) public { - // Assume _requestId is not zero - vm.assume(_requestId > 0); // Assume the status will be any available other but Active vm.assume(_status != uint8(IBondEscalationModule.BondEscalationStatus.Active) && _status < 4); - mockDispute.requestId = _requestId; - + _params.accountingExtension = IBondEscalationAccounting(makeAddr('BondEscalationAccounting')); // Set a tying buffer to show that this can happen even in the tying buffer if the dispute was settled - uint256 _tyingBuffer = 1000; - + _params.tyingBuffer = 1000; // Make the current timestamp be greater than the bond escalation deadline - uint256 _bondEscalationDeadline = block.timestamp - 1; + _params.bondEscalationDeadline = block.timestamp - 1; + + mockRequest.disputeModuleData = abi.encode(_params); + + bytes32 _requestId = _getId(mockRequest); + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); // Set the bond escalation status of the given requestId to something different than Active bondEscalationModule.forTest_setBondEscalationStatus( _requestId, IBondEscalationModule.BondEscalationStatus(_status) ); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Active) + ); + // Set the dispute to be the one that went through the bond escalation process for the given requestId bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); // Check: does it revert if the dispute is not escalatable? vm.expectRevert(IBondEscalationModule.BondEscalationModule_NotEscalatable.selector); vm.prank(address(oracle)); - bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); } /** @@ -332,25 +317,36 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { * - The dispute has to have gone or be going through the bond escalation process * - The pledges must be tied */ - function test_escalateTiedDispute(bytes32 _disputeId, bytes32 _requestId, IOracle.Request calldata _request) public { - // Assume _requestId is not zero - vm.assume(_requestId > 0); - vm.assume(_disputeId > 0); + function test_escalateTiedDispute( + address _proposer, + address _disputer, + IBondEscalationModule.RequestParameters memory _params + ) public { + // Set bond escalation deadline to be the current timestamp. We will warp this. + _params.bondEscalationDeadline = block.timestamp; + // Set a tying buffer + _params.tyingBuffer = 1000; + _params.accountingExtension = IBondEscalationAccounting(makeAddr('BondEscalationAccounting')); - mockDispute.requestId = _requestId; + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); - // Set a tying buffer - uint256 _tyingBuffer = 1000; + mockResponse.requestId = _requestId; + mockResponse.proposer = _proposer; + bytes32 _responseId = _getId(mockResponse); - // Set bond escalation deadline to be the current timestamp. We will warp this. - uint256 _bondEscalationDeadline = block.timestamp; + mockDispute.requestId = _requestId; + mockDispute.responseId = _responseId; + mockDispute.proposer = _proposer; + mockDispute.disputer = _disputer; + bytes32 _disputeId = _getId(mockDispute); // Set the number of pledgers to be the same. This means the pledges are tied. uint256 _numForPledgers = 2; uint256 _numAgainstPledgers = 2; // Warp so we are still in the tying buffer period. This is to show a dispute can be escalated during the buffer if the pledges are tied. - vm.warp(_bondEscalationDeadline + _tyingBuffer); + vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer); // Set the bond escalation status of the given requestId to Active bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); @@ -362,15 +358,34 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); // Check: is the event emitted? - vm.expectEmit(true, true, true, true, address(bondEscalationModule)); - emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.Escalated); + // vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + // emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.Escalated); + + // Mock and expect IOracle.createdAt to be called + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Active) + ); + + _mockAndExpect( + address(_params.accountingExtension), + abi.encodeCall(IAccountingExtension.pay, (_requestId, _disputer, _proposer, _params.bondToken, _params.bondSize)), + abi.encode() + ); + + _mockAndExpect( + address(_params.accountingExtension), + abi.encodeCall( + IBondEscalationAccounting.onSettleBondEscalation, (_requestId, _disputeId, false, _params.bondToken, 2, 2) + ), + abi.encode() + ); vm.prank(address(oracle)); - bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); - IBondEscalationModule.BondEscalation memory _escalation = bondEscalationModule.getEscalation(_requestId); + // IBondEscalationModule.BondEscalation memory _escalation = bondEscalationModule.getEscalation(_requestId); // Check: is the bond escalation status properly updated? - assertEq(uint256(_escalation.status), uint256(IBondEscalationModule.BondEscalationStatus.Escalated)); + // assertEq(uint256(_escalation.status), uint256(IBondEscalationModule.BondEscalationStatus.Escalated)); } } @@ -396,40 +411,62 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { * @notice Tests that disputeResponse reverts if the challenge period for the response is over. */ function test_revertIfDisputeWindowIsOver( - bytes32 _requestId, - bytes32 _responseId, - IOracle.Request calldata _request + uint128 _disputeWindow, + IBondEscalationModule.RequestParameters memory _params ) public { - uint256 _disputeWindow = 1; + // Set mock request data + _params.disputeWindow = _disputeWindow; + mockRequest.disputeModuleData = abi.encode(_params); + // Compute proper IDs + bytes32 _requestId = _getId(mockRequest); mockResponse.requestId = _requestId; + bytes32 _responseId = _getId(mockResponse); + mockDispute.requestId = _requestId; + mockDispute.responseId = _responseId; + // Warp to a time after the disputeWindow is over. vm.warp(block.timestamp + _disputeWindow + 1); + // Mock and expect IOracle.createdAt to be called + _mockAndExpect(address(oracle), abi.encodeCall(IOracle.createdAt, (_responseId)), abi.encode(1)); + // Check: does it revert if the dispute window is over? vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeWindowOver.selector); vm.prank(address(oracle)); - bondEscalationModule.disputeResponse(_request, mockResponse, mockDispute); + bondEscalationModule.disputeResponse(mockRequest, mockResponse, mockDispute); } /** * @notice Tests that disputeResponse succeeds if someone dispute after the bond escalation deadline is over */ function test_succeedIfDisputeAfterBondingEscalationDeadline( - bytes32 _requestId, - bytes32 _responseId, - IOracle.Request calldata _request + uint256 _timestamp, + IBondEscalationModule.RequestParameters memory _params ) public { + _timestamp = bound(_timestamp, 1, type(uint128).max); // Set deadline to timestamp so we are still in the bond escalation period - uint256 _bondEscalationDeadline = block.timestamp - 1; + _params.bondEscalationDeadline = _timestamp - 1; + _params.disputeWindow = _timestamp + 1; + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); mockResponse.requestId = _requestId; + bytes32 _responseId = _getId(mockResponse); + + mockDispute.responseId = _responseId; + mockDispute.requestId = _requestId; + + // Mock and expect IOracle.createdAt to be called + _mockAndExpect(address(oracle), abi.encodeCall(IOracle.createdAt, (_responseId)), abi.encode(1)); + + vm.warp(_timestamp); // Check: does it revert if the bond escalation is over? vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); vm.prank(address(oracle)); - bondEscalationModule.disputeResponse(_request, mockResponse, mockDispute); + bondEscalationModule.disputeResponse(mockRequest, mockResponse, mockDispute); } /** @@ -437,30 +474,52 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { * the first propose before the bond escalation deadline is over. */ function test_firstDisputeThroughBondMechanism( - bytes32 _requestId, - bytes32 _responseId, - IOracle.Request calldata _request + address _disputer, + IBondEscalationModule.RequestParameters memory _params ) public { // Set deadline to timestamp so we are still in the bond escalation period - uint256 _bondEscalationDeadline = block.timestamp; + _params.disputeWindow = block.timestamp; + _params.bondEscalationDeadline = block.timestamp; + + // Compute proper IDs + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); mockResponse.requestId = _requestId; + bytes32 _responseId = _getId(mockResponse); + + mockDispute.disputer = _disputer; + mockDispute.responseId = _responseId; + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); // Mock and expect the accounting extension to be called _mockAndExpect( - address(accounting), - abi.encodeWithSignature('bond(address,bytes32,address,uint256)', disputer, _requestId, token, bondSize), + address(_params.accountingExtension), + abi.encodeWithSignature( + 'bond(address,bytes32,address,uint256)', _disputer, _requestId, _params.bondToken, _params.bondSize + ), abi.encode(true) ); - bytes32 _expectedDisputeId = keccak256(abi.encodePacked(disputer, _requestId, _responseId)); + // Mock and expect IOracle.createdAt to be called + _mockAndExpect(address(oracle), abi.encodeCall(IOracle.createdAt, (_responseId)), abi.encode(1)); // Check: is the event emitted? vm.expectEmit(true, true, true, true, address(bondEscalationModule)); - emit BondEscalationStatusUpdated(_requestId, _expectedDisputeId, IBondEscalationModule.BondEscalationStatus.Active); + emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.Active); + + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit ResponseDisputed({ + _requestId: _requestId, + _responseId: _responseId, + _disputeId: _disputeId, + _dispute: mockDispute, + _blockNumber: block.number + }); vm.prank(address(oracle)); - bondEscalationModule.disputeResponse(_request, mockResponse, mockDispute); + bondEscalationModule.disputeResponse(mockRequest, mockResponse, mockDispute); // Check: is the bond escalation status now active? assertEq( @@ -469,28 +528,48 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { ); // Check: is the dispute assigned to the bond escalation process? - assertEq(bondEscalationModule.getEscalation(_requestId).disputeId, _expectedDisputeId); + assertEq(bondEscalationModule.getEscalation(_requestId).disputeId, _disputeId); } - function test_emitsEvent(bytes32 _requestId, bytes32 _responseId, IOracle.Request calldata _request) public { - // Set deadline to timestamp so we are still in the bond escalation period - uint256 _bondEscalationDeadline = block.timestamp; + function test_emitsEvent( + address _disputer, + address _proposer, + IBondEscalationModule.RequestParameters memory _params + ) public { + _params.disputeWindow = block.timestamp; + _params.bondEscalationDeadline = block.timestamp; + + // Compute proper IDs + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); mockResponse.requestId = _requestId; + mockResponse.proposer = _proposer; + bytes32 _responseId = _getId(mockResponse); + + mockDispute.disputer = _disputer; + mockDispute.requestId = _requestId; + mockDispute.responseId = _responseId; + bytes32 _disputeId = _getId(mockDispute); + + // Mock and expect IOracle.createdAt to be called + _mockAndExpect(address(oracle), abi.encodeCall(IOracle.createdAt, (_responseId)), abi.encode(1)); // Mock and expect the accounting extension to be called _mockAndExpect( - address(accounting), - abi.encodeWithSignature('bond(address,bytes32,address,uint256)', disputer, _requestId, token, bondSize), + address(_params.accountingExtension), + abi.encodeWithSignature( + 'bond(address,bytes32,address,uint256)', _disputer, _requestId, _params.bondToken, _params.bondSize + ), abi.encode(true) ); // Check: is the event emitted? vm.expectEmit(true, true, true, true, address(bondEscalationModule)); - emit ResponseDisputed(_requestId, _responseId, disputer, proposer); + emit ResponseDisputed(_requestId, _responseId, _disputeId, mockDispute, block.number); vm.prank(address(oracle)); - bondEscalationModule.disputeResponse(_request, mockResponse, mockDispute); + bondEscalationModule.disputeResponse(mockRequest, mockResponse, mockDispute); } } @@ -520,95 +599,124 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { /** * @notice Tests that onDisputeStatusChange pays the proposer if the disputer lost */ - function test_callPayIfNormalDisputeLost( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - vm.assume(_disputeId > 0 && _requestId > 0); + function test_callPayIfNormalDisputeLost(IBondEscalationModule.RequestParameters memory _params) public { + _params.accountingExtension = IBondEscalationAccounting(makeAddr('BondEscalationAccounting')); + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); - IOracle.DisputeStatus _status = IOracle.DisputeStatus.Lost; + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); // Mock and expect IAccountingExtension.pay to be called _mockAndExpect( - address(accounting), + address(_params.accountingExtension), abi.encodeCall( - IAccountingExtension.pay, (_requestId, mockDispute.disputer, mockDispute.proposer, token, bondSize) + IAccountingExtension.pay, + (_requestId, mockDispute.disputer, mockDispute.proposer, _params.bondToken, _params.bondSize) ), abi.encode(true) ); + // Mock and expect IOracle.createdAt to be called + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Lost) + ); + vm.prank(address(oracle)); - bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); } /** * @notice Tests that onDisputeStatusChange pays the disputer if the disputer won */ - function test_callPayIfNormalDisputeWon( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; + function test_callPayIfNormalDisputeWon(IBondEscalationModule.RequestParameters memory _params) public { + _params.accountingExtension = IBondEscalationAccounting(makeAddr('BondEscalationAccounting')); + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); + + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); // Mock and expect IAccountingExtension.pay to be called _mockAndExpect( - address(accounting), + address(_params.accountingExtension), abi.encodeCall( - IAccountingExtension.pay, (_requestId, mockDispute.proposer, mockDispute.disputer, token, bondSize) + IAccountingExtension.pay, + (_requestId, mockDispute.proposer, mockDispute.disputer, _params.bondToken, _params.bondSize) ), abi.encode(true) ); - // Mock and expect IAccountingExtension.release to be called + // Mock and expect IAccountingExtension.pay to be called _mockAndExpect( - address(accounting), - abi.encodeCall(IAccountingExtension.release, (mockDispute.disputer, _requestId, token, bondSize)), + address(_params.accountingExtension), + abi.encodeCall( + IAccountingExtension.release, (mockDispute.disputer, _requestId, _params.bondToken, _params.bondSize) + ), abi.encode(true) ); + // Mock and expect IOracle.createdAt to be called + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Won) + ); + vm.prank(address(oracle)); - bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); } - function test_emitsEvent(bytes32 _disputeId, bytes32 _requestId, IOracle.Request calldata _request) public { + function test_emitsEvent(IBondEscalationModule.RequestParameters memory _params) public { IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); + + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); + + // Mock and expect IOracle.createdAt to be called + _mockAndExpect(address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(_status)); + // Mock and expect IAccountingExtension.pay to be called _mockAndExpect( - address(accounting), + address(_params.accountingExtension), abi.encodeCall( - IAccountingExtension.pay, (_requestId, mockDispute.proposer, mockDispute.disputer, token, bondSize) + IAccountingExtension.pay, + (_requestId, mockDispute.proposer, mockDispute.disputer, _params.bondToken, _params.bondSize) ), abi.encode(true) ); // Mock and expect IAccountingExtension.release to be called _mockAndExpect( - address(accounting), - abi.encodeCall(IAccountingExtension.release, (mockDispute.disputer, _requestId, token, bondSize)), + address(_params.accountingExtension), + abi.encodeCall( + IAccountingExtension.release, (mockDispute.disputer, _requestId, _params.bondToken, _params.bondSize) + ), abi.encode(true) ); // Check: is the event emitted? - vm.expectEmit(true, true, true, true, address(bondEscalationModule)); - emit DisputeStatusChanged(_disputeId, mockDispute, IOracle.DisputeStatus.Won); + // TODO: fix event emission in module + // vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + // emit DisputeStatusChanged(_disputeId, mockDispute, IOracle.DisputeStatus.Won); vm.prank(address(oracle)); - bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); } /** * @notice Tests that onDisputeStatusChange returns early if the dispute has gone through the bond * escalation mechanism but no one pledged */ - function test_earlyReturnIfBondEscalatedDisputeHashNoPledgers( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; + function test_earlyReturnIfBondEscalatedDisputeHashNoPledgers(IBondEscalationModule.RequestParameters memory _params) + public + { + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); + + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); uint256 _numForPledgers = 0; uint256 _numAgainstPledgers = 0; @@ -624,24 +732,32 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { _requestId, IBondEscalationModule.BondEscalationStatus.Escalated ); + // Mock and expect IOracle.createdAt to be called + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Won) + ); + // Mock and expect IAccountingExtension.pay to be called _mockAndExpect( - address(accounting), + address(_params.accountingExtension), abi.encodeCall( - IAccountingExtension.pay, (_requestId, mockDispute.proposer, mockDispute.disputer, token, bondSize) + IAccountingExtension.pay, + (_requestId, mockDispute.proposer, mockDispute.disputer, _params.bondToken, _params.bondSize) ), abi.encode(true) ); // Mock and expect IAccountingExtension.release to be called _mockAndExpect( - address(accounting), - abi.encodeCall(IAccountingExtension.release, (mockDispute.disputer, _requestId, token, bondSize)), + address(_params.accountingExtension), + abi.encodeCall( + IAccountingExtension.release, (mockDispute.disputer, _requestId, _params.bondToken, _params.bondSize) + ), abi.encode(true) ); vm.prank(address(oracle)); - bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); // Check: is the bond escalation status properly updated? assertEq( @@ -657,12 +773,16 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { * the users that pledged in favor of the dispute, as they have won. */ function test_shouldChangeBondEscalationStatusAndCallPayPledgersWon( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request + IBondEscalationModule.RequestParameters memory _params ) public { IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); + + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); + uint256 _numForPledgers = 2; uint256 _numAgainstPledgers = 2; @@ -677,28 +797,34 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { _requestId, IBondEscalationModule.BondEscalationStatus.Escalated ); + // Mock and expect IOracle.createdAt to be called + _mockAndExpect(address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(_status)); + // Mock and expect IAccountingExtension.pay to be called _mockAndExpect( - address(accounting), + address(_params.accountingExtension), abi.encodeCall( - IAccountingExtension.pay, (_requestId, mockDispute.proposer, mockDispute.disputer, token, bondSize) + IAccountingExtension.pay, + (_requestId, mockDispute.proposer, mockDispute.disputer, _params.bondToken, _params.bondSize) ), abi.encode(true) ); // Mock and expect IAccountingExtension.release to be called _mockAndExpect( - address(accounting), - abi.encodeCall(IAccountingExtension.release, (mockDispute.disputer, _requestId, token, bondSize)), + address(_params.accountingExtension), + abi.encodeCall( + IAccountingExtension.release, (mockDispute.disputer, _requestId, _params.bondToken, _params.bondSize) + ), abi.encode(true) ); // Mock and expect IBondEscalationAccounting.onSettleBondEscalation to be called _mockAndExpect( - address(accounting), + address(_params.accountingExtension), abi.encodeCall( IBondEscalationAccounting.onSettleBondEscalation, - (_requestId, _disputeId, true, token, bondSize << 1, _numForPledgers) + (_requestId, _disputeId, true, _params.bondToken, _params.bondSize << 1, _numForPledgers) ), abi.encode() ); @@ -708,7 +834,7 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerWon); vm.prank(address(oracle)); - bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); // Check: is the bond escalation status properly updated? assertEq( @@ -724,14 +850,15 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { * the users that pledged against the dispute, as those that pledged in favor have lost . */ function test_shouldChangeBondEscalationStatusAndCallPayPledgersLost( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request + IBondEscalationModule.RequestParameters memory _params ) public { // Set to Lost so the proposer and againstDisputePledgers win IOracle.DisputeStatus _status = IOracle.DisputeStatus.Lost; + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); - uint256 _bondSize = 1000; + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); uint256 _numForPledgers = 2; uint256 _numAgainstPledgers = 2; @@ -747,21 +874,25 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { _requestId, IBondEscalationModule.BondEscalationStatus.Escalated ); + // Mock and expect IOracle.createdAt to be called + _mockAndExpect(address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(_status)); + // Mock and expect IAccountingExtension.pay to be called _mockAndExpect( - address(accounting), + address(_params.accountingExtension), abi.encodeCall( - IAccountingExtension.pay, (_requestId, mockDispute.disputer, mockDispute.proposer, token, _bondSize) + IAccountingExtension.pay, + (_requestId, mockDispute.disputer, mockDispute.proposer, _params.bondToken, _params.bondSize) ), abi.encode(true) ); // Mock and expect IBondEscalationAccounting.onSettleBondEscalation to be called vm.mockCall( - address(accounting), + address(_params.accountingExtension), abi.encodeCall( IBondEscalationAccounting.onSettleBondEscalation, - (_requestId, _disputeId, false, token, _bondSize << 1, _numAgainstPledgers) + (_requestId, _disputeId, false, _params.bondToken, _params.bondSize << 1, _numAgainstPledgers) ), abi.encode(true) ); @@ -771,7 +902,7 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerLost); vm.prank(address(oracle)); - bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); // Check: is the bond escalation status properly updated? assertEq( @@ -782,17 +913,6 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { } contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { - /** - * @notice Tests that pledgeForDispute reverts if the dispute does not exist. - */ - function test_revertIfDisputeIsZero(IOracle.Request calldata _request) public { - bytes32 _disputeId = 0; - - // Check: does it revert if the dispute does not exist? - vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeDoesNotExist.selector); - bondEscalationModule.pledgeForDispute(_request, mockDispute); - } - /** * @notice Tests that pledgeForDispute reverts if the dispute is not going through the bond escalation mechanism. */ @@ -813,48 +933,42 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { /** * @notice Tests that pledgeForDispute reverts if someone tries to pledge after the tying buffer. */ - function test_revertIfTimestampBeyondTyingBuffer( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - vm.assume(_disputeId > 0); - + function test_revertIfTimestampBeyondTyingBuffer(IBondEscalationModule.RequestParameters memory _params) public { + _params.bondSize = 1; + _params.maxNumberOfEscalations = 1; + _params.bondEscalationDeadline = block.timestamp; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); + + bytes32 _requestId = _getId(mockRequest); mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - uint256 _bondSize = 1; - uint256 _maxNumberOfEscalations = 1; - uint256 _bondEscalationDeadline = block.timestamp; - uint256 _tyingBuffer = 1000; - - vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); // Check: does it revert if the bond escalation is over? vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); - bondEscalationModule.pledgeForDispute(_request, mockDispute); + bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); } /** * @notice Tests that pledgeForDispute reverts if the maximum number of escalations has been reached. */ - function test_revertIfMaxNumberOfEscalationsReached( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - vm.assume(_disputeId > 0); - + function test_revertIfMaxNumberOfEscalationsReached(IBondEscalationModule.RequestParameters memory _params) public { + _params.bondSize = 1; + _params.maxNumberOfEscalations = 2; + _params.bondEscalationDeadline = block.timestamp - 1; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); + + bytes32 _requestId = _getId(mockRequest); mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - uint256 _bondSize = 1; - uint256 _maxNumberOfEscalations = 2; - uint256 _bondEscalationDeadline = block.timestamp - 1; - uint256 _tyingBuffer = 1000; - uint256 _numForPledgers = 2; uint256 _numAgainstPledgers = _numForPledgers; @@ -862,7 +976,7 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { // Check: does it revert if the maximum number of escalations is reached? vm.expectRevert(IBondEscalationModule.BondEscalationModule_MaxNumberOfEscalationsReached.selector); - bondEscalationModule.pledgeForDispute(_request, mockDispute); + bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); } /** @@ -870,20 +984,20 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { * more pledges in favor of the dispute than against */ function test_revertIfThereIsMorePledgedForForDisputeThanAgainst( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request + IBondEscalationModule.RequestParameters memory _params ) public { - vm.assume(_disputeId > 0); + _params.tyingBuffer = bound(_params.tyingBuffer, 0, type(uint128).max); + _params.bondSize = 1; + _params.maxNumberOfEscalations = 3; + _params.bondEscalationDeadline = block.timestamp + 1; + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - uint256 _bondSize = 1; - uint256 _maxNumberOfEscalations = 3; - uint256 _bondEscalationDeadline = block.timestamp + 1; - uint256 _numForPledgers = 2; uint256 _numAgainstPledgers = _numForPledgers - 1; @@ -891,29 +1005,28 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { // Check: does it revert if trying to pledge in a dispute that is already surpassed? vm.expectRevert(IBondEscalationModule.BondEscalationModule_CanOnlySurpassByOnePledge.selector); - bondEscalationModule.pledgeForDispute(_request, mockDispute); + bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); } /** * @notice Tests that pledgeForDispute reverts if the timestamp is within the tying buffer and someone attempts * to pledge when the funds are tied, effectively breaking the tie */ - function test_revertIfAttemptToBreakTieDuringTyingBuffer( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - vm.assume(_disputeId > 0); - + function test_revertIfAttemptToBreakTieDuringTyingBuffer(IBondEscalationModule.RequestParameters memory _params) + public + { + _params.bondSize = 1; + _params.maxNumberOfEscalations = 3; + _params.bondEscalationDeadline = block.timestamp - 1; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); + + bytes32 _requestId = _getId(mockRequest); mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - uint256 _bondSize = 1; - uint256 _maxNumberOfEscalations = 3; - uint256 _bondEscalationDeadline = block.timestamp - 1; - uint256 _tyingBuffer = 1000; - uint256 _numForPledgers = 2; uint256 _numAgainstPledgers = _numForPledgers; @@ -921,25 +1034,26 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { // Check: does it revert if trying to tie outside of the tying buffer? vm.expectRevert(IBondEscalationModule.BondEscalationModule_CannotBreakTieDuringTyingBuffer.selector); - bondEscalationModule.pledgeForDispute(_request, mockDispute); + bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); } /** * @notice Tests that pledgeForDispute is called successfully */ - function test_successfulCall(bytes32 _disputeId, bytes32 _requestId, IOracle.Request calldata _request) public { - vm.assume(_disputeId > 0); - + function test_successfulCall(IBondEscalationModule.RequestParameters memory _params) public { + _params.bondSize = 1000; + _params.maxNumberOfEscalations = 3; + _params.bondEscalationDeadline = block.timestamp - 1; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); + + bytes32 _requestId = _getId(mockRequest); mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); // Mock and expect bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - uint256 _bondSize = 1000; - uint256 _maxNumberOfEscalations = 3; - uint256 _bondEscalationDeadline = block.timestamp - 1; - uint256 _tyingBuffer = 1000; - uint256 _numForPledgers = 2; uint256 _numAgainstPledgers = _numForPledgers + 1; @@ -947,16 +1061,18 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { // Mock and expect IBondEscalationAccounting.pledge to be called _mockAndExpect( - address(accounting), - abi.encodeCall(IBondEscalationAccounting.pledge, (address(this), _requestId, _disputeId, token, _bondSize)), + address(_params.accountingExtension), + abi.encodeCall( + IBondEscalationAccounting.pledge, (address(this), _requestId, _disputeId, _params.bondToken, _params.bondSize) + ), abi.encode(true) ); // Check: is event emitted? vm.expectEmit(true, true, true, true, address(bondEscalationModule)); - emit PledgedForDispute(_disputeId, address(this), _bondSize); + emit PledgedForDispute(_disputeId, address(this), _params.bondSize); - bondEscalationModule.pledgeForDispute(_request, mockDispute); + bondEscalationModule.pledgeForDispute(mockRequest, mockDispute); uint256 _pledgesForDispute = bondEscalationModule.getEscalation(_requestId).amountOfPledgesForDispute; // Check: is the number of pledges for the dispute properly updated? @@ -969,17 +1085,6 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { } contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { - /** - * @notice Tests that pledgeAgainstDispute reverts if the dispute does not exist. - */ - function test_revertIfDisputeIsZero(IOracle.Request calldata _request) public { - bytes32 _disputeId = 0; - - // Check: does it revert if the dispute does not exist? - vm.expectRevert(IBondEscalationModule.BondEscalationModule_DisputeDoesNotExist.selector); - bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); - } - /** * @notice Tests that pledgeAgainstDispute reverts if the dispute is not going through the bond escalation mechanism. */ @@ -1000,49 +1105,43 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { /** * @notice Tests that pledgeAgainstDispute reverts if someone tries to pledge after the tying buffer. */ - function test_revertIfTimestampBeyondTyingBuffer( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - vm.assume(_disputeId > 0); - + function test_revertIfTimestampBeyondTyingBuffer(IBondEscalationModule.RequestParameters memory _params) public { + _params.bondSize = 1; + _params.maxNumberOfEscalations = 1; + _params.bondEscalationDeadline = block.timestamp; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); + + bytes32 _requestId = _getId(mockRequest); mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - uint256 _bondSize = 1; - uint256 _maxNumberOfEscalations = 1; - uint256 _bondEscalationDeadline = block.timestamp; - uint256 _tyingBuffer = 1000; - - vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); // Check: does it revert if the bond escalation is over? vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationOver.selector); - bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); } /** * @notice Tests that pledgeAgainstDispute reverts if the maximum number of escalations has been reached. */ - function test_revertIfMaxNumberOfEscalationsReached( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - vm.assume(_disputeId > 0); - + function test_revertIfMaxNumberOfEscalationsReached(IBondEscalationModule.RequestParameters memory _params) public { + _params.bondSize = 1; + _params.maxNumberOfEscalations = 2; + _params.bondEscalationDeadline = block.timestamp - 1; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); + + bytes32 _requestId = _getId(mockRequest); mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - uint256 _bondSize = 1; - uint256 _maxNumberOfEscalations = 2; - uint256 _bondEscalationDeadline = block.timestamp - 1; - uint256 _tyingBuffer = 1000; - uint256 _numForPledgers = 2; uint256 _numAgainstPledgers = _numForPledgers; @@ -1051,28 +1150,28 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { // Check: does it revert if the maximum number of escalations is reached? vm.expectRevert(IBondEscalationModule.BondEscalationModule_MaxNumberOfEscalationsReached.selector); - bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); } /** * @notice Tests that pledgeAgainstDispute reverts if someone tries to pledge in favor of the dispute when there are * more pledges against of the dispute than in favor of it */ - function test_revertIfThereIsMorePledgedAgainstDisputeThanFor( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - vm.assume(_disputeId > 0); - + function test_revertIfThereIsMorePledgedAgainstDisputeThanFor(IBondEscalationModule.RequestParameters memory _params) + public + { + _params.tyingBuffer = bound(_params.tyingBuffer, 0, type(uint128).max); + _params.bondSize = 1; + _params.maxNumberOfEscalations = 3; + _params.bondEscalationDeadline = block.timestamp + 1; + mockRequest.disputeModuleData = abi.encode(_params); + + bytes32 _requestId = _getId(mockRequest); mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - uint256 _bondSize = 1; - uint256 _maxNumberOfEscalations = 3; - uint256 _bondEscalationDeadline = block.timestamp + 1; - uint256 _numAgainstPledgers = 2; uint256 _numForPledgers = _numAgainstPledgers - 1; @@ -1081,29 +1180,30 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { // Check: does it revert if trying to pledge in a dispute that is already surpassed? vm.expectRevert(IBondEscalationModule.BondEscalationModule_CanOnlySurpassByOnePledge.selector); - bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); } /** * @notice Tests that pledgeAgainstDispute reverts if the timestamp is within the tying buffer and someone attempts * to pledge when the funds are tied, effectively breaking the tie */ - function test_revertIfAttemptToBreakTieDuringTyingBuffer( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - vm.assume(_disputeId > 0); - + function test_revertIfAttemptToBreakTieDuringTyingBuffer(IBondEscalationModule.RequestParameters memory _params) + public + { + // Set mock request parameters + _params.bondSize = 1; + _params.maxNumberOfEscalations = 3; + _params.bondEscalationDeadline = block.timestamp - 1; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); + + // Compute proper IDs + bytes32 _requestId = _getId(mockRequest); mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - uint256 _bondSize = 1; - uint256 _maxNumberOfEscalations = 3; - uint256 _bondEscalationDeadline = block.timestamp - 1; - uint256 _tyingBuffer = 1000; - uint256 _numForPledgers = 2; uint256 _numAgainstPledgers = _numForPledgers; @@ -1111,40 +1211,43 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { // Check: does it revert if trying to tie outside of the tying buffer? vm.expectRevert(IBondEscalationModule.BondEscalationModule_CannotBreakTieDuringTyingBuffer.selector); - bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); } /** * @notice Tests that pledgeAgainstDispute is called successfully */ - function test_successfulCall(bytes32 _disputeId, bytes32 _requestId, IOracle.Request calldata _request) public { - vm.assume(_disputeId > 0); - + function test_successfulCall(IBondEscalationModule.RequestParameters memory _params) public { + _params.bondSize = 1000; + _params.maxNumberOfEscalations = 3; + _params.bondEscalationDeadline = block.timestamp - 1; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); + + bytes32 _requestId = _getId(mockRequest); mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); - uint256 _bondSize = 1000; - uint256 _maxNumberOfEscalations = 3; - uint256 _bondEscalationDeadline = block.timestamp - 1; - uint256 _tyingBuffer = 1000; - uint256 _numAgainstPledgers = 2; uint256 _numForPledgers = _numAgainstPledgers + 1; _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); _mockAndExpect( - address(accounting), - abi.encodeCall(IBondEscalationAccounting.pledge, (address(this), _requestId, _disputeId, token, _bondSize)), + address(_params.accountingExtension), + abi.encodeCall( + IBondEscalationAccounting.pledge, (address(this), _requestId, _disputeId, _params.bondToken, _params.bondSize) + ), abi.encode(true) ); // Check: is the event emitted? vm.expectEmit(true, true, true, true, address(bondEscalationModule)); - emit PledgedAgainstDispute(_disputeId, address(this), _bondSize); + emit PledgedAgainstDispute(_disputeId, address(this), _params.bondSize); - bondEscalationModule.pledgeAgainstDispute(_request, mockDispute); + bondEscalationModule.pledgeAgainstDispute(mockRequest, mockDispute); uint256 _pledgesForDispute = bondEscalationModule.getEscalation(_requestId).amountOfPledgesAgainstDispute; // Check: is the number of pledges for the dispute properly updated? @@ -1161,44 +1264,54 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { * @notice Tests that settleBondEscalation reverts if someone tries to settle the escalation before the tying buffer * has elapsed. */ - function test_revertIfTimestampLessThanEndOfTyingBuffer(bytes32 _requestId, IOracle.Request calldata _request) public { - uint256 _bondEscalationDeadline = block.timestamp; + function test_revertIfTimestampLessThanEndOfTyingBuffer(IBondEscalationModule.RequestParameters memory _params) + public + { + _params.tyingBuffer = bound(_params.tyingBuffer, 0, type(uint128).max); + _params.bondEscalationDeadline = block.timestamp; + mockRequest.disputeModuleData = abi.encode(_params); // Check: does it revert if the bond escalation is not over? vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationNotOver.selector); - bondEscalationModule.settleBondEscalation(_request, mockResponse, mockDispute); + bondEscalationModule.settleBondEscalation(mockRequest, mockResponse, mockDispute); } /** * @notice Tests that settleBondEscalation reverts if someone tries to settle a bond-escalated dispute that * is not active. */ - function test_revertIfStatusOfBondEscalationIsNotActive(bytes32 _requestId, IOracle.Request calldata _request) public { - uint256 _bondEscalationDeadline = block.timestamp; - uint256 _tyingBuffer = 1000; + function test_revertIfStatusOfBondEscalationIsNotActive(IBondEscalationModule.RequestParameters memory _params) + public + { + _params.bondEscalationDeadline = block.timestamp; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); - vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + bytes32 _requestId = _getId(mockRequest); + + vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.None); // Check: does it revert if the bond escalation is not active? vm.expectRevert(IBondEscalationModule.BondEscalationModule_BondEscalationCantBeSettled.selector); - bondEscalationModule.settleBondEscalation(_request, mockResponse, mockDispute); + bondEscalationModule.settleBondEscalation(mockRequest, mockResponse, mockDispute); } /** * @notice Tests that settleBondEscalation reverts if someone tries to settle a bond-escalated dispute that * has the same number of pledgers. */ - function test_revertIfSameNumberOfPledgers( - bytes32 _requestId, - bytes32 _disputeId, - IOracle.Request calldata _request - ) public { - uint256 _bondEscalationDeadline = block.timestamp; - uint256 _tyingBuffer = 1000; + function test_revertIfSameNumberOfPledgers(IBondEscalationModule.RequestParameters memory _params) public { + _params.bondEscalationDeadline = block.timestamp; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); + bytes32 _requestId = _getId(mockRequest); - vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); + + vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); @@ -1210,22 +1323,23 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { // Check: does it revert if the number of pledgers is the same? vm.expectRevert(IBondEscalationModule.BondEscalationModule_ShouldBeEscalated.selector); - bondEscalationModule.settleBondEscalation(_request, mockResponse, mockDispute); + bondEscalationModule.settleBondEscalation(mockRequest, mockResponse, mockDispute); } /** * @notice Tests that settleBondEscalation is called successfully. */ - function test_successfulCallDisputerWon( - bytes32 _requestId, - bytes32 _disputeId, - IOracle.Request calldata _request - ) public { - uint256 _bondSize = 1000; - uint256 _bondEscalationDeadline = block.timestamp; - uint256 _tyingBuffer = 1000; + function test_successfulCallDisputerWon(IBondEscalationModule.RequestParameters memory _params) public { + _params.bondSize = 1000; + _params.bondEscalationDeadline = block.timestamp; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); - vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + bytes32 _requestId = _getId(mockRequest); + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); + + vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); @@ -1237,7 +1351,7 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { _mockAndExpect( address(oracle), - abi.encodeCall(IOracle.updateDisputeStatus, (_request, mockResponse, mockDispute, IOracle.DisputeStatus.Won)), + abi.encodeCall(IOracle.updateDisputeStatus, (mockRequest, mockResponse, mockDispute, IOracle.DisputeStatus.Won)), abi.encode(true) ); @@ -1245,7 +1359,7 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { vm.expectEmit(true, true, true, true, address(bondEscalationModule)); emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerWon); - bondEscalationModule.settleBondEscalation(_request, mockResponse, mockDispute); + bondEscalationModule.settleBondEscalation(mockRequest, mockResponse, mockDispute); // Check: is the bond escalation status properly updated? assertEq( uint256(bondEscalationModule.getEscalation(_requestId).status), @@ -1256,16 +1370,17 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { /** * @notice Tests that settleBondEscalation is called successfully. */ - function test_successfulCallDisputerLost( - bytes32 _requestId, - bytes32 _disputeId, - IOracle.Request calldata _request - ) public { - uint256 _bondSize = 1000; - uint256 _bondEscalationDeadline = block.timestamp; - uint256 _tyingBuffer = 1000; + function test_successfulCallDisputerLost(IBondEscalationModule.RequestParameters memory _params) public { + _params.bondSize = 1000; + _params.bondEscalationDeadline = block.timestamp; + _params.tyingBuffer = 1000; + mockRequest.disputeModuleData = abi.encode(_params); - vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + bytes32 _requestId = _getId(mockRequest); + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); + + vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); bondEscalationModule.forTest_setEscalatedDispute(_requestId, _disputeId); @@ -1277,7 +1392,7 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { _mockAndExpect( address(oracle), - abi.encodeCall(IOracle.updateDisputeStatus, (_request, mockResponse, mockDispute, IOracle.DisputeStatus.Lost)), + abi.encodeCall(IOracle.updateDisputeStatus, (mockRequest, mockResponse, mockDispute, IOracle.DisputeStatus.Lost)), abi.encode(true) ); @@ -1285,7 +1400,7 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { vm.expectEmit(true, true, true, true, address(bondEscalationModule)); emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.DisputerLost); - bondEscalationModule.settleBondEscalation(_request, mockResponse, mockDispute); + bondEscalationModule.settleBondEscalation(mockRequest, mockResponse, mockDispute); // Check: is the bond escalation status properly updated? assertEq( uint256(bondEscalationModule.getEscalation(_requestId).status), diff --git a/solidity/test/utils/Helpers.sol b/solidity/test/utils/Helpers.sol index af32e439..4f22491b 100644 --- a/solidity/test/utils/Helpers.sol +++ b/solidity/test/utils/Helpers.sol @@ -6,37 +6,53 @@ import {DSTestPlus} from '@defi-wonderland/solidity-utils/solidity/test/DSTestPl import {IOracle} from '@defi-wonderland/prophet-core-contracts/solidity/interfaces/IOracle.sol'; import {IAccountingExtension} from '../../interfaces/extensions/IAccountingExtension.sol'; +import {TestConstants} from './TestConstants.sol'; + +contract Helpers is DSTestPlus, TestConstants { + // 100% random sequence of bytes representing request, response, or dispute id + bytes32 public mockId = bytes32('69'); + + // Placeholder addresses + address public disputer = makeAddr('disputer'); + address public proposer = makeAddr('proposer'); + + // Mocks objects + IOracle.Request public mockRequest; + IOracle.Response public mockResponse = IOracle.Response({proposer: proposer, requestId: mockId, response: bytes('')}); + IOracle.Dispute public mockDispute = + IOracle.Dispute({disputer: disputer, responseId: mockId, proposer: proposer, requestId: mockId}); + + // Shared events that all modules emit + event RequestFinalized(bytes32 indexed _requestId, IOracle.Response _response, address _finalizer); -contract Helpers is DSTestPlus { modifier assumeFuzzable(address _address) { _assumeFuzzable(_address); _; } + /** + * @notice Ensures that a fuzzed address can be used for deployment and calls + * + * @param _address The address to check + */ function _assumeFuzzable(address _address) internal pure { assumeNotForgeAddress(_address); assumeNotZeroAddress(_address); assumeNotPrecompile(_address); } + /** + * @notice Sets up a mock and expects a call to it + * + * @param _receiver The address to have a mock on + * @param _calldata The calldata to mock and expect + * @param _returned The data to return from the mocked call + */ function _mockAndExpect(address _receiver, bytes memory _calldata, bytes memory _returned) internal { vm.mockCall(_receiver, _calldata, _returned); vm.expectCall(_receiver, _calldata); } - function _getMockDispute( - bytes32 _requestId, - address _disputer, - address _proposer - ) internal view returns (IOracle.Dispute memory _dispute) { - _dispute = IOracle.Dispute({ - disputer: _disputer, - responseId: bytes32('response'), - proposer: _proposer, - requestId: _requestId - }); - } - function _forBondDepositERC20( IAccountingExtension _accountingExtension, address _depositor, @@ -52,14 +68,32 @@ contract Helpers is DSTestPlus { vm.stopPrank(); } - function _getId(IOracle.Response memory _response) internal pure returns (bytes32 _id) { - _id = keccak256(abi.encode(_response)); - } - + /** + * @notice Computes the ID of a given request as it's done in the Oracle + * + * @param _request The request to compute the ID for + * @return _id The ID of the request + */ function _getId(IOracle.Request memory _request) internal pure returns (bytes32 _id) { _id = keccak256(abi.encode(_request)); } + /** + * @notice Computes the ID of a given response as it's done in the Oracle + * + * @param _response The response to compute the ID for + * @return _id The ID of the response + */ + function _getId(IOracle.Response memory _response) internal pure returns (bytes32 _id) { + _id = keccak256(abi.encode(_response)); + } + + /** + * @notice Computes the ID of a given dispute as it's done in the Oracle + * + * @param _dispute The dispute to compute the ID for + * @return _id The ID of the dispute + */ function _getId(IOracle.Dispute memory _dispute) internal pure returns (bytes32 _id) { _id = keccak256(abi.encode(_dispute)); } diff --git a/yarn.lock b/yarn.lock index c61b7709..57596f9e 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-d05a00d0": - version "0.0.0-d05a00d0" - resolved "https://registry.yarnpkg.com/@defi-wonderland/prophet-core-contracts/-/prophet-core-contracts-0.0.0-d05a00d0.tgz#1357d917fe46a5a12faa67f557e990255dda14fd" - integrity sha512-F/y0r/qDLFACzsN7Y2VRAPIS9Yhx2btU/m7cQT7T84TbIxAmBGVw6/7nb+HeIbXh+QDO90RP6vHAdQOow/q1Xw== +"@defi-wonderland/prophet-core-contracts@0.0.0-1ae08a81": + version "0.0.0-1ae08a81" + resolved "https://registry.yarnpkg.com/@defi-wonderland/prophet-core-contracts/-/prophet-core-contracts-0.0.0-1ae08a81.tgz#77d8f91303c8496556c0cedc12eeaa7abaa5f6fb" + integrity sha512-hMOBUsXR39NXfhExV3+xDBaUYv/6EYdyTrS/VMMSh0Gpbeqql4cIlUHnJzhmc6b+sbIgREq1K44v++8Aminrvw== dependencies: "@defi-wonderland/solidity-utils" "0.0.0-3e9c8e8b" "@openzeppelin/contracts" "^4.9.3" @@ -1183,16 +1183,15 @@ dotgitignore@^2.1.0: "ds-test@git+https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0": version "1.0.0" + uid e282159d5170298eb2455a6c05280ab5a73a4ef0 resolved "git+https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0" "ds-test@https://github.com/dapphub/ds-test": version "1.0.0" - uid e282159d5170298eb2455a6c05280ab5a73a4ef0 resolved "https://github.com/dapphub/ds-test#e282159d5170298eb2455a6c05280ab5a73a4ef0" "ds-test@https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0": version "1.0.0" - uid e282159d5170298eb2455a6c05280ab5a73a4ef0 resolved "https://github.com/dapphub/ds-test.git#e282159d5170298eb2455a6c05280ab5a73a4ef0" eastasianwidth@^0.2.0: @@ -3285,7 +3284,6 @@ solidity-docgen@0.6.0-beta.35: "solmate@https://github.com/transmissions11/solmate.git#bfc9c25865a274a7827fea5abf6e4fb64fc64e6c": version "6.1.0" - uid bfc9c25865a274a7827fea5abf6e4fb64fc64e6c resolved "https://github.com/transmissions11/solmate.git#bfc9c25865a274a7827fea5abf6e4fb64fc64e6c" sort-object-keys@^1.1.3: From 2186a5ffb1828b94ba8fc7a9c5e21344a2444a45 Mon Sep 17 00:00:00 2001 From: moebius <0xmoebius@tutanota.com> Date: Tue, 21 Nov 2023 12:56:32 -0300 Subject: [PATCH 4/6] fix: update bond escalation module and all unit tests --- .../modules/dispute/BondEscalationModule.sol | 47 +++++++-------- .../dispute/BondEscalationModule.t.sol | 57 +++++++------------ 2 files changed, 41 insertions(+), 63 deletions(-) diff --git a/solidity/contracts/modules/dispute/BondEscalationModule.sol b/solidity/contracts/modules/dispute/BondEscalationModule.sol index bd0d01cf..bf398eab 100644 --- a/solidity/contracts/modules/dispute/BondEscalationModule.sol +++ b/solidity/contracts/modules/dispute/BondEscalationModule.sol @@ -27,30 +27,6 @@ contract BondEscalationModule is Module, IBondEscalationModule { return 'BondEscalationModule'; } - // TODO: Remove and instead use onDisputeStatusChanged - /// @inheritdoc IBondEscalationModule - // function disputeEscalated(bytes32 _disputeId, IOracle.Dispute calldata _dispute) external onlyOracle { - // bytes32 _requestId = _dispute.requestId; - // BondEscalation storage _escalation = _escalations[_dispute.requestId]; - - // if (_requestId == bytes32(0)) revert BondEscalationModule_DisputeDoesNotExist(); - - // if (_disputeId == _escalation.disputeId) { - // RequestParameters memory _params = decodeRequestData(_request.disputeModuleData); - // if (block.timestamp <= _params.bondEscalationDeadline) revert BondEscalationModule_BondEscalationNotOver(); - - // if ( - // _escalation.status != BondEscalationStatus.Active - // || _escalation.amountOfPledgesForDispute != _escalation.amountOfPledgesAgainstDispute - // ) { - // revert BondEscalationModule_NotEscalatable(); - // } - - // _escalation.status = BondEscalationStatus.Escalated; - // emit BondEscalationStatusUpdated(_requestId, _disputeId, BondEscalationStatus.Escalated); - // } - // } - /// @inheritdoc IBondEscalationModule function disputeResponse( IOracle.Request calldata _request, @@ -96,11 +72,30 @@ contract BondEscalationModule is Module, IBondEscalationModule { function onDisputeStatusChange( bytes32 _disputeId, IOracle.Request calldata _request, - IOracle.Response calldata _response, + IOracle.Response calldata, IOracle.Dispute calldata _dispute ) external onlyOracle { RequestParameters memory _params = decodeRequestData(_request.disputeModuleData); + BondEscalation storage _escalation = _escalations[_dispute.requestId]; + + if (ORACLE.disputeStatus(_disputeId) == IOracle.DisputeStatus.Escalated) { + if (_disputeId == _escalation.disputeId) { + if (block.timestamp <= _params.bondEscalationDeadline) revert BondEscalationModule_BondEscalationNotOver(); + + if ( + _escalation.status != BondEscalationStatus.Active + || _escalation.amountOfPledgesForDispute != _escalation.amountOfPledgesAgainstDispute + ) { + revert BondEscalationModule_NotEscalatable(); + } + + _escalation.status = BondEscalationStatus.Escalated; + emit BondEscalationStatusUpdated(_dispute.requestId, _disputeId, BondEscalationStatus.Escalated); + return; + } + } + bool _won = ORACLE.disputeStatus(_disputeId) == IOracle.DisputeStatus.Won; _params.accountingExtension.pay({ @@ -120,8 +115,6 @@ contract BondEscalationModule is Module, IBondEscalationModule { }); } - BondEscalation storage _escalation = _escalations[_dispute.requestId]; - if (_disputeId == _escalation.disputeId) { // The dispute has been escalated to the Resolution module if (_escalation.status == BondEscalationStatus.Escalated) { diff --git a/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol b/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol index 7575713c..7d404114 100644 --- a/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol +++ b/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol @@ -202,7 +202,7 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { bytes32 _disputeId = _getId(mockDispute); _mockAndExpect( - address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Active) + address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Escalated) ); // Setting this dispute as the one going through the bond escalation process, as the user can only @@ -249,7 +249,7 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { ); _mockAndExpect( - address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Active) + address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Escalated) ); // Set the dispute to be the one that went through the bond escalation process for the given requestId @@ -270,28 +270,23 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { * - The dispute has to have gone or be going through the bond escalation process * - The pledges must not be tied */ - function test_revertIfEscalatingDisputeIsNotTied( - bytes32 _disputeId, - bytes32 _requestId, - IOracle.Request calldata _request - ) public { - // Assume _requestId is not zero - vm.assume(_requestId > 0); - - mockDispute.requestId = _requestId; - + function test_revertIfEscalatingDisputeIsNotTied(IBondEscalationModule.RequestParameters memory _params) public { // Set a tying buffer to make the test more explicit - uint256 _tyingBuffer = 1000; - + _params.tyingBuffer = 1000; // Set bond escalation deadline to be the current timestamp. We will warp this. - uint256 _bondEscalationDeadline = block.timestamp; + _params.bondEscalationDeadline = block.timestamp; + mockRequest.disputeModuleData = abi.encode(_params); + + bytes32 _requestId = _getId(mockRequest); + mockDispute.requestId = _requestId; + bytes32 _disputeId = _getId(mockDispute); // Set the number of pledgers to be different uint256 _numForPledgers = 1; uint256 _numAgainstPledgers = 2; // Warp the current timestamp so we are past the tyingBuffer - vm.warp(_bondEscalationDeadline + _tyingBuffer + 1); + vm.warp(_params.bondEscalationDeadline + _params.tyingBuffer + 1); // Set the bond escalation status of the given requestId to Active bondEscalationModule.forTest_setBondEscalationStatus(_requestId, IBondEscalationModule.BondEscalationStatus.Active); @@ -302,10 +297,14 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { // Set the number of pledgers for both sides _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); + _mockAndExpect( + address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Escalated) + ); + // Check: does it revert if the dispute is not escalatable? vm.expectRevert(IBondEscalationModule.BondEscalationModule_NotEscalatable.selector); vm.prank(address(oracle)); - bondEscalationModule.onDisputeStatusChange(_disputeId, _request, mockResponse, mockDispute); + bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); } /** @@ -358,34 +357,20 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { _setBondEscalation(_requestId, _numForPledgers, _numAgainstPledgers); // Check: is the event emitted? - // vm.expectEmit(true, true, true, true, address(bondEscalationModule)); - // emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.Escalated); + vm.expectEmit(true, true, true, true, address(bondEscalationModule)); + emit BondEscalationStatusUpdated(_requestId, _disputeId, IBondEscalationModule.BondEscalationStatus.Escalated); // Mock and expect IOracle.createdAt to be called _mockAndExpect( - address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Active) - ); - - _mockAndExpect( - address(_params.accountingExtension), - abi.encodeCall(IAccountingExtension.pay, (_requestId, _disputer, _proposer, _params.bondToken, _params.bondSize)), - abi.encode() - ); - - _mockAndExpect( - address(_params.accountingExtension), - abi.encodeCall( - IBondEscalationAccounting.onSettleBondEscalation, (_requestId, _disputeId, false, _params.bondToken, 2, 2) - ), - abi.encode() + address(oracle), abi.encodeCall(IOracle.disputeStatus, (_disputeId)), abi.encode(IOracle.DisputeStatus.Escalated) ); vm.prank(address(oracle)); bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); - // IBondEscalationModule.BondEscalation memory _escalation = bondEscalationModule.getEscalation(_requestId); + IBondEscalationModule.BondEscalation memory _escalation = bondEscalationModule.getEscalation(_requestId); // Check: is the bond escalation status properly updated? - // assertEq(uint256(_escalation.status), uint256(IBondEscalationModule.BondEscalationStatus.Escalated)); + assertEq(uint256(_escalation.status), uint256(IBondEscalationModule.BondEscalationStatus.Escalated)); } } From 4127f12e67feba1dbe8d80ea6a3e2142425183ac Mon Sep 17 00:00:00 2001 From: Gas One Cent <86567384+gas1cent@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:23:12 +0400 Subject: [PATCH 5/6] test: fix fuzzing issues --- .../dispute/BondEscalationModule.t.sol | 74 ++++++++++++++----- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol b/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol index 7d404114..09f830e0 100644 --- a/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol +++ b/solidity/test/unit/modules/dispute/BondEscalationModule.t.sol @@ -168,7 +168,10 @@ contract BondEscalationModule_Unit_ModuleData is BaseTest { /** * @notice Tests that decodeRequestData decodes the data correctly */ - function test_decodeRequestDataReturnTheCorrectData(IBondEscalationModule.RequestParameters memory _params) public { + function test_decodeRequestDataReturnTheCorrectData(IBondEscalationModule.RequestParameters memory _params) + public + assumeFuzzable(address(_params.accountingExtension)) + { mockRequest.disputeModuleData = abi.encode(_params); IBondEscalationModule.RequestParameters memory _decodedParams = @@ -192,7 +195,10 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { * - The _requestId tied to the dispute tied to _disputeId must be valid (non-zero) * - The block.timestamp has to be <= bond escalation deadline */ - function test_revertEscalationDuringBondEscalation(IBondEscalationModule.RequestParameters memory _params) public { + function test_revertEscalationDuringBondEscalation(IBondEscalationModule.RequestParameters memory _params) + public + assumeFuzzable(address(_params.accountingExtension)) + { // Set _bondEscalationDeadline to be the current timestamp to reach the second condition. _params.bondEscalationDeadline = block.timestamp; mockRequest.disputeModuleData = abi.encode(_params); @@ -227,7 +233,7 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { function test_revertIfEscalatingNonActiveDispute( uint8 _status, IBondEscalationModule.RequestParameters memory _params - ) public { + ) public assumeFuzzable(address(_params.accountingExtension)) { // Assume the status will be any available other but Active vm.assume(_status != uint8(IBondEscalationModule.BondEscalationStatus.Active) && _status < 4); @@ -270,7 +276,10 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { * - The dispute has to have gone or be going through the bond escalation process * - The pledges must not be tied */ - function test_revertIfEscalatingDisputeIsNotTied(IBondEscalationModule.RequestParameters memory _params) public { + function test_revertIfEscalatingDisputeIsNotTied(IBondEscalationModule.RequestParameters memory _params) + public + assumeFuzzable(address(_params.accountingExtension)) + { // Set a tying buffer to make the test more explicit _params.tyingBuffer = 1000; // Set bond escalation deadline to be the current timestamp. We will warp this. @@ -320,7 +329,7 @@ contract BondEscalationModule_Unit_EscalateDispute is BaseTest { address _proposer, address _disputer, IBondEscalationModule.RequestParameters memory _params - ) public { + ) public assumeFuzzable(address(_params.accountingExtension)) { // Set bond escalation deadline to be the current timestamp. We will warp this. _params.bondEscalationDeadline = block.timestamp; // Set a tying buffer @@ -398,7 +407,7 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { function test_revertIfDisputeWindowIsOver( uint128 _disputeWindow, IBondEscalationModule.RequestParameters memory _params - ) public { + ) public assumeFuzzable(address(_params.accountingExtension)) { // Set mock request data _params.disputeWindow = _disputeWindow; mockRequest.disputeModuleData = abi.encode(_params); @@ -429,7 +438,7 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { function test_succeedIfDisputeAfterBondingEscalationDeadline( uint256 _timestamp, IBondEscalationModule.RequestParameters memory _params - ) public { + ) public assumeFuzzable(address(_params.accountingExtension)) { _timestamp = bound(_timestamp, 1, type(uint128).max); // Set deadline to timestamp so we are still in the bond escalation period _params.bondEscalationDeadline = _timestamp - 1; @@ -461,7 +470,7 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { function test_firstDisputeThroughBondMechanism( address _disputer, IBondEscalationModule.RequestParameters memory _params - ) public { + ) public assumeFuzzable(address(_params.accountingExtension)) { // Set deadline to timestamp so we are still in the bond escalation period _params.disputeWindow = block.timestamp; _params.bondEscalationDeadline = block.timestamp; @@ -520,7 +529,7 @@ contract BondEscalationModule_Unit_DisputeResponse is BaseTest { address _disputer, address _proposer, IBondEscalationModule.RequestParameters memory _params - ) public { + ) public assumeFuzzable(address(_params.accountingExtension)) { _params.disputeWindow = block.timestamp; _params.bondEscalationDeadline = block.timestamp; @@ -584,7 +593,10 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { /** * @notice Tests that onDisputeStatusChange pays the proposer if the disputer lost */ - function test_callPayIfNormalDisputeLost(IBondEscalationModule.RequestParameters memory _params) public { + function test_callPayIfNormalDisputeLost(IBondEscalationModule.RequestParameters memory _params) + public + assumeFuzzable(address(_params.accountingExtension)) + { _params.accountingExtension = IBondEscalationAccounting(makeAddr('BondEscalationAccounting')); mockRequest.disputeModuleData = abi.encode(_params); bytes32 _requestId = _getId(mockRequest); @@ -614,7 +626,10 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { /** * @notice Tests that onDisputeStatusChange pays the disputer if the disputer won */ - function test_callPayIfNormalDisputeWon(IBondEscalationModule.RequestParameters memory _params) public { + function test_callPayIfNormalDisputeWon(IBondEscalationModule.RequestParameters memory _params) + public + assumeFuzzable(address(_params.accountingExtension)) + { _params.accountingExtension = IBondEscalationAccounting(makeAddr('BondEscalationAccounting')); mockRequest.disputeModuleData = abi.encode(_params); bytes32 _requestId = _getId(mockRequest); @@ -650,7 +665,10 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { bondEscalationModule.onDisputeStatusChange(_disputeId, mockRequest, mockResponse, mockDispute); } - function test_emitsEvent(IBondEscalationModule.RequestParameters memory _params) public { + function test_emitsEvent(IBondEscalationModule.RequestParameters memory _params) + public + assumeFuzzable(address(_params.accountingExtension)) + { IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; mockRequest.disputeModuleData = abi.encode(_params); @@ -696,6 +714,7 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { */ function test_earlyReturnIfBondEscalatedDisputeHashNoPledgers(IBondEscalationModule.RequestParameters memory _params) public + assumeFuzzable(address(_params.accountingExtension)) { mockRequest.disputeModuleData = abi.encode(_params); bytes32 _requestId = _getId(mockRequest); @@ -759,7 +778,7 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { */ function test_shouldChangeBondEscalationStatusAndCallPayPledgersWon( IBondEscalationModule.RequestParameters memory _params - ) public { + ) public assumeFuzzable(address(_params.accountingExtension)) { IOracle.DisputeStatus _status = IOracle.DisputeStatus.Won; mockRequest.disputeModuleData = abi.encode(_params); @@ -836,7 +855,7 @@ contract BondEscalationModule_Unit_OnDisputeStatusChange is BaseTest { */ function test_shouldChangeBondEscalationStatusAndCallPayPledgersLost( IBondEscalationModule.RequestParameters memory _params - ) public { + ) public assumeFuzzable(address(_params.accountingExtension)) { // Set to Lost so the proposer and againstDisputePledgers win IOracle.DisputeStatus _status = IOracle.DisputeStatus.Lost; mockRequest.disputeModuleData = abi.encode(_params); @@ -918,7 +937,10 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { /** * @notice Tests that pledgeForDispute reverts if someone tries to pledge after the tying buffer. */ - function test_revertIfTimestampBeyondTyingBuffer(IBondEscalationModule.RequestParameters memory _params) public { + function test_revertIfTimestampBeyondTyingBuffer(IBondEscalationModule.RequestParameters memory _params) + public + assumeFuzzable(address(_params.accountingExtension)) + { _params.bondSize = 1; _params.maxNumberOfEscalations = 1; _params.bondEscalationDeadline = block.timestamp; @@ -941,7 +963,10 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { /** * @notice Tests that pledgeForDispute reverts if the maximum number of escalations has been reached. */ - function test_revertIfMaxNumberOfEscalationsReached(IBondEscalationModule.RequestParameters memory _params) public { + function test_revertIfMaxNumberOfEscalationsReached(IBondEscalationModule.RequestParameters memory _params) + public + assumeFuzzable(address(_params.accountingExtension)) + { _params.bondSize = 1; _params.maxNumberOfEscalations = 2; _params.bondEscalationDeadline = block.timestamp - 1; @@ -970,7 +995,7 @@ contract BondEscalationModule_Unit_PledgeForDispute is BaseTest { */ function test_revertIfThereIsMorePledgedForForDisputeThanAgainst( IBondEscalationModule.RequestParameters memory _params - ) public { + ) public assumeFuzzable(address(_params.accountingExtension)) { _params.tyingBuffer = bound(_params.tyingBuffer, 0, type(uint128).max); _params.bondSize = 1; _params.maxNumberOfEscalations = 3; @@ -1202,7 +1227,10 @@ contract BondEscalationModule_Unit_PledgeAgainstDispute is BaseTest { /** * @notice Tests that pledgeAgainstDispute is called successfully */ - function test_successfulCall(IBondEscalationModule.RequestParameters memory _params) public { + function test_successfulCall(IBondEscalationModule.RequestParameters memory _params) + public + assumeFuzzable(address(_params.accountingExtension)) + { _params.bondSize = 1000; _params.maxNumberOfEscalations = 3; _params.bondEscalationDeadline = block.timestamp - 1; @@ -1314,7 +1342,10 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { /** * @notice Tests that settleBondEscalation is called successfully. */ - function test_successfulCallDisputerWon(IBondEscalationModule.RequestParameters memory _params) public { + function test_successfulCallDisputerWon(IBondEscalationModule.RequestParameters memory _params) + public + assumeFuzzable(address(_params.accountingExtension)) + { _params.bondSize = 1000; _params.bondEscalationDeadline = block.timestamp; _params.tyingBuffer = 1000; @@ -1355,7 +1386,10 @@ contract BondEscalationModule_Unit_SettleBondEscalation is BaseTest { /** * @notice Tests that settleBondEscalation is called successfully. */ - function test_successfulCallDisputerLost(IBondEscalationModule.RequestParameters memory _params) public { + function test_successfulCallDisputerLost(IBondEscalationModule.RequestParameters memory _params) + public + assumeFuzzable(address(_params.accountingExtension)) + { _params.bondSize = 1000; _params.bondEscalationDeadline = block.timestamp; _params.tyingBuffer = 1000; From 085198f61f8cc79e0f67f68396cb69d644300c41 Mon Sep 17 00:00:00 2001 From: Gas One Cent <86567384+gas1cent@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:23:21 +0400 Subject: [PATCH 6/6] feat: emit event --- solidity/contracts/modules/dispute/BondEscalationModule.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solidity/contracts/modules/dispute/BondEscalationModule.sol b/solidity/contracts/modules/dispute/BondEscalationModule.sol index bf398eab..4a5dd673 100644 --- a/solidity/contracts/modules/dispute/BondEscalationModule.sol +++ b/solidity/contracts/modules/dispute/BondEscalationModule.sol @@ -156,8 +156,8 @@ contract BondEscalationModule is Module, IBondEscalationModule { } } - // TODO: Emit event - // emit DisputeStatusChanged({_disputeId: _disputeId, _dispute: _dispute, _status: _status}); + IOracle.DisputeStatus _status = ORACLE.disputeStatus(_disputeId); + emit DisputeStatusChanged({_disputeId: _disputeId, _dispute: _dispute, _status: _status}); } ////////////////////////////////////////////////////////////////////