Skip to content

Commit

Permalink
feat: add exclusive override to xv2
Browse files Browse the repository at this point in the history
This commit adds exclusive override to xv2, where anyone can fill an
order even during exclusivity period as long as they pay a certain
amount of basis points extra on the output
  • Loading branch information
marktoda committed Feb 13, 2024
1 parent c86fbb9 commit fba5682
Show file tree
Hide file tree
Showing 14 changed files with 98 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
188808
189048
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V2DutchOrder-ExclusiveFiller.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
158712
158953
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V2DutchOrder-ExecuteBatch.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
210595
211089
Original file line number Diff line number Diff line change
@@ -1 +1 @@
221002
221496
Original file line number Diff line number Diff line change
@@ -1 +1 @@
275311
275806
Original file line number Diff line number Diff line change
@@ -1 +1 @@
204121
204615
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V2DutchOrder-ExecuteSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
155015
155256
Original file line number Diff line number Diff line change
@@ -1 +1 @@
140582
140822
Original file line number Diff line number Diff line change
@@ -1 +1 @@
164325
164567
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V2DutchOrder-InputOverride.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
159069
159310
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V2DutchOrder-OutputOverride.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
158739
158980
2 changes: 2 additions & 0 deletions src/lib/V2DutchOrderLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ struct CosignerData {
uint256 decayEndTime;
// The address who has exclusive rights to the order until decayStartTime
address exclusiveFiller;
// The amount in bps that a non-exclusive filler needs to improve the outputs by to be able to fill the order
uint256 exclusivityOverrideBps;
// The tokens that the swapper will provide when settling the order
uint256 inputOverride;
// The tokens that must be received to satisfy the order
Expand Down
7 changes: 6 additions & 1 deletion src/reactors/V2DutchOrderReactor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ contract V2DutchOrderReactor is BaseReactor {
using V2DutchOrderLib for V2DutchOrder;
using DutchDecayLib for DutchOutput[];
using DutchDecayLib for DutchInput;
using ExclusivityLib for ResolvedOrder;

/// @notice thrown when an order's deadline is before its end time
error DeadlineBeforeEndTime();
Expand Down Expand Up @@ -63,7 +64,11 @@ contract V2DutchOrderReactor is BaseReactor {
sig: signedOrder.sig,
hash: orderHash
});
ExclusivityLib.handleStrictExclusivity(order.cosignerData.exclusiveFiller, order.cosignerData.decayStartTime);
resolvedOrder.handleExclusiveOverride(
order.cosignerData.exclusiveFiller,
order.cosignerData.decayStartTime,
order.cosignerData.exclusivityOverrideBps
);
}

/// @inheritdoc BaseReactor
Expand Down
81 changes: 79 additions & 2 deletions test/reactors/V2DutchOrderReactor.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
decayStartTime: block.timestamp,
decayEndTime: request.info.deadline,
exclusiveFiller: address(0),
exclusivityOverrideBps: 0,
inputOverride: 0,
outputOverrides: outputOverrides
});
Expand Down Expand Up @@ -101,6 +102,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
decayStartTime: request.decayStartTime,
decayEndTime: request.decayEndTime,
exclusiveFiller: address(0),
exclusivityOverrideBps: 0,
inputOverride: 0,
outputOverrides: outputOverrides
});
Expand All @@ -125,6 +127,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
decayStartTime: block.timestamp,
decayEndTime: block.timestamp + 100,
exclusiveFiller: address(0),
exclusivityOverrideBps: 0,
inputOverride: 1 ether,
outputOverrides: ArrayBuilder.fill(1, 1 ether)
});
Expand All @@ -149,6 +152,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
decayStartTime: block.timestamp,
decayEndTime: block.timestamp + 100,
exclusiveFiller: address(0),
exclusivityOverrideBps: 0,
// override is more input tokens than expected
inputOverride: 0.9 ether,
outputOverrides: ArrayBuilder.fill(1, 1.1 ether)
Expand All @@ -173,6 +177,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
decayStartTime: block.timestamp,
decayEndTime: block.timestamp + 100,
exclusiveFiller: address(0),
exclusivityOverrideBps: 0,
// override is more input tokens than expected
inputOverride: 1 ether,
outputOverrides: ArrayBuilder.fill(1, 0.9 ether)
Expand All @@ -197,6 +202,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
decayStartTime: block.timestamp,
decayEndTime: block.timestamp + 100,
exclusiveFiller: address(0),
exclusivityOverrideBps: 0,
// override is more input tokens than expected
inputOverride: 1 ether,
outputOverrides: ArrayBuilder.fill(2, 1.1 ether)
Expand Down Expand Up @@ -226,6 +232,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
decayStartTime: block.timestamp,
decayEndTime: block.timestamp + 100,
exclusiveFiller: address(0),
exclusivityOverrideBps: 0,
inputOverride: overriddenInputAmount,
outputOverrides: ArrayBuilder.fill(1, 0)
});
Expand Down Expand Up @@ -261,6 +268,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
decayStartTime: block.timestamp,
decayEndTime: block.timestamp + 100,
exclusiveFiller: address(0),
exclusivityOverrideBps: 0,
inputOverride: 0,
outputOverrides: ArrayBuilder.fill(1, overriddenOutputAmount)
});
Expand All @@ -286,7 +294,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
assertEq(tokenIn.balanceOf(address(fillContract)), inputAmount);
}

