Skip to content

Commit

Permalink
Slippage checks routing (#233)
Browse files Browse the repository at this point in the history
* slippage checks on single trades

* SLippage check tests
  • Loading branch information
hensha256 authored Aug 1, 2024
1 parent f38a66f commit e77f698
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_Bytecode.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6628
6845
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134717
134844
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119848
119975
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
118993
119120
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOutputSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
133251
133648
Original file line number Diff line number Diff line change
@@ -1 +1 @@
125373
125770
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119636
120033
22 changes: 13 additions & 9 deletions src/V4Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,14 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver {
}

function _swapExactInputSingle(IV4Router.ExactInputSingleParams memory params) private {
_swap(
uint128 amountOut = _swap(
params.poolKey,
params.zeroForOne,
int256(-int128(params.amountIn)),
params.sqrtPriceLimitX96,
params.hookData
);
).toUint128();
if (amountOut < params.amountOutMinimum) revert TooLittleReceived();
}

function _swapExactInput(IV4Router.ExactInputParams memory params) private {
Expand All @@ -96,13 +97,16 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver {
}

function _swapExactOutputSingle(IV4Router.ExactOutputSingleParams memory params) private {
_swap(
params.poolKey,
params.zeroForOne,
int256(int128(params.amountOut)),
params.sqrtPriceLimitX96,
params.hookData
);
uint128 amountIn = (
-_swap(
params.poolKey,
params.zeroForOne,
int256(int128(params.amountOut)),
params.sqrtPriceLimitX96,
params.hookData
)
).toUint128();
if (amountIn > params.amountInMaximum) revert TooMuchRequested();
}

function _swapExactOutput(IV4Router.ExactOutputParams memory params) private {
Expand Down
6 changes: 3 additions & 3 deletions test/router/V4Router.gas.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ contract V4RouterTest is RoutingTestHelpers, GasSnapshot {
uint256 amountOut = 1 ether;

IV4Router.ExactOutputSingleParams memory params =
IV4Router.ExactOutputSingleParams(key0, true, uint128(amountOut), 0, 0, bytes(""));
IV4Router.ExactOutputSingleParams(key0, true, uint128(amountOut), type(uint128).max, 0, bytes(""));

plan = plan.add(Actions.SWAP_EXACT_OUT_SINGLE, abi.encode(params));
bytes memory data = plan.finalizeSwap(key0.currency0, key0.currency1, address(this));
Expand Down Expand Up @@ -271,7 +271,7 @@ contract V4RouterTest is RoutingTestHelpers, GasSnapshot {
uint256 amountOut = 1 ether;

IV4Router.ExactOutputSingleParams memory params =
IV4Router.ExactOutputSingleParams(nativeKey, true, uint128(amountOut), 0, 0, bytes(""));
IV4Router.ExactOutputSingleParams(nativeKey, true, uint128(amountOut), type(uint128).max, 0, bytes(""));

plan = plan.add(Actions.SWAP_EXACT_OUT_SINGLE, abi.encode(params));
bytes memory data = plan.finalizeSwap(nativeKey.currency0, nativeKey.currency1, address(this));
Expand All @@ -284,7 +284,7 @@ contract V4RouterTest is RoutingTestHelpers, GasSnapshot {
uint256 amountOut = 1 ether;

IV4Router.ExactOutputSingleParams memory params =
IV4Router.ExactOutputSingleParams(nativeKey, false, uint128(amountOut), 0, 0, bytes(""));
IV4Router.ExactOutputSingleParams(nativeKey, false, uint128(amountOut), type(uint128).max, 0, bytes(""));

plan = plan.add(Actions.SWAP_EXACT_OUT_SINGLE, abi.encode(params));
bytes memory data = plan.finalizeSwap(nativeKey.currency1, nativeKey.currency0, address(this));
Expand Down
83 changes: 75 additions & 8 deletions test/router/V4Router.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ contract V4RouterTest is RoutingTestHelpers {
ERC20 -> ERC20 EXACT INPUT
//////////////////////////////////////////////////////////////*/

function test_swapExactInputSingle_revertsForAmountOut() public {
uint256 amountIn = 1 ether;
uint256 expectedAmountOut = 992054607780215625;

// min amount out of 1 higher than the actual amount out
IV4Router.ExactInputSingleParams memory params = IV4Router.ExactInputSingleParams(
key0, true, uint128(amountIn), uint128(expectedAmountOut + 1), 0, bytes("")
);

plan = plan.add(Actions.SWAP_EXACT_IN_SINGLE, abi.encode(params));
bytes memory data = plan.finalizeSwap(key0.currency0, key0.currency1, address(this));

vm.expectRevert(IV4Router.TooLittleReceived.selector);
router.executeActions(data);
}

function test_swapExactInputSingle_zeroForOne() public {
uint256 amountIn = 1 ether;
uint256 expectedAmountOut = 992054607780215625;
Expand Down Expand Up @@ -56,6 +72,22 @@ contract V4RouterTest is RoutingTestHelpers {
assertEq(outputBalanceAfter - outputBalanceBefore, expectedAmountOut);
}

function test_swapExactInput_revertsForAmountOut() public {
uint256 amountIn = 1 ether;
uint256 expectedAmountOut = 992054607780215625;

tokenPath.push(currency0);
tokenPath.push(currency1);
IV4Router.ExactInputParams memory params = _getExactInputParams(tokenPath, amountIn);
params.amountOutMinimum = uint128(expectedAmountOut + 1);

plan = plan.add(Actions.SWAP_EXACT_IN, abi.encode(params));
bytes memory data = plan.finalizeSwap(key0.currency0, key0.currency1, address(this));

vm.expectRevert(IV4Router.TooLittleReceived.selector);
router.executeActions(data);
}

function test_swapExactIn_1Hop_zeroForOne() public {
uint256 amountIn = 1 ether;
uint256 expectedAmountOut = 992054607780215625;
Expand Down Expand Up @@ -287,12 +319,28 @@ contract V4RouterTest is RoutingTestHelpers {
ERC20 -> ERC20 EXACT OUTPUT
//////////////////////////////////////////////////////////////*/

function test_swapExactOutputSingle_revertsForAmountIn() public {
uint256 amountOut = 1 ether;
uint256 expectedAmountIn = 1008049273448486163;

IV4Router.ExactOutputSingleParams memory params = IV4Router.ExactOutputSingleParams(
key0, true, uint128(amountOut), uint128(expectedAmountIn - 1), 0, bytes("")
);

plan = plan.add(Actions.SWAP_EXACT_OUT_SINGLE, abi.encode(params));
bytes memory data = plan.finalizeSwap(key0.currency0, key0.currency1, address(this));

vm.expectRevert(IV4Router.TooMuchRequested.selector);
router.executeActions(data);
}

function test_swapExactOutputSingle_zeroForOne() public {
uint256 amountOut = 1 ether;
uint256 expectedAmountIn = 1008049273448486163;

IV4Router.ExactOutputSingleParams memory params =
IV4Router.ExactOutputSingleParams(key0, true, uint128(amountOut), 0, 0, bytes(""));
IV4Router.ExactOutputSingleParams memory params = IV4Router.ExactOutputSingleParams(
key0, true, uint128(amountOut), uint128(expectedAmountIn + 1), 0, bytes("")
);

plan = plan.add(Actions.SWAP_EXACT_OUT_SINGLE, abi.encode(params));

Expand All @@ -310,8 +358,9 @@ contract V4RouterTest is RoutingTestHelpers {
uint256 amountOut = 1 ether;
uint256 expectedAmountIn = 1008049273448486163;

IV4Router.ExactOutputSingleParams memory params =
IV4Router.ExactOutputSingleParams(key0, false, uint128(amountOut), 0, 0, bytes(""));
IV4Router.ExactOutputSingleParams memory params = IV4Router.ExactOutputSingleParams(
key0, false, uint128(amountOut), uint128(expectedAmountIn + 1), 0, bytes("")
);

plan = plan.add(Actions.SWAP_EXACT_OUT_SINGLE, abi.encode(params));

Expand All @@ -325,6 +374,22 @@ contract V4RouterTest is RoutingTestHelpers {
assertEq(outputBalanceAfter - outputBalanceBefore, amountOut);
}

function test_swapExactOut_revertsForAmountIn() public {
uint256 amountOut = 1 ether;
uint256 expectedAmountIn = 1008049273448486163;

tokenPath.push(currency0);
tokenPath.push(currency1);
IV4Router.ExactOutputParams memory params = _getExactOutputParams(tokenPath, amountOut);
params.amountInMaximum = uint128(expectedAmountIn - 1);

plan = plan.add(Actions.SWAP_EXACT_OUT, abi.encode(params));
bytes memory data = plan.finalizeSwap(key0.currency0, key0.currency1, address(this));

vm.expectRevert(IV4Router.TooMuchRequested.selector);
router.executeActions(data);
}

function test_swapExactOut_1Hop_zeroForOne() public {
uint256 amountOut = 1 ether;
uint256 expectedAmountIn = 1008049273448486163;
Expand Down Expand Up @@ -422,8 +487,9 @@ contract V4RouterTest is RoutingTestHelpers {
uint256 amountOut = 1 ether;
uint256 expectedAmountIn = 1008049273448486163;

IV4Router.ExactOutputSingleParams memory params =
IV4Router.ExactOutputSingleParams(nativeKey, true, uint128(amountOut), 0, 0, bytes(""));
IV4Router.ExactOutputSingleParams memory params = IV4Router.ExactOutputSingleParams(
nativeKey, true, uint128(amountOut), uint128(expectedAmountIn + 1), 0, bytes("")
);

plan = plan.add(Actions.SWAP_EXACT_OUT_SINGLE, abi.encode(params));

Expand All @@ -441,8 +507,9 @@ contract V4RouterTest is RoutingTestHelpers {
uint256 amountOut = 1 ether;
uint256 expectedAmountIn = 1008049273448486163;

IV4Router.ExactOutputSingleParams memory params =
IV4Router.ExactOutputSingleParams(nativeKey, false, uint128(amountOut), 0, 0, bytes(""));
IV4Router.ExactOutputSingleParams memory params = IV4Router.ExactOutputSingleParams(
nativeKey, false, uint128(amountOut), uint128(expectedAmountIn + 1), 0, bytes("")
);

plan = plan.add(Actions.SWAP_EXACT_OUT_SINGLE, abi.encode(params));

Expand Down

0 comments on commit e77f698

Please sign in to comment.