Read a revenue token from a script
A script that reads a revenue token's record, streams, terms, income books, doors and a wallet's claimable income, run on rHOOKR.
On this page
Everything a token's page shows can be read from the tokenizer and the token, at one block if you like. This walkthrough reads rHOOKR: its record, its terms, its income books, its doors, its streams and what one wallet can claim.
In the app
The token's page, https://hookr.fun/revenue/<address>, shows the same: its streams and when each ends, its income, the release period, and a connected wallet's income and drops to claim.
The reads
| What | Read |
|---|---|
| The record: who made it, which way in, its term and Hookr's leg | tokenizations(token) on the tokenizer |
| Each stream and how it ends | streamsOf(token) on the tokenizer |
| The terms fixed at the mint | totalSupply(), streamSeconds(), tipBps(), bridge() and bridgeBps() on the token |
| The supply that earns | earningSupply() |
| Each asset's books | assetAt(i) for i below assetCount(): received, paid, pendingStream and streamEnd |
| What a release now would move | releasable(i) |
| What a wallet can claim | claimable(wallet, i) |
| The doors | doorAt(i) for i below doorCount(): kind, contract and gas stipend |
pendingStream is income recognized but not yet released, and streamEnd the time by which all of it will have been released.
In code
With client.ts and abis.ts:
import { erc20Abi, formatUnits, zeroAddress, type Address } from "viem";
import { client, TOKENIZER } from "./client";
import { tokenAbi, tokenizerAbi } from "./abis";
const WALLET: Address = "0x…"; // whose income to read
// The second revenue token the tokenizer made is rHOOKR; tokenAt(i) never changes.
const token = await client.readContract({ address: TOKENIZER, abi: tokenizerAbi, functionName: "tokenAt", args: [1n] });
const t = { address: token, abi: tokenAbi } as const;
const [record, streams, symbol, supply, earning, period, tipBps, assetCount, doorCount] = await Promise.all([
client.readContract({ address: TOKENIZER, abi: tokenizerAbi, functionName: "tokenizations", args: [token] }),
client.readContract({ address: TOKENIZER, abi: tokenizerAbi, functionName: "streamsOf", args: [token] }),
client.readContract({ ...t, functionName: "symbol" }),
client.readContract({ ...t, functionName: "totalSupply" }),
client.readContract({ ...t, functionName: "earningSupply" }),
client.readContract({ ...t, functionName: "streamSeconds" }),
client.readContract({ ...t, functionName: "tipBps" }),
client.readContract({ ...t, functionName: "assetCount" }),
client.readContract({ ...t, functionName: "doorCount" }),
]);
console.log(`${symbol}: way ${record.way}, Hookr's leg ${record.legBps / 100}%, supply ${formatUnits(supply, 18)}, earning ${formatUnits(earning, 18)}`);
console.log(`release period ${period / 86_400} day(s), tip ${tipBps / 100}%`);
// The income books, one asset at a time: asset 0 is ETH.
for (let i = 0n; i < assetCount; i++) {
const asset = await client.readContract({ ...t, functionName: "assetAt", args: [i] });
const eth = asset.currency === zeroAddress;
const [name, decimals] = eth
? (["ETH", 18] as const)
: await Promise.all([
client.readContract({ address: asset.currency, abi: erc20Abi, functionName: "symbol" }),
client.readContract({ address: asset.currency, abi: erc20Abi, functionName: "decimals" }),
]);
const [releasable, claimable] = await Promise.all([
client.readContract({ ...t, functionName: "releasable", args: [i] }),
client.readContract({ ...t, functionName: "claimable", args: [WALLET, i] }),
]);
console.log(
`${name}: received ${formatUnits(asset.received, decimals)}, paid ${formatUnits(asset.paid, decimals)},`,
`releasing ${formatUnits(asset.pendingStream, decimals)} until ${new Date(Number(asset.streamEnd) * 1000).toISOString()},`,
`releasable now ${formatUnits(releasable, decimals)}, the wallet can claim ${formatUnits(claimable, decimals)}`,
);
}
// The doors, fixed at the mint, and the streams, read live.
const KIND = ["Rules ledger", "family door", "fee NFT door", "v4 position door", "outside door", "v3 position door"];
for (let i = 0n; i < doorCount; i++) {
const door = await client.readContract({ ...t, functionName: "doorAt", args: [i] });
console.log(`door ${i}: ${KIND[door.kind]}, gas stipend ${door.gas}`);
}
console.log(streams.map((s) => ({ kind: s.kind, endKind: s.endKind, end: s.end })));Run on rHOOKR, its fixed lines read:
rHOOKR: way 5, Hookr's leg 5%, supply 1000000000, ...
release period 1 day(s), tip 1%
door 0: fee NFT door, gas stipend 300000
door 1: Rules ledger, gas stipend 250000
door 2: family door, gas stipend 600000Its streams are the fee NFT, the Rules ledger and its market, each endKind 0, forever. The ETH line changes with every sync and claim, so it is left out here.
For many tokens at once, batch the reads with client.multicall: viem's robinhood chain carries Multicall3's address.