@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
package/README.md ADDED
@@ -0,0 +1,650 @@
1
+ # Zora Limit Order Book Protocol
2
+
3
+ ### Quick Start (Auditors & Integrators)
4
+
5
+ - **Architecture + diagrams (this doc)**: [`README.md`](./README.md)
6
+ - **Normative behavior + invariants**: [`SPEC.md`](./SPEC.md)
7
+ - **Threat model + audit checklist**: [`AUDIT_NOTES.md`](./AUDIT_NOTES.md)
8
+ - **Audit scope / process**: [`AUDIT_RFP.md`](./AUDIT_RFP.md)
9
+
10
+ If you’re auditing: read **README → SPEC → AUDIT_NOTES → code**.
11
+
12
+ ### Table of Contents
13
+
14
+ - [1. Overview](#1-overview)
15
+ - [2. Coins Platform Architecture](#2-coins-platform-architecture)
16
+ - [3. Limit Orders Architecture Overview](#3-limit-orders-architecture-overview)
17
+ - [4. Fill Execution Paths](#4-fill-execution-paths)
18
+ - [5. Security Model & Guarantees](#5-security-model--guarantees)
19
+ - [6. Gas Limits & DOS Prevention](#6-gas-limits--dos-prevention)
20
+
21
+ ### 1. Overview
22
+
23
+ The Zora Limit Order Book is an on-chain limit order system for Zora Coins integrated with Uniswap V4. Orders are created by depositing single-sided liquidity on a pool at specific price points (ticks).
24
+
25
+ **Key Capabilities**
26
+
27
+ - **Create orders** - Deposit coins as single-sided liquidity at target price ticks
28
+ - **Fill orders** - Automatically fill orders when swaps cross their price points
29
+ - **Withdraw orders** - Cancel unfilled orders and retrieve deposited coins
30
+
31
+ **Automatic Filling**
32
+
33
+ Orders are automatically filled by the hook on each swap when possible. If there are many outstanding orders to fill, some will remain unfilled. Third parties can fill these remaining orders and collect the LP fees from the positions they fill as an incentive.
34
+
35
+ ### 2. Coins Platform Architecture
36
+
37
+ For a comprehensive understanding of the Zora Coins protocol, see the [Contract Architecture documentation](../../docs/pages/coins/contracts/architecture.mdx).
38
+
39
+ #### Quick Summary
40
+
41
+ The Zora Coins platform consists of.
42
+
43
+ Coin Types
44
+
45
+ - **Creator Coins**: Backed by ZORA token, one per creator
46
+ - **Content Coins**: Backed by creator coin (or any coin), multiple per creator
47
+
48
+ Core Components
49
+
50
+ - **ZoraFactoryImpl**: Deploys coins with deterministic addresses
51
+ - BaseCoinV4: Base implementation for all coins
52
+ - ZoraV4CoinHook: Unified hook for all coins (handles fee collection and reward distribution)
53
+ - Uniswap V4 PoolManager: Manages liquidity pools and swap execution
54
+
55
+ Coin Pairing Structure.
56
+
57
+ ```mermaid
58
+ flowchart TD
59
+ ZORA[ZORA Token]
60
+ Creator[Creator Coin]
61
+ Content1[Content Coin 1]
62
+ Content2[Content Coin 2]
63
+ Content3[Content Coin 3]
64
+
65
+ Content1 <-->|Paired with| Creator
66
+ Content2 <-->|Paired with| Creator
67
+ Content3 <-->|Paired with| Creator
68
+ Creator <-->|Paired with| ZORA
69
+
70
+ style ZORA fill:#ffd700
71
+ style Creator fill:#87ceeb
72
+ style Content1 fill:#98fb98
73
+ style Content2 fill:#98fb98
74
+ style Content3 fill:#98fb98
75
+ ```
76
+
77
+ Key Relationships.
78
+
79
+ - Content coins trade against creator coins (many-to-one relationship)
80
+ - Creator coins trade against ZORA token (many-to-one relationship)
81
+ - Each pool is a Uniswap V4 liquidity pool with concentrated liquidity
82
+ - All coins use the same hook contract (unified implementation since v2.3.0)
83
+ - Hook is immutable and tied to factory version
84
+ - When factory upgrades, new coins use new hook, but existing coins remain on old hook
85
+
86
+ Hook Versioning.
87
+
88
+ - Factory can be upgraded to point to new hook version
89
+ - New coins deployed with new factory use new hook
90
+ - Existing coins can migrate liquidity to new hook via `migrateLiquidity()`
91
+ - Migration changes pool identity (new poolKeyHash)
92
+
93
+ Impact on Limit Orders.
94
+
95
+ - Limit orders are tied to specific pool (via poolKeyHash)
96
+ - When coin migrates to new hook, orders remain on old pool
97
+ - Coins on old hooks won't auto-fill orders (no `ISupportsLimitOrderFill` in hook)
98
+ - SwapWithLimitOrders router provides filling for legacy coins
99
+
100
+ ### 3. Limit Orders Architecture Overview
101
+
102
+ #### Main Components
103
+
104
+ The Zora Limit Orders system consists of three primary components.
105
+
106
+ 1. ZoraLimitOrderBook (Core Contract)
107
+
108
+ Location: [`src/ZoraLimitOrderBook.sol`](./src/ZoraLimitOrderBook.sol)
109
+
110
+ The main contract that manages the entire limit order lifecycle. It handles.
111
+
112
+ - Order creation with single-sided liquidity deposits
113
+ - Order filling through Uniswap V4 liquidity removal
114
+ - Order withdrawal and cancellation
115
+ - Balance management for makers
116
+ - Integration with Uniswap V4 PoolManager via unlock/callback pattern
117
+
118
+ All state-changing operations go through the `poolManager.unlock()` pattern, with callbacks handling the actual logic.
119
+
120
+ 2. SwapWithLimitOrders Router
121
+
122
+ Location: [`src/router/SwapWithLimitOrders.sol`](./src/router/SwapWithLimitOrders.sol)
123
+
124
+ A specialized router that combines multi-hop swaps with limit order creation and filling.
125
+
126
+ Features
127
+
128
+ - Executes V3→V4 multi-hop swaps
129
+ - Executes V4 multi-hop swaps
130
+ - Automatically creates limit orders after swaps (order ladders)
131
+ - Fills orders on legacy coins (coins without `ISupportsLimitOrderFill` support)
132
+ - Handles ETH wrapping and unwrapping
133
+
134
+ This router is critical for.
135
+
136
+ - Users who want to swap and immediately place limit orders
137
+ - Filling orders on coins using old hooks that don't support auto-fill
138
+
139
+ 3. Library System
140
+
141
+ The contract uses libraries to make logic more portable.
142
+
143
+ Components
144
+
145
+ - [`LimitOrderCreate.sol`](./src/libs/LimitOrderCreate.sol) - Order creation and validation logic
146
+ - [`LimitOrderFill.sol`](./src/libs/LimitOrderFill.sol) - Order filling and epoch management
147
+ - [`LimitOrderWithdraw.sol`](./src/libs/LimitOrderWithdraw.sol) - Order cancellation and balance withdrawal
148
+ - [`LimitOrderLiquidity.sol`](./src/libs/LimitOrderLiquidity.sol) - Uniswap V4 liquidity position management
149
+ - [`LimitOrderQueues.sol`](./src/libs/LimitOrderQueues.sol) - Linked-list queue operations
150
+ - [`LimitOrderBitmap.sol`](./src/libs/LimitOrderBitmap.sol) - Tick bitmap for efficient order discovery
151
+ - [`LimitOrderStorage.sol`](./src/libs/LimitOrderStorage.sol) - Diamond storage pattern implementation
152
+ - [`LimitOrderCommon.sol`](./src/libs/LimitOrderCommon.sol) - Common utilities and order metadata helpers
153
+ - [`SwapLimitOrders.sol`](./src/libs/SwapLimitOrders.sol) - Order ladder configuration and validation
154
+ - [`Permit2Payments.sol`](./src/libs/Permit2Payments.sol) - Permit2 token transfer support
155
+
156
+ 4. Access Control
157
+
158
+ - [`SimpleAccessManaged.sol`](./src/access/SimpleAccessManaged.sol) - Base contract for access-controlled functions
159
+ - [`SimpleAccessManager.sol`](./src/access/SimpleAccessManager.sol) - Role-based access manager implementation
160
+
161
+ #### Tick Queue System
162
+
163
+ Orders are organized in tick-based queues for efficient filling during swaps.
164
+
165
+ **Tick Queue**: Orders indexed by `(poolKeyHash, coin, tick)` tuple
166
+
167
+ - Allows efficient discovery of fillable orders at specific price points
168
+ - Used during fill operations to find orders in price range
169
+ - Orders within each tick are processed FIFO (first in, first out)
170
+
171
+ Queue and Order Structures
172
+
173
+ ```solidity
174
+ struct Queue {
175
+ bytes32 head; // First order in queue
176
+ bytes32 tail; // Last order in queue
177
+ uint128 length; // Number of orders
178
+ uint128 balance; // Total liquidity at this tick
179
+ }
180
+
181
+ struct LimitOrder {
182
+ // Linked-list pointers
183
+ bytes32 nextId; // Next order in tick queue
184
+ bytes32 prevId; // Previous order in tick queue
185
+ // Pool binding
186
+ bytes32 poolKeyHash;
187
+ // Amounts
188
+ uint128 orderSize;
189
+ uint128 liquidity;
190
+ // Order metadata
191
+ int24 tickLower;
192
+ int24 tickUpper;
193
+ uint32 createdEpoch;
194
+ OrderStatus status;
195
+ bool isCurrency0;
196
+ address maker;
197
+ }
198
+ ```
199
+
200
+ The tick queue system provides several benefits
201
+
202
+ - O(1) insertion and removal when order ID is known
203
+ - Efficient iteration over orders at specific tick
204
+ - Maintains FIFO ordering for fair filling - orders at the same tick are filled in the order they were created
205
+
206
+ Each limit order contains the following fields
207
+
208
+ | Field | Type | Purpose |
209
+ | -------------- | ----------- | ------------------------------------------------------------------------------ |
210
+ | `nextId` | bytes32 | Next order in the tick queue |
211
+ | `prevId` | bytes32 | Previous order in the tick queue |
212
+ | `poolKeyHash` | bytes32 | Identifies which pool this order belongs to (hash of PoolKey) |
213
+ | `orderSize` | uint128 | Amount of tokens deposited by maker (for refunds/accounting) |
214
+ | `liquidity` | uint128 | Actual Uniswap V4 liquidity amount (may differ from orderSize due to rounding) |
215
+ | `tickLower` | int24 | Lower tick of the liquidity position |
216
+ | `tickUpper` | int24 | Upper tick of the liquidity position (tickLower + tickSpacing for sell orders) |
217
+ | `createdEpoch` | uint32 | Epoch when order was created (used for fill protection) |
218
+ | `status` | OrderStatus | Current state: INACTIVE (withdrawn), OPEN (active), FILLED (executed) |
219
+ | `isCurrency0` | bool | Direction: true = selling currency0, false = selling currency1 |
220
+ | `maker` | address | Address that created the order and will receive proceeds |
221
+
222
+ Queue Structure Visualization
223
+
224
+ Here's how orders are organized in tick queues.
225
+
226
+ ```mermaid
227
+ flowchart LR
228
+ subgraph TickQueues["Tick Queues (for filling)"]
229
+ direction TB
230
+ subgraph Tick100["Pool A, Coin X, Tick 100"]
231
+ T100_1[Order 1] --> T100_2[Order 2]
232
+ T100_2 --> T100_3[Order 3]
233
+ T100_3 --> T100_4[Order 4]
234
+ end
235
+
236
+ subgraph Tick200["Pool A, Coin X, Tick 200"]
237
+ T200_1[Order 5] --> T200_2[Order 6]
238
+ end
239
+ end
240
+ ```
241
+
242
+ Queue Data Structures
243
+
244
+ Each tick queue is a doubly-linked list that maintains order information.
245
+
246
+ - Key: `poolKeyHash → coin → tick → Queue`
247
+ - Contains: head pointer, tail pointer, balance (total order size at tick), length (order count)
248
+ - Orders within a tick are processed head-to-tail (first in, first out)
249
+
250
+ **Maker Balances**: A separate `makerBalances[maker][coin]` mapping tracks total order size per maker for `balanceOf()` queries. This is a simple counter, not a queue structure.
251
+
252
+ Tick Discovery (Bitmap Implementation)
253
+
254
+ To efficiently find which ticks have active orders without iterating over empty ticks, the system uses a tick bitmap.
255
+
256
+ ```solidity
257
+ // Bitmap tracking which ticks have orders
258
+ mapping(int16 => uint256) tickBitmap;
259
+
260
+ // Set bit when first order added to tick
261
+ // Clear bit when last order removed from tick
262
+ ```
263
+
264
+ This bitmap allows O(1) lookup to find the next tick with orders during fills, avoiding iteration over thousands of empty tick positions.
265
+
266
+ #### Component Interaction Flows
267
+
268
+ #### Order Creation Flow
269
+
270
+ Users create limit orders through the SwapWithLimitOrders router, which combines swapping with order placement.
271
+
272
+ Order Creation State Flow
273
+
274
+ ```mermaid
275
+ stateDiagram-v2
276
+ [*] --> PullFunds: create() called
277
+
278
+ PullFunds --> DepositLiquidity: Transfer from maker to OrderBook
279
+ note right of PullFunds
280
+ 100 Coin A transfers from Maker's wallet to OrderBook contract.
281
+ Location: OrderBook contract balance.
282
+ end note
283
+
284
+ DepositLiquidity --> CreateOrder: Transfer from OrderBook to PoolManager
285
+ note right of DepositLiquidity
286
+ 100 Coin A transfers from OrderBook to PoolManager.
287
+ Deposited as single-sided liquidity at tick.
288
+ Any rounding dust refunded to maker.
289
+ Location: PoolManager contract (Uniswap V4).
290
+ end note
291
+
292
+ CreateOrder --> OrderActive: Insert in queues + set bitmap
293
+ note right of CreateOrder
294
+ Order tagged with current epoch.
295
+ Added to tick queue and maker balance counter.
296
+ Location: Still in PoolManager as liquidity position.
297
+ end note
298
+
299
+ OrderActive --> [*]: Order ready to fill
300
+ ```
301
+
302
+ Order Creation and Filling Flow
303
+
304
+ ```mermaid
305
+ sequenceDiagram
306
+ participant User
307
+ participant Router as SwapWithLimitOrders
308
+ participant PoolManager as Uniswap V4 PoolManager
309
+ participant Hook as Coin Hook
310
+ participant OrderBook as ZoraLimitOrderBook
311
+
312
+ Note over User,OrderBook: PHASE 1: Order Creation
313
+ User->>Router: swapAndCreateLimitOrders()
314
+ Router->>PoolManager: swap() for V3→V4 or V4 multi-hop
315
+ PoolManager-->>Router: swapped coins
316
+ Router->>OrderBook: create(orders)
317
+
318
+ loop For each order
319
+ OrderBook->>PoolManager: modifyLiquidity() (add single-sided)
320
+ Note over OrderBook,PoolManager: Deposit 100 Content Coin at tick
321
+ OrderBook->>OrderBook: Create limit order
322
+ OrderBook->>OrderBook: Insert into tick queue + increment maker balance counter
323
+ end
324
+
325
+ OrderBook-->>Router: orderIds
326
+ Router-->>User: orderIds
327
+
328
+ Note over User,OrderBook: PHASE 2: Swap Triggers Fill
329
+ User->>Router: swap() through router
330
+ Router->>PoolManager: getTick()
331
+ PoolManager-->>Router: tickBefore
332
+ Router->>PoolManager: swap() on coin pool
333
+ PoolManager->>Hook: beforeSwap()
334
+
335
+ alt Hook v4+ (supports auto-fill)
336
+ Hook->>Hook: check ISupportsLimitOrderFill
337
+ Hook->>PoolManager: getTick()
338
+ PoolManager-->>Hook: tickBefore
339
+ Hook->>Hook: store tickBefore
340
+ else Hook pre-v4 (no auto-fill)
341
+ Hook-->>PoolManager: return
342
+ end
343
+
344
+ Note over PoolManager: Swap executes
345
+ PoolManager->>Hook: afterSwap()
346
+
347
+ alt Hook v4+ (supports auto-fill)
348
+ Hook->>PoolManager: getTick()
349
+ PoolManager-->>Hook: tickAfter
350
+ Hook->>OrderBook: fill(tickBefore, tickAfter)
351
+ Note over Hook,OrderBook: New hook gets ticks and auto-fills
352
+ else Hook pre-v4 (no auto-fill)
353
+ Hook-->>PoolManager: return
354
+ Note over Hook,PoolManager: Old hook version doesn't fill
355
+ end
356
+
357
+ PoolManager-->>Router: tickAfter
358
+ Router->>Router: check hook.supportsInterface(ISupportsLimitOrderFill)
359
+
360
+ alt Hook does not support auto-fill
361
+ Router->>OrderBook: fill(tickBefore, tickAfter)
362
+ Note over Router,OrderBook: Router fills for old hooks using saved ticks
363
+ end
364
+
365
+ Note over User,OrderBook: PHASE 3: Fill Execution (shared for both scenarios)
366
+ OrderBook->>OrderBook: increment epoch
367
+ OrderBook->>OrderBook: traverse tick queues (tickBefore to tickAfter)
368
+
369
+ loop For each fillable order
370
+ OrderBook->>PoolManager: modifyLiquidity() (remove)
371
+ OrderBook->>OrderBook: remove from queues
372
+ OrderBook->>PoolManager: Multi-hop swap to backing currency
373
+ Note over OrderBook,PoolManager: Content Coin → Creator Coin → ZORA
374
+ OrderBook->>OrderBook: payout to maker + fill referral
375
+ end
376
+ ```
377
+
378
+ #### Withdrawal Flow
379
+
380
+ Users can cancel orders and withdraw funds at any time.
381
+
382
+ **`withdraw(orderIds, coin, minAmountOut, recipient)`** - Cancel specific orders by ID
383
+
384
+ - `orderIds`: Array of order IDs to cancel
385
+ - `coin`: The coin address for these orders
386
+ - `minAmountOut`: Minimum amount to receive (reverts if not met)
387
+ - `recipient`: Address to receive the withdrawn funds
388
+
389
+ Notes:
390
+
391
+ - Orders are cancelled in the order provided in `orderIds` until `minAmountOut` is reached.
392
+ - Cancellation is whole-order only (no proportional/partial cancellation).
393
+
394
+ ```mermaid
395
+ sequenceDiagram
396
+ participant User
397
+ participant OrderBook as ZoraLimitOrderBook
398
+ participant PoolManager as Uniswap V4 PoolManager
399
+
400
+ User->>OrderBook: withdraw(orderIds, coin, minAmountOut, recipient)
401
+ Note over User,OrderBook: Cancel specific orders by ID
402
+
403
+ OrderBook->>PoolManager: unlock(WITHDRAW_ORDERS callback)
404
+ PoolManager->>OrderBook: unlockCallback(WITHDRAW_ORDERS)
405
+
406
+ loop For each order to cancel
407
+ OrderBook->>PoolManager: modifyLiquidity() (remove liquidity)
408
+ Note over OrderBook,PoolManager: Extracts 100 Content Coin from position
409
+ OrderBook->>PoolManager: take(Content Coin)
410
+ Note over OrderBook,PoolManager: OrderBook receives 100 Content Coin from PoolManager
411
+ OrderBook->>OrderBook: remove from tick queue
412
+
413
+ alt v4+ coin with swap path (common case)
414
+ OrderBook->>PoolManager: sync() and transfer Content Coin back
415
+ OrderBook->>PoolManager: swap() via multi-hop path
416
+ Note over OrderBook,PoolManager: Content Coin → Creator Coin → ZORA
417
+ OrderBook->>PoolManager: take(ZORA)
418
+ Note over OrderBook,PoolManager: OrderBook receives ~100 ZORA
419
+ PoolManager->>User: Transfer ZORA to recipient
420
+ else Legacy coin or no swap path
421
+ PoolManager->>User: Transfer Content Coin to recipient
422
+ end
423
+ end
424
+
425
+ OrderBook-->>PoolManager: return
426
+ PoolManager-->>OrderBook: return
427
+ OrderBook-->>User: success
428
+ ```
429
+
430
+ #### Key Architectural Decisions
431
+
432
+ Single-Sided Liquidity
433
+
434
+ Orders are created by depositing one side of a liquidity position at a specific tick range. This implements Uniswap's [range orders concept](https://docs.uniswap.org/concepts/protocol/range-orders).
435
+
436
+ - Sell orders: Deposit coin, receive backing currency (e.g., content coin → creator coin)
437
+ - Buy orders: Deposit backing currency, receive coin
438
+
439
+ This leverages Uniswap V4's concentrated liquidity to ensure orders fill at exact prices.
440
+
441
+ Epoch-Based Protection
442
+
443
+ Each pool has an epoch counter that increments on every fill operation. Orders store their creation epoch and can only be filled in future epochs. This prevents same-transaction manipulation attacks.
444
+
445
+ Diamond Storage Pattern
446
+
447
+ All state is stored in a single struct at a deterministic storage slot.
448
+
449
+ ```solidity
450
+ bytes32 constant STORAGE_SLOT = keccak256("zora.limit.order.book.storage");
451
+ ```
452
+
453
+ This enables.
454
+
455
+ - Clean separation of storage from logic
456
+ - Potential future upgradeability
457
+ - Gas-efficient storage access
458
+
459
+ Access Control via SimpleAccessManaged
460
+
461
+ Uses OpenZeppelin's access control pattern with a separate `AccessManager` contract.
462
+
463
+ - Flexible role-based permissions
464
+ - Can restrict `create()` to specific routers/hooks if needed
465
+ - Authority can be transferred or upgraded
466
+
467
+ ### 4. Fill Execution Paths
468
+
469
+ Orders can be filled through three distinct paths depending on whether the hook supports auto-fill and how swaps are executed.
470
+
471
+ ```mermaid
472
+ flowchart TD
473
+ Swap[Swap Executed] --> HookCheck{Hook can<br/>auto-fill?}
474
+ HookCheck -->|Yes| Filled1[Orders filled in afterSwap]
475
+ HookCheck -->|No| RouterCheck{Called via<br/>SwapWithLimitOrders?}
476
+ RouterCheck -->|Yes| Filled2[Router fills after swap]
477
+ RouterCheck -->|No| Unfilled[Orders remain unfilled]
478
+ Unfilled --> ThirdParty[Third-party fill required]
479
+ ```
480
+
481
+ **Path 1: Auto-Fill (Hook)** - [`ZoraV4CoinHook.sol`](../coins/src/hooks/ZoraV4CoinHook.sol)
482
+
483
+ Triggered automatically on every swap (whether via Zora router or directly on Uniswap) for coins using hooks that support auto-fill. The hook captures the tick before and after the swap, then calls `fill()` in `afterSwap`. No external action required - orders fill seamlessly during normal trading.
484
+
485
+ **Path 2: Router Fill** - [`SwapWithLimitOrders.sol:410-428`](./src/router/SwapWithLimitOrders.sol)
486
+
487
+ For coins on hooks that don't support auto-fill. The SwapWithLimitOrders router detects this and calls `fill()` after the swap completes. This is the fallback mechanism when swaps go through the Zora router.
488
+
489
+ **Path 3: Third-Party Fill** - [`ZoraLimitOrderBook.sol:81-138`](./src/ZoraLimitOrderBook.sol)
490
+
491
+ Direct calls to `fill()` by Zora backend, arbitrage bots, or other third parties. Required when:
492
+
493
+ - Swaps bypass the Zora router on hooks that don't support auto-fill
494
+ - `maxFillCount` is reached during auto-fill or router fill, leaving orders unfilled
495
+
496
+ Supports both range-based fills (by tick range) and order-specific fills (by order IDs). Fillers are incentivized by collecting LP fees from filled positions.
497
+
498
+ ```mermaid
499
+ flowchart TD
500
+ Fill[Fill Executed] --> MaxCheck{maxFillCount<br/>reached?}
501
+ MaxCheck -->|No| Done[All eligible orders filled]
502
+ MaxCheck -->|Yes| Remaining[Orders remain unfilled]
503
+ Remaining --> ThirdParty[Zora or third-party<br/>calls fill]
504
+ ```
505
+
506
+ #### Hook Migration & Fill Path Dependencies
507
+
508
+ **Auto-Fill Requires Hook Support**
509
+
510
+ For a coin to benefit from automatic order filling via Path 1, the coin must use a hook that supports auto-fill. Some coins may be on older hooks that don't have this capability.
511
+
512
+ **Fallback Scenarios**
513
+
514
+ | Scenario | Fill Path Used |
515
+ | ------------------------------------------------------------- | ------------------------- |
516
+ | Hook supports auto-fill + swap via any method | Auto-Fill (Hook) |
517
+ | Hook doesn't support auto-fill + swap via SwapWithLimitOrders | Router Fill |
518
+ | Hook doesn't support auto-fill + direct Uniswap swap | Third-Party Fill required |
519
+ | Any hook + maxFillCount reached | Third-Party Fill required |
520
+
521
+ **Direct Uniswap Swaps (Bypassing Zora Router)**
522
+
523
+ When users swap directly on Uniswap without using the SwapWithLimitOrders router:
524
+
525
+ - If hook supports auto-fill: Orders auto-fill via hook
526
+ - If hook doesn't support auto-fill: **Orders will NOT auto-fill** - relies on:
527
+ - Zora backend monitoring and filling orders
528
+ - Third-party arbitrageurs/bots filling for profit
529
+ - Manual fill calls
530
+
531
+ This is an important consideration for coins on older hooks - their order filling depends on external actors when swaps bypass the Zora router.
532
+
533
+ #### Universal Uniswap V4 Compatibility
534
+
535
+ **Not Restricted to Zora Coins**
536
+
537
+ The ZoraLimitOrderBook is designed to work with **any Uniswap V4 pool**, not just Zora coins. Third parties can use this order book for any V4 token pair.
538
+
539
+ **For Non-Zora V4 Pools**
540
+
541
+ | Operation | How It Works |
542
+ | --------------- | ------------------------------------------------------------------ |
543
+ | Create Orders | Call `create()` with any valid V4 PoolKey (requires create access) |
544
+ | Fill Orders | Call `fill()` directly - no hook integration needed |
545
+ | Withdraw Orders | Call `withdraw()` - always available to maker |
546
+
547
+ ```mermaid
548
+ flowchart TD
549
+ NonZora[Non-Zora V4 Pool] --> NoHook[No Zora hook integration]
550
+ NoHook --> ManualFill[Third-party/bot must call fill directly]
551
+ ManualFill --> Incentive[Filler earns LP fees as incentive]
552
+ ```
553
+
554
+ **Key Differences for Non-Zora Pools:**
555
+
556
+ - No auto-fill from hook (hook must implement `ISupportsLimitOrderFill` and call the order book)
557
+ - No router fill (SwapWithLimitOrders is designed for Zora coin swap paths)
558
+ - Orders rely entirely on third-party fillers or direct `fill()` calls
559
+ - Fillers are incentivized by collecting LP fees from filled positions
560
+
561
+ **Integration Path for Other Protocols**
562
+
563
+ Other protocols can integrate limit order filling by:
564
+
565
+ 1. Implementing `ISupportsLimitOrderFill` in their hook
566
+ 2. Calling `limitOrderBook.fill()` in their `afterSwap` hook
567
+ 3. Registering their hook with the Zora hook registry (for unlocked fill access)
568
+
569
+ This makes the order book a general-purpose infrastructure component that can serve the broader Uniswap V4 ecosystem.
570
+
571
+ ### 5. Security Model & Guarantees
572
+
573
+ #### Protocol Concerns
574
+
575
+ 1. **Fund Safety**: Users can ALWAYS withdraw via `withdraw()` - no admin lock possible
576
+ 2. **Trading Continuity**: No pause mechanism exists that can block coin swaps
577
+ 3. **DOS Prevention**: `maxFillCount` limits orders per fill operation
578
+ 4. **Admin Boundaries**: Admin cannot touch user funds or block withdrawals
579
+
580
+ #### Actor Analysis
581
+
582
+ | Actor | Can Impact Others? | Protections |
583
+ | ------------ | ------------------ | -------------------------------------------------------------------------- |
584
+ | Order Makers | No | Orders isolated in per-tick queues + maker balances are tracked separately |
585
+ | Fillers | No | Bad fill attempts don't affect other users |
586
+ | Admin | Limited | Can adjust maxFillCount but cannot lock funds |
587
+
588
+ #### Admin Capabilities
589
+
590
+ **Admin CAN:**
591
+
592
+ - Restrict `create()` access via authority contract
593
+ - Set `maxFillCount` to limit orders per fill operation
594
+ - Change the authority contract
595
+
596
+ **Admin CANNOT:**
597
+
598
+ - Withdraw user funds
599
+ - Block withdrawals
600
+ - Force fills
601
+ - Pause the system
602
+
603
+ #### Why `create()` is Access Controlled
604
+
605
+ The `create()` function is restricted to authorized callers for operational safety:
606
+
607
+ 1. **Bad Order Detection**: If malicious actors insert pathological orders designed to grief fills or game the system, admin can revoke their access without affecting other users
608
+
609
+ 2. **Contract Upgradability**: The SwapWithLimitOrders router may need additional validation in the future. By controlling access:
610
+
611
+ - Admin can disable the current router's create access
612
+ - Deploy a new router version with improved validation
613
+ - Grant create access to the new router
614
+ - No contract upgrade or user migration required
615
+
616
+ 3. **Gradual Rollout**: New integrations can be added incrementally by granting create access to vetted contracts
617
+
618
+ This design allows the limit order system to evolve without requiring upgrades to the core ZoraLimitOrderBook contract, while maintaining user fund safety (withdrawals always work regardless of create restrictions).
619
+
620
+ ### 6. Gas Limits & DOS Prevention
621
+
622
+ #### maxFillCount Parameter
623
+
624
+ The `maxFillCount` parameter limits how many orders can be processed in a single fill operation:
625
+
626
+ - Prevents unbounded gas consumption during fills
627
+ - Default: 50 orders (configurable by admin)
628
+ - If more orders exist than `maxFillCount`, remaining orders are filled in subsequent calls
629
+
630
+ #### Gas Analysis from Testing
631
+
632
+ | Orders | Block Utilization | Safety Margin | Recommendation |
633
+ | ------ | ----------------- | ------------- | -------------- |
634
+ | 25 | ~25% | 4× | Conservative |
635
+ | 40 | ~37% | 2.7× | Recommended |
636
+ | 50 | ~43% | 2.3× | Aggressive |
637
+
638
+ #### Fusaka/Future Hard Fork Considerations
639
+
640
+ - `maxFillCount` can be reduced if gas costs change in future hard forks
641
+ - Admin adjustment doesn't require contract upgrade
642
+ - Multiple fill calls handle orders beyond `maxFillCount`
643
+ - System designed to remain functional even with significant gas repricing
644
+
645
+ #### Admin DOS Risk
646
+
647
+ - Setting the stored `maxFillCount` to `0` disables fills only when callers pass `maxFillCount=0` (i.e. request the stored default). Callers can still pass a non-zero `maxFillCount` to execute range fills.
648
+ - Mitigation: Use multisig/timelock for admin authority
649
+ - User funds remain safe - only auto-fill would be affected
650
+ - Users can always withdraw their orders regardless of fill settings