Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 

Repository files navigation

Hookify Technical Documentation & Whitepaper

Version: 4.0
Date: August 5, 2026
Status: Production (Ethereum Mainnet Live)
Core Stack: Uniswap v4 Hooks · React + Vite · Node.js · Fastify · PostgreSQL · Ethereum Mainnet


Table of Contents

  1. Executive Summary
  2. Project Vision
  3. System Overview
  4. Smart Contract Architecture
  5. Token Launch Flow
  6. Fee & Revenue Model
  7. Frontend Architecture
  8. Backend & API Layer
  9. Indexing & Data Pipeline
  10. Database Schema
  11. Security Architecture
  12. Mainnet Deployment
  13. Engineering Standards
  14. Implementation Status
  15. Roadmap

1. Executive Summary

Hookify is a token launch infrastructure protocol built entirely on top of Uniswap v4's hook system. It enables creators to launch ERC-20 tokens with programmable lifecycle management from the first trade to permanent liquidity enforced fully on-chain with no admin keys, no upgradeable contracts, and no external keepers.

Metric Value
Protocol Uniswap v4 Hooks (Custom Accounting)
Live Network Ethereum Mainnet (chainId 1)
Token Supply Fixed 1,000,000,000 (1B) per launch
Protocol Fee Hard-capped at 0.15% (immutable)
Creator Fee Creator-configurable (up to ~3%)
LP Lock Permanent (no admin override possible)
Anti-Sniper Static on-chain, zero off-chain keepers

What makes Hookify different:

  • Tokens trade inside a Uniswap v4 pool from block one no separate pre-DEX bonding curve phase
  • Liquidity lock is enforced by contract immutability, not a timer or trusted multisig
  • The hook manages the entire token lifecycle (BC mode → AMM mode) atomically without any external calls
  • Creators earn trading fees and can claim them permissionlessly from their own vault

2. Project Vision

Hookify is designed as a Protocol Operating System for Uniswap v4 Hooks not a token launchpad template.

The product philosophy:

"The protocol is the product. The user should feel they are operating protocol infrastructure, not filling out forms on a website."

Core design rules:

  1. Launch flow works even if the API/indexer is offline frontend builds calldata directly from ABIs
  2. All state that matters lives on-chain; databases are rebuildable caches
  3. No contract can be paused, upgraded, or admin-controlled immutability is the product
  4. Fees are transparent and fixed at the time of launch no setter functions

3. System Overview

flowchart TB
    subgraph CLIENTS["Client Layer"]
        WEB["Web3 DApp\napp.hookify.fun\nReact + Vite"]
        MKT["Marketing Site\nhookify.fun\nStatic"]
    end

    subgraph EDGE["Edge / API Layer"]
        API["API Server\nFastify + Node.js\nPort: ENV-assigned"]
        AUTH["Auth\nSIWE / JWT"]
    end

    subgraph DATA["Data Layer"]
        IDX["Event Indexer\nNode.js Workers"]
        DB[("PostgreSQL\nRebuildable Cache")]
    end

    subgraph CHAIN["Ethereum Mainnet (chainId 1)"]
        PM["Uniswap v4\nPoolManager\n0x000000000004444c..."]
        HOOK["EthCreatorFeeHookV3\n0xC5C72B770396..."]
        LAUNCHER["ClassicLauncher\n0xE3faC65ce486..."]
        VAULT["FeeSplitVaultV1\nper-token"]
        FACTORY["FeeSplitVaultFactoryV1\n0x4a178da4a496..."]
    end

    WEB -->|REST / SSE| API
    WEB -->|wagmi + viem direct RPC| CHAIN
    MKT -->|Static| EDGE
    API --> DB
    IDX --> DB
    IDX -->|eth_getLogs polling| CHAIN
    API -->|RPC reads| CHAIN
    API --> AUTH

    LAUNCHER -->|initialize pool| PM
    PM -->|beforeSwap / afterSwap| HOOK
    HOOK -->|NativeSwapFeesAccrued| IDX
    HOOK -->|fees flush| VAULT
    FACTORY -->|deploy| VAULT
    LAUNCHER -->|register| VAULT
Loading

3.1 Layer Responsibilities

Layer Responsibility Technology
Contracts Token creation, v4 pool management, fee accrual, LP lock Solidity ^0.8.26, Foundry, Uniswap v4
Frontend DApp Explore, Launch, Trade, Dashboard, Claim React 18, Vite, wagmi v2, viem, Privy
Marketing Site Landing page, docs, SEO Static HTML / Next.js SSG
API Server Token discovery, metadata, creator data, auth Node.js 20, Fastify, Drizzle ORM
Indexer Workers On-chain event indexing (Launched, Swap, FeeAccrued) Node.js, viem, PostgreSQL
Database Rebuildable cache of on-chain state PostgreSQL 16, Drizzle ORM

4. Smart Contract Architecture

4.1 Contract Interaction Map

