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
LaunchedLauncherA new token: token, creator, referrer, paired asset, name, symbol, grade, score, rulesHash, creator buy
0xc78f22cb147553b8af20fd7edf94f032b9abd8574c1016d477e44562d113cb74CurveOpenedFee HookA new token's bonding curve opened: token, virtualPair, virtualToken, target, curveTokens, poolTokens and the pool's starting sqrtPriceX96
0xb44c9ef34cc51a413b583ef056aaa6a960ddd522a886394258358c15b2864046CurveTradeFee HookA trade on the curve: token, trader, isBuy, pairAmount, tokenAmount, pairFee, burnTokens, and the curve's raised and sold totals after the trade
0xff97a934874a0b13dbb8d5d75e670ef1518b2691d415a892491d156802c93059GraduatedFee HookA token moved from its curve into its pool: token, pairToPool, tokensToPool, the position's liquidity and its sqrtPriceX96
0x6f64721a1c428a5bb355f1b372ed989c0cf8ed0280caaa7ae11f977b6471aaa1FeeChargedFee HookA 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
0xe6c8d0336431f8c44d1cff80dedf371862f349f2d7f84d14ea251320f5ac9a07HolderRewardsDistributedFee HookPaired asset sent into a token's holder reward stream, including any amount deferred earlier
0x7f5e6c1dacd282ea410f0c65d163a83507c237de4fdee05e44c070856d1b89deHolderRewardsDeferredFee HookThe reward payout failed (for example the paired asset froze the token or the hook). The amount stays pending for a later distribute
0x6adcd2aecab79e77d14b69afac7aef6e5c720dc99f6dd06311c88fd01e9b5117TokensBurnedFee HookBurn share destroyed. Can appear without HolderRewardsDistributed when the reward payout was deferred
0xfd38818f5291bf0bb3a2a48aadc06ba8757865d1dabd804585338aab3009dcb6CreatorFeesClaimedFee HookA creator collected their share, in the token's paired asset
0xaa4ecfd5324d73e3b54d038b3ae8ac8f88866d49dc334dc1f02fe36e0f935748CastFeesWithdrawnFee HookCAST's share in one paired asset paid out: asset, recipient, amount
0x3f43a78a033c53d15589ad18fe20150b6506fb8b951d8a87385c0bd8f9c1f94bRewardsDepositedTokenRewards 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
0xc47834cdb0191cfa2b90f6ae336198ff404f06226170200c57ab15ade0510ae6RewardsClaimedTokenA holder collected rewards
0xfc30cddea38e2bf4d6ea7d3f9ed3b6ad7f176419f4963bd81318067a4aee73feHoldStreakResetTokenA wallet sold and its streak restarted
0x32e363d1c0a568554de355b2d7f950c56dcf0cb5504d6d18983e9fd1d3576f32CreatorTokensReleasedTokenThe creator released locked tokens
0xa3addcdc3e9b85cd85d4d81ed66bb164226c7eb417e983ae652fe45cccc4c911PairListedPairsAn asset listed, or reopened, for new launches, with its virtualReserve and graduationTarget
0x00baad906d1967e4f08022092d6ab74cf5595a95be1d53a3a6af103bbf4599a3PairClosedPairsAn 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 field | On a buy | On a sell |
|---|---|---|
| pairAmount | Paired asset that reached the curve, after pairFee | Paired asset that left the curve, before pairFee |
| tokenAmount | Tokens that left the curve, before burnTokens | Tokens that reached the curve, after burnTokens |
| raised, sold | The curve's totals after this trade | The same |
Index launches
Read Launched from the Launcher's first block, in bounded ranges.
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.
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.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`);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
| Tip | Why |
|---|---|
| Start at the Launcher's first block | Nothing CAST happened earlier. The block number is on Network and contracts. |
| Filter logs by contract address | Other contracts can emit events with the same signature. |
| Keep log ranges bounded | Public RPCs reject or time out on very wide ranges. 100,000 blocks at a time works on the public RPC. |
| Wait for a few confirmations | Robinhood Chain is fast; waiting 20 blocks costs about two seconds and avoids reorg surprises. |
| Find a token's pool by its key | Hash 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 hook | CurveOpened 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 twice | HolderRewardsDeferred 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 decimals | Fees, 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.