Skip to main content

Overview

GeodeFactory handles one-transaction token launches. It deploys a GeodeToken ERC20, mints the full supply to the hook, initializes the v4 pool as an empty shell, configures the hook, and registers the permanent bonding curve — all atomically. The v4 pool is initialized at sqrtPriceAtTick(0) — a hollow shell. The hook handles all pricing via BEFORE_SWAP_RETURNS_DELTA. There is no graduation.

Immutables

IPoolManager public immutable poolManager;
GeodeHook public immutable hook;
Currency public immutable weth;    // WETH or native ETH wrapper

Launch Parameters

struct LaunchParams {
    string name;                    // Token name
    string symbol;                  // Token symbol
    uint256 totalSupply;            // Total supply (18 decimals)
    uint256 virtualTokenReserve;    // Vt (>= totalSupply for asymptotic pricing)
    uint256 virtualEthReserve;      // Ve (starting price = Ve / Vt)
    uint24 deployerRoyaltyBps;      // Deployer's surplus share (max 50%)
    uint256 batchInterval;          // Blocks between settlements
    uint24 directSwapFeeBps;        // Direct swap fee (0.3% default for curve pools)
    uint256 settlementFeeBps;       // Settlement fee per intent
    uint256 maxBatchSize;           // Max intents per side
    int24 tickSpacing;              // Pool tick spacing
}

Launch Flow

launch()

function launch(LaunchParams calldata params)
    external returns (address tokenAddress, PoolId poolId)
1

Validate

  • totalSupply > 0
  • virtualTokenReserve > 0 and virtualEthReserve > 0
  • deployerRoyaltyBps <= 5000 (max 50%)
  • maxBatchSize between 1 and 256
  • virtualTokenReserve >= totalSupply (all tokens go to curve)
2

Deploy ERC20

GeodeToken token = new GeodeToken(
    params.name, params.symbol,
    params.totalSupply, address(hook)
);
Full supply minted to the hook contract.
3

Determine Currency Ordering

Uniswap v4 requires address(currency0) < address(currency1). WETH is always currency0 (lower address). If the token address is lower than WETH, the launch reverts with TokenSortOrderViolation.
4

Build Pool Key & Initialize

PoolKey memory key = PoolKey({
    currency0: weth,
    currency1: Currency.wrap(tokenAddress),
    fee: 0x800000,    // Dynamic fee flag
    tickSpacing: 60,   // Default
    hooks: IHooks(address(hook))
});
The pool IS initialized in v4 at sqrtPriceAtTick(0) — but as an empty shell with no liquidity. The hook handles all pricing.
5

Configure Hook

Calls hook.geodeInitializePool(key, config) with the curve parameters. Because config.deployer != address(0), this call is restricted to the factory.
6

Register Launch

Calls hook.registerLaunch() to set up permanent LaunchState tracking with virtual reserves and token reference.
7

Initialize Price Beacon

Initializes a second v4 pool (same token pair, no hook) at the curve’s starting price. This secondary pool starts with zero liquidity — it serves as a price beacon for external integrators. Community members can add LP organically by buying tokens from the curve and providing two-sided liquidity to this AMM.

Launch Record

struct LaunchRecord {
    address deployer;
    address tokenAddress;
    PoolId poolId;
    uint256 totalSupply;
    uint256 launchBlock;
    bool exists;
}
Stored in mapping(PoolId => LaunchRecord) public launches with a global launchCount.

Events

event TokenLaunched(
    PoolId indexed poolId,
    address indexed deployer,
    address indexed tokenAddress,
    string name, string symbol,
    uint256 totalSupply,
    uint256 virtualTokenReserve, uint256 virtualEthReserve,
    uint24 deployerRoyaltyBps
);

Source

GeodeFactory.sol

View the full source code on GitHub (~230 lines).