@zoralabs/limit-orders 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/.turbo/turbo-build$colon$js.log +50 -49
  2. package/CHANGELOG.md +73 -0
  3. package/abis/ISetLimitOrderConfig.json +27 -0
  4. package/abis/IWETH.json +118 -0
  5. package/abis/IZoraLimitOrderBook.json +5 -0
  6. package/abis/LimitOrderLiquidity.json +7 -0
  7. package/abis/LimitOrderViews.json +62 -0
  8. package/abis/{SimpleAccessManaged.json → Ownable.json} +29 -10
  9. package/abis/Ownable2Step.json +115 -0
  10. package/abis/PermittedCallers.json +181 -0
  11. package/abis/SwapWithLimitOrders.json +134 -14
  12. package/abis/ZoraLimitOrderBook.json +187 -35
  13. package/cache/solidity-files-cache.json +1 -1
  14. package/dist/index.cjs +219 -34
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.js +219 -34
  17. package/dist/index.js.map +1 -1
  18. package/dist/wagmiGenerated.d.ts +254 -41
  19. package/dist/wagmiGenerated.d.ts.map +1 -1
  20. package/out/BalanceDelta.sol/BalanceDeltaLibrary.json +1 -1
  21. package/out/BeforeSwapDelta.sol/BeforeSwapDeltaLibrary.json +1 -1
  22. package/out/BitMath.sol/BitMath.json +1 -1
  23. package/out/BytesLib.sol/BytesLib.json +1 -1
  24. package/out/CoinCommon.sol/CoinCommon.json +1 -1
  25. package/out/CoinConfigurationVersions.sol/CoinConfigurationVersions.json +1 -1
  26. package/out/CoinConstants.sol/CoinConstants.json +1 -1
  27. package/out/Context.sol/Context.json +1 -1
  28. package/out/Currency.sol/CurrencyLibrary.json +1 -1
  29. package/out/CurrencyReserves.sol/CurrencyReserves.json +1 -1
  30. package/out/CustomRevert.sol/CustomRevert.json +1 -1
  31. package/out/DopplerMath.sol/DopplerMath.json +1 -1
  32. package/out/FixedPoint128.sol/FixedPoint128.json +1 -1
  33. package/out/FixedPoint96.sol/FixedPoint96.json +1 -1
  34. package/out/FullMath.sol/FullMath.json +1 -1
  35. package/out/IAllowanceTransfer.sol/IAllowanceTransfer.json +1 -1
  36. package/out/ICoin.sol/ICoin.json +1 -1
  37. package/out/ICoin.sol/IHasCoinType.json +1 -1
  38. package/out/ICoin.sol/IHasPoolKey.json +1 -1
  39. package/out/ICoin.sol/IHasSwapPath.json +1 -1
  40. package/out/ICoin.sol/IHasTotalSupplyForPositions.json +1 -1
  41. package/out/IDeployedCoinVersionLookup.sol/IDeployedCoinVersionLookup.json +1 -1
  42. package/out/IDopplerErrors.sol/IDopplerErrors.json +1 -1
  43. package/out/IEIP712.sol/IEIP712.json +1 -1
  44. package/out/IERC1363.sol/IERC1363.json +1 -1
  45. package/out/IERC165.sol/IERC165.json +1 -1
  46. package/out/IERC20.sol/IERC20.json +1 -1
  47. package/out/IERC20Minimal.sol/IERC20Minimal.json +1 -1
  48. package/out/IERC6909Claims.sol/IERC6909Claims.json +1 -1
  49. package/out/IERC7572.sol/IERC7572.json +1 -1
  50. package/out/IExtsload.sol/IExtsload.json +1 -1
  51. package/out/IExttload.sol/IExttload.json +1 -1
  52. package/out/IHasRewardsRecipients.sol/IHasRewardsRecipients.json +1 -1
  53. package/out/IHooks.sol/IHooks.json +1 -1
  54. package/out/IMsgSender.sol/IMsgSender.json +1 -1
  55. package/out/IPoolManager.sol/IPoolManager.json +1 -1
  56. package/out/IProtocolFees.sol/IProtocolFees.json +1 -1
  57. package/out/ISetLimitOrderConfig.sol/ISetLimitOrderConfig.json +1 -0
  58. package/out/ISupportsLimitOrderFill.sol/ISupportsLimitOrderFill.json +1 -1
  59. package/out/ISwapPathRouter.sol/ISwapPathRouter.json +1 -1
  60. package/out/ISwapRouter.sol/ISwapRouter.json +1 -1
  61. package/out/IUniswapV3SwapCallback.sol/IUniswapV3SwapCallback.json +1 -1
  62. package/out/IUpgradeableV4Hook.sol/IUpgradeableDestinationV4Hook.json +1 -1
  63. package/out/IUpgradeableV4Hook.sol/IUpgradeableDestinationV4HookWithUpdateableFee.json +1 -1
  64. package/out/IUpgradeableV4Hook.sol/IUpgradeableV4Hook.json +1 -1
  65. package/out/IWETH.sol/IWETH.json +1 -0
  66. package/out/IZoraHookRegistry.sol/IZoraHookRegistry.json +1 -1
  67. package/out/IZoraLimitOrderBook.sol/IZoraLimitOrderBook.json +1 -1
  68. package/out/IZoraLimitOrderBookCoinsInterface.sol/IZoraLimitOrderBookCoinsInterface.json +1 -1
  69. package/out/IZoraV4CoinHook.sol/IZoraV4CoinHook.json +1 -1
  70. package/out/LimitOrderBitmap.sol/LimitOrderBitmap.json +1 -1
  71. package/out/LimitOrderCommon.sol/LimitOrderCommon.json +1 -1
  72. package/out/LimitOrderCreate.sol/LimitOrderCreate.json +1 -1
  73. package/out/LimitOrderFill.sol/LimitOrderFill.json +1 -1
  74. package/out/LimitOrderLiquidity.sol/LimitOrderLiquidity.json +1 -1
  75. package/out/LimitOrderQueues.sol/LimitOrderQueues.json +1 -1
  76. package/out/LimitOrderStorage.sol/LimitOrderStorage.json +1 -1
  77. package/out/LimitOrderTypes.sol/LimitOrderTypes.json +1 -1
  78. package/out/LimitOrderViews.sol/LimitOrderViews.json +1 -0
  79. package/out/LimitOrderWithdraw.sol/LimitOrderWithdraw.json +1 -1
  80. package/out/LiquidityAmounts.sol/LiquidityAmounts.json +1 -1
  81. package/out/LiquidityMath.sol/LiquidityMath.json +1 -1
  82. package/out/Lock.sol/Lock.json +1 -1
  83. package/out/NonzeroDeltaCount.sol/NonzeroDeltaCount.json +1 -1
  84. package/out/Ownable.sol/Ownable.json +1 -0
  85. package/out/Ownable2Step.sol/Ownable2Step.json +1 -0
  86. package/out/Path.sol/Path.json +1 -1
  87. package/out/PathKey.sol/PathKeyLibrary.json +1 -1
  88. package/out/Permit2Payments.sol/Permit2Payments.json +1 -1
  89. package/out/PermittedCallers.sol/PermittedCallers.json +1 -0
  90. package/out/PoolId.sol/PoolIdLibrary.json +1 -1
  91. package/out/Position.sol/Position.json +1 -1
  92. package/out/SafeCast.sol/SafeCast.json +1 -1
  93. package/out/SafeCast160.sol/SafeCast160.json +1 -1
  94. package/out/SafeERC20.sol/SafeERC20.json +1 -1
  95. package/out/SqrtPriceMath.sol/SqrtPriceMath.json +1 -1
  96. package/out/StateLibrary.sol/StateLibrary.json +1 -1
  97. package/out/SwapLimitOrders.sol/SwapLimitOrders.json +1 -1
  98. package/out/SwapWithLimitOrders.sol/SwapWithLimitOrders.json +1 -1
  99. package/out/TickBitmap.sol/TickBitmap.json +1 -1
  100. package/out/TickMath.sol/TickMath.json +1 -1
  101. package/out/TransientSlot.sol/TransientSlot.json +1 -1
  102. package/out/TransientStateLibrary.sol/TransientStateLibrary.json +1 -1
  103. package/out/UniV4SwapToCurrency.sol/UniV4SwapToCurrency.json +1 -1
  104. package/out/UnsafeMath.sol/UnsafeMath.json +1 -1
  105. package/out/V3ToV4SwapLib.sol/V3ToV4SwapLib.json +1 -1
  106. package/out/ZoraLimitOrderBook.sol/ZoraLimitOrderBook.json +1 -1
  107. package/out/build-info/37e0124d88d60569.json +1 -0
  108. package/out/uniswap/BitMath.sol/BitMath.json +1 -1
  109. package/out/uniswap/CustomRevert.sol/CustomRevert.json +1 -1
  110. package/out/uniswap/FullMath.sol/FullMath.json +1 -1
  111. package/out/uniswap/SafeCast.sol/SafeCast.json +1 -1
  112. package/out/uniswap/TickMath.sol/TickMath.json +1 -1
  113. package/package/wagmiGenerated.ts +218 -33
  114. package/package.json +1 -1
  115. package/src/IZoraLimitOrderBook.sol +5 -5
  116. package/src/ZoraLimitOrderBook.sol +24 -41
  117. package/src/access/PermittedCallers.sol +41 -0
  118. package/src/libs/LimitOrderBitmap.sol +0 -51
  119. package/src/libs/LimitOrderCommon.sol +48 -30
  120. package/src/libs/LimitOrderCreate.sol +5 -18
  121. package/src/libs/LimitOrderFill.sol +32 -161
  122. package/src/libs/LimitOrderLiquidity.sol +92 -71
  123. package/src/libs/LimitOrderViews.sol +168 -0
  124. package/src/libs/LimitOrderWithdraw.sol +13 -4
  125. package/src/libs/SwapLimitOrders.sol +14 -7
  126. package/src/router/ISetLimitOrderConfig.sol +12 -0
  127. package/src/router/SwapWithLimitOrders.sol +46 -33
  128. package/test/LimitOrderAccessControl.t.sol +173 -156
  129. package/test/LimitOrderBitmap.t.sol +13 -7
  130. package/test/LimitOrderFill.t.sol +42 -4
  131. package/test/LimitOrderLibraries.t.sol +18 -10
  132. package/test/LimitOrderLiquidityPayouts.t.sol +280 -3
  133. package/test/LimitOrderWithdraw.t.sol +28 -1
  134. package/test/SwapWithLimitOrders.t.sol +3 -5
  135. package/test/SwapWithLimitOrdersRouter.t.sol +108 -13
  136. package/test/gas/LimitOrderFillGas.t.sol +0 -7
  137. package/test/gas/LimitOrderSwapGas.t.sol +0 -6
  138. package/test/unit/LimitOrderBitmapUnit.t.sol +0 -134
  139. package/test/unit/LimitOrderCreateUnit.t.sol +32 -0
  140. package/test/unit/SwapLimitOrdersUnit.t.sol +231 -33
  141. package/test/unit/SwapLimitOrdersValidation.t.sol +28 -42
  142. package/test/unit/SwapWithLimitOrdersUnit.t.sol +21 -88
  143. package/test/utils/BaseTest.sol +34 -22
  144. package/test/utils/MockWETH.sol +39 -0
  145. package/test/utils/TestableZoraLimitOrderBook.sol +5 -7
  146. package/abis/IAuthority.json +0 -31
  147. package/abis/SimpleAccessManager.json +0 -351
  148. package/out/IAuthority.sol/IAuthority.json +0 -1
  149. package/out/SimpleAccessManaged.sol/SimpleAccessManaged.json +0 -1
  150. package/out/SimpleAccessManager.sol/SimpleAccessManager.json +0 -1
  151. package/out/build-info/69718f10d1dc37f0.json +0 -1
  152. package/src/access/SimpleAccessManaged.sol +0 -76
  153. package/src/access/SimpleAccessManager.sol +0 -268
  154. package/test/SimpleAccessManager.t.sol +0 -420
