AngstromL2Factory
AngstromL2Factory.sol deploys, verifies, and governs Angstrom L2 hook instances. It inherits Solady’s Ownable, keeps hook bytecode in SSTORE2 to stay under the bytecode limit, and mediates protocol-level fee configuration plus emergency controls.
Immutables & Core State
UNI_V4— targetIPoolManagerevery hook must attach to.HOOK_ADDRESS_MINER— external contract that mines salts for deterministic hook addresses (createNewHookAndPoolWithMiner).HOOK_INITCODE_STORE— SSTORE2 pointer containing the rawAngstromL2creation bytecode.withdrawOnly— global circuit-breaker; whentruehooks must refuse swaps and liquidity adds.defaultProtocolSwapFeeAsMultipleE6— protocol swap-fee multiple (E6) applied when a pool is created.defaultProtocolTaxFeeE6— default protocol share of the MEV tax (E6).isVerifiedHook[AngstromL2]— whitelist used to gate hook-only calls such asrecordPoolCreationAndGetStartingProtocolFee.
Constants mirror on-chain guards: MAX_DEFAULT_PROTOCOL_FEE_MULTIPLE_E6 = 3e6, MAX_PROTOCOL_SWAP_FEE_E6 = 0.05e6, MAX_PROTOCOL_TAX_FEE_E6 = 0.75e6.
Hook Deployment APIs
deployNewHook(address owner, bytes32 salt)
Directly deploys a hook using create2. The factory concatenates the stored initcode with (UNI_V4, owner) constructor arguments. Deployment reverts if withdrawOnly is active. Successful hooks are marked as verified.
createNewHookAndPoolWithMiner(...)
Convenience helper that:
- Asks
HOOK_ADDRESS_MINERto mine a salt for the desiredinitialOwner. - Calls
deployNewHook. - Sets the supplied
PoolKey’shooksaddress and invokesinitializeNewPoolon the newly created hook.
createNewHookAndPoolWithSalt(...)
Same as above but accepts a caller-supplied salt, letting integrators pick deterministic addresses themselves.
deployNewHook is permissionless—any address may call it (subject to withdrawOnly being false). Governance typically exposes the convenience helpers above so integrators do not have to manage salts themselves, but direct calls remain available for advanced deployments.
Pool Creation Bookkeeping
recordPoolCreationAndGetStartingProtocolFee(PoolKey key, uint24 creatorSwapFeeE6, uint24 creatorTaxFeeE6)
- Only callable by verified hooks while
withdrawOnlyis false. - Computes the protocol swap fee using
getDefaultProtocolSwapFee(bounded byMAX_PROTOCOL_SWAP_FEE_E6) and returns the default protocol tax fee. - Emits a
PoolCreatedevent containing the creator & protocol splits for monitoring.
Helpers exposed to routers and monitoring systems:
getDefaultProtocolSwapFee(uint256 creatorSwapFeeE6, uint256 lpFeeE6)— solves for the protocol swap feef_prgiven the default multiple and the pool’s LP + creator percentages.getDefaultNetPoolSafeSwapFee(uint256 creatorSwapFeeE6, uint256 lpFeeE6)— returns the combined swap fee (LP + creator + protocol) in E6.
Protocol Fee Governance
Owner-only setters:
setDefaultProtocolSwapFeeMultiple(uint24 newMultipleE6)— updates the default swap fee multiple, enforcing the3e6cap.setDefaultProtocolTaxFee(uint24 newTaxE6)— updates the default protocol tax share, enforcing the0.75e6cap.
Per-pool overrides (also owner-only, executed through the factory):
setProtocolSwapFee(AngstromL2 hook, PoolKey key, uint256 newFeeE6)setProtocolTaxFee(AngstromL2 hook, PoolKey key, uint256 newFeeE6)
Both functions delegate to the hook, which re-checks the 100% total-fee bound. Events ProtocolSwapFeeUpdated / ProtocolTaxFeeUpdated fire on success.
Emergency Controls & Revenue
setEmergencyWithdrawOnly()— setswithdrawOnly = trueand emitsWithdrawOnly. Hooks observe the flag via their cached copy (throughpullWidthrawOnly).withdrawRevenue(Currency currency, address to, uint256 amount)— owner-only sweep of protocol swap/tax revenue that hooks streamed into the factory.withdrawOnly()(view) — surfaces the current circuit breaker for integrators.
Ownership Utilities
Because the factory extends Solady’s Ownable, it supports the full ownership-handover flow. The ABI includes:
requestOwnershipHandover(),cancelOwnershipHandover(),completeOwnershipHandover(address pendingOwner)— two-step transfer.transferOwnership(address newOwner)— immediate transfer.renounceOwnership()— drop ownership.ownershipHandoverExpiresAt(address pendingOwner)— view the expiry timestamp for an outstanding handover request.owner()— view the current owner.
These functions enable governance contracts or multisigs to rotate control safely.
Verification & Errors
isVerifiedHook(AngstromL2 hook)— public view helper for dashboards and callers.NotVerifiedHook,WithdrawOnlyMode, andProtocolFeeExceedsMaximumare the primary revert reasons surfaced to integrators.FlashBlockNumberProviderAlreadySetandFlashBlockNumberProviderUpdatedevents are reserved for optional latency tooling; they are not currently wired into the deployment flow.
Events Summary
PoolCreated— emitted when a verified hook initialises a pool.ProtocolSwapFeeUpdated/ProtocolTaxFeeUpdated— per-pool overrides.DefaultProtocolSwapFeeE6Updated/DefaultProtocolTaxFeeE6Updated— global default changes.WithdrawOnly— emergency mode engaged.FlashBlockNumberProviderUpdated— placeholder for future flash-block integrations.
Usage Workflow
- Governance (factory owner) deploys the factory with the desired
UNI_V4andHOOK_ADDRESS_MINER. - A deployer calls
createNewHookAndPoolWithMiner(or usesdeployNewHook+ manual initialise) to spin up a market. - The newly created hook calls
recordPoolCreationAndGetStartingProtocolFee; the factory logsPoolCreated. - Routers interact only with the hook and pool manager. Governance uses the factory to tweak protocol fees, withdraw revenue, or pause swaps if necessary.
Routers and LPs never integrate with the factory directly—it exists purely as the control plane for verified hook instances.