Skip to main content

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 — target IPoolManager every hook must attach to.
  • HOOK_ADDRESS_MINER — external contract that mines salts for deterministic hook addresses (createNewHookAndPoolWithMiner).
  • HOOK_INITCODE_STORE — SSTORE2 pointer containing the raw AngstromL2 creation bytecode.
  • withdrawOnly — global circuit-breaker; when true hooks 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 as recordPoolCreationAndGetStartingProtocolFee.

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:

  1. Asks HOOK_ADDRESS_MINER to mine a salt for the desired initialOwner.
  2. Calls deployNewHook.
  3. Sets the supplied PoolKey’s hooks address and invokes initializeNewPool on 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 withdrawOnly is false.
  • Computes the protocol swap fee using getDefaultProtocolSwapFee (bounded by MAX_PROTOCOL_SWAP_FEE_E6) and returns the default protocol tax fee.
  • Emits a PoolCreated event containing the creator & protocol splits for monitoring.

Helpers exposed to routers and monitoring systems:

  • getDefaultProtocolSwapFee(uint256 creatorSwapFeeE6, uint256 lpFeeE6) — solves for the protocol swap fee f_pr given 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 the 3e6 cap.
  • setDefaultProtocolTaxFee(uint24 newTaxE6) — updates the default protocol tax share, enforcing the 0.75e6 cap.

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() — sets withdrawOnly = true and emits WithdrawOnly. Hooks observe the flag via their cached copy (through pullWidthrawOnly).
  • 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, and ProtocolFeeExceedsMaximum are the primary revert reasons surfaced to integrators.
  • FlashBlockNumberProviderAlreadySet and FlashBlockNumberProviderUpdated events 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

  1. Governance (factory owner) deploys the factory with the desired UNI_V4 and HOOK_ADDRESS_MINER.
  2. A deployer calls createNewHookAndPoolWithMiner (or uses deployNewHook + manual initialise) to spin up a market.
  3. The newly created hook calls recordPoolCreationAndGetStartingProtocolFee; the factory logs PoolCreated.
  4. 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.