@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.
- package/.turbo/turbo-build$colon$js.log +85 -0
- package/AUDIT_NOTES.md +33 -0
- package/AUDIT_RFP.md +408 -0
- package/CHANGELOG.md +25 -0
- package/GAS_COMPARISON_RESULTS.md +194 -0
- package/LICENSE +21 -0
- package/README.md +650 -0
- package/SPEC.md +291 -0
- package/abis/BalanceDeltaLibrary.json +15 -0
- package/abis/BeforeSwapDeltaLibrary.json +15 -0
- package/abis/CurrencyLibrary.json +25 -0
- package/abis/CustomRevert.json +28 -0
- package/abis/IAllowanceTransfer.json +486 -0
- package/abis/IAuthority.json +31 -0
- package/abis/ICoin.json +1074 -0
- package/abis/IDeployedCoinVersionLookup.json +21 -0
- package/abis/IDopplerErrors.json +44 -0
- package/abis/IEIP712.json +15 -0
- package/abis/IERC1363.json +373 -0
- package/abis/IERC165.json +21 -0
- package/abis/IERC20.json +185 -0
- package/abis/IERC20Minimal.json +172 -0
- package/abis/IERC6909Claims.json +288 -0
- package/abis/IERC7572.json +21 -0
- package/abis/IExtsload.json +64 -0
- package/abis/IExttload.json +40 -0
- package/abis/IHasCoinType.json +15 -0
- package/abis/IHasPoolKey.json +42 -0
- package/abis/IHasRewardsRecipients.json +54 -0
- package/abis/IHasSwapPath.json +60 -0
- package/abis/IHasTotalSupplyForPositions.json +15 -0
- package/abis/IHooks.json +789 -0
- package/abis/IMsgSender.json +15 -0
- package/abis/IPoolManager.json +1286 -0
- package/abis/IProtocolFees.json +174 -0
- package/abis/ISupportsLimitOrderFill.json +15 -0
- package/abis/ISwapPathRouter.json +92 -0
- package/abis/ISwapRouter.json +219 -0
- package/abis/IUniswapV3SwapCallback.json +25 -0
- package/abis/IUpgradeableDestinationV4Hook.json +84 -0
- package/abis/IUpgradeableDestinationV4HookWithUpdateableFee.json +95 -0
- package/abis/IUpgradeableV4Hook.json +112 -0
- package/abis/IZoraHookRegistry.json +188 -0
- package/abis/IZoraLimitOrderBook.json +623 -0
- package/abis/IZoraLimitOrderBookCoinsInterface.json +67 -0
- package/abis/IZoraV4CoinHook.json +610 -0
- package/abis/Permit2Payments.json +7 -0
- package/abis/Position.json +7 -0
- package/abis/SafeCast.json +7 -0
- package/abis/SafeCast160.json +7 -0
- package/abis/SafeERC20.json +34 -0
- package/abis/SimpleAccessManaged.json +57 -0
- package/abis/SimpleAccessManager.json +351 -0
- package/abis/SqrtPriceMath.json +22 -0
- package/abis/StateLibrary.json +80 -0
- package/abis/SwapLimitOrders.json +22 -0
- package/abis/SwapWithLimitOrders.json +457 -0
- package/abis/TickBitmap.json +18 -0
- package/abis/TickMath.json +24 -0
- package/abis/V3ToV4SwapLib.json +28 -0
- package/abis/ZoraLimitOrderBook.json +771 -0
- package/cache/solidity-files-cache.json +1 -0
- package/dist/index.cjs +760 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +731 -0
- package/dist/index.js.map +1 -0
- package/dist/wagmiGenerated.d.ts +1012 -0
- package/dist/wagmiGenerated.d.ts.map +1 -0
- package/foundry.toml +29 -0
- package/gas_comparison.py +49 -0
- package/out/BalanceDelta.sol/BalanceDeltaLibrary.json +1 -0
- package/out/BeforeSwapDelta.sol/BeforeSwapDeltaLibrary.json +1 -0
- package/out/BitMath.sol/BitMath.json +1 -0
- package/out/BytesLib.sol/BytesLib.json +1 -0
- package/out/CoinCommon.sol/CoinCommon.json +1 -0
- package/out/CoinConfigurationVersions.sol/CoinConfigurationVersions.json +1 -0
- package/out/CoinConstants.sol/CoinConstants.json +1 -0
- package/out/Context.sol/Context.json +1 -0
- package/out/Currency.sol/CurrencyLibrary.json +1 -0
- package/out/CurrencyReserves.sol/CurrencyReserves.json +1 -0
- package/out/CustomRevert.sol/CustomRevert.json +1 -0
- package/out/DopplerMath.sol/DopplerMath.json +1 -0
- package/out/FixedPoint128.sol/FixedPoint128.json +1 -0
- package/out/FixedPoint96.sol/FixedPoint96.json +1 -0
- package/out/FullMath.sol/FullMath.json +1 -0
- package/out/IAllowanceTransfer.sol/IAllowanceTransfer.json +1 -0
- package/out/IAuthority.sol/IAuthority.json +1 -0
- package/out/ICoin.sol/ICoin.json +1 -0
- package/out/ICoin.sol/IHasCoinType.json +1 -0
- package/out/ICoin.sol/IHasPoolKey.json +1 -0
- package/out/ICoin.sol/IHasSwapPath.json +1 -0
- package/out/ICoin.sol/IHasTotalSupplyForPositions.json +1 -0
- package/out/IDeployedCoinVersionLookup.sol/IDeployedCoinVersionLookup.json +1 -0
- package/out/IDopplerErrors.sol/IDopplerErrors.json +1 -0
- package/out/IEIP712.sol/IEIP712.json +1 -0
- package/out/IERC1363.sol/IERC1363.json +1 -0
- package/out/IERC165.sol/IERC165.json +1 -0
- package/out/IERC20.sol/IERC20.json +1 -0
- package/out/IERC20Minimal.sol/IERC20Minimal.json +1 -0
- package/out/IERC6909Claims.sol/IERC6909Claims.json +1 -0
- package/out/IERC7572.sol/IERC7572.json +1 -0
- package/out/IExtsload.sol/IExtsload.json +1 -0
- package/out/IExttload.sol/IExttload.json +1 -0
- package/out/IHasRewardsRecipients.sol/IHasRewardsRecipients.json +1 -0
- package/out/IHooks.sol/IHooks.json +1 -0
- package/out/IMsgSender.sol/IMsgSender.json +1 -0
- package/out/IPoolManager.sol/IPoolManager.json +1 -0
- package/out/IProtocolFees.sol/IProtocolFees.json +1 -0
- package/out/ISupportsLimitOrderFill.sol/ISupportsLimitOrderFill.json +1 -0
- package/out/ISwapPathRouter.sol/ISwapPathRouter.json +1 -0
- package/out/ISwapRouter.sol/ISwapRouter.json +1 -0
- package/out/IUniswapV3SwapCallback.sol/IUniswapV3SwapCallback.json +1 -0
- package/out/IUpgradeableV4Hook.sol/IUpgradeableDestinationV4Hook.json +1 -0
- package/out/IUpgradeableV4Hook.sol/IUpgradeableDestinationV4HookWithUpdateableFee.json +1 -0
- package/out/IUpgradeableV4Hook.sol/IUpgradeableV4Hook.json +1 -0
- package/out/IZoraHookRegistry.sol/IZoraHookRegistry.json +1 -0
- package/out/IZoraLimitOrderBook.sol/IZoraLimitOrderBook.json +1 -0
- package/out/IZoraLimitOrderBookCoinsInterface.sol/IZoraLimitOrderBookCoinsInterface.json +1 -0
- package/out/IZoraV4CoinHook.sol/IZoraV4CoinHook.json +1 -0
- package/out/LimitOrderBitmap.sol/LimitOrderBitmap.json +1 -0
- package/out/LimitOrderCommon.sol/LimitOrderCommon.json +1 -0
- package/out/LimitOrderCreate.sol/LimitOrderCreate.json +1 -0
- package/out/LimitOrderFill.sol/LimitOrderFill.json +1 -0
- package/out/LimitOrderLiquidity.sol/LimitOrderLiquidity.json +1 -0
- package/out/LimitOrderQueues.sol/LimitOrderQueues.json +1 -0
- package/out/LimitOrderStorage.sol/LimitOrderStorage.json +1 -0
- package/out/LimitOrderTypes.sol/LimitOrderTypes.json +1 -0
- package/out/LimitOrderWithdraw.sol/LimitOrderWithdraw.json +1 -0
- package/out/LiquidityAmounts.sol/LiquidityAmounts.json +1 -0
- package/out/LiquidityMath.sol/LiquidityMath.json +1 -0
- package/out/Lock.sol/Lock.json +1 -0
- package/out/NonzeroDeltaCount.sol/NonzeroDeltaCount.json +1 -0
- package/out/Path.sol/Path.json +1 -0
- package/out/PathKey.sol/PathKeyLibrary.json +1 -0
- package/out/Permit2Payments.sol/Permit2Payments.json +1 -0
- package/out/PoolId.sol/PoolIdLibrary.json +1 -0
- package/out/Position.sol/Position.json +1 -0
- package/out/SafeCast.sol/SafeCast.json +1 -0
- package/out/SafeCast160.sol/SafeCast160.json +1 -0
- package/out/SafeERC20.sol/SafeERC20.json +1 -0
- package/out/SimpleAccessManaged.sol/SimpleAccessManaged.json +1 -0
- package/out/SimpleAccessManager.sol/SimpleAccessManager.json +1 -0
- package/out/SqrtPriceMath.sol/SqrtPriceMath.json +1 -0
- package/out/StateLibrary.sol/StateLibrary.json +1 -0
- package/out/SwapLimitOrders.sol/SwapLimitOrders.json +1 -0
- package/out/SwapWithLimitOrders.sol/SwapWithLimitOrders.json +1 -0
- package/out/TickBitmap.sol/TickBitmap.json +1 -0
- package/out/TickMath.sol/TickMath.json +1 -0
- package/out/TransientSlot.sol/TransientSlot.json +1 -0
- package/out/TransientStateLibrary.sol/TransientStateLibrary.json +1 -0
- package/out/UniV4SwapToCurrency.sol/UniV4SwapToCurrency.json +1 -0
- package/out/UnsafeMath.sol/UnsafeMath.json +1 -0
- package/out/V3ToV4SwapLib.sol/V3ToV4SwapLib.json +1 -0
- package/out/ZoraLimitOrderBook.sol/ZoraLimitOrderBook.json +1 -0
- package/out/build-info/69718f10d1dc37f0.json +1 -0
- package/out/uniswap/BitMath.sol/BitMath.json +1 -0
- package/out/uniswap/CustomRevert.sol/CustomRevert.json +1 -0
- package/out/uniswap/FullMath.sol/FullMath.json +1 -0
- package/out/uniswap/SafeCast.sol/SafeCast.json +1 -0
- package/out/uniswap/TickMath.sol/TickMath.json +1 -0
- package/package/index.ts +1 -0
- package/package/wagmiGenerated.ts +738 -0
- package/package.json +57 -0
- package/remappings.txt +11 -0
- package/src/IZoraLimitOrderBook.sol +195 -0
- package/src/ZoraLimitOrderBook.sol +220 -0
- package/src/access/SimpleAccessManaged.sol +76 -0
- package/src/access/SimpleAccessManager.sol +268 -0
- package/src/libs/LimitOrderBitmap.sol +84 -0
- package/src/libs/LimitOrderCommon.sol +91 -0
- package/src/libs/LimitOrderCreate.sol +277 -0
- package/src/libs/LimitOrderFill.sol +362 -0
- package/src/libs/LimitOrderLiquidity.sol +222 -0
- package/src/libs/LimitOrderQueues.sol +101 -0
- package/src/libs/LimitOrderStorage.sol +34 -0
- package/src/libs/LimitOrderTypes.sol +41 -0
- package/src/libs/LimitOrderWithdraw.sol +100 -0
- package/src/libs/Permit2Payments.sol +41 -0
- package/src/libs/SwapLimitOrders.sol +209 -0
- package/src/router/SwapWithLimitOrders.sol +454 -0
- package/test/LimitOrderAccessControl.t.sol +461 -0
- package/test/LimitOrderBitmap.t.sol +194 -0
- package/test/LimitOrderCreate.t.sol +348 -0
- package/test/LimitOrderFill.t.sol +1005 -0
- package/test/LimitOrderLibraries.t.sol +354 -0
- package/test/LimitOrderLiquidityPayouts.t.sol +333 -0
- package/test/LimitOrderV4Pools.t.sol +157 -0
- package/test/LimitOrderWithdraw.t.sol +653 -0
- package/test/SimpleAccessManager.t.sol +420 -0
- package/test/SwapWithLimitOrders.t.sol +107 -0
- package/test/SwapWithLimitOrdersRouter.t.sol +1073 -0
- package/test/gas/LimitOrderFillGas.t.sol +1008 -0
- package/test/gas/LimitOrderSwapGas.t.sol +403 -0
- package/test/gas/logs/gas_benchmarks_fill_20251201.log +30 -0
- package/test/gas/logs/gas_benchmarks_swap_20251201.log +27 -0
- package/test/unit/LimitOrderBitmapUnit.t.sol +276 -0
- package/test/unit/LimitOrderCreateUnit.t.sol +358 -0
- package/test/unit/SwapLimitOrdersUnit.t.sol +672 -0
- package/test/unit/SwapLimitOrdersValidation.t.sol +423 -0
- package/test/unit/SwapWithLimitOrdersUnit.t.sol +321 -0
- package/test/utils/BaseTest.sol +793 -0
- package/test/utils/TestableZoraLimitOrderBook.sol +54 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +11 -0
- 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
|