Skip to content

Commit fb7d37d

Browse files
committed
proper fix for slippage control in instructions
1 parent 45e6ce6 commit fb7d37d

File tree

2 files changed

+66
-2
lines changed

2 files changed

+66
-2
lines changed

src/V3Utils.sol

+6-2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ contract V3Utils is IERC721Receiver {
7373
// target token for swaps (if this is address(0) no swaps are executed)
7474
address targetToken;
7575

76+
// for removing liquidity slippage
77+
uint256 amountRemoveMin0;
78+
uint256 amountRemoveMin1;
79+
7680
// amountIn0 is used for swap and also as minAmount0 for decreased liquidity + collected fees
7781
uint256 amountIn0;
7882
// if token0 needs to be swapped to targetToken - set values
@@ -155,11 +159,11 @@ contract V3Utils is IERC721Receiver {
155159
uint256 amount0;
156160
uint256 amount1;
157161
if (instructions.liquidity != 0) {
158-
(amount0, amount1) = _decreaseLiquidity(tokenId, instructions.liquidity, instructions.deadline, 0, 0); // slippage check is done after collecting
162+
(amount0, amount1) = _decreaseLiquidity(tokenId, instructions.liquidity, instructions.deadline, instructions.amountRemoveMin0, instructions.amountRemoveMin1);
159163
}
160164
(amount0, amount1) = _collectFees(tokenId, IERC20(token0), IERC20(token1), instructions.feeAmount0 == type(uint128).max ? type(uint128).max : (amount0 + instructions.feeAmount0).toUint128(), instructions.feeAmount1 == type(uint128).max ? type(uint128).max : (amount1 + instructions.feeAmount1).toUint128());
161165

162-
// do slippage check after liquidity AND fees have been collected
166+
// check if enough tokens are available for swaps
163167
if (amount0 < instructions.amountIn0 || amount1 < instructions.amountIn1) {
164168
revert AmountError();
165169
}

test/integration/V3Utils.t.sol

+60
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ contract V3UtilsIntegrationTest is IntegrationTestBase {
2020
address(0),
2121
0,
2222
0,
23+
0,
24+
0,
2325
"",
2426
0,
2527
0,
@@ -83,6 +85,54 @@ contract V3UtilsIntegrationTest is IntegrationTestBase {
8385
address(USDC),
8486
1000000000000000001,
8587
400000,
88+
1000000000000000001,
89+
400000,
90+
_get05DAIToUSDCSwapData(),
91+
0,
92+
0,
93+
"",
94+
type(uint128).max, // take all fees
95+
type(uint128).max, // take all fees
96+
100, // change fee as well
97+
MIN_TICK_100,
98+
-MIN_TICK_100,
99+
liquidityBefore, // take all liquidity
100+
0,
101+
0,
102+
block.timestamp,
103+
TEST_NFT_ACCOUNT,
104+
TEST_NFT_ACCOUNT,
105+
false,
106+
"",
107+
""
108+
);
109+
110+
vm.prank(TEST_NFT_ACCOUNT);
111+
vm.expectRevert("Price slippage check");
112+
NPM.safeTransferFrom(
113+
TEST_NFT_ACCOUNT,
114+
address(v3utils),
115+
TEST_NFT,
116+
abi.encode(inst)
117+
);
118+
}
119+
120+
function testTransferAmountError() external {
121+
// add liquidity to existing (empty) position (add 1 DAI / 0 USDC)
122+
_increaseLiquidity();
123+
124+
(, , , , , , , uint128 liquidityBefore, , , , ) = NPM.positions(
125+
TEST_NFT
126+
);
127+
128+
// swap a bit more dai than available - fails with slippage error because not enough liquidity + fees is collected
129+
V3Utils.Instructions memory inst = V3Utils.Instructions(
130+
V3Utils.WhatToDo.CHANGE_RANGE,
131+
address(USDC),
132+
0,
133+
0,
134+
1000000000000000001,
135+
400000,
86136
_get05DAIToUSDCSwapData(),
87137
0,
88138
0,
@@ -127,6 +177,8 @@ contract V3UtilsIntegrationTest is IntegrationTestBase {
127177
V3Utils.Instructions memory inst = V3Utils.Instructions(
128178
V3Utils.WhatToDo.CHANGE_RANGE,
129179
address(USDC),
180+
0,
181+
0,
130182
500000000000000000,
131183
400000,
132184
_get05DAIToUSDCSwapData(),
@@ -172,6 +224,8 @@ contract V3UtilsIntegrationTest is IntegrationTestBase {
172224
address(0),
173225
0,
174226
0,
227+
0,
228+
0,
175229
"",
176230
0,
177231
0,
@@ -225,6 +279,8 @@ contract V3UtilsIntegrationTest is IntegrationTestBase {
225279
V3Utils.Instructions memory inst = V3Utils.Instructions(
226280
V3Utils.WhatToDo.COMPOUND_FEES,
227281
address(USDC),
282+
0,
283+
0,
228284
500000000000000000,
229285
400000,
230286
_get05DAIToUSDCSwapData(),
@@ -286,6 +342,8 @@ contract V3UtilsIntegrationTest is IntegrationTestBase {
286342
V3Utils.Instructions memory inst = V3Utils.Instructions(
287343
V3Utils.WhatToDo.WITHDRAW_AND_COLLECT_AND_SWAP,
288344
address(USDC),
345+
0,
346+
0,
289347
990099009900989844, // uniswap returns 1 less when getting liquidity - this must be traded
290348
900000,
291349
_get1DAIToUSDSwapData(),
@@ -347,6 +405,8 @@ contract V3UtilsIntegrationTest is IntegrationTestBase {
347405
V3Utils.Instructions memory inst = V3Utils.Instructions(
348406
V3Utils.WhatToDo.WITHDRAW_AND_COLLECT_AND_SWAP,
349407
address(USDC),
408+
0,
409+
0,
350410
990099009900989844, // uniswap returns 1 less when getting liquidity - this must be traded
351411
900000,
352412
_get1DAIToUSDSwapData(),

0 commit comments

Comments
 (0)