Skip to content

Commit

Permalink
Tell watchtower to retry if order is too small
Browse files Browse the repository at this point in the history
  • Loading branch information
fedgiac committed Feb 6, 2024
1 parent 63f5464 commit ea64e83
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 4 deletions.
5 changes: 4 additions & 1 deletion src/ConstantProduct.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "lib/composable-cow/src/BaseConditionalOrder.sol";

import {IPriceOracle} from "./interfaces/IPriceOracle.sol";
import {IWatchtowerCustomErrors} from "./interfaces/IWatchtowerCustomErrors.sol";

/**
* @title CoW AMM
Expand Down Expand Up @@ -128,7 +129,9 @@ contract ConstantProduct is IConditionalOrderGenerator {
}

if (tradedAmountToken0 < data.minTradedToken0) {
revert IConditionalOrder.OrderNotValid("traded amount too small");
revert IWatchtowerCustomErrors.PollTryAtEpoch(
Utils.validToBucket(MAX_ORDER_DURATION) + 1, "traded amount too small"
);
}

order = GPv2Order.Data(
Expand Down
19 changes: 19 additions & 0 deletions src/interfaces/IWatchtowerCustomErrors.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;

/**
* @title Watchtower Custom Error Interface
* @author CoW Protocol Developers
* @dev An interface that collects all custom error message for the watchtower.
* Different error messages lead to different watchtower behaviors when creating
* an order.
* @dev The watchtower is a service that automatically posts orders to the CoW
* Protocol orderbook at regular intervals.
*/
contract IWatchtowerCustomErrors {
/**
* No order is currently available for trading, but the watchtower should
* try again at the specified timestamp.
*/
error PollTryAtEpoch(uint256 timestamp, string message);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
pragma solidity ^0.8.13;

import {ConstantProductTestHarness} from "../ConstantProductTestHarness.sol";
import {ConstantProduct, GPv2Order, IConditionalOrder} from "../../../src/ConstantProduct.sol";
import {ConstantProduct, GPv2Order, IConditionalOrder} from "src/ConstantProduct.sol";
import {IWatchtowerCustomErrors} from "src/interfaces/IWatchtowerCustomErrors.sol";

abstract contract ValidateOrderParametersTest is ConstantProductTestHarness {
function testValidOrderParameters() public {
Expand Down Expand Up @@ -44,12 +45,26 @@ abstract contract ValidateOrderParametersTest is ConstantProductTestHarness {
setUpDefaultReserves(orderOwner);
setUpDefaultReferencePairReserves(42, 1337);

uint256 smallOffset = 42;
require(smallOffset < constantProduct.MAX_ORDER_DURATION());
uint256 nextTimestamp = 1337 * constantProduct.MAX_ORDER_DURATION() + smallOffset;
uint256 nextBucket = 1338 * constantProduct.MAX_ORDER_DURATION();
require(
nextTimestamp % constantProduct.MAX_ORDER_DURATION() != 0,
"test was designed so that the timestamp doesn't fall exactly at the start of a bucket, please change the offset"
);
vm.warp(nextTimestamp);

GPv2Order.Data memory order = getTradeableOrderWrapper(orderOwner, defaultData);
require(order.sellToken == defaultData.token0, "test was design for token0 to be the sell token");
defaultData.minTradedToken0 = order.sellAmount;
order = getTradeableOrderWrapper(orderOwner, defaultData);
defaultData.minTradedToken0 = order.sellAmount + 1;
vm.expectRevert(abi.encodeWithSelector(IConditionalOrder.OrderNotValid.selector, "traded amount too small"));
vm.expectRevert(
abi.encodeWithSelector(
IWatchtowerCustomErrors.PollTryAtEpoch.selector, nextBucket + 1, "traded amount too small"
)
);
getTradeableOrderUncheckedWrapper(orderOwner, defaultData);
}

Expand All @@ -58,12 +73,26 @@ abstract contract ValidateOrderParametersTest is ConstantProductTestHarness {
setUpDefaultReserves(orderOwner);
setUpDefaultReferencePairReserves(1337, 42);

uint256 smallOffset = 42;
require(smallOffset < constantProduct.MAX_ORDER_DURATION());
uint256 nextTimestamp = 1337 * constantProduct.MAX_ORDER_DURATION() + smallOffset;
uint256 nextBucket = 1338 * constantProduct.MAX_ORDER_DURATION();
require(
nextTimestamp % constantProduct.MAX_ORDER_DURATION() != 0,
"test was designed so that the timestamp doesn't fall exactly at the start of a bucket, please change the offset"
);
vm.warp(nextTimestamp);

GPv2Order.Data memory order = getTradeableOrderWrapper(orderOwner, defaultData);
require(order.buyToken == defaultData.token0, "test was design for token0 to be the buy token");
defaultData.minTradedToken0 = order.buyAmount;
order = getTradeableOrderWrapper(orderOwner, defaultData);
defaultData.minTradedToken0 = order.buyAmount + 1;
vm.expectRevert(abi.encodeWithSelector(IConditionalOrder.OrderNotValid.selector, "traded amount too small"));
vm.expectRevert(
abi.encodeWithSelector(
IWatchtowerCustomErrors.PollTryAtEpoch.selector, nextBucket + 1, "traded amount too small"
)
);
getTradeableOrderUncheckedWrapper(orderOwner, defaultData);
}
}

0 comments on commit ea64e83

Please sign in to comment.