Architecture

Architecture

NFTX’s on-chain surface is small but layered. Three contracts handle the hot path; everything else is auxiliary.

Contracts at a glance

ContractWhat it ownsKey public surface
LockerCollection registry, CollectionToken clones, NFT custody.createCollection, initializeCollection, deposit, redeem, swap, swapBatch.
ListingsLiquid + dutch + floor listings, tax accounting.createListings, fillListings, relist, modifyListings, cancelListings, extendListings, transferOwnership.
NFTXV4HookUniswap V4 hook for every collection pool, AMM-fee escrow, fee config.registerCollection, initializeCollection, depositFees, setDefaultFee, setFee, setAmmFee, setAmmBeneficiary, setFeeExemption, removeFeeExemption, withdraw (escrow).
CollectionTokenOne ERC-20 clone per collection.Standard ERC-20 + permit; minted/burned by Locker only.
LockerManagerOps gate for privileged manager mints (Locker.unbackedDeposit).setManager, isManager.
TokenEscrowPer-payee, per-token claimable balances. Inherited by Listings and NFTXV4Hook.withdraw, balances.
CollectionShutdownQuorum-based shutdown lifecycle.start, vote, reclaimVote, execute, claim, cancel, preventShutdown.
AirdropRecipientMerkle-distributed airdrops on top of CT supply.requestAirdrop, distributeAirdrop, claim, setAirdropTarget.
TaxCalculatorPure tax math for listings.calculateTax, getProtectedListingHealth.
NFTXZapConvenience wrapper to bootstrap a pool.zap.

Pool fee lifecycle

This is the part the FE most often needs to render correctly:

  1. Listings charges tax on liquid/dutch listings; relevant slices flow into Listings.depositFees(collection, amount).
  2. NFTXV4Hook.depositFees stores the deposit on the hook against the collection’s V4-sorted (currency0, currency1) and emits PoolFeesQueued(collection, amount0, amount1).
  3. On the next swap that touches the pool, the hook donates the queued amount back to LPs via the V4 donate mechanic and emits PoolFeesDonated(collection, amount0, amount1).
  4. The subgraph mirrors this: PoolFees.queuedAmount0/1 increments on PoolFeesQueued and clears on PoolFeesDonated, while totalDonated0/1 accumulates lifetime amounts.
⚠️

The pre-rename “PoolFeesReceived / PoolFeesDistributed / PoolFeesSwapped” pipeline has been retired. If any FE code is still reading those events or PoolFees.ethAvailable / tokenAvailable / totalEthOut / totalTokenIn fields, it must be migrated to the queued / donated pair documented above.

AMM fee + beneficiary

The hook also takes a configurable AMM cut on top of the LP fee:

  • NFTXV4Hook.setAmmFee(uint24) — basis-points-style fee taken from the unspecified leg of every swap.
  • NFTXV4Hook.setAmmBeneficiary(address) — recipient of the AMM cut. May be address(0), in which case the cut is burnt.
  • Per-beneficiary FeeOverrideSet(beneficiary, flatFee) / FeeOverrideRemoved(beneficiary) — flat fee applied when a swap is routed through the beneficiary; replaces the previous “beneficiary royalty” model.
  • The cut is escrowed into the same TokenEscrow that backs listing refunds; the beneficiary calls NFTXV4Hook.withdraw(token) (inherited from TokenEscrow) to claim.

Config.ammFee, Config.ammBeneficiary, and the per-beneficiary FeeOverride rows in the subgraph reflect these states 1-for-1.