Pre-launch. RC4 code-frozen for external audit (rc4-audit-candidate, aca5fcd). Report pending. Status, updated 2026-10-08
GENESpecimen 001

Documentation · Reference

Errors

Every custom error GENE contracts can raise, with its selector and the exact condition that throws it.

Selectors were produced by forge inspect at commit aca5fcd3bbc9ecb1026de58bea431d406d8ecfff (see Selectors). GeneLens defines no custom errors. Errors raised inside the PoolManager or inside OpenZeppelin ERC20 during a GENE call carry their own selectors.

#GeneHook errors

Error Selector Declared Thrown at
NotPoolManager() 0xae18210a GeneHook.sol:52 GeneHook.sol:359, GeneHook.sol:372, GeneHook.sol:396
NotLauncher() 0x9e6ff739 GeneHook.sol:53 GeneHook.sol:253
AlreadyLaunched() 0xcfa6d878 GeneHook.sol:54 GeneHook.sol:254
NotLaunched() 0x8dda39df GeneHook.sol:55 GeneHook.sol:1000
Forbidden() 0xee90c468 GeneHook.sol:56 GeneHook.sol:333, GeneHook.sol:342, GeneHook.sol:351
WrongPool() 0xd3c913ea GeneHook.sol:57 GeneHook.sol:363
UnexpectedCallback() 0xdab1e993 GeneHook.sol:58 GeneHook.sol:403
HookAddressInvalid() 0xf0dd0d55 GeneHook.sol:59 GeneHook.sol:226, GeneHook.sol:228, GeneHook.sol:229
GeneNotExpressed(uint8) 0x2abc0a56 GeneHook.sol:60 GeneHook.sol:283, GeneHook.sol:298
Cooldown(uint256) 0xff658a1c GeneHook.sol:61 GeneHook.sol:285, GeneHook.sol:300
InvariantBroken() 0xb2151907 GeneHook.sol:62 GeneHook.sol:425, GeneHook.sol:660, GeneHook.sol:694

#NotPoolManager

error NotPoolManager(), selector 0xae18210a. Thrown when msg.sender is not the PoolManager in beforeSwap (GeneHook.sol:359), afterSwap (GeneHook.sol:372) or unlockCallback (GeneHook.sol:396).

#NotLauncher

error NotLauncher(), selector 0x9e6ff739. Thrown by launch() when msg.sender != launcher (GeneHook.sol:253).

#AlreadyLaunched

error AlreadyLaunched(), selector 0xcfa6d878. Thrown by launch() when launched is already true, so a second launch always reverts (GeneHook.sol:254).

#NotLaunched

error NotLaunched(), selector 0x8dda39df. Thrown by _requireLaunched() (GeneHook.sol:999-1001), which metabolize(), regenerate() and evolve() call first (GeneHook.sol:282, GeneHook.sol:297, GeneHook.sol:313).

#Forbidden

error Forbidden(), selector 0xee90c468. beforeInitialize, beforeAddLiquidity and beforeRemoveLiquidity always throw it (GeneHook.sol:332-352). The PoolManager calls them for every initialize or liquidity change on a pool naming this hook, except when the hook itself is the caller.

A caller sees it wrapped: v4's callHook reverts with WrappedError(address target, bytes4 selector, bytes reason, bytes details) (selector 0x90bfb865), where target is the hook, selector is the callback's selector, reason holds Forbidden() and details holds HookCallFailed() (selector 0xa9e35b2f) (v4 Hooks.sol:74, Hooks.sol:131-137, CustomRevert.sol:11, CustomRevert.sol:83-115).

#WrongPool

error WrongPool(), selector 0xd3c913ea. Thrown by beforeSwap when the key passed by the PoolManager differs from the canonical key in currency0, currency1, fee or tickSpacing (GeneHook.sol:360-363). afterSwap has no key check.

#UnexpectedCallback

error UnexpectedCallback(), selector 0xdab1e993. Thrown by unlockCallback when the transient ACTION_SLOT is zero or differs from the action decoded from the callback data (GeneHook.sol:397-403). The slot is set only by the hook's own _unlock (GeneHook.sol:409-418).

#HookAddressInvalid

