Building a v4 hook? Pick what it does, set the numbers, and deploy it as your own hook or stacked on v4 Marketplace. Open the builder ↗
v4 Builder docs
Build a v4 hook from modules

Recipes

A recipe is everything a hook is written from: a name, a target, a base fee and the modules with their numbers. The same recipe always gives the same source, which is why the factory records it with every hook.

The shape

Recipe
type Recipe = {
  v: 1;
  name: string;             // up to 40 characters, also the contract's name
  target: "hook" | "stack"; // own v4 hook, or stacked on v4 Marketplace
  baseFee: number;          // pips, 0 to 100,000: the floor of every swap's fee
  modules: { id: ModuleId; p: Record<string, number | string> }[];
};

type ModuleId =
  | "launchFee" | "volatilityFee" | "offHoursFee" | "cooldown" | "priceGuard"
  | "launchAllowlist" | "holderRebate" | "agentRebate" | "swapTax" | "lpLock";

Each module's parameters are listed with their units and bounds on the modules page, and served as JSON by GET /api/modules.

How a recipe is checked

Before a line is written, the recipe is normalized. Anything out of bounds is refused with the name of the module and the parameter:

  • v must be 1. A target other than stack is read as hook. An empty name becomes My Hook.
  • Each module at most once, at least one module, and every number a whole number inside its bounds.
  • Addresses are checksummed and cannot be zero. An allowlist holds up to 50 wallets, duplicates removed. A token amount is a whole number of the token's own units.
  • The volatility fee's cap must be at least its calm fee.
  • A stacked recipe cannot take swap tax or liquidity lock: a stacked hook never moves funds.
  • Modules are sorted into the builder's fixed order, so the same choice always gives the same source and the same address.

The string on chain

The factory records the normalized recipe as compact JSON, with g naming the generator that wrote the source, today v4builder/2. This is the string the Fair Launch example records:

recipeString(Fair Launch)
{"v":1,"g":"v4builder/2","name":"Fair Launch","target":"hook","baseFee":3000,"modules":[{"id":"launchFee","p":{"startFee":250000,"duration":600}},{"id":"cooldown","p":{"seconds":30}},{"id":"lpLock","p":{"days":30}}]}

Rebuilding a hook

Every hook built here can be checked from the chain alone. The recipe is in the factory's Built event; the generator turns it into the same Solidity; the compiler settings are fixed. The verify endpoint runs these steps and publishes the result on Sourcify.

From the event to the bytecode
// 1. Read the Built event for the hook from the factory.
event Built(address indexed hook, address indexed builder, uint8 indexed kind,
            uint16 flags, bytes32 recipeHash, string recipe, uint256 paid);

// 2. keccak256(bytes(recipe)) == recipeHash, and the same hash is in built(hook).
// 3. JSON.parse(recipe), then build it with the generator named in "g".
// 4. Compile with solc 0.8.26, optimizer 200 runs, evmVersion cancun
//    (the IR pipeline only if the legacy one runs out of stack).
// 5. Match the result against the hook's creation transaction, as Sourcify does.

The three examples

The builder starts from any of these, or from a single module.

Fair launch: Anti-snipe fee, cooldown and a 30-day liquidity lock for a new coin.
{
  "v": 1,
  "name": "Fair Launch",
  "target": "hook",
  "baseFee": 3000,
  "modules": [
    {
      "id": "launchFee",
      "p": {
        "startFee": 250000,
        "duration": 600
      }
    },
    {
      "id": "cooldown",
      "p": {
        "seconds": 30
      }
    },
    {
      "id": "lpLock",
      "p": {
        "days": 30
      }
    }
  ]
}
Stock pool: Volatility fee, off-hours fee and a price guard for a tokenized stock against USDG.
{
  "v": 1,
  "name": "Stock Hours",
  "target": "stack",
  "baseFee": 500,
  "modules": [
    {
      "id": "volatilityFee",
      "p": {
        "calmFee": 500,
        "maxFee": 20000,
        "sensitivity": 2000
      }
    },
    {
      "id": "offHoursFee",
      "p": {
        "closedFee": 10000
      }
    },
    {
      "id": "priceGuard",
      "p": {
        "maxMove": 500,
        "window": 900
      }
    }
  ]
}
Agent pool: A lower fee for AI agents and holders, and a cut of every swap to the treasury.
{
  "v": 1,
  "name": "Agent Lane",
  "target": "hook",
  "baseFee": 3000,
  "modules": [
    {
      "id": "agentRebate",
      "p": {
        "rebate": 50
      }
    },
    {
      "id": "swapTax",
      "p": {
        "bps": 20,
        "to": "0x000000000000000000000000000000000000dEaD"
      }
    }
  ]
}