V2 integration
V2 uses separate deployment addresses and TokenCreatedV2. Use the generated factoryV2Abi, lockerV2Abi and launchTokenV2Abi from @lucrari/shared. Record creatorFeeBps, poolFee, feeMode, totalSupply, devBuyTokens, curve bounds and the emitting factory's locker for each launch. Discover V2 pools/tokens from this event and index Swap/Transfer separately from V1. A burn Transfer to the zero address decreases V2 supply and FDV.
claimFees(positionId) returns (tokenBurned, quoteCollected) in V2; these are not token0/token1 payouts. Holder claims use the token's claimRewards(recipient, unwrap). Buyback queues use the V2 locker's pendingBuyback(positionId) / processBuyback(positionId). No V1 position ID should be submitted to the V2 locker or vice versa.
See V2 rollout in the repository for deployment and collector configuration. V2 addresses are assigned only after deployment.
The reference below describes existing V1 deployments.
Integration
A minimal, verifiable integration surface. Everything reads directly off the contracts: index the factory's TokenCreated event, register each emitted pool, and index its Swap events. Onchain events are the authoritative source of truth — the lucrari interface and its indexer do exactly this and nothing more.
Network
| Network | GIWA Sepolia (testnet) |
| Chain ID | 91342 |
| Native asset | ETH |
| Public RPC | https://sepolia-rpc.giwa.io |
| Explorer | https://sepolia-explorer.giwa.io |
| Pool fee | 10000 (1%) |
| Launch fee | 0.001 ETH |
| Supply | 1,000,000,000 (1e9) |
GIWA mainnet (chain ID 9134) is registered but not live. The public RPC rate-limits wide eth_getLogs ranges — backfill in bounded block chunks from each contract's start block.
Contracts
Deployed on GIWA Sepolia. The active factory and locker serve current launches; superseded addresses remain for tokens deployed before the current version and are listed in the repo for reference only — do not wire them back in.
| Contract | Address | Start block |
|---|---|---|
| LaunchpadFactory | 0x215E46690169764b89fa090C32a1Df333B2278C8 | 33812568 |
| LpLocker | 0x01ABf2BC3FAA9AF0d9A96964EE99C943d7EE0c27 | 33811334 |
| V3 factory | 0xe6B2FCB942567016f5F6021c1aEF91A98C38C5bb | — |
| Position manager | 0xb0329f7097c464DF1E71248d1ecdB460c6eDAf82 | — |
| SwapRouter02 | 0xEA87adf193565eeef214b5Ab209F60506826a37C | — |
| QuoterV2 | 0x1495bF5deaeb178Afc9a876A41160a5F6c640093 | — |
| WETH (quote) | 0x4200000000000000000000000000000000000006 | — |
| Treasury | 0x1648240579D1410dcDCD548809429B3c0898fff4 | — |
Onchain events
TokenCreated is emitted once per launch by the factory. Each pool it emits is a Uniswap V3 pool whose Swap events drive all price, volume and trade history.
| Event | topic0 |
|---|---|
TokenCreated(address,address,address,address,uint256,bool,uint64,uint64,uint256,string,string,string,uint256) | 0x634412d53e8566833b6ee5677e3240da031370ef2a5812d91f4c6fb090c352a7 |
Swap(address,address,int256,int256,uint160,uint128,int24) | 0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67 |
import { createPublicClient, http, parseAbiItem } from "viem";
const client = createPublicClient({
chain: {
id: 91342,
name: "GIWA Sepolia",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: { default: { http: ["https://sepolia-rpc.giwa.io"] } },
},
transport: http(),
});
const launches = await client.getLogs({
address: "0x215E46690169764b89fa090C32a1Df333B2278C8",
event: parseAbiItem(
"event TokenCreated(address indexed token, address indexed creator, address feeRecipient, address pool, uint256 positionId, bool tokenIsToken0, uint64 launchTime, uint64 antiSnipeWindow, uint256 devBuyUsdc, string name, string symbol, string metadataURI, uint256 buyTaxBps)",
),
fromBlock: 33812568n,
toBlock: "latest",
});Derive trade direction from the swap amounts and the token ordering, which the launch event already tells you:
// amount0/amount1 are signed: positive = the pool received that token.
const pairSigned = tokenIsToken0 ? amount1 : amount0; // the WETH leg
const side = pairSigned > 0 ? "buy" : "sell"; // WETH in = someone bought the launch tokenSign convention
A positive WETH leg means WETH flowed into the pool, which is a buy of the launch token; a negative WETH leg is a sell. Orient with tokenIsToken0 from the launch event rather than comparing addresses yourself.
Reading launch state
The factory records every launch in tokenInfo, including the pool, the locked position id, and the curve orientation needed for pricing:
import { parseAbi } from "viem";
const factoryAbi = parseAbi([
"function tokenInfo(address token) view returns (address token, address pool, uint256 positionId, address creator, address feeRecipient, bool tokenIsToken0, uint64 launchTime, uint64 antiSnipeWindow, uint256 buyTaxBps)",
"function curveTickLower() view returns (int24)",
"function curveTickUpper() view returns (int24)",
"function creationFeeUsdc() view returns (uint256)",
"function minBuyTaxBps() view returns (uint256)",
"function maxBuyTaxBps() view returns (uint256)",
]);
const info = await client.readContract({
address: factory,
abi: factoryAbi,
functionName: "tokenInfo",
args: [token],
});
// info.tokenIsToken0 pool orientation (required for pricing and trade direction)
// info.buyTaxBps creator-chosen tax, 100–300 (1–3%), immutable
// info.feeRecipient where the creator's fee streams pay outToken metadata (image, description, socials) is passed at creation as a JSON document in the metadataURI argument of TokenCreated; the name and symbol are also on the token contract itself.
Pricing and graduation
Price comes from the pool's slot0. Token and WETH are both 18 decimals, so no scaling is needed:
import { parseAbiItem } from "viem";
const [sqrtPriceX96, tick] = await client.readContract({
address: pool,
abi: [
parseAbiItem(
"function slot0() view returns (uint160 sqrtPriceX96, int24 tick, uint16, uint16, uint16, uint8, bool)",
),
],
functionName: "slot0",
});
const ratio = Number(sqrtPriceX96) / 2 ** 96;
const token1PerToken0 = ratio * ratio;
// WETH per token, whichever side the launch token sits on:
const priceEth = info.tokenIsToken0 ? token1PerToken0 : 1 / token1PerToken0;
const priceUsd = priceEth * ethUsd; // any ETH oracle
const fdvUsd = priceUsd * 1e9; // supply is fixed: FDV and market cap share the price
const marketCapUsd = priceUsd * circulatingSupply;Graduation progress is how far the pool tick has travelled through the launch curve, oriented by tokenIsToken0:
const [lower, upper] = [-203800, -177400]; // curveTickLower/Upper on the factory
// Buys push the tick toward the upper end of the range when the token is token1,
// toward the lower end when it is token0 — orientation matters.
const progress = info.tokenIsToken0
? (upper - tick) / (upper - lower)
: (tick - lower) / (upper - lower);
const graduated = progress >= 1;There is no graduation transaction and no migration event. Graduation only means the range order has been fully bought through.
Fee claims
Creator fee claims settle through the locker, permissionlessly:
const lockerAbi = parseAbi([
"function creatorOf(uint256 positionId) view returns (address)",
"function creatorFeeBps() view returns (uint256)",
"function claimFees(uint256 positionId) returns (uint256 amount0, uint256 amount1)",
"function claimFeesBatch(uint256[] positionIds)",
]);
// Simulate first: the returned amounts are the exact token/WETH legs this claim will pay,
// already net of any fee-on-transfer effects, split creatorFeeBps/10000 to the creator.
const { result } = await client.simulateContract({
address: locker,
abi: lockerAbi,
functionName: "claimFees",
args: [info.positionId],
});claimFees pays the creator's fee wallet and the treasury directly — the caller only provides the gas, so keepers can service claims without ever being able to redirect funds.
Indexer API
The reference indexer (Ponder, source in the repo) exposes the REST surface the interface consumes. Self-host it, or mirror its queries against your own event pipeline:
| Endpoint | Returns |
|---|---|
GET /trending?sort=volume|new|marketcap|txcount&graduated=&limit= | token list with price/volume stats |
GET /token/:address | one token: pool, creator, fee recipient, tax, price, graduation |
GET /token/:address/trades | recent swaps |
GET /token/:address/candles?interval=300 | OHLC candles |
GET /token/:address/holders | top holders, pool balance, supply shares |
GET /token/:address/volume24h | 24h volume and trade count |
GET /user/:address | tokens deployed and held by a wallet |