Pre-launch. RC4 code-frozen for external audit (rc4-audit-candidate, aca5fcd). Report pending. Status, updated 2026-10-08
GENESpecimen 001
Specimen 001, dissected along its price axis: the pylome, IMMUNITY's ribs, METABOLISM's septa, the Genome core and the GENESIS skeleton.
Genome anatomySpecimen 001, dissected along its price axis: the pylome, IMMUNITY's ribs, METABOLISM's septa, the Genome core and the GENESIS skeleton. Rendered from the frozen Genome; no chain state.

Protocol

The GENE architecture on Ethereum and Uniswap v4: one hook, one token, one pool, how fees become Permanent Mass, and what GENE trusts and controls.

GENE is a fixed-supply ERC-20 token and a single Uniswap v4 hook that is the only liquidity provider in one ETH/GENE pool. Swaps pay the pool's static LP fee to the hook's positions. Permissionless calls collect those fees into reserves and, within per-epoch budgets and caps, add reserves back to the pool as liquidity that no function can withdraw. This page describes the architecture as frozen in the RC4 audit candidate (tag rc4-audit-candidate, commit aca5fcd).

Status, updated 2026-10-08
DeploymentPre-launch. Addresses will be listed here at launch.
CodeRC4 frozen audit candidate: tag rc4-audit-candidate, commit aca5fcd. Genome VERSION 4.
Internal gate5,920 production tests, 0 failed, 0 skipped; 53,929 attack scenarios on the production code; in every one the protocol ended at or above its no-attack baseline. Verdict: ready for external audit.
External auditNext step. The code is frozen for it; the report will be linked here when it is published.
Bug bountyTo be announced.
Security contactTo be announced.
Admin powersNone after launch(): no owner, no upgrade path, no function that removes liquidity. Before launch, the launcher alone decides when the pool opens.
Outside GENEUniswap v4's protocol fee is controlled by Uniswap governance, not by GENE. If switched on (up to 0.1% per direction), the total fee becomes about 1.099% and that share never reaches GENE's positions.
Known risksPublished in full: Known limitations.

#Deployment model

GENE has three on-chain parts: one hook, one token and one pool.

Part Role Source
GeneHook The Uniswap v4 hook and the whole protocol: block-start references, epoch records, gene state machines, fee reserves and every protocol position GeneHook.sol:20-23
GeneToken Plain fixed-supply ERC-20 with burn of the caller's own balance; created by the hook's constructor, which receives the whole supply GeneToken.sol:6-18
The pool The single canonical ETH/GENE pool at the v4 PoolManager, keyed to GeneHook GeneHook.sol:236-244

The Genome is a library of compile-time constants committed by genomeHash (Genome.sol:4-7; GeneHook.sol:245-246); see Genome. GeneLens is an optional, stateless decoder that the hook never calls and anyone may deploy (GeneLens.sol:12-16).

#Address mining and CREATE2

Uniswap v4 reads a hook's permissions from the low 14 bits of its address (Hooks.sol:27-47). GeneHook is therefore deployed with CREATE2 through the canonical deterministic deployer, with a salt mined so that the address bits equal exactly 0x2AC0 and the top byte is not 0x91 (Deploy.s.sol:31-58; GeneSaltMiner.sol:7-33). Under CREATE2 the constructor's caller is the factory, so the launcher is passed as a constructor argument (Deploy.s.sol:31-32).

The constructor (GeneHook.sol:224-248):

  1. reverts HookAddressInvalid if the address bits are not exactly 0x2AC0, if the top byte is 0x91, if the PoolManager has no code, or if the launcher is zero;
  2. deploys GeneToken, which mints the whole supply to the hook;
  3. builds the PoolKey, stores poolId and genomeHash = keccak256(Genome.encode());
  4. emits Genesis.

The deploy script then re-checks the permission bits, prefix, PoolManager, launcher, unlaunched state, genome hash, supply held by the hook and every PoolKey field (Deploy.s.sol:66-95). Both scripts simulate unless broadcast is requested (Deploy.s.sol:4-9; Launch.s.sol:6).

#Launch sequence

