@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
@@ -14,6 +14,7 @@ contract SwapLimitOrdersValidationTest is Test {
14
14
 
15
15
  PoolKey internal testKey;
16
16
  int24 constant TICK_SPACING = 10;
17
+ uint256 constant TEST_ORDER_SIZE = 1e18; // Convenient test size (1 token for 18-decimal)
17
18
  SwapLimitOrdersWrapper internal wrapper;
18
19
 
19
20
  function setUp() public {
@@ -36,7 +37,7 @@ contract SwapLimitOrdersValidationTest is Test {
36
37
  bool result = wrapper.isLimitOrder(
37
38
  false, // not a coin buy
38
39
  makeAddr("testSwapper"),
39
- int128(uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE)),
40
+ int128(uint128(TEST_ORDER_SIZE)),
40
41
  params
41
42
  );
42
43
 
@@ -46,7 +47,7 @@ contract SwapLimitOrdersValidationTest is Test {
46
47
  function test_isLimitOrder_NoOrders() public {
47
48
  LimitOrderConfig memory params = _createEmptyParams(makeAddr("maker"));
48
49
 
49
- bool result = wrapper.isLimitOrder(true, makeAddr("testSwapper"), int128(uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE)), params);
50
+ bool result = wrapper.isLimitOrder(true, makeAddr("testSwapper"), int128(uint128(TEST_ORDER_SIZE)), params);
50
51
 
51
52
  assertFalse(result, "should return false when no orders configured");
52
53
  }
@@ -57,7 +58,7 @@ contract SwapLimitOrdersValidationTest is Test {
57
58
  bool result = wrapper.isLimitOrder(
58
59
  true,
59
60
  address(0), // zero swapper
60
- int128(uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE)),
61
+ int128(uint128(TEST_ORDER_SIZE)),
61
62
  params
62
63
  );
63
64
 
@@ -90,23 +91,23 @@ contract SwapLimitOrdersValidationTest is Test {
90
91
  assertFalse(result, "should return false for zero coinDelta");
91
92
  }
92
93
 
93
- function test_isLimitOrder_BelowMinimumSize() public {
94
+ function test_isLimitOrder_SmallSize() public {
94
95
  LimitOrderConfig memory params = _createValidParams(makeAddr("maker"), 1);
95
96
 
96
97
  bool result = wrapper.isLimitOrder(
97
98
  true,
98
99
  makeAddr("testSwapper"),
99
- int128(uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE - 1)), // dust amount
100
+ int128(1), // small amount
100
101
  params
101
102
  );
102
103
 
103
- assertFalse(result, "should return false for dust amounts below minimum");
104
+ assertTrue(result, "should return true for any positive amount");
104
105
  }
105
106
 
106
107
  function test_isLimitOrder_ValidCase() public {
107
108
  LimitOrderConfig memory params = _createValidParams(makeAddr("maker"), 1);
108
109
 
109
- bool result = wrapper.isLimitOrder(true, makeAddr("testSwapper"), int128(uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE)), params);
110
+ bool result = wrapper.isLimitOrder(true, makeAddr("testSwapper"), int128(uint128(TEST_ORDER_SIZE)), params);
110
111
 
111
112
  assertTrue(result, "should return true when all conditions met");
112
113
  }
@@ -166,7 +167,7 @@ contract SwapLimitOrdersValidationTest is Test {
166
167
  wrapper.validate(params);
167
168
  }
168
169
 
169
- function test_validate_UnderOneHundredPercent() public {
170
+ function test_validate_UnderOneHundredPercent() public pure {
170
171
  LimitOrderConfig memory params;
171
172
  params.multiples = new uint256[](2);
172
173
  params.percentages = new uint256[](2);
@@ -180,14 +181,13 @@ contract SwapLimitOrdersValidationTest is Test {
180
181
  assertEq(totalPercent, 8000, "should allow undershoot");
181
182
  }
182
183
 
183
- function test_computeOrders_BelowMinimum() public {
184
+ function test_computeOrders_ZeroSize() public {
184
185
  LimitOrderConfig memory params = _createValidParams(makeAddr("maker"), 2);
185
- uint128 dustSize = uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE - 1);
186
186
 
187
187
  (Orders memory orders, uint128 allocated, uint128 unallocated) = SwapLimitOrders.computeOrders(
188
188
  testKey,
189
189
  true, // isCurrency0
190
- uint128(dustSize),
190
+ 0, // zero size
191
191
  0, // baseTick
192
192
  TickMath.getSqrtPriceAtTick(0),
193
193
  params
@@ -196,10 +196,10 @@ contract SwapLimitOrdersValidationTest is Test {
196
196
  assertEq(orders.sizes.length, 0, "should return empty orders");
197
197
  assertEq(orders.ticks.length, 0, "should return empty ticks");
198
198
  assertEq(allocated, 0, "should have zero allocated");
199
- assertEq(unallocated, uint128(dustSize), "all should be unallocated");
199
+ assertEq(unallocated, 0, "unallocated should be zero");
200
200
  }
201
201
 
202
- function test_computeOrders_SkipsZeroSizeOrders() public {
202
+ function test_computeOrders_SkipsZeroSizeOrders() public view {
203
203
  LimitOrderConfig memory params;
204
204
  params.multiples = new uint256[](4);
205
205
  params.percentages = new uint256[](4);
@@ -218,7 +218,7 @@ contract SwapLimitOrdersValidationTest is Test {
218
218
  params.percentages[3] = 1; // 0.01% - may round to zero with small size
219
219
 
220
220
  // Use exactly MIN size which is 1e18
221
- uint128 totalSize = uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE);
221
+ uint128 totalSize = uint128(TEST_ORDER_SIZE);
222
222
 
223
223
  (Orders memory orders, uint128 allocated, uint128 unallocated) = SwapLimitOrders.computeOrders(
224
224
  testKey,
@@ -229,13 +229,13 @@ contract SwapLimitOrdersValidationTest is Test {
229
229
  params
230
230
  );
231
231
 
232
- // With MIN_LIMIT_ORDER_SIZE (1e18) and 1bp = 1e14, orders should not be skipped
232
+ // With TEST_ORDER_SIZE (1e18) and 1bp = 1e14, orders should not be skipped
233
233
  // Let's just verify we get at least one order
234
234
  assertGt(orders.sizes.length, 0, "should create at least one order");
235
235
  assertEq(orders.sizes.length, orders.ticks.length, "sizes and ticks should match");
236
236
  }
237
237
 
238
- function test_computeOrders_ClampsToMaxTick() public {
238
+ function test_computeOrders_ClampsToMaxTick() public view {
239
239
  LimitOrderConfig memory params;
240
240
  params.multiples = new uint256[](1);
241
241
  params.percentages = new uint256[](1);
@@ -246,14 +246,7 @@ contract SwapLimitOrdersValidationTest is Test {
246
246
 
247
247
  int24 maxTick = TickMath.maxUsableTick(TICK_SPACING);
248
248
 
249
- (Orders memory orders, , ) = SwapLimitOrders.computeOrders(
250
- testKey,
251
- true,
252
- uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
253
- 0,
254
- TickMath.getSqrtPriceAtTick(0),
255
- params
256
- );
249
+ (Orders memory orders, , ) = SwapLimitOrders.computeOrders(testKey, true, uint128(TEST_ORDER_SIZE), 0, TickMath.getSqrtPriceAtTick(0), params);
257
250
 
258
251
  assertEq(orders.ticks.length, 1, "should create one order");
259
252
  // Should clamp to max usable tick - the function applies clamping logic
@@ -264,7 +257,7 @@ contract SwapLimitOrdersValidationTest is Test {
264
257
  assertGt(orders.ticks[0], 0, "should be positive tick for buy orders");
265
258
  }
266
259
 
267
- function test_computeOrders_ClampsToMinTick() public {
260
+ function test_computeOrders_ClampsToMinTick() public view {
268
261
  LimitOrderConfig memory params;
269
262
  params.multiples = new uint256[](1);
270
263
  params.percentages = new uint256[](1);
@@ -279,7 +272,7 @@ contract SwapLimitOrdersValidationTest is Test {
279
272
  (Orders memory orders, , ) = SwapLimitOrders.computeOrders(
280
273
  testKey,
281
274
  false, // isCurrency0 = false means selling, inverts multiple
282
- uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
275
+ uint128(TEST_ORDER_SIZE),
283
276
  startTick,
284
277
  TickMath.getSqrtPriceAtTick(startTick),
285
278
  params
@@ -289,7 +282,7 @@ contract SwapLimitOrdersValidationTest is Test {
289
282
  assertLe(orders.ticks[0], TickMath.maxUsableTick(TICK_SPACING), "should be within valid range");
290
283
  }
291
284
 
292
- function test_computeOrders_MinimumSeparationCurrency0() public {
285
+ function test_computeOrders_MinimumSeparationCurrency0() public view {
293
286
  LimitOrderConfig memory params;
294
287
  params.multiples = new uint256[](1);
295
288
  params.percentages = new uint256[](1);
@@ -303,7 +296,7 @@ contract SwapLimitOrdersValidationTest is Test {
303
296
  (Orders memory orders, , ) = SwapLimitOrders.computeOrders(
304
297
  testKey,
305
298
  true, // isCurrency0
306
- uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
299
+ uint128(TEST_ORDER_SIZE),
307
300
  baseTick,
308
301
  TickMath.getSqrtPriceAtTick(baseTick),
309
302
  params
@@ -314,7 +307,7 @@ contract SwapLimitOrdersValidationTest is Test {
314
307
  assertGe(orders.ticks[0], baseTick + TICK_SPACING, "should maintain minimum separation");
315
308
  }
316
309
 
317
- function test_computeOrders_MinimumSeparationCurrency1() public {
310
+ function test_computeOrders_MinimumSeparationCurrency1() public view {
318
311
  LimitOrderConfig memory params;
319
312
  params.multiples = new uint256[](1);
320
313
  params.percentages = new uint256[](1);
@@ -328,7 +321,7 @@ contract SwapLimitOrdersValidationTest is Test {
328
321
  (Orders memory orders, , ) = SwapLimitOrders.computeOrders(
329
322
  testKey,
330
323
  false, // not isCurrency0
331
- uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
324
+ uint128(TEST_ORDER_SIZE),
332
325
  baseTick,
333
326
  TickMath.getSqrtPriceAtTick(baseTick),
334
327
  params
@@ -339,7 +332,7 @@ contract SwapLimitOrdersValidationTest is Test {
339
332
  assertLe(orders.ticks[0], baseTick - TICK_SPACING, "should maintain minimum separation");
340
333
  }
341
334
 
342
- function test_computeOrders_MultiplierInversionForCurrency1() public {
335
+ function test_computeOrders_MultiplierInversionForCurrency1() public view {
343
336
  LimitOrderConfig memory params;
344
337
  params.multiples = new uint256[](1);
345
338
  params.percentages = new uint256[](1);
@@ -354,7 +347,7 @@ contract SwapLimitOrdersValidationTest is Test {
354
347
  (Orders memory orders, , ) = SwapLimitOrders.computeOrders(
355
348
  testKey,
356
349
  false, // not isCurrency0 - triggers inversion
357
- uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
350
+ uint128(TEST_ORDER_SIZE),
358
351
  baseTick,
359
352
  TickMath.getSqrtPriceAtTick(baseTick),
360
353
  params
@@ -365,7 +358,7 @@ contract SwapLimitOrdersValidationTest is Test {
365
358
  assertLt(orders.ticks[0], baseTick, "inverted multiplier should place tick below base");
366
359
  }
367
360
 
368
- function test_computeOrders_AllBoundaryClampingBranches() public {
361
+ function test_computeOrders_AllBoundaryClampingBranches() public view {
369
362
  LimitOrderConfig memory params;
370
363
 
371
364
  // Test 1: aligned > maxTick (line 167)
@@ -374,14 +367,7 @@ contract SwapLimitOrdersValidationTest is Test {
374
367
  params.multiples[0] = 1000000e18;
375
368
  params.percentages[0] = 10000;
376
369
 
377
- (Orders memory orders1, , ) = SwapLimitOrders.computeOrders(
378
- testKey,
379
- true,
380
- uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
381
- 0,
382
- TickMath.getSqrtPriceAtTick(0),
383
- params
384
- );
370
+ (Orders memory orders1, , ) = SwapLimitOrders.computeOrders(testKey, true, uint128(TEST_ORDER_SIZE), 0, TickMath.getSqrtPriceAtTick(0), params);
385
371
  assertLe(orders1.ticks[0], TickMath.maxUsableTick(TICK_SPACING), "should clamp to maxTick");
386
372
 
387
373
  // Test 2: aligned < minTick (line 168)
@@ -390,7 +376,7 @@ contract SwapLimitOrdersValidationTest is Test {
390
376
  (Orders memory orders2, , ) = SwapLimitOrders.computeOrders(
391
377
  testKey,
392
378
  false, // inverted, goes negative
393
- uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
379
+ uint128(TEST_ORDER_SIZE),
394
380
  veryHighTick,
395
381
  TickMath.getSqrtPriceAtTick(veryHighTick),
396
382
  params
@@ -14,24 +14,14 @@ contract SwapWithLimitOrdersLogicWrapper {
14
14
  return true; // Would proceed to fill
15
15
  }
16
16
 
17
- /// @notice Tests tick ordering logic for currency0 (lines 403-406)
18
- function orderTicks_currency0(int24 tickBeforeSwap, int24 tickAfterSwap) external pure returns (int24 startTick, int24 endTick) {
19
- bool isCurrency0 = true;
20
- if (isCurrency0) {
21
- // Currency0 orders need ascending tick range
22
- startTick = tickBeforeSwap < tickAfterSwap ? tickBeforeSwap : tickAfterSwap;
23
- endTick = tickBeforeSwap < tickAfterSwap ? tickAfterSwap : tickBeforeSwap;
24
- }
25
- }
26
-
27
- /// @notice Tests tick ordering logic for currency1 (lines 407-411)
28
- function orderTicks_currency1(int24 tickBeforeSwap, int24 tickAfterSwap) external pure returns (int24 startTick, int24 endTick) {
29
- bool isCurrency0 = false;
30
- if (!isCurrency0) {
31
- // Currency1 orders need descending tick range
32
- startTick = tickBeforeSwap > tickAfterSwap ? tickBeforeSwap : tickAfterSwap;
33
- endTick = tickBeforeSwap > tickAfterSwap ? tickAfterSwap : tickBeforeSwap;
17
+ /// @notice Tests _fillOrders direction derivation from tick movement
18
+ /// @return shouldFill Whether fill should proceed
19
+ /// @return isCurrency0 The derived fill direction
20
+ function deriveFillDirection(int24 tickBeforeSwap, int24 tickAfterSwap) external pure returns (bool shouldFill, bool isCurrency0) {
21
+ if (tickAfterSwap == tickBeforeSwap) {
22
+ return (false, false);
34
23
  }
24
+ return (true, tickAfterSwap > tickBeforeSwap);
35
25
  }
36
26
 
37
27
  /// @notice Tests settlement unallocated branch (line 356)
@@ -109,70 +99,24 @@ contract SwapWithLimitOrdersUnitTest is Test {
109
99
  assertTrue(shouldFill, "should fill when maxFillCount is 1");
110
100
  }
111
101
 
112
- /// @notice Tests currency0 tick ordering when tickBefore < tickAfter (ascending)
113
- function test_orderTicks_currency0_ascending() public view {
114
- int24 tickBefore = 1000;
115
- int24 tickAfter = 2000;
116
-
117
- (int24 startTick, int24 endTick) = wrapper.orderTicks_currency0(tickBefore, tickAfter);
118
-
119
- assertEq(startTick, 1000, "startTick should be tickBefore");
120
- assertEq(endTick, 2000, "endTick should be tickAfter");
102
+ /// @notice Tests fill direction derivation: tick increase currency0
103
+ function test_deriveFillDirection_tickIncreases_isCurrency0() public view {
104
+ (bool shouldFill, bool isCurrency0) = wrapper.deriveFillDirection(1000, 2000);
105
+ assertTrue(shouldFill, "should fill when tick moved");
106
+ assertTrue(isCurrency0, "tick increase = currency0 orders fillable");
121
107
  }
122
108
 
123
- /// @notice Tests currency0 tick ordering when tickBefore > tickAfter (needs swap)
124
- function test_orderTicks_currency0_descending_swaps() public view {
125
- int24 tickBefore = 2000;
126
- int24 tickAfter = 1000;
127
-
128
- (int24 startTick, int24 endTick) = wrapper.orderTicks_currency0(tickBefore, tickAfter);
129
-
130
- assertEq(startTick, 1000, "startTick should be minimum");
131
- assertEq(endTick, 2000, "endTick should be maximum");
109
+ /// @notice Tests fill direction derivation: tick decrease currency1
110
+ function test_deriveFillDirection_tickDecreases_isCurrency1() public view {
111
+ (bool shouldFill, bool isCurrency0) = wrapper.deriveFillDirection(2000, 1000);
112
+ assertTrue(shouldFill, "should fill when tick moved");
113
+ assertFalse(isCurrency0, "tick decrease = currency1 orders fillable");
132
114
  }
133
115
 
134
- /// @notice Tests currency0 tick ordering when ticks are equal
135
- function test_orderTicks_currency0_equal() public view {
136
- int24 tickBefore = 1500;
137
- int24 tickAfter = 1500;
138
-
139
- (int24 startTick, int24 endTick) = wrapper.orderTicks_currency0(tickBefore, tickAfter);
140
-
141
- assertEq(startTick, 1500, "startTick should equal tickBefore");
142
- assertEq(endTick, 1500, "endTick should equal tickBefore");
143
- }
144
-
145
- /// @notice Tests currency1 tick ordering when tickBefore > tickAfter (descending)
146
- function test_orderTicks_currency1_descending() public view {
147
- int24 tickBefore = 2000;
148
- int24 tickAfter = 1000;
149
-
150
- (int24 startTick, int24 endTick) = wrapper.orderTicks_currency1(tickBefore, tickAfter);
151
-
152
- assertEq(startTick, 2000, "startTick should be tickBefore");
153
- assertEq(endTick, 1000, "endTick should be tickAfter");
154
- }
155
-
156
- /// @notice Tests currency1 tick ordering when tickBefore < tickAfter (needs swap)
157
- function test_orderTicks_currency1_ascending_swaps() public view {
158
- int24 tickBefore = 1000;
159
- int24 tickAfter = 2000;
160
-
161
- (int24 startTick, int24 endTick) = wrapper.orderTicks_currency1(tickBefore, tickAfter);
162
-
163
- assertEq(startTick, 2000, "startTick should be maximum");
164
- assertEq(endTick, 1000, "endTick should be minimum");
165
- }
166
-
167
- /// @notice Tests currency1 tick ordering with negative ticks
168
- function test_orderTicks_currency1_negativeTicks() public view {
169
- int24 tickBefore = -1000;
170
- int24 tickAfter = -2000;
171
-
172
- (int24 startTick, int24 endTick) = wrapper.orderTicks_currency1(tickBefore, tickAfter);
173
-
174
- assertEq(startTick, -1000, "startTick should be -1000");
175
- assertEq(endTick, -2000, "endTick should be -2000");
116
+ /// @notice Tests fill direction derivation: no movement no fill
117
+ function test_deriveFillDirection_noMovement_noFill() public view {
118
+ (bool shouldFill, ) = wrapper.deriveFillDirection(1500, 1500);
119
+ assertFalse(shouldFill, "should not fill when tick unchanged");
176
120
  }
177
121
 
178
122
  /// @notice Tests that unallocated > 0 triggers take
@@ -302,17 +246,6 @@ contract SwapWithLimitOrdersUnitTest is Test {
302
246
  assertEq(result, "Valid");
303
247
  }
304
248
 
305
- /// @notice Tests tick ordering with max int24 values
306
- function test_orderTicks_maxValues() public view {
307
- int24 maxTick = type(int24).max;
308
- int24 minTick = type(int24).min;
309
-
310
- (int24 startTick, int24 endTick) = wrapper.orderTicks_currency0(maxTick, minTick);
311
-
312
- assertEq(startTick, minTick, "should handle max/min correctly");
313
- assertEq(endTick, maxTick, "should handle max/min correctly");
314
- }
315
-
316
249
  /// @notice Tests unallocated with max uint128 value
317
250
  function test_shouldTakeUnallocated_maxValue() public view {
318
251
  bool shouldTake = wrapper.shouldTakeUnallocated(type(uint128).max);
@@ -2,7 +2,6 @@
2
2
  pragma solidity ^0.8.13;
3
3
 
4
4
  import {Vm} from "forge-std/Vm.sol";
5
- import {AccessManager} from "@openzeppelin/contracts/access/manager/AccessManager.sol";
6
5
  import {V4TestSetup} from "@zoralabs/coins/test/utils/V4TestSetup.sol";
7
6
  import {IZoraLimitOrderBook} from "../../src/IZoraLimitOrderBook.sol";
8
7
  import {TestableZoraLimitOrderBook} from "./TestableZoraLimitOrderBook.sol";
@@ -26,6 +25,8 @@ import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";
26
25
  import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol";
27
26
  import {AddressConstants} from "@zoralabs/coins/test/utils/hookmate/constants/AddressConstants.sol";
28
27
  import {ICoin} from "@zoralabs/coins/src/interfaces/ICoin.sol";
28
+ import {IWETH} from "@zoralabs/coins/src/interfaces/IWETH.sol";
29
+ import {MockWETH} from "./MockWETH.sol";
29
30
 
30
31
  /**
31
32
  * @title BaseTest
@@ -34,8 +35,8 @@ import {ICoin} from "@zoralabs/coins/src/interfaces/ICoin.sol";
34
35
  */
35
36
  contract BaseTest is V4TestSetup, IMsgSender {
36
37
  using PoolIdLibrary for PoolKey;
38
+
37
39
  TestableZoraLimitOrderBook internal limitOrderBook = TestableZoraLimitOrderBook(payable(makeAddr("limitOrderBook")));
38
- AccessManager internal accessManager;
39
40
  SwapWithLimitOrders internal swapWithLimitOrders;
40
41
 
41
42
  function setUp() public virtual {
@@ -57,28 +58,26 @@ contract BaseTest is V4TestSetup, IMsgSender {
57
58
  }
58
59
 
59
60
  function _setupLimitOrderBook() internal {
60
- // Deploy AccessManager with this contract as admin
61
- accessManager = new AccessManager(address(this));
61
+ // Deploy WETH mock if not already set
62
+ if (address(weth) == address(0)) {
63
+ weth = IWETH(address(new MockWETH()));
64
+ }
62
65
 
66
+ // Deploy ZoraLimitOrderBook with this contract as owner
63
67
  deployCodeTo(
64
68
  "TestableZoraLimitOrderBook.sol:TestableZoraLimitOrderBook",
65
- abi.encode(address(poolManager), address(factory), address(zoraHookRegistry), address(accessManager)),
69
+ abi.encode(address(poolManager), address(factory), address(zoraHookRegistry), address(this), address(weth)),
66
70
  address(limitOrderBook)
67
71
  );
68
- require(limitOrderBook.authority() == address(accessManager), "ZoraLimitOrderBook authority is not the access manager");
69
-
70
- // Set create() and setMaxFillCount() functions to PUBLIC_ROLE to allow anyone to call them initially
71
- bytes4[] memory selectors = new bytes4[](2);
72
- selectors[0] = IZoraLimitOrderBook.create.selector;
73
- selectors[1] = IZoraLimitOrderBook.setMaxFillCount.selector;
74
- accessManager.setTargetFunctionRole(address(limitOrderBook), selectors, accessManager.PUBLIC_ROLE());
72
+ require(limitOrderBook.owner() == address(this), "ZoraLimitOrderBook owner is not this contract");
75
73
 
76
74
  limitOrderBook.setMaxFillCount(50);
77
75
 
78
76
  vm.label(address(limitOrderBook), "LIMIT_ORDER_BOOK");
79
77
 
80
- swapWithLimitOrders = new SwapWithLimitOrders(poolManager, limitOrderBook, swapRouter, AddressConstants.getPermit2Address());
78
+ swapWithLimitOrders = new SwapWithLimitOrders(poolManager, limitOrderBook, swapRouter, AddressConstants.getPermit2Address(), address(this));
81
79
  vm.label(address(swapWithLimitOrders), "SWAP_WITH_LIMIT_ORDERS");
80
+
82
81
  // Now create the real ZoraLimitOrderBook for tests that need it
83
82
  _deployTestCoins();
84
83
  }
@@ -96,7 +95,9 @@ contract BaseTest is V4TestSetup, IMsgSender {
96
95
  keccak256("LimitOrderFilled(address,address,address,uint128,uint128,address,uint128,bytes32,int24,bytes32)");
97
96
  bytes32 internal constant LIMIT_ORDER_UPDATED_TOPIC = keccak256("LimitOrderUpdated(address,address,bytes32,bool,int24,uint128,bytes32,bool)");
98
97
  bytes32 internal constant SWAP_WITH_LIMIT_ORDERS_EXECUTED_TOPIC =
99
- keccak256("SwapWithLimitOrdersExecuted(address,address,(address,address,uint24,int24,address),int256,int24,int24,(bytes32,uint256,uint256)[])");
98
+ keccak256(
99
+ "SwapWithLimitOrdersExecuted(address,address,(address,address,uint24,int24,address),int24,int24,int128,int128,uint160,(bytes32,uint256,uint256)[])"
100
+ );
100
101
 
101
102
  struct QueueSnapshot {
102
103
  bytes32 head;
@@ -150,9 +151,11 @@ contract BaseTest is V4TestSetup, IMsgSender {
150
151
  address sender;
151
152
  address recipient;
152
153
  PoolKey poolKey;
153
- int256 delta;
154
154
  int24 tickBefore;
155
155
  int24 tickAfter;
156
+ int128 amount0;
157
+ int128 amount1;
158
+ uint160 sqrtPriceX96;
156
159
  CreatedOrder[] orders;
157
160
  }
158
161
 
@@ -464,6 +467,10 @@ contract BaseTest is V4TestSetup, IMsgSender {
464
467
  return isCurrency0 ? baseTick + offset : baseTick - offset;
465
468
  }
466
469
 
470
+ function _fillableTick(bool isCurrency0, int24 orderTick, int24 spacing) internal pure returns (int24) {
471
+ return isCurrency0 ? orderTick + spacing : orderTick - spacing;
472
+ }
473
+
467
474
  function _buildDeterministicOrders(
468
475
  PoolKey memory key,
469
476
  bool isCurrency0,
@@ -667,18 +674,25 @@ contract BaseTest is V4TestSetup, IMsgSender {
667
674
  Vm.Log memory log = logs[i];
668
675
  if (log.topics.length == 0 || log.topics[0] != SWAP_WITH_LIMIT_ORDERS_EXECUTED_TOPIC) continue;
669
676
 
670
- (PoolKey memory poolKey, int256 delta, int24 tickBefore, int24 tickAfter, CreatedOrder[] memory orders) = abi.decode(
671
- log.data,
672
- (PoolKey, int256, int24, int24, CreatedOrder[])
673
- );
677
+ (
678
+ PoolKey memory poolKey,
679
+ int24 tickBefore,
680
+ int24 tickAfter,
681
+ int128 amount0,
682
+ int128 amount1,
683
+ uint160 sqrtPriceX96,
684
+ CreatedOrder[] memory orders
685
+ ) = abi.decode(log.data, (PoolKey, int24, int24, int128, int128, uint160, CreatedOrder[]));
674
686
 
675
687
  swaps[idx] = SwapExecutedLog({
676
688
  sender: address(uint160(uint256(log.topics[1]))),
677
689
  recipient: address(uint160(uint256(log.topics[2]))),
678
690
  poolKey: poolKey,
679
- delta: delta,
680
691
  tickBefore: tickBefore,
681
692
  tickAfter: tickAfter,
693
+ amount0: amount0,
694
+ amount1: amount1,
695
+ sqrtPriceX96: sqrtPriceX96,
682
696
  orders: orders
683
697
  });
684
698
  ++idx;
@@ -782,12 +796,10 @@ contract BaseTest is V4TestSetup, IMsgSender {
782
796
 
783
797
  function _disableAutoFill() internal returns (uint256 previousMaxFillCount) {
784
798
  previousMaxFillCount = limitOrderBook.getMaxFillCount();
785
- vm.prank(users.factoryOwner);
786
799
  limitOrderBook.setMaxFillCount(0);
787
800
  }
788
801
 
789
802
  function _restoreAutoFill(uint256 previousMaxFillCount) internal {
790
- vm.prank(users.factoryOwner);
791
803
  limitOrderBook.setMaxFillCount(previousMaxFillCount);
792
804
  }
793
805
  }
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
5
+ import {IWETH} from "@zoralabs/coins/src/interfaces/IWETH.sol";
6
+
7
+ contract MockWETH is IWETH, ERC20 {
8
+ constructor() ERC20("WETH", "WETH") {}
9
+
10
+ function approve(address spender, uint256 value) public override(ERC20, IWETH) returns (bool) {
11
+ return super.approve(spender, value);
12
+ }
13
+
14
+ function transfer(address to, uint256 value) public override(ERC20, IWETH) returns (bool) {
15
+ return super.transfer(to, value);
16
+ }
17
+
18
+ function transferFrom(address from, address to, uint256 value) public override(ERC20, IWETH) returns (bool) {
19
+ return super.transferFrom(from, to, value);
20
+ }
21
+
22
+ function balanceOf(address account) public view override(ERC20, IWETH) returns (uint256) {
23
+ return super.balanceOf(account);
24
+ }
25
+
26
+ function deposit() external payable {
27
+ _mint(msg.sender, msg.value);
28
+ }
29
+
30
+ function withdraw(uint256 wad) external {
31
+ _burn(msg.sender, wad);
32
+ (bool ok, ) = msg.sender.call{value: wad}("");
33
+ require(ok, "WETH withdraw failed");
34
+ }
35
+
36
+ receive() external payable {
37
+ _mint(msg.sender, msg.value);
38
+ }
39
+ }
@@ -4,7 +4,7 @@ pragma solidity ^0.8.28;
4
4
  import {ZoraLimitOrderBook} from "../../src/ZoraLimitOrderBook.sol";
5
5
  import {LimitOrderTypes} from "../../src/libs/LimitOrderTypes.sol";
6
6
  import {LimitOrderStorage} from "../../src/libs/LimitOrderStorage.sol";
7
- import {LimitOrderFill} from "../../src/libs/LimitOrderFill.sol";
7
+ import {LimitOrderViews} from "../../src/libs/LimitOrderViews.sol";
8
8
  import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
9
9
 
10
10
  contract TestableZoraLimitOrderBook is ZoraLimitOrderBook {
@@ -12,8 +12,9 @@ contract TestableZoraLimitOrderBook is ZoraLimitOrderBook {
12
12
  address poolManager_,
13
13
  address zoraCoinVersionLookup_,
14
14
  address zoraHookRegistry_,
15
- address authority_
16
- ) ZoraLimitOrderBook(poolManager_, zoraCoinVersionLookup_, zoraHookRegistry_, authority_) {}
15
+ address owner_,
16
+ address weth_
17
+ ) ZoraLimitOrderBook(poolManager_, zoraCoinVersionLookup_, zoraHookRegistry_, owner_, weth_) {}
17
18
 
18
19
  function exposedTickQueue(bytes32 poolKeyHash, address coin, int24 tick) external view returns (LimitOrderTypes.Queue memory) {
19
20
  return getTickQueue(poolKeyHash, coin, tick);
@@ -38,10 +39,7 @@ contract TestableZoraLimitOrderBook is ZoraLimitOrderBook {
38
39
  int24 endTick
39
40
  ) external view returns (int24 resolvedStart, int24 resolvedEnd) {
40
41
  LimitOrderStorage.Layout storage state = LimitOrderStorage.layout();
41
- LimitOrderFill.Context memory ctx;
42
- ctx.poolManager = poolManager;
43
- ctx.versionLookup = zoraCoinVersionLookup;
44
- (, resolvedStart, resolvedEnd) = LimitOrderFill.validateTickRange(state, ctx, key, isCurrency0, startTick, endTick);
42
+ (, resolvedStart, resolvedEnd) = LimitOrderViews.validateTickRange(state, poolManager, key, isCurrency0, startTick, endTick);
45
43
  }
46
44
 
47
45
  function exposedOrder(bytes32 orderId) external view returns (LimitOrderTypes.LimitOrder memory) {
@@ -1,31 +0,0 @@
1
- [
2
- {
3
- "type": "function",
4
- "name": "canCall",
5
- "inputs": [
6
- {
7
- "name": "caller",
8
- "type": "address",
9
- "internalType": "address"
10
- },
11
- {
12
- "name": "target",
13
- "type": "address",
14
- "internalType": "address"
15
- },
16
- {
17
- "name": "selector",
18
- "type": "bytes4",
19
- "internalType": "bytes4"
20
- }
21
- ],
22
- "outputs": [
23
- {
24
- "name": "allowed",
25
- "type": "bool",
26
- "internalType": "bool"
27
- }
28
- ],
29
- "stateMutability": "view"
30
- }
31
- ]