Skip to content

Events and indexing

CAST is fully on-chain, so events are the source of truth. These examples build on the client and CAST addresses from Reading token state.

Events

  • LaunchedLauncher

    A new token: token, creator, referrer, paired asset, name, symbol, grade, score, rulesHash, creator buy

    0xc78f22cb147553b8af20fd7edf94f032b9abd8574c1016d477e44562d113cb74
  • CurveOpenedFee Hook

    A new token's bonding curve opened: token, virtualPair, virtualToken, target, curveTokens, poolTokens and the pool's starting sqrtPriceX96

    0xb44c9ef34cc51a413b583ef056aaa6a960ddd522a886394258358c15b2864046
  • CurveTradeFee Hook

    A trade on the curve: token, trader, isBuy, pairAmount, tokenAmount, pairFee, burnTokens, and the curve's raised and sold totals after the trade

    0xff97a934874a0b13dbb8d5d75e670ef1518b2691d415a892491d156802c93059
  • GraduatedFee Hook

    A token moved from its curve into its pool: token, pairToPool, tokensToPool, the position's liquidity and its sqrtPriceX96

    0x6f64721a1c428a5bb355f1b372ed989c0cf8ed0280caaa7ae11f977b6471aaa1
  • FeeChargedFee Hook

    A fee on one side of a trade, on the curve or in the pool: token, trader, isBuy, pairFee (in the paired asset), burnTokens and the split

    0xe6c8d0336431f8c44d1cff80dedf371862f349f2d7f84d14ea251320f5ac9a07
  • HolderRewardsDistributedFee Hook

    Paired asset sent into a token's holder reward stream, including any amount deferred earlier

    0x7f5e6c1dacd282ea410f0c65d163a83507c237de4fdee05e44c070856d1b89de
  • HolderRewardsDeferredFee Hook

    The reward payout failed (for example the paired asset froze the token or the hook). The amount stays pending for a later distribute

    0x6adcd2aecab79e77d14b69afac7aef6e5c720dc99f6dd06311c88fd01e9b5117
  • TokensBurnedFee Hook

    Burn share destroyed. Can appear without HolderRewardsDistributed when the reward payout was deferred

    0xfd38818f5291bf0bb3a2a48aadc06ba8757865d1dabd804585338aab3009dcb6
  • CreatorFeesClaimedFee Hook

    A creator collected their share, in the token's paired asset

    0xaa4ecfd5324d73e3b54d038b3ae8ac8f88866d49dc334dc1f02fe36e0f935748
  • CastFeesWithdrawnFee Hook

    CAST's share in one paired asset paid out: asset, recipient, amount

    0x3f43a78a033c53d15589ad18fe20150b6506fb8b951d8a87385c0bd8f9c1f94b
  • RewardsDepositedToken

    Rewards added to the stream: amount, rate and periodFinish. The whole stream is released by periodFinish, at most 24 hours after the deposit and sometimes sooner. A deposit never lowers the rate of a stream already running

    0xc47834cdb0191cfa2b90f6ae336198ff404f06226170200c57ab15ade0510ae6
  • RewardsClaimedToken

    A holder collected rewards

    0xfc30cddea38e2bf4d6ea7d3f9ed3b6ad7f176419f4963bd81318067a4aee73fe
  • HoldStreakResetToken

    A wallet sold and its streak restarted

    0x32e363d1c0a568554de355b2d7f950c56dcf0cb5504d6d18983e9fd1d3576f32
  • CreatorTokensReleasedToken

    The creator released locked tokens

    0xa3addcdc3e9b85cd85d4d81ed66bb164226c7eb417e983ae652fe45cccc4c911
  • PairListedPairs

    An asset listed, or reopened, for new launches, with its virtualReserve and graduationTarget

    0x00baad906d1967e4f08022092d6ab74cf5595a95be1d53a3a6af103bbf4599a3
  • PairClosedPairs

    An asset closed to new launches. Tokens already paired with it keep trading

    0x725ce242eab8e0af78f8f088256eeb9ab04885c03aec453f8e353acd857341ff

