@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,268 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {IAuthority} from "@openzeppelin/contracts/access/manager/IAuthority.sol";
5
+ import {Context} from "@openzeppelin/contracts/utils/Context.sol";
6
+
7
+ /**
8
+ * @title SimpleAccessManager
9
+ * @author Zora
10
+ * @notice A simplified single-contract access manager providing role-based access control
11
+ * without time-based validation.
12
+ *
13
+ * @dev This contract is designed to manage access for a single target contract by mapping
14
+ * function selectors directly to required roles. It implements the IAuthority interface
15
+ * for compatibility with SimpleAccessManaged contracts.
16
+ *
17
+ * Key features:
18
+ * - Maps function selectors to roles (selector -> roleId)
19
+ * - Immediate role grants/revokes (no delays)
20
+ * - PUBLIC_ROLE grants access to all addresses
21
+ * - ADMIN_ROLE (role 0) can configure function roles and manage other roles
22
+ * - Each role can have a custom admin role for delegation
23
+ *
24
+ * This is intentionally simpler than OpenZeppelin's AccessManager:
25
+ * - No execution delays or scheduled operations
26
+ * - No guardian role
27
+ * - No target address tracking (single-contract authority)
28
+ */
29
+ contract SimpleAccessManager is Context, IAuthority {
30
+ /// @notice The admin role identifier (0). Members can configure function roles and grant other roles.
31
+ uint64 public constant ADMIN_ROLE = type(uint64).min;
32
+
33
+ /// @notice The public role identifier (max uint64). All addresses implicitly have this role.
34
+ uint64 public constant PUBLIC_ROLE = type(uint64).max;
35
+
36
+ /// @notice Configuration for setting a function's required role at deployment
37
+ /// @param selector The function selector to configure
38
+ /// @param roleId The role required to call the function
39
+ struct InitialFunctionRole {
40
+ bytes4 selector;
41
+ uint64 roleId;
42
+ }
43
+
44
+ /// @dev Maps function selector to the role required to call it
45
+ mapping(bytes4 selector => uint64 roleId) private _functionRoles;
46
+
47
+ /// @dev Maps role to its members
48
+ mapping(uint64 roleId => mapping(address user => bool isMember)) private _roleMembers;
49
+
50
+ /// @dev Maps role to its admin role (the role that can grant/revoke it)
51
+ mapping(uint64 roleId => uint64 admin) private _roleAdmins;
52
+
53
+ /// @notice Emitted when a role is granted to an account
54
+ /// @param roleId The role that was granted
55
+ /// @param account The account that received the role
56
+ event RoleGranted(uint64 indexed roleId, address indexed account);
57
+
58
+ /// @notice Emitted when a role is revoked from an account
59
+ /// @param roleId The role that was revoked
60
+ /// @param account The account that lost the role
61
+ event RoleRevoked(uint64 indexed roleId, address indexed account);
62
+
63
+ /// @notice Emitted when a role's admin is changed
64
+ /// @param roleId The role whose admin changed
65
+ /// @param admin The new admin role
66
+ event RoleAdminChanged(uint64 indexed roleId, uint64 indexed admin);
67
+
68
+ /// @notice Emitted when a function's required role is updated
69
+ /// @param selector The function selector that was configured
70
+ /// @param roleId The new required role
71
+ event FunctionRoleUpdated(bytes4 indexed selector, uint64 indexed roleId);
72
+
73
+ /// @notice Thrown when the initial admin address is invalid (zero address)
74
+ /// @param initialAdmin The invalid admin address provided
75
+ error AccessManagerInvalidInitialAdmin(address initialAdmin);
76
+
77
+ /// @notice Thrown when an account lacks the required role
78
+ /// @param account The account that attempted the action
79
+ /// @param roleId The role that was required
80
+ error AccessManagerUnauthorizedAccount(address account, uint64 roleId);
81
+
82
+ /// @notice Thrown when attempting to modify a locked role (ADMIN_ROLE or PUBLIC_ROLE)
83
+ /// @param roleId The locked role that was targeted
84
+ error AccessManagerLockedRole(uint64 roleId);
85
+
86
+ /// @dev Restricts function access to accounts with ADMIN_ROLE.
87
+ modifier onlyAdmin() {
88
+ if (!hasRole(ADMIN_ROLE, _msgSender())) {
89
+ revert AccessManagerUnauthorizedAccount(_msgSender(), ADMIN_ROLE);
90
+ }
91
+ _;
92
+ }
93
+
94
+ /// @dev Restricts function access to accounts that have the admin role for the specified role.
95
+ /// @param roleId The role whose admin is required to call the function
96
+ modifier onlyRoleAdmin(uint64 roleId) {
97
+ uint64 adminRole = getRoleAdmin(roleId);
98
+ if (!hasRole(adminRole, _msgSender())) {
99
+ revert AccessManagerUnauthorizedAccount(_msgSender(), adminRole);
100
+ }
101
+ _;
102
+ }
103
+
104
+ /// @notice Initializes the access manager with an admin and optional function role configurations.
105
+ /// @dev The initial admin is granted ADMIN_ROLE immediately. Initial function roles allow
106
+ /// configuring access permissions at deployment time without additional transactions.
107
+ /// @param initialAdmin The address that will receive ADMIN_ROLE (cannot be zero address)
108
+ /// @param initialFunctionRoles Array of selector-to-role mappings to configure at deployment
109
+ constructor(address initialAdmin, InitialFunctionRole[] memory initialFunctionRoles) {
110
+ if (initialAdmin == address(0)) {
111
+ revert AccessManagerInvalidInitialAdmin(address(0));
112
+ }
113
+
114
+ // Admin is active immediately
115
+ _grantRole(ADMIN_ROLE, initialAdmin);
116
+
117
+ // Set up initial function roles
118
+ for (uint256 i = 0; i < initialFunctionRoles.length; ++i) {
119
+ _setFunctionRole(initialFunctionRoles[i].selector, initialFunctionRoles[i].roleId);
120
+ }
121
+ }
122
+
123
+ // =================================================== IAuthority ====================================================
124
+
125
+ /// @inheritdoc IAuthority
126
+ /// @notice Checks if a caller is authorized to invoke a function.
127
+ /// @dev The target parameter is ignored since this is a single-contract authority.
128
+ /// Returns true if the caller has the role required for the selector, or if the
129
+ /// selector is mapped to PUBLIC_ROLE (which all addresses implicitly have).
130
+ /// @param caller The address attempting to call the function
131
+ /// @param selector The function selector being called
132
+ /// @return True if the caller is authorized to call the function
133
+ function canCall(
134
+ address caller,
135
+ address,
136
+ /* target */
137
+ bytes4 selector
138
+ ) external view override returns (bool) {
139
+ uint64 roleId = _functionRoles[selector];
140
+ return hasRole(roleId, caller);
141
+ }
142
+
143
+ // =================================================== GETTERS ====================================================
144
+
145
+ /// @notice Returns the role required to call a specific function.
146
+ /// @dev If no role has been set for the selector, returns 0 (ADMIN_ROLE), meaning
147
+ /// only admins can call unconfigured functions by default.
148
+ /// @param selector The function selector to query
149
+ /// @return The role ID required to call the function
150
+ function getFunctionRole(bytes4 selector) public view returns (uint64) {
151
+ return _functionRoles[selector];
152
+ }
153
+
154
+ /// @notice Returns the admin role for a given role.
155
+ /// @dev The admin role is the role that can grant/revoke the specified role.
156
+ /// If no admin has been set, returns 0 (ADMIN_ROLE).
157
+ /// @param roleId The role to query the admin for
158
+ /// @return The admin role ID that can manage the specified role
159
+ function getRoleAdmin(uint64 roleId) public view returns (uint64) {
160
+ return _roleAdmins[roleId];
161
+ }
162
+
163
+ /// @notice Checks if an account has a specific role.
164
+ /// @dev PUBLIC_ROLE always returns true for any account. For other roles,
165
+ /// returns true only if the account has been explicitly granted the role.
166
+ /// @param roleId The role to check
167
+ /// @param account The account to check
168
+ /// @return True if the account has the role
169
+ function hasRole(uint64 roleId, address account) public view returns (bool) {
170
+ if (roleId == PUBLIC_ROLE) {
171
+ return true;
172
+ }
173
+ return _roleMembers[roleId][account];
174
+ }
175
+
176
+ // =============================================== ROLE MANAGEMENT ===============================================
177
+
178
+ /// @notice Grants a role to an account.
179
+ /// @dev Only callable by accounts that have the admin role for the specified role.
180
+ /// Emits {RoleGranted} if the account did not already have the role.
181
+ /// @param roleId The role to grant
182
+ /// @param account The account to receive the role
183
+ function grantRole(uint64 roleId, address account) public onlyRoleAdmin(roleId) {
184
+ _grantRole(roleId, account);
185
+ }
186
+
187
+ /// @notice Revokes a role from an account.
188
+ /// @dev Only callable by accounts that have the admin role for the specified role.
189
+ /// Emits {RoleRevoked} if the account had the role.
190
+ /// @param roleId The role to revoke
191
+ /// @param account The account to lose the role
192
+ function revokeRole(uint64 roleId, address account) public onlyRoleAdmin(roleId) {
193
+ _revokeRole(roleId, account);
194
+ }
195
+
196
+ /// @notice Allows an account to renounce a role it has.
197
+ /// @dev The callerConfirmation parameter must match msg.sender to prevent accidental
198
+ /// role renunciation. Emits {RoleRevoked} if the account had the role.
199
+ /// @param roleId The role to renounce
200
+ /// @param callerConfirmation Must be msg.sender's address as confirmation
201
+ function renounceRole(uint64 roleId, address callerConfirmation) public {
202
+ if (callerConfirmation != _msgSender()) {
203
+ revert AccessManagerUnauthorizedAccount(_msgSender(), roleId);
204
+ }
205
+ _revokeRole(roleId, callerConfirmation);
206
+ }
207
+
208
+ /// @notice Sets the admin role for a given role.
209
+ /// @dev Only callable by accounts with ADMIN_ROLE. Cannot modify the admin of
210
+ /// ADMIN_ROLE or PUBLIC_ROLE. Emits {RoleAdminChanged}.
211
+ /// @param roleId The role to set the admin for
212
+ /// @param admin The new admin role ID
213
+ function setRoleAdmin(uint64 roleId, uint64 admin) public onlyAdmin {
214
+ if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
215
+ revert AccessManagerLockedRole(roleId);
216
+ }
217
+
218
+ _roleAdmins[roleId] = admin;
219
+ emit RoleAdminChanged(roleId, admin);
220
+ }
221
+
222
+ /// @dev Grants a role to an account without access control checks.
223
+ /// @param roleId The role to grant (cannot be PUBLIC_ROLE)
224
+ /// @param account The account to receive the role
225
+ function _grantRole(uint64 roleId, address account) internal {
226
+ if (roleId == PUBLIC_ROLE) {
227
+ revert AccessManagerLockedRole(roleId);
228
+ }
229
+
230
+ if (!_roleMembers[roleId][account]) {
231
+ _roleMembers[roleId][account] = true;
232
+ emit RoleGranted(roleId, account);
233
+ }
234
+ }
235
+
236
+ /// @dev Revokes a role from an account without access control checks.
237
+ /// @param roleId The role to revoke (cannot be PUBLIC_ROLE)
238
+ /// @param account The account to lose the role
239
+ function _revokeRole(uint64 roleId, address account) internal {
240
+ if (roleId == PUBLIC_ROLE) {
241
+ revert AccessManagerLockedRole(roleId);
242
+ }
243
+
244
+ if (_roleMembers[roleId][account]) {
245
+ _roleMembers[roleId][account] = false;
246
+ emit RoleRevoked(roleId, account);
247
+ }
248
+ }
249
+
250
+ // ============================================= FUNCTION MANAGEMENT ==============================================
251
+
252
+ /// @notice Sets the role required to call a specific function.
253
+ /// @dev Only callable by accounts with ADMIN_ROLE. Set to PUBLIC_ROLE to make a
254
+ /// function callable by anyone. Emits {FunctionRoleUpdated}.
255
+ /// @param selector The function selector to configure
256
+ /// @param roleId The role that will be required to call the function
257
+ function setFunctionRole(bytes4 selector, uint64 roleId) public onlyAdmin {
258
+ _setFunctionRole(selector, roleId);
259
+ }
260
+
261
+ /// @dev Sets a function's required role without access control checks.
262
+ /// @param selector The function selector to configure
263
+ /// @param roleId The role that will be required to call the function
264
+ function _setFunctionRole(bytes4 selector, uint64 roleId) internal {
265
+ _functionRoles[selector] = roleId;
266
+ emit FunctionRoleUpdated(selector, roleId);
267
+ }
268
+ }
@@ -0,0 +1,84 @@
1
+ // SPDX-License-Identifier: ZORA-DELAYED-OSL-v1
2
+ // This software is licensed under the Zora Delayed Open Source License.
3
+ // Under this license, you may use, copy, modify, and distribute this software for
4
+ // non-commercial purposes only. Commercial use and competitive products are prohibited
5
+ // until the "Open Date" (3 years from first public distribution or earlier at Zora's discretion),
6
+ // at which point this software automatically becomes available under the MIT License.
7
+ // Full license terms available at: https://docs.zora.co/coins/license
8
+ pragma solidity ^0.8.23;
9
+
10
+ import {TickBitmap} from "@uniswap/v4-core/src/libraries/TickBitmap.sol";
11
+ import {LimitOrderTypes} from "./LimitOrderTypes.sol";
12
+
13
+ library LimitOrderBitmap {
14
+ function setIfFirst(mapping(int16 => uint256) storage bm, int24 tick, int24 spacing, uint256 sizeBefore) internal {
15
+ if (sizeBefore == 0) {
16
+ int24 compressed = TickBitmap.compress(tick, spacing);
17
+ (int16 wordPos, uint8 bitPos) = TickBitmap.position(compressed);
18
+ bm[wordPos] |= (1 << bitPos);
19
+ }
20
+ }
21
+
22
+ function clearIfEmpty(mapping(int16 => uint256) storage bm, int24 tick, int24 spacing, uint256 sizeAfter) internal {
23
+ if (sizeAfter == 0) {
24
+ int24 compressed = TickBitmap.compress(tick, spacing);
25
+ (int16 wordPos, uint8 bitPos) = TickBitmap.position(compressed);
26
+ bm[wordPos] &= ~(1 << bitPos);
27
+ }
28
+ }
29
+
30
+ function getExecutableTicks(
31
+ mapping(int16 => uint256) storage bm,
32
+ mapping(int24 => LimitOrderTypes.Queue) storage poolQueue,
33
+ int24 tickSpacing,
34
+ bool zeroForOne,
35
+ int24 tickBeforeSwap,
36
+ int24 tickAfterSwap
37
+ ) internal view returns (int24[] memory) {
38
+ uint256 numTicksCrossed = uint256(int256(_abs(tickBeforeSwap - tickAfterSwap)));
39
+ uint256 numTicksToCheck = numTicksCrossed / uint256(int256(tickSpacing)) + 1;
40
+
41
+ int24[] memory ticksToCheck = new int24[](numTicksToCheck);
42
+ uint256 numExecutableTicks;
43
+
44
+ int24 targetTick = tickAfterSwap;
45
+ int24 currentTick = tickBeforeSwap;
46
+
47
+ while (true) {
48
+ if (zeroForOne ? currentTick <= targetTick : currentTick >= targetTick) {
49
+ break;
50
+ }
51
+
52
+ (int24 nextInitializedTick, bool initialized) = TickBitmap.nextInitializedTickWithinOneWord(bm, currentTick, tickSpacing, zeroForOne);
53
+
54
+ bool crossesTarget = zeroForOne ? nextInitializedTick <= targetTick : nextInitializedTick > targetTick;
55
+
56
+ if (crossesTarget) {
57
+ nextInitializedTick = targetTick;
58
+ initialized = false;
59
+ }
60
+
61
+ if (initialized) {
62
+ if (poolQueue[nextInitializedTick].length > 0) {
63
+ ticksToCheck[numExecutableTicks++] = nextInitializedTick;
64
+ }
65
+ }
66
+
67
+ if (nextInitializedTick == targetTick) {
68
+ break;
69
+ }
70
+
71
+ currentTick = zeroForOne ? nextInitializedTick - 1 : nextInitializedTick;
72
+ }
73
+
74
+ assembly {
75
+ mstore(ticksToCheck, numExecutableTicks)
76
+ }
77
+
78
+ return ticksToCheck;
79
+ }
80
+
81
+ function _abs(int24 x) private pure returns (int24) {
82
+ return x < 0 ? -x : x;
83
+ }
84
+ }
@@ -0,0 +1,91 @@
1
+ // SPDX-License-Identifier: ZORA-DELAYED-OSL-v1
2
+ // This software is licensed under the Zora Delayed Open Source License.
3
+ // Under this license, you may use, copy, modify, and distribute this software for
4
+ // non-commercial purposes only. Commercial use and competitive products are prohibited
5
+ // until the "Open Date" (3 years from first public distribution or earlier at Zora's discretion),
6
+ // at which point this software automatically becomes available under the MIT License.
7
+ // Full license terms available at: https://docs.zora.co/coins/license
8
+ pragma solidity ^0.8.28;
9
+
10
+ import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
11
+ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
12
+
13
+ import {IZoraLimitOrderBook} from "../IZoraLimitOrderBook.sol";
14
+ import {LimitOrderStorage} from "./LimitOrderStorage.sol";
15
+ import {LimitOrderTypes} from "./LimitOrderTypes.sol";
16
+ import {LimitOrderQueues} from "./LimitOrderQueues.sol";
17
+ import {LimitOrderBitmap} from "./LimitOrderBitmap.sol";
18
+
19
+ library LimitOrderCommon {
20
+ /// @dev Currency0 orders are executed when the price rises to the lower tick.
21
+ /// Currency1 orders are executed when the price falls to the upper tick.
22
+ function getOrderTick(LimitOrderTypes.LimitOrder storage order) internal view returns (int24) {
23
+ return order.isCurrency0 ? order.tickLower : order.tickUpper;
24
+ }
25
+
26
+ function getOrderCoin(PoolKey memory key, bool isCurrency0) internal pure returns (address) {
27
+ return Currency.unwrap(isCurrency0 ? key.currency0 : key.currency1);
28
+ }
29
+
30
+ function recordCreation(
31
+ LimitOrderStorage.Layout storage state,
32
+ PoolKey memory key,
33
+ bytes32 poolKeyHash,
34
+ bytes32 orderId,
35
+ address maker,
36
+ address coin,
37
+ bool isCurrency0,
38
+ int24 orderTick,
39
+ int24 currentTick,
40
+ uint256 epoch,
41
+ uint128 liquidity,
42
+ uint128 realizedSize,
43
+ int24 tickLower,
44
+ int24 tickUpper
45
+ ) internal {
46
+ LimitOrderTypes.LimitOrder storage order = state.limitOrders[orderId];
47
+ order.orderSize = realizedSize;
48
+ order.liquidity = liquidity;
49
+ order.tickLower = tickLower;
50
+ order.tickUpper = tickUpper;
51
+ order.createdEpoch = uint32(epoch);
52
+ order.status = LimitOrderTypes.OrderStatus.OPEN;
53
+ order.isCurrency0 = isCurrency0;
54
+ order.maker = maker;
55
+ order.poolKeyHash = poolKeyHash;
56
+
57
+ LimitOrderTypes.Queue storage tickQueue = state.tickQueues[poolKeyHash][coin][orderTick];
58
+ tickQueue.balance += realizedSize;
59
+
60
+ LimitOrderBitmap.setIfFirst(state.tickBitmaps[poolKeyHash][coin], orderTick, key.tickSpacing, tickQueue.length);
61
+ LimitOrderQueues.enqueue(tickQueue, state.limitOrders, orderId);
62
+
63
+ uint256 newMakerBalance = state.makerBalances[maker][coin] + realizedSize;
64
+ state.makerBalances[maker][coin] = newMakerBalance;
65
+
66
+ emit IZoraLimitOrderBook.LimitOrderCreated(maker, coin, poolKeyHash, isCurrency0, orderTick, currentTick, realizedSize, orderId);
67
+ emit IZoraLimitOrderBook.MakerBalanceUpdated(maker, coin, newMakerBalance);
68
+ }
69
+
70
+ function removeOrder(
71
+ LimitOrderStorage.Layout storage state,
72
+ PoolKey memory key,
73
+ address coin,
74
+ LimitOrderTypes.Queue storage tickQueue,
75
+ LimitOrderTypes.LimitOrder storage order
76
+ ) internal returns (int24 orderTick) {
77
+ uint128 size = order.orderSize;
78
+ tickQueue.balance -= size;
79
+
80
+ LimitOrderQueues.unlink(tickQueue, state.limitOrders, order);
81
+ LimitOrderQueues.clearLinks(order);
82
+
83
+ orderTick = getOrderTick(order);
84
+ LimitOrderBitmap.clearIfEmpty(state.tickBitmaps[order.poolKeyHash][coin], orderTick, key.tickSpacing, tickQueue.length);
85
+
86
+ address maker = order.maker;
87
+ uint256 newMakerBalance = state.makerBalances[maker][coin] - size;
88
+ state.makerBalances[maker][coin] = newMakerBalance;
89
+ emit IZoraLimitOrderBook.MakerBalanceUpdated(maker, coin, newMakerBalance);
90
+ }
91
+ }