flowchart LR
    CREATOR["Creator Wallet"] -->|"launch(params)\n{value: initialBuyEth}"| LAUNCHER

    LAUNCHER -->|"deploy token"| TOKEN["HookToken\nFixed 1B supply\nERC-20"]
    LAUNCHER -->|"initializePool"| PM["Uniswap v4\nPoolManager"]
    LAUNCHER -->|"register"| VAULT_FAC["FeeSplitVaultFactory"]
    VAULT_FAC -->|"CREATE2 deploy"| VAULT["FeeSplitVaultV1\n(per-token)"]

    PM -->|"beforeSwap\nafterSwap"| HOOK["EthCreatorFeeHookV3"]
    HOOK -->|"NativeSwapFeesAccrued event"| LOG["Event Log"]
    HOOK -->|"claimCreatorFees(poolId)"| VAULT

    TRADER["Trader"] -->|"swap via\nPoolSwapTest"| PM

    CREATOR -->|"1. claimCreatorFees(poolId)"| HOOK
    HOOK -->|"flush ETH"| VAULT
    CREATOR -->|"2. claim()"| VAULT
    VAULT -->|"ETH"| CREATOR
Loading

4.2 Current Deployed Contracts (Ethereum Mainnet — Programmable Classic V3)

Contract Address Role
Uniswap v4 PoolManager 0x000000000004444c5dc75cB358380D2e3dE08A90 Core AMM singleton
PoolSwapTest (our deploy) 0xa8D342A66DC7936338bA2420a0293ab4789ccD0C Swap executor on mainnet
FeeSplitVaultFactoryV1 0x4a178da4a496032103e77d92EAC25b39502852b9 Deploys per-token vaults
EthCreatorFeeHookFactoryV3 0xBe540C9319853833ABdaA2D2486D13E1B497a54A Deploys hooks
EthCreatorFeeHookV3 0xC5C72B770396050C088BBc111B1A448Ec3Dce0cc Active hook (fee routing)
ClassicLaunchPolicyV1 0xE364D0bdbEDdB308E602Bed1B28B176Be9f02cdB Launch policy rules
ClassicCtoAuthorityV1 0x3f11E06Eb1bA20670dFE28567359DADaEf490377 CTO transfer authority
ClassicInitialBuyVestingWalletFactoryV1 0xA68c3543d1206Fc29Ee4c851A01c1Fac1dbf6153 Vesting wallets
ClassicLauncher 0xE3faC65ce48695988584a46feAc505fa6B53a854 Primary launch contract

Deployment note: Uniswap v4 PoolSwapTest is NOT deployed on Ethereum mainnet by Uniswap. Hookify deployed it at the address above as part of DeployClassic.s.sol. Hook address was mined using CREATE2 (1,511 iterations, salt 0x...05e7) to satisfy Uniswap v4's required permission bit pattern.

4.3 Hook Permission Bits

The EthCreatorFeeHookV3 hook address encodes permission bits in its lower bytes (Uniswap v4 requirement):

Permission Required Reason
beforeSwap Custom fee logic
afterSwap Fee accrual after each swap
beforeSwapReturnDelta Custom Accounting (V4 delta pricing)

Hook flags: 0x20CC

4.4 V4 Custom Accounting — How Fee Collection Works

sequenceDiagram
    participant T as Trader
    participant PST as PoolSwapTest
    participant PM as PoolManager
    participant H as EthCreatorFeeHookV3
    participant V as FeeSplitVaultV1

    T->>PST: swap(key, params, testSettings)
    PST->>PM: unlock(calldata)
    PM->>PST: unlockCallback()
    PST->>PM: swap(key, params)
    PM->>H: beforeSwap(key, params)
    H-->>PM: hookData (fee override)
    PM-->>PST: BalanceDelta
    PST->>PM: settle(currency0/1)
    PM->>H: afterSwap(key, delta, hookData)
    H->>H: compute fee split (creator / protocol)
    H->>H: emit NativeSwapFeesAccrued(poolId, trader, isBuy, feeBps, grossAmt, creatorFee, launcherFee)
    Note over H: ETH fees tracked internally in hook storage<br/>Not yet in vault — requires explicit flush

    Note over T,V: — LATER: Creator Claims —
    T->>H: claimCreatorFees(poolId)
    H->>PM: unlock(calldata) → take ETH from PM
    H->>V: send ETH to vault
    T->>V: claim()
    V-->>T: ETH transferred
Loading

Important: In V4 Custom Accounting, eth_getBalance(hookAddress) always shows 0. The ETH is tracked as ERC-6909 claims in the PoolManager (40,000+ ETH in PM singleton). claimCreatorFees(poolId) converts these claims to real ETH and sends them to the vault.

4.5 Key Contract Invariants

# Invariant Enforcement
I-1 Token supply fixed at exactly 1,000,000,000 — no mint, no admin burn HookToken constructor
I-2 Protocol fee hard-capped at 0.15% — immutable constant, no setter FeeMath
I-3 Fees cannot be changed after launch Hook has no fee setter
I-4 No contract is upgradeable; no admin can pause trading Contract immutability
I-5 Beneficiaries can only claim their own accrued balance FeeSplitVaultV1
I-6 LP lock enforced by contract immutability — no removeLiquidity LaunchRegistry

5. Token Launch Flow