error HookAddressInvalid(), selector 0xf0dd0d55. Thrown by the constructor when:

  1. the low 14 bits of the hook address are not exactly REQUIRED_FLAGS (0x2AC0) (GeneHook.sol:226);
  2. the top byte of the address is 0x91 (GeneHook.sol:228);
  3. the PoolManager address has no code, or the launcher is address(0) (GeneHook.sol:229).

#GeneNotExpressed

error GeneNotExpressed(uint8 gene), selector 0x2abc0a56. Thrown with gene = 1 by metabolize() while METABOLISM is not EXPRESSED (GeneHook.sol:283), and with gene = 2 by regenerate() while REGENERATION is not EXPRESSED (GeneHook.sol:298). After launch the first metabolize() therefore needs METABOLISM to have been expressed by an earlier evolve().

#Cooldown

error Cooldown(uint256 readyAtBlock), selector 0xff658a1c. Thrown when the stored last block is non-zero and block.number < last + COOLDOWN_BLOCKS (COOLDOWN_BLOCKS); readyAtBlock is last + COOLDOWN_BLOCKS.

Function Stored block Written when Source
metabolize() lastMetabolizeBlock At the start of every call that passes the checks GeneHook.sol:284-286
regenerate() lastRegenerateBlock Only after a call that spent its whole allotment GeneHook.sol:299-307

#InvariantBroken

error InvariantBroken(), selector 0xb2151907. Thrown when a liquidity operation returns a principal that the code does not allow:

Where Condition Source
launch() genesis add The genesis band required any ETH GeneHook.sol:425
_addMass (every placement) An ETH placement required GENE, or an ask required ETH GeneHook.sol:654-660
_poke (harvest) A zero-liquidity poke returned any principal GeneHook.sol:691-694

A revert rolls back the whole call, including its harvest and its cooldown write (GeneHook.sol:284-286). An ETH placement requires GENE when the pool price is at or beyond the band's lower tick. Audit scope note: metabolize() reverting InvariantBroken while the price sits at or beyond the METABOLISM or churn bid band (see Security).

#GeneLens revert

positionAt(hook, i) reverts with the string "index" (Error(string)) when i >= positionCount(hook) (GeneLens.sol:93-94). GeneLens has no other revert of its own.

#GeneToken errors

GeneToken defines no custom errors. It raises the OpenZeppelin ERC-6093 errors below (declared in draft-IERC6093.sol). OZ is lib/v4-core/lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol.

Error Selector Thrown when Source
ERC20InsufficientBalance(address,uint256,uint256) 0xe450d38c A transfer, transferFrom or burn exceeds the sender's balance draft-IERC6093.sol:16; OZ:195
ERC20InvalidSender(address) 0x96c6fd1e A transfer or burn from address(0) draft-IERC6093.sol:22; OZ:173, OZ:243
ERC20InvalidReceiver(address) 0xec442f05 A transfer or mint to address(0) draft-IERC6093.sol:28; OZ:176, OZ:228
ERC20InsufficientAllowance(address,uint256,uint256) 0xfb8f41b2 transferFrom exceeds the allowance draft-IERC6093.sol:36; OZ:309
ERC20InvalidApprover(address) 0xe602df05 An approval from address(0) draft-IERC6093.sol:42; OZ:286
ERC20InvalidSpender(address) 0x94280d62 An approval for address(0) draft-IERC6093.sol:48; OZ:289
Sources (20)
  • src/GeneHook.sol:52-62
  • src/GeneHook.sol:224-229
  • src/GeneHook.sol:252-254
  • src/GeneHook.sol:281-300
  • src/GeneHook.sol:312-313
  • src/GeneHook.sol:332-372
  • src/GeneHook.sol:395-403
  • src/GeneHook.sol:420-426
  • src/GeneHook.sol:484
  • src/GeneHook.sol:516-518
  • src/GeneHook.sol:654-697
  • src/GeneHook.sol:999-1001
  • src/GeneLens.sol:93-94
  • src/GeneToken.sol:20-23
  • lib/v4-core/src/libraries/Hooks.sol:74
  • lib/v4-core/src/libraries/Hooks.sol:131-137
  • lib/v4-core/src/libraries/CustomRevert.sol:11
  • lib/v4-core/src/libraries/CustomRevert.sol:83-115
  • lib/v4-core/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol:16-48
  • lib/v4-core/lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol:171-310

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