function testExclusivityInvalidCaller() public {
function testStrictExclusivityInvalidCaller() public {
uint256 inputAmount = 1 ether;
tokenIn.mint(swapper, inputAmount);
tokenOut.mint(address(fillContract), inputAmount);
Expand All @@ -295,6 +303,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
decayStartTime: block.timestamp,
decayEndTime: block.timestamp + 100,
exclusiveFiller: address(1),
exclusivityOverrideBps: 0,
inputOverride: 0,
outputOverrides: ArrayBuilder.fill(1, 0)
});
Expand All @@ -314,7 +323,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
fillContract.execute(signedOrder);
}

function testExclusivityValidCaller() public {
function testStrictExclusivityValidCaller() public {
uint256 inputAmount = 1 ether;
uint256 outputAmount = 1 ether;
tokenIn.mint(swapper, inputAmount);
Expand All @@ -324,6 +333,7 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
decayStartTime: block.timestamp,
decayEndTime: block.timestamp + 100,
exclusiveFiller: address(fillContract),
exclusivityOverrideBps: 0,
inputOverride: 0,
outputOverrides: ArrayBuilder.fill(1, 0)
});
Expand All @@ -350,6 +360,73 @@ contract V2DutchOrderTest is PermitSignature, DeployPermit2, BaseDutchOrderReact
assertEq(tokenIn.balanceOf(address(fillContract)), inputAmount);
}

function testExclusiveOverrideInvalidCallerCosignedAmountOutput() public {
uint256 inputAmount = 1 ether;
uint256 outputAmount = 1 ether;
uint256 exclusivityOverrideBps = 10;
tokenIn.mint(swapper, inputAmount);
tokenOut.mint(address(fillContract), outputAmount * 2);
tokenIn.forceApprove(swapper, address(permit2), type(uint256).max);
CosignerData memory cosignerData = CosignerData({
decayStartTime: block.timestamp,
decayEndTime: block.timestamp + 100,
exclusiveFiller: address(1),
exclusivityOverrideBps: exclusivityOverrideBps,
inputOverride: 0,
outputOverrides: ArrayBuilder.fill(1, outputAmount)
});

V2DutchOrder memory order = V2DutchOrder({
info: OrderInfoBuilder.init(address(reactor)).withSwapper(swapper),
cosigner: vm.addr(cosignerPrivateKey),
input: DutchInput(tokenIn, inputAmount, inputAmount),
outputs: OutputsBuilder.singleDutch(address(tokenOut), 1 ether, 0.9 ether, swapper),
cosignerData: cosignerData,
cosignature: bytes("")
});
order.cosignature = cosignOrder(order.hash(), cosignerData);
SignedOrder memory signedOrder =
SignedOrder(abi.encode(order), signOrder(swapperPrivateKey, address(permit2), order));
fillContract.execute(signedOrder);
assertEq(tokenIn.balanceOf(swapper), 0);
assertEq(tokenOut.balanceOf(swapper), outputAmount * (10000 + exclusivityOverrideBps) / 10000);
assertEq(tokenIn.balanceOf(address(fillContract)), inputAmount);
}

function testExclusiveOverrideInvalidCallerNoCosignedAmountOutput() public {
uint256 inputAmount = 1 ether;
uint256 outputAmount = 1 ether;
uint256 exclusivityOverrideBps = 10;
tokenIn.mint(swapper, inputAmount);
tokenOut.mint(address(fillContract), outputAmount * 2);
tokenIn.forceApprove(swapper, address(permit2), type(uint256).max);
CosignerData memory cosignerData = CosignerData({
decayStartTime: block.timestamp,
decayEndTime: block.timestamp + 100,
exclusiveFiller: address(1),
exclusivityOverrideBps: exclusivityOverrideBps,
inputOverride: 0,
outputOverrides: ArrayBuilder.fill(1, 0)
});

V2DutchOrder memory order = V2DutchOrder({
info: OrderInfoBuilder.init(address(reactor)).withSwapper(swapper),
cosigner: vm.addr(cosignerPrivateKey),
input: DutchInput(tokenIn, inputAmount, inputAmount),
outputs: OutputsBuilder.singleDutch(address(tokenOut), outputAmount, 0.9 ether, swapper),
cosignerData: cosignerData,
cosignature: bytes("")
});
order.cosignature = cosignOrder(order.hash(), cosignerData);
SignedOrder memory signedOrder =
SignedOrder(abi.encode(order), signOrder(swapperPrivateKey, address(permit2), order));
fillContract.execute(signedOrder);
assertEq(tokenIn.balanceOf(swapper), 0);
// still overrides the base swapper signed amount
assertEq(tokenOut.balanceOf(swapper), outputAmount * (10000 + exclusivityOverrideBps) / 10000);
assertEq(tokenIn.balanceOf(address(fillContract)), inputAmount);
}

function cosignOrder(bytes32 orderHash, CosignerData memory cosignerData) private pure returns (bytes memory sig) {
bytes32 msgHash = keccak256(abi.encodePacked(orderHash, abi.encode(cosignerData)));
(uint8 v, bytes32 r, bytes32 s) = vm.sign(cosignerPrivateKey, msgHash);
Expand Down

0 comments on commit fba5682

Please sign in to comment.