5.1 Classic Launch (Current — Mainnet)

The Programmable Classic model launches a token directly into a Uniswap v4 pool with the EthCreatorFeeHookV3 hook active from block one. There is no bonding curve phase.

flowchart TD
    A["Creator: Fill Launch Form\nname / symbol / image / fees / social links"] --> B["Frontend: Build LaunchParams\nclient-side via lib/contracts.ts"]
    B --> C{"API Online?"}
    C -->|Yes| D["API: Pre-flight validation\nmetadata upload → IPFS CID"]
    C -->|No - offline mode| E["Client-side only path\n(Rule R6 — API never required)"]
    D --> F["Frontend: Summary Review\nfee breakdown + risk disclosure"]
    E --> F
    F --> G["User Signs Single Transaction\nmsg.value = initialBuyEth"]
    G --> H["ClassicLauncher.launch(params)"]

    H --> I["Deploy HookToken\n1B fixed supply\nCREATE2 deterministic address"]
    H --> J["Initialize v4 Pool\nPoolManager.initialize(key, sqrtPriceX96)"]
    H --> K["FeeSplitVaultFactory.deploy()\nper-token vault → beneficiaries configured"]
    H --> L["Register pool with EthCreatorFeeHookV3\nhook registers poolId → vault mapping"]
    H --> M["Initial Buy Executed\nfirst swap in the pool"]

    I & J & K & L & M --> N["Launched Event Emitted\n(token, poolId, vault, creator, launchId)"]
    N --> O["Indexer picks up Launched event\ntoken row created in DB"]
    O --> P["Token appears in Explorer\nReal-time via SSE stream"]
Loading

5.2 Transaction Structure

Parameter Type Description
name string Token name (e.g. "Luna Q")
symbol string Ticker (e.g. "LNQ")
metadataUri string IPFS CID of metadata JSON (image, description, social)
initialBuyEth uint256 ETH to spend on the initial buy (msg.value)
maxWalletBps uint16 Max wallet size in basis points (e.g. 100 = 1%)
buyFeeBps uint16 Creator fee on buys (e.g. 300 = 3%)
sellFeeBps uint16 Creator fee on sells
rewardWallets address[] Fee beneficiaries (1–5 addresses)
rewardSharesBps uint16[] Share per beneficiary (must sum to 10000)

Single transaction: all of the above — token deploy, pool init, vault deploy, hook registration, and initial buy — execute atomically in one ClassicLauncher.launch() call.

5.3 Claim Revenue Flow

flowchart LR
    DASH["Creator Dashboard\nShows Available Revenue"] --> CHECK{"fee_balances\nempty?"}
    CHECK -->|"Yes (current state)"| FALLBACK["Fallback: use\nSUM(trades.fee_collected_wei)\nas claimable amount"]
    CHECK -->|"No"| DB_VAL["Use fee_balances.claimable_wei"]
    
    FALLBACK & DB_VAL --> DISPLAY["Display claimable ETH\nEnables Claim Button"]
    
    DISPLAY --> SIGN["User clicks 'Claim Revenue'\nWallet switches to mainnet"]
    SIGN --> TX1["TX 1: hook.claimCreatorFees(poolId)\nFlushes ETH from hook → vault\nGas: 200k"]
    TX1 --> WAIT["Wait for receipt\npublicClient.waitForTransactionReceipt"]
    WAIT --> TX2["TX 2: vault.claim()\nSends ETH to creator wallet\nGas: 200k"]
    TX2 --> DONE["ETH received\nDashboard refreshes"]
Loading

Note on current claim state: claimCreatorFees(bytes32) selector is 0xaf8d60b5. Investigation in progress on mainnet revert — ETH is held as ERC-6909 claims in PoolManager (V4 custom accounting), not as direct hook ETH balance. Hook balance reads as 0 but fees ARE accumulated.


6. Fee & Revenue Model

6.1 Fee Breakdown Per Swap

flowchart LR
    SWAP["Swap\n(ETH amount)"] --> TOTAL["Total Fee\ne.g. 3.15% on buy"]
    TOTAL --> CREATOR["Creator Fee\ne.g. 3.00%\n→ FeeSplitVaultV1"]
    TOTAL --> PROTOCOL["Protocol Fee\n0.15% fixed\n→ Protocol Treasury"]
    CREATOR --> BENEFICIARIES["Split by rewardWallets\ne.g. 100% to creator\nor 50/50 to team"]
Loading

6.2 Fee Configuration Options

Parameter Range Default Notes
Buy Fee 0 – 10% Creator choice Paid by buyer
Sell Fee 0 – 10% Creator choice Paid by seller
Protocol Fee 0.15% fixed 0.15% Immutable constant
Max Wallet 0.01% – 100% 1% (100 bps) Applied first N blocks

6.3 Revenue Dashboard Metrics

Metric Source Update Frequency
Lifetime Fees SUM(trades.fee_collected_wei) Per trade (indexer)
Available to Claim fee_balances.claimable_wei OR trades fallback Per trade
Volume SUM(trades.amount_eth) Per trade
Trades Count COUNT(trades) Per trade
Revenue Trend fee_collected_wei grouped by day Per trade

