@zoralabs/limit-orders 0.2.0

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 (208) hide show
  1. package/.turbo/turbo-build$colon$js.log +85 -0
  2. package/AUDIT_NOTES.md +33 -0
  3. package/AUDIT_RFP.md +408 -0
  4. package/CHANGELOG.md +25 -0
  5. package/GAS_COMPARISON_RESULTS.md +194 -0
  6. package/LICENSE +21 -0
  7. package/README.md +650 -0
  8. package/SPEC.md +291 -0
  9. package/abis/BalanceDeltaLibrary.json +15 -0
  10. package/abis/BeforeSwapDeltaLibrary.json +15 -0
  11. package/abis/CurrencyLibrary.json +25 -0
  12. package/abis/CustomRevert.json +28 -0
  13. package/abis/IAllowanceTransfer.json +486 -0
  14. package/abis/IAuthority.json +31 -0
  15. package/abis/ICoin.json +1074 -0
  16. package/abis/IDeployedCoinVersionLookup.json +21 -0
  17. package/abis/IDopplerErrors.json +44 -0
  18. package/abis/IEIP712.json +15 -0
  19. package/abis/IERC1363.json +373 -0
  20. package/abis/IERC165.json +21 -0
  21. package/abis/IERC20.json +185 -0
  22. package/abis/IERC20Minimal.json +172 -0
  23. package/abis/IERC6909Claims.json +288 -0
  24. package/abis/IERC7572.json +21 -0
  25. package/abis/IExtsload.json +64 -0
  26. package/abis/IExttload.json +40 -0
  27. package/abis/IHasCoinType.json +15 -0
  28. package/abis/IHasPoolKey.json +42 -0
  29. package/abis/IHasRewardsRecipients.json +54 -0
  30. package/abis/IHasSwapPath.json +60 -0
  31. package/abis/IHasTotalSupplyForPositions.json +15 -0
  32. package/abis/IHooks.json +789 -0
  33. package/abis/IMsgSender.json +15 -0
  34. package/abis/IPoolManager.json +1286 -0
  35. package/abis/IProtocolFees.json +174 -0
  36. package/abis/ISupportsLimitOrderFill.json +15 -0
  37. package/abis/ISwapPathRouter.json +92 -0
  38. package/abis/ISwapRouter.json +219 -0
  39. package/abis/IUniswapV3SwapCallback.json +25 -0
  40. package/abis/IUpgradeableDestinationV4Hook.json +84 -0
  41. package/abis/IUpgradeableDestinationV4HookWithUpdateableFee.json +95 -0
  42. package/abis/IUpgradeableV4Hook.json +112 -0
  43. package/abis/IZoraHookRegistry.json +188 -0
  44. package/abis/IZoraLimitOrderBook.json +623 -0
  45. package/abis/IZoraLimitOrderBookCoinsInterface.json +67 -0
  46. package/abis/IZoraV4CoinHook.json +610 -0
  47. package/abis/Permit2Payments.json +7 -0
  48. package/abis/Position.json +7 -0
  49. package/abis/SafeCast.json +7 -0
  50. package/abis/SafeCast160.json +7 -0
  51. package/abis/SafeERC20.json +34 -0
  52. package/abis/SimpleAccessManaged.json +57 -0
  53. package/abis/SimpleAccessManager.json +351 -0
  54. package/abis/SqrtPriceMath.json +22 -0
  55. package/abis/StateLibrary.json +80 -0
  56. package/abis/SwapLimitOrders.json +22 -0
  57. package/abis/SwapWithLimitOrders.json +457 -0
  58. package/abis/TickBitmap.json +18 -0
  59. package/abis/TickMath.json +24 -0
  60. package/abis/V3ToV4SwapLib.json +28 -0
  61. package/abis/ZoraLimitOrderBook.json +771 -0
  62. package/cache/solidity-files-cache.json +1 -0
  63. package/dist/index.cjs +760 -0
  64. package/dist/index.cjs.map +1 -0
  65. package/dist/index.d.ts +2 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +731 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/wagmiGenerated.d.ts +1012 -0
  70. package/dist/wagmiGenerated.d.ts.map +1 -0
  71. package/foundry.toml +29 -0
  72. package/gas_comparison.py +49 -0
  73. package/out/BalanceDelta.sol/BalanceDeltaLibrary.json +1 -0
  74. package/out/BeforeSwapDelta.sol/BeforeSwapDeltaLibrary.json +1 -0
  75. package/out/BitMath.sol/BitMath.json +1 -0
  76. package/out/BytesLib.sol/BytesLib.json +1 -0
  77. package/out/CoinCommon.sol/CoinCommon.json +1 -0
  78. package/out/CoinConfigurationVersions.sol/CoinConfigurationVersions.json +1 -0
  79. package/out/CoinConstants.sol/CoinConstants.json +1 -0
  80. package/out/Context.sol/Context.json +1 -0
  81. package/out/Currency.sol/CurrencyLibrary.json +1 -0
  82. package/out/CurrencyReserves.sol/CurrencyReserves.json +1 -0
  83. package/out/CustomRevert.sol/CustomRevert.json +1 -0
  84. package/out/DopplerMath.sol/DopplerMath.json +1 -0
  85. package/out/FixedPoint128.sol/FixedPoint128.json +1 -0
  86. package/out/FixedPoint96.sol/FixedPoint96.json +1 -0
  87. package/out/FullMath.sol/FullMath.json +1 -0
  88. package/out/IAllowanceTransfer.sol/IAllowanceTransfer.json +1 -0
  89. package/out/IAuthority.sol/IAuthority.json +1 -0
  90. package/out/ICoin.sol/ICoin.json +1 -0
  91. package/out/ICoin.sol/IHasCoinType.json +1 -0
  92. package/out/ICoin.sol/IHasPoolKey.json +1 -0
  93. package/out/ICoin.sol/IHasSwapPath.json +1 -0
  94. package/out/ICoin.sol/IHasTotalSupplyForPositions.json +1 -0
  95. package/out/IDeployedCoinVersionLookup.sol/IDeployedCoinVersionLookup.json +1 -0
  96. package/out/IDopplerErrors.sol/IDopplerErrors.json +1 -0
  97. package/out/IEIP712.sol/IEIP712.json +1 -0
  98. package/out/IERC1363.sol/IERC1363.json +1 -0
  99. package/out/IERC165.sol/IERC165.json +1 -0
  100. package/out/IERC20.sol/IERC20.json +1 -0
  101. package/out/IERC20Minimal.sol/IERC20Minimal.json +1 -0
  102. package/out/IERC6909Claims.sol/IERC6909Claims.json +1 -0
  103. package/out/IERC7572.sol/IERC7572.json +1 -0
  104. package/out/IExtsload.sol/IExtsload.json +1 -0
  105. package/out/IExttload.sol/IExttload.json +1 -0
  106. package/out/IHasRewardsRecipients.sol/IHasRewardsRecipients.json +1 -0
  107. package/out/IHooks.sol/IHooks.json +1 -0
  108. package/out/IMsgSender.sol/IMsgSender.json +1 -0
  109. package/out/IPoolManager.sol/IPoolManager.json +1 -0
  110. package/out/IProtocolFees.sol/IProtocolFees.json +1 -0
  111. package/out/ISupportsLimitOrderFill.sol/ISupportsLimitOrderFill.json +1 -0
  112. package/out/ISwapPathRouter.sol/ISwapPathRouter.json +1 -0
  113. package/out/ISwapRouter.sol/ISwapRouter.json +1 -0
  114. package/out/IUniswapV3SwapCallback.sol/IUniswapV3SwapCallback.json +1 -0
  115. package/out/IUpgradeableV4Hook.sol/IUpgradeableDestinationV4Hook.json +1 -0
  116. package/out/IUpgradeableV4Hook.sol/IUpgradeableDestinationV4HookWithUpdateableFee.json +1 -0
  117. package/out/IUpgradeableV4Hook.sol/IUpgradeableV4Hook.json +1 -0
  118. package/out/IZoraHookRegistry.sol/IZoraHookRegistry.json +1 -0
  119. package/out/IZoraLimitOrderBook.sol/IZoraLimitOrderBook.json +1 -0
  120. package/out/IZoraLimitOrderBookCoinsInterface.sol/IZoraLimitOrderBookCoinsInterface.json +1 -0
  121. package/out/IZoraV4CoinHook.sol/IZoraV4CoinHook.json +1 -0
  122. package/out/LimitOrderBitmap.sol/LimitOrderBitmap.json +1 -0
  123. package/out/LimitOrderCommon.sol/LimitOrderCommon.json +1 -0
  124. package/out/LimitOrderCreate.sol/LimitOrderCreate.json +1 -0
  125. package/out/LimitOrderFill.sol/LimitOrderFill.json +1 -0
  126. package/out/LimitOrderLiquidity.sol/LimitOrderLiquidity.json +1 -0
  127. package/out/LimitOrderQueues.sol/LimitOrderQueues.json +1 -0
  128. package/out/LimitOrderStorage.sol/LimitOrderStorage.json +1 -0
  129. package/out/LimitOrderTypes.sol/LimitOrderTypes.json +1 -0
  130. package/out/LimitOrderWithdraw.sol/LimitOrderWithdraw.json +1 -0
  131. package/out/LiquidityAmounts.sol/LiquidityAmounts.json +1 -0
  132. package/out/LiquidityMath.sol/LiquidityMath.json +1 -0
  133. package/out/Lock.sol/Lock.json +1 -0
  134. package/out/NonzeroDeltaCount.sol/NonzeroDeltaCount.json +1 -0
  135. package/out/Path.sol/Path.json +1 -0
  136. package/out/PathKey.sol/PathKeyLibrary.json +1 -0
  137. package/out/Permit2Payments.sol/Permit2Payments.json +1 -0
  138. package/out/PoolId.sol/PoolIdLibrary.json +1 -0
  139. package/out/Position.sol/Position.json +1 -0
  140. package/out/SafeCast.sol/SafeCast.json +1 -0
  141. package/out/SafeCast160.sol/SafeCast160.json +1 -0
  142. package/out/SafeERC20.sol/SafeERC20.json +1 -0
  143. package/out/SimpleAccessManaged.sol/SimpleAccessManaged.json +1 -0
  144. package/out/SimpleAccessManager.sol/SimpleAccessManager.json +1 -0
  145. package/out/SqrtPriceMath.sol/SqrtPriceMath.json +1 -0
  146. package/out/StateLibrary.sol/StateLibrary.json +1 -0
  147. package/out/SwapLimitOrders.sol/SwapLimitOrders.json +1 -0
  148. package/out/SwapWithLimitOrders.sol/SwapWithLimitOrders.json +1 -0
  149. package/out/TickBitmap.sol/TickBitmap.json +1 -0
  150. package/out/TickMath.sol/TickMath.json +1 -0
  151. package/out/TransientSlot.sol/TransientSlot.json +1 -0
  152. package/out/TransientStateLibrary.sol/TransientStateLibrary.json +1 -0
  153. package/out/UniV4SwapToCurrency.sol/UniV4SwapToCurrency.json +1 -0
  154. package/out/UnsafeMath.sol/UnsafeMath.json +1 -0
  155. package/out/V3ToV4SwapLib.sol/V3ToV4SwapLib.json +1 -0
  156. package/out/ZoraLimitOrderBook.sol/ZoraLimitOrderBook.json +1 -0
  157. package/out/build-info/69718f10d1dc37f0.json +1 -0
  158. package/out/uniswap/BitMath.sol/BitMath.json +1 -0
  159. package/out/uniswap/CustomRevert.sol/CustomRevert.json +1 -0
  160. package/out/uniswap/FullMath.sol/FullMath.json +1 -0
  161. package/out/uniswap/SafeCast.sol/SafeCast.json +1 -0
  162. package/out/uniswap/TickMath.sol/TickMath.json +1 -0
  163. package/package/index.ts +1 -0
  164. package/package/wagmiGenerated.ts +738 -0
  165. package/package.json +57 -0
  166. package/remappings.txt +11 -0
  167. package/src/IZoraLimitOrderBook.sol +195 -0
  168. package/src/ZoraLimitOrderBook.sol +220 -0
  169. package/src/access/SimpleAccessManaged.sol +76 -0
  170. package/src/access/SimpleAccessManager.sol +268 -0
  171. package/src/libs/LimitOrderBitmap.sol +84 -0
  172. package/src/libs/LimitOrderCommon.sol +91 -0
  173. package/src/libs/LimitOrderCreate.sol +277 -0
  174. package/src/libs/LimitOrderFill.sol +362 -0
  175. package/src/libs/LimitOrderLiquidity.sol +222 -0
  176. package/src/libs/LimitOrderQueues.sol +101 -0
  177. package/src/libs/LimitOrderStorage.sol +34 -0
  178. package/src/libs/LimitOrderTypes.sol +41 -0
  179. package/src/libs/LimitOrderWithdraw.sol +100 -0
  180. package/src/libs/Permit2Payments.sol +41 -0
  181. package/src/libs/SwapLimitOrders.sol +209 -0
  182. package/src/router/SwapWithLimitOrders.sol +454 -0
  183. package/test/LimitOrderAccessControl.t.sol +461 -0
  184. package/test/LimitOrderBitmap.t.sol +194 -0
  185. package/test/LimitOrderCreate.t.sol +348 -0
  186. package/test/LimitOrderFill.t.sol +1005 -0
  187. package/test/LimitOrderLibraries.t.sol +354 -0
  188. package/test/LimitOrderLiquidityPayouts.t.sol +333 -0
  189. package/test/LimitOrderV4Pools.t.sol +157 -0
  190. package/test/LimitOrderWithdraw.t.sol +653 -0
  191. package/test/SimpleAccessManager.t.sol +420 -0
  192. package/test/SwapWithLimitOrders.t.sol +107 -0
  193. package/test/SwapWithLimitOrdersRouter.t.sol +1073 -0
  194. package/test/gas/LimitOrderFillGas.t.sol +1008 -0
  195. package/test/gas/LimitOrderSwapGas.t.sol +403 -0
  196. package/test/gas/logs/gas_benchmarks_fill_20251201.log +30 -0
  197. package/test/gas/logs/gas_benchmarks_swap_20251201.log +27 -0
  198. package/test/unit/LimitOrderBitmapUnit.t.sol +276 -0
  199. package/test/unit/LimitOrderCreateUnit.t.sol +358 -0
  200. package/test/unit/SwapLimitOrdersUnit.t.sol +672 -0
  201. package/test/unit/SwapLimitOrdersValidation.t.sol +423 -0
  202. package/test/unit/SwapWithLimitOrdersUnit.t.sol +321 -0
  203. package/test/utils/BaseTest.sol +793 -0
  204. package/test/utils/TestableZoraLimitOrderBook.sol +54 -0
  205. package/tsconfig.build.json +10 -0
  206. package/tsconfig.json +9 -0
  207. package/tsup.config.ts +11 -0
  208. package/wagmi.config.ts +18 -0
