-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor handler errors to try on next block watch tower pattern (#87)
# Description Refactor Stop Loss, Trade Above Threshold, and Good After Time errors to watch tower try again pattern. # Changes - [x] Add `revertPollAtNextBlock`method on `ConditionalOrdersUtilsLib` - [x] Stop Loss Refactoring - [x] Trade Above Threshold Refactoring - [x] Good After Time Refactoring ## How to test 1. Run modified test files 2. Stop Loss Deployment on sepolia: `0x410c267baf50b36475B30051Da2aE734f6726F01` ## Related Issues - Fix #85 --------- Co-authored-by: mfw78 <[email protected]>
- Loading branch information
1 parent
24d556b
commit 7c815d4
Showing
7 changed files
with
150 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,10 +10,21 @@ import {IERC165} from "safe/interfaces/IERC165.sol"; | |
* @author CoW Protocol Developers + mfw78 <[email protected]> | ||
*/ | ||
interface IConditionalOrder { | ||
|
||
/// @dev This error is returned by the `getTradeableOrder` function if the order condition is not met. | ||
/// A parameter of `string` type is included to allow the caller to specify the reason for the failure. | ||
error OrderNotValid(string); | ||
|
||
// --- errors specific for polling | ||
// Signal to a watch tower that polling should be attempted again. | ||
error PollTryNextBlock(string reason); | ||
// Signal to a watch tower that polling should be attempted again at a specific block number. | ||
error PollTryAtBlock(uint256 blockNumber, string reason); | ||
// Signal to a watch tower that polling should be attempted again at a specific epoch (unix timestamp). | ||
error PollTryAtEpoch(uint256 timestamp, string reason); | ||
// Signal to a watch tower that the conditional order should not be polled again (delete). | ||
error PollNever(string reason); | ||
|
||
/** | ||
* @dev This struct is used to uniquely identify a conditional order for an owner. | ||
* H(handler || salt || staticInput) **MUST** be unique for an owner. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity >=0.8.0 <0.9.0; | ||
|
||
import {ERC1271} from "safe/handler/extensible/SignatureVerifierMuxer.sol"; | ||
|
||
import "./ComposableCoW.base.t.sol"; | ||
|
||
import "../src/types/TradeAboveThreshold.sol"; | ||
import {ConditionalOrdersUtilsLib as Utils} from "../src/types/ConditionalOrdersUtilsLib.sol"; | ||
|
||
contract ComposableCoWTatTest is BaseComposableCoWTest { | ||
using ComposableCoWLib for IConditionalOrder.ConditionalOrderParams[]; | ||
using SafeLib for Safe; | ||
|
||
TradeAboveThreshold tat; | ||
|
||
function setUp() public virtual override(BaseComposableCoWTest) { | ||
super.setUp(); | ||
|
||
// deploy the TAT handler | ||
tat = new TradeAboveThreshold(); | ||
} | ||
|
||
/** | ||
* @dev Fuzz test revert on balance too low | ||
*/ | ||
function test_getTradeableOrder_FuzzRevertBelowThreshold(uint256 currentBalance, uint256 threshold) public { | ||
// Revert when the current balance is below the minimum balance | ||
vm.assume(currentBalance < threshold); | ||
|
||
TradeAboveThreshold.Data memory o = _tatTest(); | ||
o.threshold = threshold; | ||
|
||
// set the current balance | ||
deal(address(o.sellToken), address(safe1), currentBalance); | ||
|
||
// should revert when the current balance is below the minimum balance | ||
vm.expectRevert( | ||
abi.encodeWithSelector( | ||
IConditionalOrder.PollTryNextBlock.selector, | ||
BALANCE_INSUFFICIENT | ||
) | ||
); | ||
tat.getTradeableOrder(address(safe1), address(0), bytes32(0), abi.encode(o), bytes("")); | ||
} | ||
|
||
function test_BalanceMet_fuzz( | ||
address receiver, | ||
uint256 threshold, | ||
bytes32 appData, | ||
uint256 currentBalance | ||
) public { | ||
vm.assume(threshold > 0); | ||
vm.assume(currentBalance >= threshold); | ||
|
||
// Use same time data from stop loss test | ||
vm.warp(1687718451); | ||
|
||
TradeAboveThreshold.Data memory data = TradeAboveThreshold.Data({ | ||
sellToken: token0, | ||
buyToken: token1, | ||
receiver: receiver, | ||
validityBucketSeconds: 15 minutes, | ||
threshold: threshold, | ||
appData: appData | ||
}); | ||
|
||
|
||
// // set the current balance | ||
deal(address(token0), address(safe1), currentBalance); | ||
|
||
// This should not revert | ||
GPv2Order.Data memory order = | ||
tat.getTradeableOrder(address(safe1), address(0), bytes32(0), abi.encode(data), bytes("")); | ||
|
||
|
||
assertEq(address(order.sellToken), address(token0)); | ||
assertEq(address(order.buyToken), address(token1)); | ||
assertEq(order.sellAmount, currentBalance); | ||
assertEq(order.buyAmount, 1); | ||
assertEq(order.receiver, receiver); | ||
assertEq(order.validTo, 1687718700); | ||
assertEq(order.appData, appData); | ||
assertEq(order.feeAmount, 0); | ||
assertEq(order.kind, GPv2Order.KIND_SELL); | ||
assertEq(order.partiallyFillable, false); | ||
assertEq(order.sellTokenBalance, GPv2Order.BALANCE_ERC20); | ||
assertEq(order.buyTokenBalance, GPv2Order.BALANCE_ERC20); | ||
} | ||
|
||
// --- Helper functions --- | ||
|
||
function _tatTest() internal view returns (TradeAboveThreshold.Data memory) { | ||
return TradeAboveThreshold.Data({ | ||
sellToken: token0, | ||
buyToken: token1, | ||
receiver: address(0), | ||
validityBucketSeconds: 15 minutes, | ||
threshold: 200e18, | ||
appData: keccak256("TradeAboveThreshold") | ||
}); | ||
} | ||
} |