@@ -38,17 +38,21 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
3838    address  strategyAdmin =  makeAddr ("strategyAdmin " );
3939    address  allowanceTargetOwner =  makeAddr ("allowanceTargetOwner " );
4040    uint256  takerPrivateKey =  uint256 (1 );
41+     uint256  alicePrivateKey =  uint256 (2 );
4142    address  taker =  vm.addr (takerPrivateKey);
43+     address  alice =  vm.addr (alicePrivateKey);
4244    uint256  defaultExpiry =  block .timestamp  +  1 ;
4345    address  defaultInputToken =  USDT_ADDRESS;
4446    uint256  defaultInputAmount =  10  *  1e6 ;
4547    address  defaultOutputToken =  DAI_ADDRESS;
4648    address [] defaultPath =  [defaultInputToken, defaultOutputToken];
4749    uint24 [] defaultV3Fees =  [3000 ];
4850    bytes  defaultTakerPermit;
51+     bytes  alicePermit;
4952    SmartOrderStrategy smartStrategy;
5053    GenericSwap genericSwap;
5154    GenericSwapData defaultGSData;
55+     GenericSwapData aliceGSData;
5256    MockStrategy mockStrategy;
5357    AllowanceTarget allowanceTarget;
5458
@@ -62,6 +66,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
6266
6367        genericSwap =  new  GenericSwap (UNISWAP_PERMIT2_ADDRESS, address (allowanceTarget));
6468        smartStrategy =  new  SmartOrderStrategy (strategyAdmin, address (genericSwap), WETH_ADDRESS);
69+ 
6570        mockStrategy =  new  MockStrategy ();
6671        vm.prank (strategyAdmin);
6772        address [] memory  tokenList =  new  address [](1 );
@@ -72,7 +77,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
7277
7378        IUniswapV3Quoter v3Quoter =  IUniswapV3Quoter (UNISWAP_V3_QUOTER_ADDRESS);
7479        bytes  memory  encodedPath =  UniswapV3.encodePath (defaultPath, defaultV3Fees);
75-         uint256  expectedOut =  v3Quoter.quoteExactInput (encodedPath, defaultInputAmount); 
80+         uint256  expectedOut =  v3Quoter.quoteExactInput (encodedPath, defaultInputAmount)  -   2 ;  // leaving 1 wei in GS and SOS separately 
7681        uint256  minOutputAmount =  (expectedOut *  95 ) /  100 ; // default 5% slippage tolerance 
7782        bytes  memory  routerPayload =  abi.encodeCall (
7883            IUniswapSwapRouter02.exactInputSingle,
@@ -101,6 +106,8 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
101106
102107        deal (taker, 100  ether);
103108        setTokenBalanceAndApprove (taker, UNISWAP_PERMIT2_ADDRESS, tokens, 100000 );
109+         deal (alice, 100  ether);
110+         setTokenBalanceAndApprove (alice, UNISWAP_PERMIT2_ADDRESS, tokens, 100000 );
104111        deal (address (mockStrategy), 100  ether);
105112        setTokenBalanceAndApprove (address (mockStrategy), UNISWAP_PERMIT2_ADDRESS, tokens, 100000 );
106113
@@ -156,16 +163,17 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
156163        Snapshot memory  makerMakerToken =  BalanceSnapshot.take ({ owner: address (mockStrategy), token: gsData.makerToken });
157164
158165        uint256  actualOutput =  900 ;
166+         uint256  realChangedInGS =  actualOutput -  1 ; // leaving 1 wei in GS 
159167
160168        // 800 < 900 < 1000 
161169        mockStrategy.setOutputAmountAndRecipient (actualOutput, payable (address (genericSwap)));
162170        vm.expectEmit (true , true , true , true );
163-         emit  Swap (getGSDataHash (gsData), gsData.maker, taker, taker, gsData.takerToken, gsData.takerTokenAmount, gsData.makerToken, actualOutput );
171+         emit  Swap (getGSDataHash (gsData), gsData.maker, taker, taker, gsData.takerToken, gsData.takerTokenAmount, gsData.makerToken, realChangedInGS );
164172        vm.prank (taker);
165173        genericSwap.executeSwap (gsData, defaultTakerPermit);
166174
167175        takerTakerToken.assertChange (- int256 (gsData.takerTokenAmount));
168-         takerMakerToken.assertChange (int256 (actualOutput ));
176+         takerMakerToken.assertChange (int256 (realChangedInGS ));
169177        makerTakerToken.assertChange (int256 (gsData.takerTokenAmount));
170178        makerMakerToken.assertChange (- int256 (actualOutput));
171179    }
@@ -176,19 +184,21 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
176184        gsData.takerToken =  Constant.ETH_ADDRESS;
177185        gsData.takerTokenAmount =  1  ether ;
178186
187+         uint256  realChangedInGS =  gsData.makerTokenAmount -  1 ; // leaving 1 wei in GS 
188+ 
179189        Snapshot memory  takerTakerToken =  BalanceSnapshot.take ({ owner: taker, token: gsData.takerToken });
180190        Snapshot memory  takerMakerToken =  BalanceSnapshot.take ({ owner: taker, token: gsData.makerToken });
181191        Snapshot memory  makerTakerToken =  BalanceSnapshot.take ({ owner: address (mockStrategy), token: gsData.takerToken });
182192        Snapshot memory  makerMakerToken =  BalanceSnapshot.take ({ owner: address (mockStrategy), token: gsData.makerToken });
183193
184194        mockStrategy.setOutputAmountAndRecipient (gsData.makerTokenAmount, payable (address (genericSwap)));
185195        vm.expectEmit (true , true , true , true );
186-         emit  Swap (getGSDataHash (gsData), gsData.maker, taker, taker, gsData.takerToken, gsData.takerTokenAmount, gsData.makerToken, gsData.makerTokenAmount );
196+         emit  Swap (getGSDataHash (gsData), gsData.maker, taker, taker, gsData.takerToken, gsData.takerTokenAmount, gsData.makerToken, realChangedInGS );
187197        vm.prank (taker);
188198        genericSwap.executeSwap { value: gsData.takerTokenAmount }(gsData, defaultTakerPermit);
189199
190200        takerTakerToken.assertChange (- int256 (gsData.takerTokenAmount));
191-         takerMakerToken.assertChange (int256 (gsData.makerTokenAmount ));
201+         takerMakerToken.assertChange (int256 (realChangedInGS ));
192202        makerTakerToken.assertChange (int256 (gsData.takerTokenAmount));
193203        makerMakerToken.assertChange (- int256 (gsData.makerTokenAmount));
194204    }
@@ -200,19 +210,21 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
200210        gsData.makerTokenAmount =  1  ether ;
201211        gsData.minMakerTokenAmount =  1  ether  -  1000 ;
202212
213+         uint256  realChangedInGS =  gsData.makerTokenAmount -  1 ; // leaving 1 wei in GS 
214+ 
203215        Snapshot memory  takerTakerToken =  BalanceSnapshot.take ({ owner: taker, token: gsData.takerToken });
204216        Snapshot memory  takerMakerToken =  BalanceSnapshot.take ({ owner: taker, token: gsData.makerToken });
205217        Snapshot memory  makerTakerToken =  BalanceSnapshot.take ({ owner: address (mockStrategy), token: gsData.takerToken });
206218        Snapshot memory  makerMakerToken =  BalanceSnapshot.take ({ owner: address (mockStrategy), token: gsData.makerToken });
207219
208220        mockStrategy.setOutputAmountAndRecipient (gsData.makerTokenAmount, payable (address (genericSwap)));
209221        vm.expectEmit (true , true , true , true );
210-         emit  Swap (getGSDataHash (gsData), gsData.maker, taker, taker, gsData.takerToken, gsData.takerTokenAmount, gsData.makerToken, gsData.makerTokenAmount );
222+         emit  Swap (getGSDataHash (gsData), gsData.maker, taker, taker, gsData.takerToken, gsData.takerTokenAmount, gsData.makerToken, realChangedInGS );
211223        vm.prank (taker);
212224        genericSwap.executeSwap (gsData, defaultTakerPermit);
213225
214226        takerTakerToken.assertChange (- int256 (gsData.takerTokenAmount));
215-         takerMakerToken.assertChange (int256 (gsData.makerTokenAmount ));
227+         takerMakerToken.assertChange (int256 (realChangedInGS ));
216228        makerTakerToken.assertChange (int256 (gsData.takerTokenAmount));
217229        makerMakerToken.assertChange (- int256 (gsData.makerTokenAmount));
218230    }
@@ -306,4 +318,73 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper
306318        vm.expectRevert (IGenericSwap.AlreadyFilled.selector );
307319        genericSwap.executeSwapWithSig (defaultGSData, defaultTakerPermit, taker, takerSig);
308320    }
321+ 
322+     function testLeaveOneWeiWithMultipleUsers  () public  {
323+         Snapshot memory  takerTakerToken =  BalanceSnapshot.take ({ owner: taker, token: defaultGSData.takerToken });
324+         Snapshot memory  takerMakerToken =  BalanceSnapshot.take ({ owner: taker, token: defaultGSData.makerToken });
325+         Snapshot memory  gsTakerToken =  BalanceSnapshot.take ({ owner: address (genericSwap), token: defaultGSData.takerToken });
326+         Snapshot memory  gsMakerToken =  BalanceSnapshot.take ({ owner: address (genericSwap), token: defaultGSData.makerToken });
327+         Snapshot memory  makerTakerToken =  BalanceSnapshot.take ({ owner: defaultGSData.maker, token: defaultGSData.takerToken });
328+         Snapshot memory  makerMakerToken =  BalanceSnapshot.take ({ owner: defaultGSData.maker, token: defaultGSData.makerToken });
329+ 
330+         // the first user: taker 
331+         // his makerTokenAmount has already been reduced by 2 in the setup function 
332+         // leaving 1 wei in GS and SOS separately 
333+         vm.expectEmit (true , true , true , true );
334+         emit  Swap (
335+             getGSDataHash (defaultGSData),
336+             defaultGSData.maker,
337+             taker,
338+             taker,
339+             defaultGSData.takerToken,
340+             defaultGSData.takerTokenAmount,
341+             defaultGSData.makerToken,
342+             defaultGSData.makerTokenAmount
343+         );
344+ 
345+         vm.prank (taker);
346+         genericSwap.executeSwap (defaultGSData, defaultTakerPermit);
347+ 
348+         // the second user: Alice 
349+         // his makerTokenAmount is recalculate by `quoteExactInput() function base on the current state` 
350+         // but there is no need to reduce it by 2 this time 
351+         aliceGSData =  defaultGSData;
352+ 
353+         IUniswapV3Quoter v3Quoter =  IUniswapV3Quoter (UNISWAP_V3_QUOTER_ADDRESS);
354+         bytes  memory  encodedPath =  UniswapV3.encodePath (defaultPath, defaultV3Fees);
355+         uint256  aliceExpectedOut =  v3Quoter.quoteExactInput (encodedPath, defaultInputAmount);
356+ 
357+         aliceGSData.recipient =  payable (alice);
358+         aliceGSData.makerTokenAmount =  aliceExpectedOut;
359+         alicePermit =  getTokenlonPermit2Data (alice, alicePrivateKey, aliceGSData.takerToken, address (genericSwap));
360+ 
361+         Snapshot memory  aliceTakerToken =  BalanceSnapshot.take ({ owner: alice, token: aliceGSData.takerToken });
362+         Snapshot memory  aliceMakerToken =  BalanceSnapshot.take ({ owner: alice, token: aliceGSData.makerToken });
363+ 
364+         vm.expectEmit (true , true , true , true );
365+ 
366+         emit  Swap (
367+             getGSDataHash (aliceGSData),
368+             aliceGSData.maker,
369+             alice,
370+             alice,
371+             aliceGSData.takerToken,
372+             aliceGSData.takerTokenAmount,
373+             aliceGSData.makerToken,
374+             aliceGSData.makerTokenAmount
375+         );
376+ 
377+         vm.startPrank (alice);
378+         genericSwap.executeSwap (aliceGSData, alicePermit);
379+         vm.stopPrank ();
380+ 
381+         takerTakerToken.assertChange (- int256 (defaultGSData.takerTokenAmount));
382+         takerMakerToken.assertChange (int256 (defaultGSData.makerTokenAmount));
383+         aliceTakerToken.assertChange (- int256 (aliceGSData.takerTokenAmount));
384+         aliceMakerToken.assertChange (int256 (aliceGSData.makerTokenAmount));
385+         gsTakerToken.assertChange (0 );
386+         gsMakerToken.assertChange (1 );
387+         makerTakerToken.assertChange (0 );
388+         makerMakerToken.assertChange (1 );
389+     }
309390}
0 commit comments