7. Frontend Architecture

7.1 Stack

Concern Choice Notes
Framework React 18 + Vite SPA, path-based routing
Language TypeScript (strict) No any without justification
Styling Tailwind CSS + CSS variables Dark-first (.dark on <html>)
Wallet Privy + wagmi v2 + viem Embedded wallet support
State / Cache TanStack Query v5 Auto-invalidation on mutations
Charts TradingView Lightweight Charts OHLCV candles
Codegen Orval → typed API client From openapi.yaml
Auth SIWE (EIP-4361) → JWT 15-min sliding access token

7.2 Page Architecture

flowchart TD
    subgraph APP["artifacts/app (Hookify DApp)"]
        EX["/explore\nToken feed, filters,\nlive price table"]
        TD["/token/:chain/:address\nChart + trades + swap panel\n+ creator info"]
        LA["/launch\nProtocol Initializer\nSection 00-05 + Summary"]
        DA["/dashboard\nCreator revenue + claim\nactive tokens"]
        PO["/portfolio\nHolder positions + PnL"]
        SE["/settings\nWallet prefs, RPC, theme"]
        DO["/docs\nDocumentation"]
    end

    subgraph MKT["artifacts/hookify-web (Marketing)"]
        HM["/ (Landing)\nHero + features + stats"]
    end

    NAV["Floating Nav Bar\n(pill-shaped, glass)"] --> EX & LA & DA & PO & SE & DO
Loading

7.3 Key Component Hierarchy

artifacts/app/src/
├── pages/
│   ├── explore.tsx          # Token table, trending, live SSE feed
│   ├── token-detail.tsx     # Chart, trades, swap panel, info panel
│   ├── launch.tsx           # 6-section launch form + summary sidebar
│   ├── dashboard.tsx        # Creator revenue, claim flow, token cards
│   ├── portfolio.tsx        # Holder positions
│   └── settings.tsx         # App preferences
├── hooks/
│   ├── use-token-swap.ts    # Buy/sell swap flow (PoolSwapTest)
│   ├── use-swap-quote.ts    # Price quotes from on-chain
│   └── use-onchain-price.ts # Live price reads
├── lib/
│   ├── contracts.ts         # ABIs + chain addresses
│   ├── api-client.ts        # Generated from openapi.yaml (Orval)
│   └── format.ts            # Number/price formatting
└── components/
    ├── ui/                  # shadcn/ui base components
    └── charts/              # TradingView wrappers

7.4 Swap Architecture (Classic Tokens)

sequenceDiagram
    participant U as User
    participant FE as Frontend
    participant PST as PoolSwapTest
    participant PM as PoolManager
    participant H as Hook

    U->>FE: Click Buy (amount ETH)
    FE->>FE: useSwapQuote() — estimate output
    FE->>FE: Show: amount out, fee, price impact
    U->>FE: Confirm
    FE->>FE: Privy: wallet.getEthereumProvider()
    FE->>PST: swap(poolKey, {zeroForOne, amtSpec, sqrtLimit}, {takeClaims, settleUsingBurn})
    Note over FE,PST: Classic uses PoolSwapTest 0xa8D342...<br/>selector: 0x2229d0b4
    PST->>PM: unlock + swap callback
    PM->>H: beforeSwap / afterSwap
    H->>H: NativeSwapFeesAccrued emitted
    PM-->>FE: BalanceDelta (tokens received)
    FE->>U: Toast: "Bought X LNQ"
Loading

7.5 Chain Configuration (Active)

Chain Status Chain ID RPC Env Var
Ethereum Mainnet Active 1 VITE_RPC_URL
Base 🔜 Coming Soon 8453
Robinhood Chain 🔜 Coming Soon 8453001

8. Backend & API Layer

8.1 Services

Service File Responsibility
HTTP Server app.ts / index.ts Fastify + CORS + auth middleware
Token Routes routes/tokens.ts Feed, detail, candles, trades, holders
Creator Routes routes/creators.ts Dashboard, fee timeseries
Auth Routes routes/auth.ts SIWE login/refresh
Launch Routes routes/launch.ts Pre-flight validation
Upload Routes routes/uploads.ts Image upload (serves static files)
Stream Routes routes/stream.ts SSE live token feed
Launched Indexer workers/launched-indexer.ts ClassicLauncher Launched events
Trade Indexer workers/trade-indexer.ts PoolManager Swap events
Fee Indexer workers/fee-indexer.ts Hook NativeSwapFeesAccrued events

8.2 API Endpoints

Method Path Auth Description
GET /api/v1/health No Health check
GET /api/v1/chains No Active chains
GET /api/v1/tokens No Token feed (paginated, sorted)
GET /api/v1/tokens/preview No SSE preview (landing hero)
GET /api/v1/tokens/:chain/:addr No Token detail
GET /api/v1/tokens/:chain/:addr/trades No Trade history
GET /api/v1/tokens/:chain/:addr/candles No OHLCV candles
GET /api/v1/tokens/:chain/:addr/holders No Holder list
POST /api/v1/auth/login No SIWE login → JWT
POST /api/v1/auth/refresh JWT Refresh access token
GET /api/v1/creators/:addr/dashboard JWT Creator revenue dashboard
GET /api/v1/creators/:addr/fee-timeseries JWT Daily fee history
GET /api/v1/portfolio/:addr No Holder positions
POST /api/v1/uploads/image JWT Token image upload
GET /api/v1/stream No SSE live launch + trade feed