@@ -0,0 +1,157 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {BaseTest} from "./utils/BaseTest.sol";
5
+ import {LimitOrderCommon} from "../src/libs/LimitOrderCommon.sol";
6
+ import {CoinCommon} from "@zoralabs/coins/src/libs/CoinCommon.sol";
7
+ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
8
+ import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
9
+ import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
10
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
11
+
12
+ contract LimitOrderV4PoolsTest is BaseTest {
13
+ uint160 private constant INIT_SQRT_PRICE = 79228162514264337593543950336;
14
+
15
+ function _buildPoolKey(address token0, address token1, IHooks hooks) internal pure returns (PoolKey memory) {
16
+ bool isToken0Lower = token0 < token1;
17
+
18
+ return
19
+ PoolKey({
20
+ currency0: Currency.wrap(isToken0Lower ? token0 : token1),
21
+ currency1: Currency.wrap(isToken0Lower ? token1 : token0),
22
+ fee: 10_000,
23
+ tickSpacing: 200,
24
+ hooks: hooks
25
+ });
26
+ }
27
+
28
+ function test_existingPool_stillWorksAfterRemovingHookCheck() public {
29
+ PoolKey memory key = creatorCoin.getPoolKey();
30
+ bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
31
+ address orderCoin = isCurrency0 ? address(creatorCoin) : Currency.unwrap(key.currency1);
32
+
33
+ uint256[] memory orderSizes = new uint256[](1);
34
+ orderSizes[0] = 50e18;
35
+
36
+ int24 baseTick = _currentTick(key);
37
+ baseTick = _alignedTick(baseTick, key.tickSpacing);
38
+
39
+ int24[] memory orderTicks = new int24[](1);
40
+ orderTicks[0] = _alignedTickForOrder(isCurrency0, baseTick, key.tickSpacing, 0);
41
+
42
+ // Fund maker
43
+ deal(orderCoin, users.seller, orderSizes[0]);
44
+ vm.startPrank(users.seller);
45
+ IERC20(orderCoin).approve(address(limitOrderBook), orderSizes[0]);
46
+ vm.stopPrank();
47
+
48
+ // Create order
49
+ vm.prank(users.seller);
50
+ bytes32[] memory orderIds = limitOrderBook.create(key, isCurrency0, orderSizes, orderTicks, users.seller);
51
+
52
+ assertEq(orderIds.length, 1, "order not created");
53
+ }
54
+
55
+ function test_existingPool_fillWorksAfterPriceMovement() public {
56
+ PoolKey memory key = creatorCoin.getPoolKey();
57
+ bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
58
+ address orderCoin = LimitOrderCommon.getOrderCoin(key, isCurrency0);
59
+
60
+ // Build orders
61
+ (uint256[] memory orderSizes, int24[] memory orderTicks) = _buildDeterministicOrders(key, isCurrency0, 2, 25e18);
62
+ uint256 totalSize = orderSizes[0] + orderSizes[1];
63
+
64
+ // Fund maker
65
+ deal(orderCoin, users.seller, totalSize);
66
+ vm.startPrank(users.seller);
67
+ IERC20(orderCoin).approve(address(limitOrderBook), totalSize);
68
+ vm.stopPrank();
69
+
70
+ // Create orders
71
+ vm.recordLogs();
72
+ vm.prank(users.seller);
73
+ limitOrderBook.create(key, isCurrency0, orderSizes, orderTicks, users.seller);
74
+ CreatedOrderLog[] memory created = _decodeCreatedLogs(vm.getRecordedLogs());
75
+
76
+ assertEq(created.length, 2, "expected 2 orders created");
77
+ uint256 realizedSize = _sumOrderSizes(created);
78
+ assertEq(_makerBalance(users.seller, orderCoin), realizedSize, "maker balance mismatch after create");
79
+
80
+ uint256 previousMax = limitOrderBook.getMaxFillCount();
81
+ limitOrderBook.setMaxFillCount(0); // Disable autofill
82
+
83
+ address mover = makeAddr("price-mover");
84
+ uint128 swapAmount = uint128(DEFAULT_LIMIT_ORDER_AMOUNT * 10);
85
+ deal(address(zoraToken), mover, uint256(swapAmount));
86
+ _swapSomeCurrencyForCoin(creatorCoin, address(zoraToken), swapAmount, mover);
87
+
88
+ limitOrderBook.setMaxFillCount(previousMax); // Re-enable autofill
89
+
90
+ // Get tick window for fills
91
+ (int24 startTick, int24 endTick) = _tickWindow(created, key);
92
+ bytes32 poolKeyHash = CoinCommon.hashPoolKey(key);
93
+ uint256 epochBefore = _poolEpoch(poolKeyHash);
94
+
95
+ // Fill orders
96
+ vm.recordLogs();
97
+ limitOrderBook.fill(key, isCurrency0, startTick, endTick, created.length, address(0));
98
+ FilledOrderLog[] memory fills = _decodeFilledLogs(vm.getRecordedLogs());
99
+
100
+ // Verify fills
101
+ assertEq(fills.length, created.length, "fill count mismatch");
102
+ for (uint256 i; i < fills.length; ++i) {
103
+ assertEq(fills[i].maker, users.seller, "maker mismatch");
104
+ assertEq(fills[i].coinIn, orderCoin, "coin mismatch");
105
+ assertEq(fills[i].fillReferral, address(0), "unexpected referral");
106
+ }
107
+
108
+ // Verify cleanup
109
+ assertEq(_makerBalance(users.seller, orderCoin), 0, "maker balance should be zero after fill");
110
+ assertGt(_poolEpoch(poolKeyHash), epochBefore, "pool epoch should increment");
111
+ }
112
+
113
+ function test_arbitraryPool_fillWithReferral() public {
114
+ PoolKey memory key = creatorCoin.getPoolKey();
115
+ bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
116
+ address orderCoin = LimitOrderCommon.getOrderCoin(key, isCurrency0);
117
+
118
+ // Build single order
119
+ (uint256[] memory orderSizes, int24[] memory orderTicks) = _buildDeterministicOrders(key, isCurrency0, 1, 30e18);
120
+
121
+ // Fund maker
122
+ deal(orderCoin, users.seller, orderSizes[0]);
123
+ vm.startPrank(users.seller);
124
+ IERC20(orderCoin).approve(address(limitOrderBook), orderSizes[0]);
125
+ vm.stopPrank();
126
+
127
+ // Create order
128
+ vm.recordLogs();
129
+ vm.prank(users.seller);
130
+ limitOrderBook.create(key, isCurrency0, orderSizes, orderTicks, users.seller);
131
+ CreatedOrderLog[] memory created = _decodeCreatedLogs(vm.getRecordedLogs());
132
+
133
+ // Move price using disable/enable pattern
134
+ uint256 previousMax = limitOrderBook.getMaxFillCount();
135
+ limitOrderBook.setMaxFillCount(0);
136
+
137
+ address mover = makeAddr("price-mover");
138
+ uint128 swapAmount = uint128(DEFAULT_LIMIT_ORDER_AMOUNT * 10);
139
+ deal(address(zoraToken), mover, uint256(swapAmount));
140
+ _swapSomeCurrencyForCoin(creatorCoin, address(zoraToken), swapAmount, mover);
141
+
142
+ limitOrderBook.setMaxFillCount(previousMax);
143
+
144
+ // Fill with referral
145
+ address referral = makeAddr("fillReferral");
146
+ (int24 startTick, int24 endTick) = _tickWindow(created, key);
147
+
148
+ vm.recordLogs();
149
+ limitOrderBook.fill(key, isCurrency0, startTick, endTick, created.length, referral);
150
+ FilledOrderLog[] memory fills = _decodeFilledLogs(vm.getRecordedLogs());
151
+
152
+ // Verify referral received fees
153
+ assertEq(fills.length, 1, "expected 1 fill");
154
+ assertEq(fills[0].fillReferral, referral, "referral address mismatch");
155
+ assertGt(fills[0].fillReferralAmount, 0, "referral should receive fees");
156
+ }
157
+ }