@@ -32,8 +32,8 @@ contract SwapLimitOrdersWrapper {
32
32
  return false;
33
33
  }
34
34
 
35
- // Must be positive and above minimum threshold
36
- return coinDelta > 0 && uint128(coinDelta) >= SwapLimitOrders.MIN_LIMIT_ORDER_SIZE;
35
+ // Must be positive
36
+ return coinDelta > 0;
37
37
  }
38
38
  }
39
39
 
@@ -44,7 +44,7 @@ contract SwapLimitOrdersUnitTest is Test {
44
44
  int24 constant TICK_SPACING = 200;
45
45
  uint256 constant MULTIPLE_SCALE = 1e18;
46
46
  uint256 constant PERCENT_SCALE = 10_000;
47
- uint256 constant MIN_LIMIT_ORDER_SIZE = 1e18;
47
+ uint256 constant TEST_ORDER_SIZE = 1e18; // Convenient test size (1 token for 18-decimal)
48
48
 
49
49
  PoolKey internal testKey;
50
50
  SwapLimitOrdersWrapper internal wrapper;
@@ -202,8 +202,8 @@ contract SwapLimitOrdersUnitTest is Test {
202
202
  assertEq(totalPercent, 10000, "total percent should be 10000");
203
203
  }
204
204
 
205
- /// @notice Tests computeOrders with totalSize below MIN_LIMIT_ORDER_SIZE (dust)
206
- function test_computeOrders_belowMinSize_returnsEmpty() public {
205
+ /// @notice Tests computeOrders with totalSize of zero returns empty
206
+ function test_computeOrders_zeroSize_returnsEmpty() public {
207
207
  LimitOrderConfig memory params;
208
208
  params.multiples = new uint256[](2);
209
209
  params.percentages = new uint256[](2);
@@ -212,7 +212,7 @@ contract SwapLimitOrdersUnitTest is Test {
212
212
  params.percentages[0] = 5000;
213
213
  params.percentages[1] = 5000;
214
214
 
215
- uint128 totalSize = uint128(MIN_LIMIT_ORDER_SIZE - 1); // Below minimum
215
+ uint128 totalSize = 0;
216
216
  int24 baseTick = 0;
217
217
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
218
218
 
@@ -229,11 +229,11 @@ contract SwapLimitOrdersUnitTest is Test {
229
229
  assertEq(orders.sizes.length, 0, "sizes should be empty");
230
230
  assertEq(orders.ticks.length, 0, "ticks should be empty");
231
231
  assertEq(allocated, 0, "allocated should be 0");
232
- assertEq(unallocated, totalSize, "unallocated should equal totalSize");
232
+ assertEq(unallocated, 0, "unallocated should be 0");
233
233
  }
234
234
 
235
- /// @notice Tests computeOrders with totalSize exactly at MIN_LIMIT_ORDER_SIZE
236
- function test_computeOrders_exactlyMinSize_createsOrders() public {
235
+ /// @notice Tests computeOrders with small totalSize creates orders
236
+ function test_computeOrders_smallSize_createsOrders() public {
237
237
  LimitOrderConfig memory params;
238
238
  params.multiples = new uint256[](2);
239
239
  params.percentages = new uint256[](2);
@@ -242,7 +242,7 @@ contract SwapLimitOrdersUnitTest is Test {
242
242
  params.percentages[0] = 5000;
243
243
  params.percentages[1] = 5000;
244
244
 
245
- uint128 totalSize = uint128(MIN_LIMIT_ORDER_SIZE); // Exactly at minimum
245
+ uint128 totalSize = uint128(TEST_ORDER_SIZE); // Exactly at minimum
246
246
  int24 baseTick = 0;
247
247
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
248
248
 
@@ -262,7 +262,7 @@ contract SwapLimitOrdersUnitTest is Test {
262
262
  }
263
263
 
264
264
  /// @notice Tests computeOrders with multiple orders (verifying skip logic exists even if hard to trigger)
265
- /// @dev Note: Zero-rounding skip is virtually impossible with MIN_LIMIT_ORDER_SIZE=1e18 and PERCENT_SCALE=10000
265
+ /// @dev Note: Zero-rounding skip is virtually impossible with reasonable sizes and PERCENT_SCALE=10000
266
266
  /// since even 1 basis point of 1e18 = 1e14. The skip logic exists for safety in edge cases.
267
267
  function test_computeOrders_multipleOrders_createsAll() public {
268
268
  LimitOrderConfig memory params;
@@ -273,7 +273,7 @@ contract SwapLimitOrdersUnitTest is Test {
273
273
  params.percentages[0] = 5000;
274
274
  params.percentages[1] = 5000;
275
275
 
276
- uint128 totalSize = uint128(MIN_LIMIT_ORDER_SIZE * 10);
276
+ uint128 totalSize = uint128(TEST_ORDER_SIZE * 10);
277
277
  int24 baseTick = 0;
278
278
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
279
279
 
@@ -302,7 +302,7 @@ contract SwapLimitOrdersUnitTest is Test {
302
302
  params.percentages[i] = 1000; // 10% each (60% total)
303
303
  }
304
304
 
305
- uint128 totalSize = uint128(MIN_LIMIT_ORDER_SIZE * 100);
305
+ uint128 totalSize = uint128(TEST_ORDER_SIZE * 100);
306
306
  int24 baseTick = 0;
307
307
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
308
308
 
@@ -328,7 +328,7 @@ contract SwapLimitOrdersUnitTest is Test {
328
328
  params.multiples[0] = 2 * MULTIPLE_SCALE;
329
329
  params.percentages[0] = 10000;
330
330
 
331
- uint128 totalSize = uint128(MIN_LIMIT_ORDER_SIZE * 10);
331
+ uint128 totalSize = uint128(TEST_ORDER_SIZE * 10);
332
332
  int24 baseTick = 0;
333
333
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
334
334
 
@@ -355,7 +355,7 @@ contract SwapLimitOrdersUnitTest is Test {
355
355
  params.percentages[0] = 6000; // 60%
356
356
  params.percentages[1] = 3000; // 30% (total 90%)
357
357
 
358
- uint128 totalSize = uint128(1000 * MIN_LIMIT_ORDER_SIZE);
358
+ uint128 totalSize = uint128(1000 * TEST_ORDER_SIZE);
359
359
  int24 baseTick = 0;
360
360
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
361
361
 
@@ -392,7 +392,7 @@ contract SwapLimitOrdersUnitTest is Test {
392
392
  params.percentages[1] = 2000; // 20% of remaining
393
393
  params.percentages[2] = 2000; // 20% of remaining
394
394
 
395
- uint128 totalSize = uint128(100 * MIN_LIMIT_ORDER_SIZE); // 100 units for easy math
395
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE); // 100 units for easy math
396
396
  int24 baseTick = 0;
397
397
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
398
398
 
@@ -430,7 +430,7 @@ contract SwapLimitOrdersUnitTest is Test {
430
430
  params.multiples[0] = 1000 * MULTIPLE_SCALE; // 1000x - extremely high
431
431
  params.percentages[0] = 10000; // 100%
432
432
 
433
- uint128 totalSize = uint128(100 * MIN_LIMIT_ORDER_SIZE);
433
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
434
434
  int24 baseTick = 0;
435
435
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
436
436
 
@@ -453,7 +453,7 @@ contract SwapLimitOrdersUnitTest is Test {
453
453
  params.percentages[0] = 5000;
454
454
  params.percentages[1] = 5000;
455
455
 
456
- uint128 totalSize = uint128(100 * MIN_LIMIT_ORDER_SIZE);
456
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
457
457
  int24 baseTick = 10000;
458
458
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
459
459
 
@@ -474,7 +474,7 @@ contract SwapLimitOrdersUnitTest is Test {
474
474
  params.multiples[0] = 1000000 * MULTIPLE_SCALE; // 1,000,000x
475
475
  params.percentages[0] = 10000;
476
476
 
477
- uint128 totalSize = uint128(100 * MIN_LIMIT_ORDER_SIZE);
477
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
478
478
  // Use moderate base tick (TickMath has max/min around ±887272)
479
479
  int24 baseTick = 100000; // Moderate positive tick
480
480
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
@@ -494,7 +494,7 @@ contract SwapLimitOrdersUnitTest is Test {
494
494
  params.multiples[0] = 2 * MULTIPLE_SCALE;
495
495
  params.percentages[0] = 10000;
496
496
 
497
- uint128 totalSize = uint128(100 * MIN_LIMIT_ORDER_SIZE);
497
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
498
498
  // Use moderate negative base tick for currency1
499
499
  int24 baseTick = -100000; // Moderate negative tick
500
500
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
@@ -515,7 +515,7 @@ contract SwapLimitOrdersUnitTest is Test {
515
515
  params.multiples[0] = MULTIPLE_SCALE + (MULTIPLE_SCALE / 100); // 1.01x
516
516
  params.percentages[0] = 10000;
517
517
 
518
- uint128 totalSize = uint128(100 * MIN_LIMIT_ORDER_SIZE);
518
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
519
519
  int24 baseTick = 0;
520
520
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
521
521
 
@@ -534,7 +534,7 @@ contract SwapLimitOrdersUnitTest is Test {
534
534
  params.multiples[0] = MULTIPLE_SCALE + (MULTIPLE_SCALE / 100); // 1.01x
535
535
  params.percentages[0] = 10000;
536
536
 
537
- uint128 totalSize = uint128(100 * MIN_LIMIT_ORDER_SIZE);
537
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
538
538
  int24 baseTick = 0;
539
539
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
540
540
 
@@ -553,7 +553,7 @@ contract SwapLimitOrdersUnitTest is Test {
553
553
  params.multiples[0] = 0; // Zero multiple - should revert in _sqrtMultiple
554
554
  params.percentages[0] = 10000;
555
555
 
556
- uint128 totalSize = uint128(100 * MIN_LIMIT_ORDER_SIZE);
556
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
557
557
  int24 baseTick = 0;
558
558
  uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
559
559
 
@@ -571,7 +571,7 @@ contract SwapLimitOrdersUnitTest is Test {
571
571
 
572
572
  bool isCoinBuy = false; // NOT isCoinBuy - tests first branch
573
573
  address swapper = address(0x1234);
574
- int128 coinDelta = int128(int256(MIN_LIMIT_ORDER_SIZE * 2));
574
+ int128 coinDelta = int128(int256(TEST_ORDER_SIZE * 2));
575
575
 
576
576
  assertFalse(wrapper.isLimitOrder(isCoinBuy, swapper, coinDelta, params), "should return false when not coin buy");
577
577
  }
@@ -584,7 +584,7 @@ contract SwapLimitOrdersUnitTest is Test {
584
584
 
585
585
  bool isCoinBuy = true; // Pass first condition
586
586
  address swapper = address(0x1234);
587
- int128 coinDelta = int128(int256(MIN_LIMIT_ORDER_SIZE * 2));
587
+ int128 coinDelta = int128(int256(TEST_ORDER_SIZE * 2));
588
588
 
589
589
  assertFalse(wrapper.isLimitOrder(isCoinBuy, swapper, coinDelta, params), "should return false when no orders");
590
590
  }
@@ -597,8 +597,8 @@ contract SwapLimitOrdersUnitTest is Test {
597
597
 
598
598
  bool isCoinBuy = true;
599
599
  address swapper = address(0x1234);
600
- // casting to 'int256' is safe because MIN_LIMIT_ORDER_SIZE will not overflow int128
601
- int128 coinDelta = int128(int256(MIN_LIMIT_ORDER_SIZE * 2));
600
+ // casting to 'int256' is safe because TEST_ORDER_SIZE will not overflow int128
601
+ int128 coinDelta = int128(int256(TEST_ORDER_SIZE * 2));
602
602
 
603
603
  assertFalse(wrapper.isLimitOrder(isCoinBuy, swapper, coinDelta, params), "should return false when no orders");
604
604
  }
@@ -611,7 +611,7 @@ contract SwapLimitOrdersUnitTest is Test {
611
611
 
612
612
  bool isCoinBuy = true;
613
613
  address swapper = address(0); // Zero address
614
- int128 coinDelta = int128(int256(MIN_LIMIT_ORDER_SIZE * 2));
614
+ int128 coinDelta = int128(int256(TEST_ORDER_SIZE * 2));
615
615
 
616
616
  assertFalse(wrapper.isLimitOrder(isCoinBuy, swapper, coinDelta, params), "should return false when swapper is zero");
617
617
  }
@@ -642,17 +642,17 @@ contract SwapLimitOrdersUnitTest is Test {
642
642
  assertFalse(wrapper.isLimitOrder(isCoinBuy, swapper, coinDelta, params), "should return false for zero delta");
643
643
  }
644
644
 
645
- /// @notice Tests isLimitOrder when coinDelta below MIN_LIMIT_ORDER_SIZE (dust)
646
- function test_isLimitOrder_belowMinSize_returnsFalse() public {
645
+ /// @notice Tests isLimitOrder accepts any positive coinDelta
646
+ function test_isLimitOrder_smallSize_returnsTrue() public {
647
647
  LimitOrderConfig memory params;
648
648
  params.multiples = new uint256[](1);
649
649
  params.percentages = new uint256[](1);
650
650
 
651
651
  bool isCoinBuy = true;
652
652
  address swapper = address(0x1234);
653
- int128 coinDelta = int128(int256(MIN_LIMIT_ORDER_SIZE - 1)); // Below minimum
653
+ int128 coinDelta = 1; // Small positive amount
654
654
 
655
- assertFalse(wrapper.isLimitOrder(isCoinBuy, swapper, coinDelta, params), "should return false when below min size");
655
+ assertTrue(wrapper.isLimitOrder(isCoinBuy, swapper, coinDelta, params), "should return true for any positive amount");
656
656
  }
657
657
 
658
658
  /// @notice Tests isLimitOrder when all conditions are met (success case)
@@ -665,8 +665,206 @@ contract SwapLimitOrdersUnitTest is Test {
665
665
 
666
666
  bool isCoinBuy = true;
667
667
  address swapper = address(0x1234);
668
- int128 coinDelta = int128(int256(MIN_LIMIT_ORDER_SIZE * 2));
668
+ int128 coinDelta = int128(int256(TEST_ORDER_SIZE * 2));
669
669
 
670
670
  assertTrue(wrapper.isLimitOrder(isCoinBuy, swapper, coinDelta, params), "should return true when all conditions met");
671
671
  }
672
+
673
+ /// @notice Tests computeOrders with misaligned baseTick produces aligned output ticks
674
+ /// @dev This test verifies the fix for MKT-24: tick misalignment in minAway calculation
675
+ function test_computeOrders_misalignedBaseTick_producesValidTicks() public {
676
+ // Use a pool with tickSpacing = 10 for easier verification
677
+ PoolKey memory smallSpacingKey = PoolKey({
678
+ currency0: Currency.wrap(address(0x1000)),
679
+ currency1: Currency.wrap(address(0x2000)),
680
+ fee: 3000,
681
+ tickSpacing: 10, // Small tick spacing to make misalignment easier to reproduce
682
+ hooks: IHooks(address(0))
683
+ });
684
+
685
+ LimitOrderConfig memory params;
686
+ params.multiples = new uint256[](1);
687
+ params.percentages = new uint256[](1);
688
+ // Use small multiple that would normally produce tick close to base
689
+ params.multiples[0] = MULTIPLE_SCALE + (MULTIPLE_SCALE / 100); // 1.01x
690
+ params.percentages[0] = 10000;
691
+
692
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
693
+
694
+ // Use a baseTick that is NOT aligned to tickSpacing=10
695
+ int24 baseTick = 205; // Not divisible by 10 - misaligned
696
+ uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(baseTick);
697
+
698
+ // Test for currency0 (buy orders, tick should be >= baseTick + tickSpacing)
699
+ (Orders memory orders, , ) = SwapLimitOrders.computeOrders(
700
+ smallSpacingKey,
701
+ true, // isCurrency0
702
+ totalSize,
703
+ baseTick,
704
+ sqrtPriceX96,
705
+ params
706
+ );
707
+
708
+ // Verify the returned tick is aligned to tickSpacing
709
+ assertEq(orders.ticks[0] % 10, 0, "tick must be aligned to tick spacing of 10");
710
+
711
+ // Verify tick is at least one tickSpacing away from the aligned baseTick
712
+ // For currency0, baseTick=205 should align down to 200, so minAway=210
713
+ assertGe(orders.ticks[0], 210, "tick should be >= aligned baseTick (200) + spacing (10)");
714
+
715
+ // Test for currency1 (sell orders, tick should be <= baseTick - tickSpacing)
716
+ (Orders memory orders1, , ) = SwapLimitOrders.computeOrders(
717
+ smallSpacingKey,
718
+ false, // !isCurrency0
719
+ totalSize,
720
+ baseTick,
721
+ sqrtPriceX96,
722
+ params
723
+ );
724
+
725
+ // Verify the returned tick is aligned to tickSpacing
726
+ assertEq(orders1.ticks[0] % 10, 0, "tick must be aligned to tick spacing of 10");
727
+
728
+ // Verify tick is at least one tickSpacing away from the aligned baseTick
729
+ // For currency1, baseTick=205 should align up to 210, so minAway=200
730
+ assertLe(orders1.ticks[0], 200, "tick should be <= aligned baseTick (210) - spacing (10)");
731
+ }
732
+
733
+ /// @notice Tests computeOrders with various misaligned baseTicks and tick spacings
734
+ function test_computeOrders_variousMisalignments_allProduceValidTicks() public view {
735
+ LimitOrderConfig memory params;
736
+ params.multiples = new uint256[](1);
737
+ params.percentages = new uint256[](1);
738
+ params.multiples[0] = MULTIPLE_SCALE + (MULTIPLE_SCALE / 50); // 1.02x
739
+ params.percentages[0] = 10000;
740
+
741
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
742
+
743
+ // Test case 1: baseTick=205, tickSpacing=10
744
+ PoolKey memory key10 = testKey;
745
+ key10.tickSpacing = 10;
746
+ int24 baseTick1 = 205;
747
+ uint160 sqrtPrice1 = TickMath.getSqrtPriceAtTick(baseTick1);
748
+ (Orders memory orders1, , ) = SwapLimitOrders.computeOrders(key10, true, totalSize, baseTick1, sqrtPrice1, params);
749
+ assertEq(orders1.ticks[0] % 10, 0, "case 1: tick must be aligned to spacing 10");
750
+
751
+ // Test case 2: baseTick=1505, tickSpacing=200
752
+ PoolKey memory key200 = testKey;
753
+ key200.tickSpacing = 200;
754
+ int24 baseTick2 = 1505;
755
+ uint160 sqrtPrice2 = TickMath.getSqrtPriceAtTick(baseTick2);
756
+ (Orders memory orders2, , ) = SwapLimitOrders.computeOrders(key200, true, totalSize, baseTick2, sqrtPrice2, params);
757
+ assertEq(orders2.ticks[0] % 200, 0, "case 2: tick must be aligned to spacing 200");
758
+
759
+ // Test case 3: negative misaligned baseTick=-95, tickSpacing=10
760
+ int24 baseTick3 = -95;
761
+ uint160 sqrtPrice3 = TickMath.getSqrtPriceAtTick(baseTick3);
762
+ (Orders memory orders3, , ) = SwapLimitOrders.computeOrders(key10, true, totalSize, baseTick3, sqrtPrice3, params);
763
+ assertEq(orders3.ticks[0] % 10, 0, "case 3: tick must be aligned to spacing 10");
764
+ }
765
+
766
+ /// @notice Tests computeOrders with already-aligned baseTicks remain aligned
767
+ /// @dev Verifies that the fix doesn't break the common case where baseTick is already aligned
768
+ function test_computeOrders_alignedBaseTick_remainsAligned() public view {
769
+ LimitOrderConfig memory params;
770
+ params.multiples = new uint256[](1);
771
+ params.percentages = new uint256[](1);
772
+ params.multiples[0] = MULTIPLE_SCALE + (MULTIPLE_SCALE / 50); // 1.02x
773
+ params.percentages[0] = 10000;
774
+
775
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
776
+
777
+ // Test case 1: baseTick=200 (aligned), tickSpacing=10
778
+ PoolKey memory key10 = testKey;
779
+ key10.tickSpacing = 10;
780
+ int24 baseTick1 = 200; // Already divisible by 10
781
+ uint160 sqrtPrice1 = TickMath.getSqrtPriceAtTick(baseTick1);
782
+ (Orders memory orders1, , ) = SwapLimitOrders.computeOrders(key10, true, totalSize, baseTick1, sqrtPrice1, params);
783
+ assertEq(orders1.ticks[0] % 10, 0, "aligned case 1: tick must be aligned to spacing 10");
784
+ assertGe(orders1.ticks[0], baseTick1 + 10, "aligned case 1: tick should be >= baseTick + spacing");
785
+
786
+ // Test case 2: baseTick=1400 (aligned), tickSpacing=200
787
+ PoolKey memory key200 = testKey;
788
+ key200.tickSpacing = 200;
789
+ int24 baseTick2 = 1400; // Already divisible by 200
790
+ uint160 sqrtPrice2 = TickMath.getSqrtPriceAtTick(baseTick2);
791
+ (Orders memory orders2, , ) = SwapLimitOrders.computeOrders(key200, true, totalSize, baseTick2, sqrtPrice2, params);
792
+ assertEq(orders2.ticks[0] % 200, 0, "aligned case 2: tick must be aligned to spacing 200");
793
+ assertGe(orders2.ticks[0], baseTick2 + 200, "aligned case 2: tick should be >= baseTick + spacing");
794
+
795
+ // Test case 3: baseTick=0 (aligned), tickSpacing=10
796
+ int24 baseTick3 = 0; // Already divisible by any spacing
797
+ uint160 sqrtPrice3 = TickMath.getSqrtPriceAtTick(baseTick3);
798
+ (Orders memory orders3, , ) = SwapLimitOrders.computeOrders(key10, true, totalSize, baseTick3, sqrtPrice3, params);
799
+ assertEq(orders3.ticks[0] % 10, 0, "aligned case 3: tick must be aligned to spacing 10");
800
+ assertGe(orders3.ticks[0], baseTick3 + 10, "aligned case 3: tick should be >= baseTick + spacing");
801
+
802
+ // Test case 4: negative aligned baseTick=-100, tickSpacing=10
803
+ int24 baseTick4 = -100; // Already divisible by 10
804
+ uint160 sqrtPrice4 = TickMath.getSqrtPriceAtTick(baseTick4);
805
+ (Orders memory orders4, , ) = SwapLimitOrders.computeOrders(key10, true, totalSize, baseTick4, sqrtPrice4, params);
806
+ assertEq(orders4.ticks[0] % 10, 0, "aligned case 4: tick must be aligned to spacing 10");
807
+ assertGe(orders4.ticks[0], baseTick4 + 10, "aligned case 4: tick should be >= baseTick + spacing");
808
+ }
809
+
810
+ /// @notice Tests fix for MKT-35: skip orders when baseTick at maxTick
811
+ /// @dev Uses baseTick at maxTick to simulate swap exhausting liquidity
812
+ function test_computeOrders_baseTickAtMaxTick_skipsOrders() public view {
813
+ LimitOrderConfig memory params;
814
+ params.multiples = new uint256[](1);
815
+ params.percentages = new uint256[](1);
816
+ params.multiples[0] = 2 * MULTIPLE_SCALE;
817
+ params.percentages[0] = 10000;
818
+
819
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
820
+ int24 maxTick = TickMath.maxUsableTick(TICK_SPACING);
821
+ int24 baseTick = maxTick;
822
+ // Use MAX_SQRT_PRICE directly since we can't compute sqrt price at maxTick
823
+ uint160 sqrtPriceX96 = TickMath.MAX_SQRT_PRICE - 1;
824
+
825
+ // For currency0 (buy orders), should skip and return all as unallocated
826
+ (Orders memory orders, uint128 allocated, uint128 unallocated) = SwapLimitOrders.computeOrders(
827
+ testKey,
828
+ true,
829
+ totalSize,
830
+ baseTick,
831
+ sqrtPriceX96,
832
+ params
833
+ );
834
+
835
+ assertEq(orders.sizes.length, 0, "should create no orders");
836
+ assertEq(allocated, 0, "should not allocate any funds");
837
+ assertEq(unallocated, totalSize, "all funds should be unallocated");
838
+ }
839
+
840
+ /// @notice Tests fix for MKT-35: skip orders when baseTick at minTick
841
+ /// @dev Uses baseTick at minTick to simulate swap exhausting liquidity
842
+ function test_computeOrders_baseTickAtMinTick_skipsOrders() public view {
843
+ LimitOrderConfig memory params;
844
+ params.multiples = new uint256[](1);
845
+ params.percentages = new uint256[](1);
846
+ params.multiples[0] = 2 * MULTIPLE_SCALE;
847
+ params.percentages[0] = 10000;
848
+
849
+ uint128 totalSize = uint128(100 * TEST_ORDER_SIZE);
850
+ int24 maxTick = TickMath.maxUsableTick(TICK_SPACING);
851
+ int24 minTick = -maxTick;
852
+ int24 baseTick = minTick;
853
+ // Use MIN_SQRT_PRICE directly since we can't compute sqrt price at minTick
854
+ uint160 sqrtPriceX96 = TickMath.MIN_SQRT_PRICE + 1;
855
+
856
+ // For currency1 (sell orders), should skip and return all as unallocated
857
+ (Orders memory orders, uint128 allocated, uint128 unallocated) = SwapLimitOrders.computeOrders(
858
+ testKey,
859
+ false,
860
+ totalSize,
861
+ baseTick,
862
+ sqrtPriceX96,
863
+ params
864
+ );
865
+
866
+ assertEq(orders.sizes.length, 0, "should create no orders");
867
+ assertEq(allocated, 0, "should not allocate any funds");
868
+ assertEq(unallocated, totalSize, "all funds should be unallocated");
869
+ }
672
870
  }