8.3 Auth Flow (SIWE — Sign-In With Ethereum)

sequenceDiagram
    participant FE as Frontend
    participant API as API Server
    participant DB as PostgreSQL

    FE->>API: POST /auth/login { message, signature }
    API->>API: verifyMessage(message, signature) → wallet address
    API->>DB: upsert siwe_sessions(address, nonce, expires_at)
    API-->>FE: { accessToken (15m), refreshToken (24h, single-use) }

    Note over FE: Stored in memory (access) + localStorage (refresh)

    FE->>API: GET /creators/:addr/dashboard\nAuthorization: Bearer <accessToken>
    API->>API: verifyJWT → confirm address matches param
    API-->>FE: Dashboard data

    Note over FE: < 3 min remaining on access token
    FE->>API: POST /auth/refresh { refreshToken }
    API->>DB: rotate refresh token (invalidate old)
    API-->>FE: { new accessToken, new refreshToken }
Loading

8.4 Indexer Architecture

flowchart TD
    subgraph WORKERS["Worker Processes (always-on)"]
        LI["launched-indexer.ts\nPolls ClassicLauncher\nfor Launched events\nevery ~12s"]
        TI["trade-indexer.ts\nPolls PoolManager\nfor Swap events\nevery ~12s"]
        FI["fee-indexer.ts\nPolls EthCreatorFeeHookV3\nfor NativeSwapFeesAccrued\nevery ~12s"]
    end

    subgraph CHECKPOINTS["Checkpoint System"]
        CP[("indexer_checkpoints\ntable\n{chain, event_type,\nlast_block}")]
    end

    subgraph PROCESSING["Processing"]
        DECODE["decodeEventLog(ABI, log)"]
        UPSERT["Idempotent upsert\nUNIQUE(chain, tx_hash, log_index)"]
    end

    ETH["Ethereum Mainnet\n(Alchemy RPC)"] -->|eth_getLogs| WORKERS
    WORKERS --> CHECKPOINTS
    WORKERS --> DECODE
    DECODE --> UPSERT
    UPSERT --> DB[("PostgreSQL")]

    DB -->|"cache for API reads"| API["Fastify API"]
    API -->|"SSE push"| FE["Frontend"]
Loading

Checkpoint reset procedure (used when DEPLOY_BLOCK is wrong):

UPDATE indexer_checkpoints 
SET last_block = 25687631 
WHERE chain = 'ethereum';

Critical: Always set DEPLOY_BLOCK env var to the actual hook deployment block before starting indexers. If set too low, indexers start millions of blocks behind mainnet.


9. Indexing & Data Pipeline

9.1 Event Types Indexed

Event Contract Worker Table Written
Launched ClassicLauncher launched-indexer.ts tokens
Swap PoolManager trade-indexer.ts trades, ohlcv_candles
NativeSwapFeesAccrued EthCreatorFeeHookV3 fee-indexer.ts trades.fee_collected_wei

9.2 Amount Extraction (V4 Custom Accounting)

Important: In V4 with Custom Accounting, the Swap event always has amount0 = amount1 = 0. Actual amounts must be extracted differently:

Amount Source
ETH in (buy) NativeSwapFeesAccrued.grossNativeAmount
ETH out (sell) NativeSwapFeesAccrued.grossNativeAmount
Creator fee NativeSwapFeesAccrued.creatorFee
Protocol fee NativeSwapFeesAccrued.launcherFee
Trader address tx.from (not in event logs)

Correct Swap topic hash: 0x40e9cecb... (NOT the standard 0xc42... topic)

9.3 Candle Aggregation

OHLCV candles are computed from raw trades by the trade indexer on write:

Interval Use Case
1m Live chart
5m Short-term chart
15m Medium chart
1h Standard chart
4h Aggregated (requires post-processing)
1d Daily chart
1w Weekly chart

10. Database Schema

10.1 Core Tables