launch() is callable once, by the launcher only (NotLauncher, AlreadyLaunched). In one transaction it (GeneHook.sol:252-273, :420-435):

  1. initializes the pool at the opening tick (Genome.sol:18);
  2. opens an unlock, adds the genesis band from the genesis lower tick to the opening tick holding the whole supply in GENE, transfers and settles the GENE, registers position 0 and emits MassAdded kind 0;
  3. burns any GENE dust left in the hook;
  4. seeds every price reference at the opening tick and records the launch block;
  5. sets GENESIS to EXPRESSED and emits GeneTransition and Launched.

The pool is never initialized and empty across a transaction boundary (GeneHook.sol:252-273). The launch must be submitted through a private bundle or private RPC: GENE has no anti-snipe surcharge, so ordering is the only launch-block protection (Launch.s.sol:4-10). The launch script asserts the opening tick, the LP fee, that the hook holds no GENE and that the PoolManager holds the whole supply (Launch.s.sol:35-42).

#The launcher's one-time role

Before launch, the launcher alone decides whether and when launch() runs; until then the whole supply sits in the hook and no one else can open the pool (GeneHook.sol:233, :252-254, :332-334). After launch, no function checks the caller (GeneHook.sol:33; invariant ROLE). The launcher should be an account with no code; the chain accepts a launcher with code, so this must be checked before deployment.

#Uniswap v4 integration

GENE uses the following Uniswap v4 concepts, in Uniswap's own terms:

  • Singleton PoolManager. The PoolManager is a single contract that serves as the entry point for all v4 pools (Uniswap: PoolManager). GENE's pool and all of its liquidity live there; GeneHook stores the PoolManager as an immutable (GeneHook.sol:161).
  • Hooks. A hook is an external contract attached to a pool, called at points the address bits enable (Uniswap: hooks). A hook can serve many pools; GeneHook's beforeInitialize reverts, so it serves exactly one.
  • Flash accounting. Operations inside unlock update per-currency deltas that must net to zero before the unlock returns (Uniswap: flash accounting; PoolManager.sol:104-115). GeneHook opens an unlock for launch, metabolize() and regenerate(). A transient-storage slot records which action the hook started, and unlockCallback reverts UnexpectedCallback for any other (GeneHook.sol:395-418). This needs the Cancun EVM (foundry.toml:12).
  • ERC-6909 claims. The PoolManager can mint a claim token for tokens left inside it (Uniswap: ERC-6909). GeneHook holds its fee reserves this way, under id 0 for ETH and the token's address for GENE (GeneHook.sol:235, :474-475).

The build pins v4-core 1.0.2 (59d3ecf5) (foundry.toml:1-3).

#PoolKey and poolId

A PoolKey identifies a v4 pool (Uniswap: create a pool). GENE's key is fixed in the constructor and returned by poolKey() (GeneHook.sol:236-242, :965-973):

Field Value Source
currency0 native ETH, address(0) GeneHook.sol:237
currency1 GENE GeneHook.sol:238
fee 10,000 pips (1.00%), static GeneHook.sol:239; Genome.sol:16
tickSpacing 60 GeneHook.sol:240; Genome.sol:17
hooks GeneHook GeneHook.sol:241

poolId is key.toId(), stored as an immutable and emitted in Genesis (GeneHook.sol:243-247). After deployment, verify both as described in verify the deployment. See permissions and PoolKey.

#Hook permissions

REQUIRED_FLAGS is 0x2AC0: bits 13, 11, 9, 7 and 6 (GeneHook.sol:148-149; Hooks.sol:29-39). There are no return-delta flags, no donate flags and no after-initialize or after-liquidity flags (GeneHook.sol:40-45).

Callback Behaviour Reverts Source
beforeInitialize none always, Forbidden GeneHook.sol:332-334
beforeAddLiquidity none always, Forbidden GeneHook.sol:337-343
beforeRemoveLiquidity none always, Forbidden GeneHook.sol:346-352
beforeSwap canonical-key check; on a block's first swap, block-start bookkeeping; returns zero delta and fee override 0 NotPoolManager, WrongPool GeneHook.sol:355-366
afterSwap accumulates net curve GENE, block ETH volume and, on buys, the revenue metric; returns 0 NotPoolManager GeneHook.sol:368-390

The PoolManager skips a hook's callbacks when the hook itself is the caller (Hooks.sol:171-175, :253). So the three always-reverting callbacks still let GeneHook open the pool and add its own liquidity, while every other initialize, add or remove naming this hook reverts. Callbacks without a flag are not implemented, and the contract has no fallback or receive function (GeneHook.sol:43-45). Donations are not blocked; they become fees owed to the protocol's in-range positions (GeneHook.sol:669-697).

The hook contains no rule that rejects a swap by size, direction or sender. From every state the invariant runs reach, a small and a very large sell succeed (GenePermanence.t.sol:167-185).

#Pool mechanics

Ticks in this pool are log base 1.0001 of GENE per ETH, so a higher tick is a cheaper GENE and a buy moves the tick down. Positions use ticks that are multiples of the tick spacing, 60 (Uniswap: glossary; Genome.sol:17).

Genesis is single-sided. The pool opens at tick 178,080 and the genesis band spans 85,920 to 178,080 (Genome.sol:18-19). Because the current tick sits at the band's upper edge, the band needs only GENE, and the launch callback reverts InvariantBroken if any ETH was required (GeneHook.sol:423-425). Every later placement is also single-sided: ETH bids below the price, GENE asks above it, checked in _addMass (GeneHook.sol:655-660). How anchors position those bands is described in the learn pages.

#Fee anatomy and accounting

Uniswap separates the LP fee, the protocol fee and hook fees (Uniswap: fees).

  • LP fee. The 1% LP fee is charged by Uniswap v4 on the input token (ETH on buys, GENE on sells) and accrues to the hook's positions, because the hook is the pool's only liquidity provider (GeneHook.sol:337-343). It is static, and GeneHook contains no call that changes it (GeneHook.sol:26-27).
  • Uniswap protocol fee. It is governed by Uniswap and outside GENE's control. v4 core caps it at 1,000 pips (0.1%) per direction (ProtocolFeeLibrary.sol:8); if enabled at the cap, a swapper pays 1.099% in total.
  • Hook fee. GENE takes no hook fee: the swap callbacks return zero deltas (GeneHook.sol:365, :389).

"Revenue" in an epoch record is 1% of the ETH buyers paid in, recorded as evidence for gene conditions (GeneHook.sol:385-387). It is not the fees collected.

Fees are collected by metabolize() into the reserves. Anyone can call it once METABOLISM is expressed, at most once per COOLDOWN_BLOCKS (GeneHook.sol:281-286, 451-476):

  1. Harvest. Zero-liquidity pokes of the genesis position and up to eight registry positions, round-robin, return accrued fees only; a non-zero principal reverts InvariantBroken (GeneHook.sol:669-697; Genome.sol:45).
  2. Credit. ETH to ethReserve, or 20% to regenReserve and 80% to ethReserve once REGENERATION is expressed; GENE to geneReserve (GeneHook.sol:707-718). Fees auto-collected when adding to an existing position are credited the same way (GeneHook.sol:554-559).
  3. Place. Once evolution is caught up: IMMUNITY (if its deployment test passes), METABOLISM bids and the churn tier; then, in every call, asks; each within its own budget and caps; see net-flow budgets. ETH above the budgets stays in ethReserve. A placement that is not single-sided reverts the whole call InvariantBroken (GeneHook.sol:655-660).
  4. Settle. Fees collected (credits) are netted against principal added (debits) per currency, and the difference is minted or burned as the hook's own ERC-6909 claims, so the unlock closes with zero delta (GeneHook.sol:720-725).

The source comment states that claims then equal the sum of reserves (GeneHook.sol:720-721), and the invariant suite checks the claim balances against the reserves (GenePermanence.t.sol:139-151). The hook never takes ETH and takes GENE only to itself, to burn it in regenerate() (GeneHook.sol:30-31, :586). No one else can collect the protocol's accrued fees, and there is no creator or team share: the hook is the only liquidity provider and earns the whole LP fee (GeneHook.sol:28-31, 337-343).

#Permanent Mass

Permanent Mass is every liquidity position in the canonical pool owned by GeneHook. The liquidity units cannot be withdrawn: _modifyLiquidity is the only call to modifyLiquidity, its amount is unsigned, and outside removes revert (GeneHook.sol:594-608, :346-352; invariants PM1, PM2). Mass is measured in liquidity units; its ETH/GENE mix moves with price.

MassAdded kinds are 0 genesis, 1 METABOLISM bid, 2 IMMUNITY bid, 3 ask and 4 churn-tier bid (GeneHook.sol:138). METABOLISM bids and the churn tier start at alignUp(atlTick + 60, 600) with a 1,200-tick band (GeneHook.sol:484, :516-518, :785). Every ETH band starts at tick 178,200 or higher, below the opening price, in every state the tests reach, because the invariant suite asserts that the all-time low is never above the opening price (GenePermanence.t.sol:101). IMMUNITY starts at alignUp(max(spot, atlTick) + 60, 600) (GeneHook.sol:498-501). Asks end at alignDown(min(spot, athTick) - 60, 600) (GeneHook.sol:544-545, :788-792). Oversized placements are skipped with PlacementSkipped (GeneHook.sol:610-627). See Permanent Mass.

#Trust assumptions and dependencies

Item Treatment Basis
Uniswap v4 PoolManager correctness trusted, an assumption design assumption
Ethereum consensus, the Solidity compiler trusted design assumption
The launcher trusted once, for launch() only GeneHook.sol:252-255
Routers, aggregators, solvers, builders, wash traders, external pools untrusted design assumption
Uniswap protocol fee Uniswap governance, outside GENE's control ProtocolFeeLibrary.sol:8
Uniswap Labs routing allow and deny lists affect discoverability in the Uniswap interface, not tradability off-chain
Aggregator and route coverage set by each router; observable once the pool exists off-chain

The constructor's 0x91 prefix check exists because, per the source comment, the Uniswap routing auto-allowlist excludes such addresses (GeneHook.sol:227-228). Routing policy is off-chain; whether a given router routes GENE is an assumption, not a property of the contracts. GeneHook calls only the PoolManager and GeneToken, so it uses no external price oracle (GeneHook.sol:32).

#Admin powers

After launch there are none. GeneHook has no owner, proxy, timelock, pause, fee setter, withdrawal path or upgrade path, and no function behaves differently by caller (GeneHook.sol:25-33; invariant ROLE). GeneToken has no owner, no further mint, no pause, no blacklist and no transfer fee (GeneToken.sol:6-10). Every Genome value is a compile-time constant that no function takes as input (Genome.sol:5-6).

Before launch, the launcher's only power is to call launch() once, or not at all (GeneHook.sol:252-255). The Security page records the audit status.

Sources (37)
  • src/GeneHook.sol:20-46
  • src/GeneHook.sol:131-149
  • src/GeneHook.sol:161-186
  • src/GeneHook.sol:224-273
  • src/GeneHook.sol:276-316
  • src/GeneHook.sol:332-390
  • src/GeneHook.sol:395-435
  • src/GeneHook.sol:451-552
  • src/GeneHook.sol:554-559
  • src/GeneHook.sol:594-725
  • src/GeneHook.sol:782-792
  • src/GeneHook.sol:965-973
  • src/GeneToken.sol:6-23
  • src/Genome.sol:4-19
  • src/GeneLens.sol:12-16
  • script/Deploy.s.sol:4-14
  • script/Deploy.s.sol:31-95
  • script/Launch.s.sol:4-14
  • script/Launch.s.sol:35-42
  • script/GeneSaltMiner.sol:7-33
  • lib/v4-core/src/libraries/Hooks.sol:27-47
  • lib/v4-core/src/libraries/Hooks.sol:171-175
  • lib/v4-core/src/libraries/Hooks.sol:253
  • lib/v4-core/src/libraries/ProtocolFeeLibrary.sol:8
  • test/invariant/GenePermanence.t.sol:101
  • test/invariant/GenePermanence.t.sol:139-151
  • test/invariant/GenePermanence.t.sol:167-185
  • lib/v4-core/src/PoolManager.sol:104-115
  • foundry.toml:1-3
  • foundry.toml:12
  • https://developers.uniswap.org/docs/protocols/v4/concepts/poolmanager
  • https://developers.uniswap.org/docs/protocols/v4/concepts/hooks
  • https://developers.uniswap.org/docs/protocols/v4/concepts/flash-accounting
  • https://developers.uniswap.org/docs/protocols/v4/concepts/erc-6909
  • https://developers.uniswap.org/docs/protocols/v4/guides/create-pool
  • https://developers.uniswap.org/docs/get-started/concepts/fees
  • https://developers.uniswap.org/docs/get-started/concepts/glossary

Paths are relative to the GENE repository at tag rc4-audit-candidate (aca5fcd).