Tokens also emit the standard ERC-20 Transfer and Approval events. Trades in a graduated token's pool are logged by the Uniswap v4 PoolManager as Swap. Trades on the bonding curve are logged by the Fee Hook as CurveTrade.

CurveTrade fieldOn a buyOn a sell
pairAmountPaired asset that reached the curve, after pairFeePaired asset that left the curve, before pairFee
tokenAmountTokens that left the curve, before burnTokensTokens that reached the curve, after burnTokens
raised, soldThe curve's totals after this tradeThe same

Index launches

Read Launched from the Launcher's first block, in bounded ranges.

launches.ts
import { parseAbiItem } from "viem";

const launched = parseAbiItem(
  "event Launched(address indexed token, address indexed creator, address indexed referrer, address pairAsset, string name, string symbol, uint8 grade, uint256 score, bytes32 rulesHash, uint256 creatorBuyIn, uint256 creatorBuyTokens)",
);

const DEPLOY_BLOCK = 93_140_866n; // the Launcher's first block
const STEP = 100_000n; // keep each eth_getLogs request to a bounded range

const head = await client.getBlockNumber();
const launches = [];
for (let from = DEPLOY_BLOCK; from <= head; from += STEP) {
  const to = from + STEP - 1n < head ? from + STEP - 1n : head;
  launches.push(...(await client.getLogs({ address: CAST.launcher, event: launched, fromBlock: from, toBlock: to })));
}

for (const l of launches) {
  const pairedWith = l.args.pairAsset === zeroAddress ? "ETH" : l.args.pairAsset;
  console.log(l.args.symbol, l.args.token, `paired with ${pairedWith}`, `grade ${"ABCDF"[l.args.grade!]}`, `block ${l.blockNumber}`);
}

Index trades

While a token is on its bonding curve, trade sizes come from the Fee Hook's CurveTrade. After it graduates, they come from the PoolManager's Swap event for the token's pool. What each trade paid comes from the Fee Hook's FeeCharged either way.

Zero-amount Swap logs on the curve
The PoolManager logs a Swap for every curve trade too, but with zero amounts, because the curve fills the trade and the pool swaps nothing. Skip those and read CurveTrade instead. The buy that graduates a token logs both: a CurveTrade for the part the curve filled, then Graduated, then a Swap with real amounts for whatever was left of the buy, if anything was, made in the new pool.
trades.ts
import { encodeAbiParameters, keccak256, parseAbiItem } from "viem";

const token = launches[0].args.token!; // any CAST token

// 1. Every CAST token has one Uniswap v4 pool. Its id is the hash of its PoolKey.
const poolKeyAbi = parseAbi([
  "struct PoolKey { address currency0; address currency1; uint24 fee; int24 tickSpacing; address hooks; }",
  "function poolKey() view returns (PoolKey)",
]);
const key = await client.readContract({ address: token, abi: poolKeyAbi, functionName: "poolKey" });
const poolId = keccak256(
  encodeAbiParameters(
    [{ type: "address" }, { type: "address" }, { type: "uint24" }, { type: "int24" }, { type: "address" }],
    [key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks],
  ),
);

// 2. Pool trades are logged by the PoolManager as Swap. currency0 is always the paired asset (ETH, USDG or a
//    stock token), so amount0 < 0 means the paired asset went in: a buy. Amounts are in each currency's smallest unit.
//    While a token is on its bonding curve, the PoolManager still logs a Swap for each trade, but with zero
//    amounts, because the curve fills the trade and the pool swaps nothing. Skip those.
const swap = parseAbiItem(
  "event Swap(bytes32 indexed id, address indexed sender, int128 amount0, int128 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick, uint24 fee)",
);
const latest = await client.getBlockNumber(); // on a busy chain, page through the range as above
const swaps = (
  await client.getLogs({
    address: CAST.poolManager,
    event: swap,
    args: { id: poolId },
    fromBlock: DEPLOY_BLOCK,
    toBlock: latest,
  })
).filter((s) => s.args.amount0 !== 0n || s.args.amount1 !== 0n);

// 3. Trades on the bonding curve are logged by the Fee Hook as CurveTrade, with the amounts that reached
//    and left the curve, and the curve's raised and sold totals after the trade.
const curveTrade = parseAbiItem(
  "event CurveTrade(address indexed token, address indexed trader, bool isBuy, uint256 pairAmount, uint256 tokenAmount, uint256 pairFee, uint256 burnTokens, uint256 raised, uint256 sold)",
);
const curveTrades = await client.getLogs({
  address: CAST.hook,
  event: curveTrade,
  args: { token },
  fromBlock: DEPLOY_BLOCK,
  toBlock: latest,
});

// 4. The Fee Hook logs what each trade paid, on the curve or in the pool, split by who received it.
//    pairFee is in the paired asset.
const feeCharged = parseAbiItem(
  "event FeeCharged(address indexed token, address indexed trader, bool isBuy, uint256 pairFee, uint256 burnTokens, uint256 cast, uint256 creator, uint256 holders, uint256 botTax)",
);
const fees = await client.getLogs({
  address: CAST.hook,
  event: feeCharged,
  args: { token },
  fromBlock: DEPLOY_BLOCK,
  toBlock: latest,
});

// A buy that graduates a token logs both: a CurveTrade for the part the curve filled, then a Swap for the
// rest, made in the new pool. So count buys by transaction.
const buyTxs = [...curveTrades.filter((t) => t.args.isBuy), ...swaps.filter((s) => s.args.amount0! < 0n)].map((l) => l.transactionHash);
const buys = new Set(buyTxs).size;
console.log(`${curveTrades.length} curve trades, ${swaps.length} pool swaps, ${buys} buys, ${fees.length} fee logs`);
One trade, two FeeCharged logs
In the pool, the hook charges the input side of a swap before it runs and the output side after. A buy logs its paired-asset fee just before the Swap and its burn just after; a sell logs its burn before and its paired-asset fee after. On the curve, both are logged just before the CurveTrade, which also repeats them as pairFee and burnTokens. Match fee logs to a trade by transaction, token and isBuy.

Indexing tips

TipWhy
Start at the Launcher's first blockNothing CAST happened earlier. The block number is on Network and contracts.
Filter logs by contract addressOther contracts can emit events with the same signature.
Keep log ranges boundedPublic RPCs reject or time out on very wide ranges. 100,000 blocks at a time works on the public RPC.
Wait for a few confirmationsRobinhood Chain is fast; waiting 20 blocks costs about two seconds and avoids reorg surprises.
Find a token's pool by its keyHash the token's poolKey(), or match the PoolManager's Initialize event whose currency1 is the token and hooks is the Fee Hook. currency0 is the paired asset.
Track graduation from the hookCurveOpened gives each token's target at launch, each CurveTrade carries raised, so progress is raised / target, and Graduated marks the switch to the pool. curves(token) gives the same state directly.
Don't count deferred rewards twiceHolderRewardsDeferred means a reward payout failed and the amount is still pending. A later HolderRewardsDistributed includes it, so sum HolderRewardsDistributed for what reached holders.
Read amounts in the pair's decimalsFees, rewards and the pair side of a swap are in the paired asset's smallest unit. ETH and the stock tokens have 18 decimals, USDG has 6. Launched gives each token's pairAsset.

For anything you can read directly, such as rules, rates and rewards, prefer calling the contracts. See Reading token state.

Popular
  • QuickstartGet started · Set up a wallet, get free test funds and buy your first CAST token.
  • Launch a tokenGet started · Pick a pair and a template, check the grade and seal your rules in one transaction.
  • Fees and Bot TaxConcepts · The three fees a trade can pay, who receives them, and a worked example.
  • Network and contractsDevelopers · Chain settings, contract addresses, pairs and protocol constants.
  • RisksTrust and safety · What sealed rules protect you from, and what they don't.

Connect wallet

New to wallets? Start here.
By connecting, you agree to the Terms and Privacy Policy.