erDiagram
    chains ||--o{ tokens : "chain"
    tokens ||--o{ trades : "token_id"
    tokens ||--o{ ohlcv_candles : "token_id"
    tokens ||--o{ fee_accruals : "token_id"
    tokens ||--o{ fee_balances : "token_id"
    tokens ||--o{ fee_claims : "token_id"
    tokens ||--o{ token_holders : "token_id"
    tokens ||--o{ trending_scores : "token_id"

    chains {
        varchar chain PK
        integer chain_id
        varchar display_name
        varchar native_currency
        varchar pool_manager_addr
        varchar hook_factory_addr
        bigint deploy_block
        boolean is_active
    }

    tokens {
        uuid id PK
        varchar chain FK
        varchar address
        varchar pool_id
        varchar launch_id
        varchar creator_address
        varchar name
        varchar symbol
        varchar phase
        varchar model_type
        smallint buy_fee_bps
        smallint sell_fee_bps
        varchar vault_address
        numeric volume_total_eth_wei
        numeric price_eth_wei
        integer holder_count
        timestamptz launched_at
    }

    trades {
        uuid id PK
        varchar chain FK
        uuid token_id FK
        varchar tx_hash
        varchar trader_address
        varchar side
        numeric amount_in_wei
        numeric amount_out_wei
        numeric fee_collected_wei
        numeric price_eth_wei
        timestamptz block_timestamp
    }

    fee_balances {
        serial id PK
        uuid token_id FK
        varchar vault_address
        varchar beneficiary_address
        numeric accrued_wei
        numeric claimed_wei
        numeric claimable_wei
    }
Loading

10.2 Table Details

chains — Chain Configuration

Populated at deploy time. One row per supported chain.

Column Type Notes
chain VARCHAR(16) PK 'ethereum', 'base', 'robinhood'
chain_id INTEGER EVM chainId (1, 8453, 8453001)
pool_manager_addr VARCHAR(66) Uniswap v4 PoolManager
hook_factory_addr VARCHAR(66) Hookify hook factory
deploy_block BIGINT First Hookify deployment block
is_active BOOLEAN Whether chain is live

tokens — Token Registry

One row per Launched event. Central table.

Column Type Notes
id UUID PK Internal identifier
chain VARCHAR(16) Chain key
address VARCHAR(66) ERC-20 address (lowercase)
pool_id VARCHAR(66) Uniswap v4 PoolId (bytes32)
vault_address VARCHAR(66) FeeSplitVaultV1 address
phase ENUM 'curve' | 'graduated' | 'classic'
buy_fee_bps SMALLINT Creator buy fee (e.g. 300 = 3%)
volume_total_eth_wei NUMERIC(38,0) Cumulative trade volume in wei
price_eth_wei NUMERIC(38,18) Latest price per token in wei
holder_count INTEGER Approximate holder count

trades — Swap History

One row per Swap event. Idempotent upsert on (chain, tx_hash, log_index).

Column Type Notes
fee_collected_wei NUMERIC(38,0) Creator fee in wei (from NativeSwapFeesAccrued.creatorFee)
amount_eth NUMERIC ETH gross amount (computed)
side ENUM 'buy' | 'sell'
phase ENUM 'curve' | 'dex'

fee_balances — Claimable Balance Per Beneficiary

Materialized from fee accruals. claimable_wei is a generated column (accrued_wei - claimed_wei).

Current state: fee_accruals events are not yet being indexed → fee_balances is always empty. The API falls back to SUM(trades.fee_collected_wei) as the claimable amount (see creator-repository.ts).

indexer_checkpoints — Indexer State

Tracks last processed block per event type per chain.

Column Notes
chain Chain key
event_type 'Launched', 'Swap', 'NativeSwapFeesAccrued'
last_block Last processed block number

11. Security Architecture

11.1 Golden Rules (Non-Negotiable)

# Rule
R1 No mainnet contract deploys without a published audit from a reputable firm
R2 No upgradeable contracts. No proxies, no admin pause, no hidden setters
R3 Bots and backend services never hold private keys, never sign user transactions
R4 On-chain is the source of truth. DB is a rebuildable cache. Chain wins on conflict
R5 Protocol fee hard-capped at 0.15% in immutable constant
R6 Launches must work with API offline — frontend builds all calldata client-side
R7 No payment flows, no custody, no fiat rails
R8 Every deployment is reproducible from a tagged commit
R9 User-facing numbers come from chain or indexed chain data — never hardcoded
R10 When in doubt, choose the option that protects user funds

11.2 Contract Security Properties

flowchart LR
    subgraph ATTACK_SURFACE["Eliminated Attack Surfaces"]
        A["❌ Admin key exploit\n(no admin keys)"]
        B["❌ Upgrade exploit\n(no proxies)"]
        C["❌ Graduation MEV\n(atomic, no graduate() tx)"]
        D["❌ LP drain\n(no removeLiquidity)"]
        E["❌ Fee rug\n(immutable fee constants)"]
    end

    subgraph PROTECTIONS["Active Protections"]
        P1["✅ 1-block sell delay at graduation\n(MEV guard)"]
        P2["✅ 1% max wallet first 5 blocks\n(anti-sniper)"]
        P3["✅ Time-decay sell tax\n(fully on-chain)"]
        P4["✅ Reentrancy guards\non all vault claim paths"]
        P5["✅ Checks-effects-interactions\non all external calls"]
    end
Loading

11.3 Frontend Security

Concern Implementation
Wallet signing Privy embedded wallet + wagmi (never backend-signed)
Address normalization All on-chain addresses .toLowerCase() before DB storage / navigation
Chain switch wallet.switchChain(mainnet.id) before any claim tx
RPC Alchemy mainnet via VITE_RPC_URL env var
Secrets Replit Secrets manager — never in code or .env files
SIWE auth EIP-4361 message signing — wallet proves ownership

11.4 Incident Response

Severity Definition Response Time Example
SEV-1 User funds at risk 15-min ack, war room Vault exploit, fee miscalc
SEV-2 Core flow broken, no funds at risk 4h fix/rollback Launch down, trade broken
SEV-3 Degraded experience Next business day Indexer lag, slow UI

Because contracts are immutable (R2), incident response is about transparency and frontend mitigation, not admin intervention.


12. Mainnet Deployment

12.1 Active Deployment (August 5, 2026)

Chain: Ethereum Mainnet (chainId 1)
Deployer: 0xA48416A848ce870bC158A9c9aEEA8902B353ddc9
Deploy TX gas: ~19.9M gas @ 0.251 gwei ≈ 0.005 ETH
Hook salt: 0x00000000000000000000000000000000000000000000000000000000000005e7
Salt iterations: 1,511

12.2 First Live Token — LNQ (Luna Q)

Property Value
Token Address 0x783a17b3dac99c9f2e3325dc163724cbcaaacdf0
Pool ID 0xa67cf14c0823e8486cf6e5379008c52d554cedd1973321cd3c557d8f76d9b73e
Vault Address 0x9b67627365f6b63c87725be01d27c86cdd034ae9
Launch Block 25,690,436
Indexer Start Block 25,687,631

12.3 Environment Variables

Variable Scope Purpose
RPC_URL Backend Alchemy mainnet RPC
DATABASE_URL Backend PostgreSQL connection
SESSION_SECRET Backend SIWE JWT signing
CHAIN_KEY Backend Active chain ('ethereum')
HOOK_ADDRESS Backend 0xC5C72B770396050C088BBc111B1A448Ec3Dce0cc
LAUNCHER_ADDRESS Backend 0xE3faC65ce48695988584a46feAc505fa6B53a854
POOL_MANAGER_ADDRESS Backend 0x000000000004444c5dc75cB358380D2e3dE08A90
DEPLOY_BLOCK Backend 25687632 (indexer start)
VITE_RPC_URL Frontend Alchemy mainnet RPC
VITE_CHAIN_ID Frontend 1
VITE_CONTRACT_FEE_HOOK Frontend Hook address
VITE_CONTRACT_LAUNCHER Frontend Launcher address
VITE_CONTRACT_POOL_SWAP_TEST Frontend PoolSwapTest address
ETHERSCAN_API_KEY Backend Contract verification

13. Engineering Standards

13.1 Code Conventions

Rule Standard
Language TypeScript strict mode throughout
any type Requires eslint-disable comment with justification
Contract writes All go through lib/contracts.ts — no scattered encodeFunctionData
Address format Always lowercase in DB/state; checksummed only for on-chain calls
Number formatting lib/format.ts only — consistent K/M/B, subscript zeros
Error handling Structured JSON { code, message, details } — never leak stack traces
API versioning All endpoints at /api/v1 — breaking changes go to /v2

13.2 Wallet Interaction Pattern

// Correct pattern (Privy + wagmi)
const provider = await wallet.getEthereumProvider();  // Privy primary
const walletClient = createWalletClient({ chain: mainnet, transport: custom(provider) });
const publicClient = createPublicClient({ chain: mainnet, transport: http(MAINNET_RPC) });
const [account] = await walletClient.getAddresses();

// Chain switch before any mainnet tx
if (chainId !== mainnet.id) {
  await wallet.switchChain(mainnet.id);
}

13.3 Idempotent Indexing Pattern

All indexed events use UNIQUE(chain, tx_hash, log_index) and are written with ON CONFLICT DO NOTHING:

await db.insert(trades).values(row)
  .onConflictDoNothing({ target: [trades.chain, trades.txHash, trades.logIndex] });

This ensures full reindexing from genesis is always safe.

13.4 Common Pitfalls (Documented)

Pitfall Correct Approach
V4 Swap event has amount0=amount1=0 Read amounts from NativeSwapFeesAccrued event
Hook ETH balance always 0 ETH tracked as ERC-6909 in PoolManager — V4 Custom Accounting
claimCreatorFees needs PoolManager unlock Requires EOA tx to hook directly (not a view)
decodeEventLog returns checksummed addresses Always .toLowerCase() before DB/state
Drizzle .default(0n) breaks migrations Use .default(sql\0`)` instead
Dark mode not applied Add class="dark" to <html> in index.html
Orval codegen duplicates index.ts exports Clean before each codegen run
DEPLOY_BLOCK wrong → indexer 14M blocks behind Set env var to actual hook deploy block

14. Implementation Status

System Status Notes
ClassicLauncher (mainnet) Live Token LNQ successfully launched
EthCreatorFeeHookV3 Live Emitting NativeSwapFeesAccrued events
FeeSplitVaultV1 Deployed Per-token vault exists
Trade Indexer Running Indexing all PoolManager Swaps
Launched Indexer Running Indexing ClassicLauncher events
Fee Indexer 🟡 Partial Emitting correct events; fee_accruals table not yet populated
Creator Dashboard Working Revenue via trades fallback
Claim Revenue flow 🟡 Debugging 2-TX flow (flush + claim) implemented; investigating claimCreatorFees revert
Explore page Working Live trades, correct chain, timeAgo display
Token Detail page Working Chart, trades, swap panel
Swap (buy/sell) Working Via PoolSwapTest on mainnet
Launch form Working Network selector, Ethereum Mainnet only
OHLCV Charts Working 1m/5m/15m/1h/1d/1w (4h needs aggregation)
Holder tracking 🟡 Partial Percentages show 0 (no Transfer indexer)
Audit tracking Planned Show "audited / not audited" labels
Social links on Explore Planned Twitter/Telegram links from token metadata
Bonding Curve mode Planned Architecture designed; Classic V3 is current model
Base chain Planned Phase 2
Robinhood Chain Planned Phase 2

15. Roadmap

Phase 1 — Mainnet MVP (Complete)

  • Deploy Classic V3 contracts to Ethereum mainnet
  • Token launch flow (single transaction)
  • Trade indexing + OHLCV charts
  • Creator dashboard with fee visibility
  • Explore page with live token feed

Phase 2 — Revenue & Data Completeness

Priority Task
🔴 Critical Fix claimCreatorFees revert — understand V4 ERC-6909 claim pattern
🔴 Critical Implement fee_accruals indexing from NativeSwapFeesAccrued events
🔴 Critical Fix holder percentages (index Transfer events)
🟠 High Keep price charts live as new trades arrive (real-time candle updates)
🟠 High 4H chart timeframe with aggregated candle data
🟠 High Show beneficiary fee breakdown on dashboard
🟠 High Social links on Explore cards
🟡 Medium Audit label on token detail (verified / not audited)
🟡 Medium Revenue trend accuracy across all timeframes
🟡 Medium Claimable ETH per-token view on dashboard

Phase 3 — Multi-Chain

  • Base chain deployment + indexer
  • Robinhood Chain deployment + indexer
  • Cross-chain token feed

Phase 4 — Bonding Curve + Social

  • UnifiedLaunchHook (Native BC→AMM) model
  • Auto-graduation on-chain (atomic, no keeper)
  • Social bot layer (Twitter/X, Farcaster, Telegram) — deferred from MVP
  • $HOOK governance token (TBD, pending legal review)

Phase 5 — Ecosystem

  • Immunefi bug bounty program (target: $100K critical)
  • Third-party smart contract audit
  • Solana adapter (Meteora DBC)
  • Builder Program (third-party hook developers)
  • AI Copilot (launch name/description generation)
  • Dividend Vault for top token holders

Appendix A — Uniswap V4 Key Concepts

Concept Description
PoolManager Singleton contract holding all V4 pool state and liquidity
Hook Contract called before/after swaps — enables custom logic
Custom Accounting Hook uses BeforeSwapDelta to override pricing; amounts are 0 in Swap event
ERC-6909 Claims V4 tracks balances as fungible claims in PoolManager (not direct ETH transfers)
take() Hook pulls ETH from PoolManager to itself during afterSwap
settle() Hook sends ETH to PoolManager
DYNAMIC_FEE_FLAG Pool flag required for hook to override swap fees
PoolId keccak256(token0, token1, fee, tickSpacing, hooks) — unique pool identifier
PoolKey Struct {currency0, currency1, fee, tickSpacing, hooks} — used for all operations

Appendix B — Fee Math

totalFeeWei = swapAmountWei × (buyFeeBps + protocolFeeBps) / 10000

creatorFeeWei = totalFeeWei × buyFeeBps / (buyFeeBps + protocolFeeBps)
protocolFeeWei = totalFeeWei × protocolFeeBps / (buyFeeBps + protocolFeeBps)

// Example: 1 ETH swap, 3% buy fee, 0.15% protocol fee
totalFee = 1e18 × 315 / 10000 = 0.0315 ETH
creatorFee = 0.0315 × 300/315 = 0.030 ETH  (3%)
protocolFee = 0.0315 × 15/315 = 0.0015 ETH (0.15%)

Appendix C — Glossary

Term Definition
Hookify Token launch infrastructure built on Uniswap v4 hooks
Classic Launch model: direct v4 pool with fee hook, no bonding curve phase
Native BC→AMM Launch model: bonding curve inside hook, auto-graduates to AMM (planned)
PoolSwapTest Uniswap v4 test contract used as swap router on mainnet (no Universal Router on mainnet)
FeeVault / FeeSplitVaultV1 Per-token contract holding creator fees pending claim
claimCreatorFees Hook function that flushes accumulated ETH from hook → vault
Custom Accounting V4 hook feature: hook intercepts price/amounts via BeforeSwapDelta
NativeSwapFeesAccrued Hook event emitted on every swap — contains fee amounts and trader info
SIWE Sign-In With Ethereum (EIP-4361) — wallet-based auth
Phase Token lifecycle stage: curve (bonding curve) | graduated | classic
PoolId 32-byte hash identifying a Uniswap v4 pool
Pool Key Struct identifying a pool: {token0, token1, fee, tickSpacing, hookAddress}

This document is the single source of truth for Hookify engineering. When source code differs from this document, update this document first, then implement.

Maintained by: Hookify Engineering
Last updated: August 5, 2026

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors