State Tracking
Record on-chain state over time on your Stagenet.
A tracked item is a piece of state — a balance, an ERC20 holding, a storage slot, or the result of a view function — that your Stagenet snapshots as it runs. The history shows up in the contract’s Workspace, alongside transactions and balances.
These methods back the contract.dev track CLI — use the SDK directly when you want tracking driven by your own scripts (deploy-time setup, scenario runners, test fixtures).
How it works
Each addTrackedItem creates a record on your Stagenet. By default, the Stagenet samples the item after every transaction. Periodic triggers (every-n-blocks and cron) are also supported for state that doesn’t change with every tx but should still be sampled on a cadence.
For function-result items, the Stagenet runs an eth_call against the current state on every trigger; for storage-variable items it reads the slot directly; for balance types it reads from the state manager (no eth_call).
Tracked-item shape
| Field | Type | Description |
|---|---|---|
address | string | The address the item is anchored to (see per-type notes below) |
name | string | Display name |
type | string | One of native-token-balance, erc20-balance, function-result, storage-variable |
functionAbi | string? | JSON ABI fragment or human-readable signature. Required for erc20-balance and function-result. |
functionArgs | string? | JSON-encoded args array. For erc20-balance: '["<holder>"]'. For storage-variable: '["<32-byte slot hex>"]'. |
functionOutputPath | string? | Dot-separated path into a struct/tuple return (e.g. info.balance). Omit to store the full return. |
recordTrigger | string? | every-tx (default), every-n-blocks, or cron |
trackingFrequency | number? | Required for every-n-blocks (blocks) and cron (seconds) |
tags | string[]? | Free-form tags for filtering |
address per type:
native-token-balance,storage-variable— the address whose balance / storage to readerc20-balance— the token contract; the holder goes infunctionArgsfunction-result— the contract whose view function is being called
Methods
stagenet.addTrackedItem(item)
Create a tracked item.
// Native balance
const treasury = await stagenet.addTrackedItem({
type: "native-token-balance",
address: "0x1111111111111111111111111111111111111111",
name: "treasury ETH",
});
// ERC20 balance — the CLI fills balanceOf() for you; the SDK takes it explicitly
const usdcHeld = await stagenet.addTrackedItem({
type: "erc20-balance",
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
name: "treasury USDC",
functionAbi: JSON.stringify({
type: "function",
name: "balanceOf",
inputs: [{ name: "account", type: "address" }],
outputs: [{ name: "", type: "uint256" }],
stateMutability: "view",
}),
functionArgs: JSON.stringify(["0x1111111111111111111111111111111111111111"]),
});
// View-function result, sampled every 100 blocks
const totalSupply = await stagenet.addTrackedItem({
type: "function-result",
address: "0xVault...",
name: "vault totalSupply",
functionAbi: "function totalSupply() view returns (uint256)",
functionArgs: "[]",
recordTrigger: "every-n-blocks",
trackingFrequency: 100,
tags: ["vault", "snapshot"],
});
// Specific storage slot
const ownerSlot = await stagenet.addTrackedItem({
type: "storage-variable",
address: "0xContract...",
name: "owner slot",
functionArgs: JSON.stringify([
"0x0000000000000000000000000000000000000000000000000000000000000000",
]),
});Returns { id, address, type, name }. Use getTrackedItems({ address }) to fetch the full record.
stagenet.getTrackedItems(filter?)
List tracked items on this stagenet.
// All items
const all = await stagenet.getTrackedItems();
// Items anchored to a specific contract
const forContract = await stagenet.getTrackedItems({ address: "0xVault..." });
// Items by tag
const snapshots = await stagenet.getTrackedItems({ tag: "snapshot" });
// Annotate each item with its latest recorded value
const withLatest = await stagenet.getTrackedItems({ withValues: true });
for (const item of withLatest) {
console.log(item.name, item.latestValue?.value);
}| Field | Type | Description |
|---|---|---|
address | string? | Filter to items anchored to this address |
tag | string? | Filter to items carrying this tag |
withValues | boolean? | Include latestValue on each item |
stagenet.getTrackedItemValues(id, options?)
Read recorded values for a tracked item, oldest-first.
// Last 24h, with an anchor sample just before the window
const recent = await stagenet.getTrackedItemValues(totalSupply.id, {
period: "1d",
});
console.log(recent.values.length, "samples; anchor:", recent.anchor?.value);
// Everything up to and including block 19_000_000
const upTo = await stagenet.getTrackedItemValues(totalSupply.id, {
maxBlockNumber: 19_000_000,
});
// All values (no window)
const everything = await stagenet.getTrackedItemValues(totalSupply.id);| Field | Type | Description |
|---|---|---|
period | '10min' | '1h' | '1d' | '1w' | '1m' | Restrict to a relative window. The response also includes an anchor sample just before the window for chart continuity. |
maxBlockNumber | number | Return values at or before this block, in ascending order |
Returns { values: TrackedItemValue[], anchor?: TrackedItemValue } where each value is { value, blockNumber, timestamp, txHash }.
stagenet.updateTrackedItem(id, updates)
Mutate a tracked item in place.
await stagenet.updateTrackedItem(totalSupply.id, {
name: "vault.totalSupply (1k blocks)",
trackingFrequency: 1000,
tags: ["vault", "low-frequency"],
});Changing a semantic field — type, address, functionAbi, functionArgs, functionOutputPath — clears the item’s recorded history server-side so old and new measurements don’t mix on the same chart. Renaming, retagging, or changing the trigger preserves history.
Returns the updated item.
stagenet.removeTrackedItem(id)
Delete a tracked item and its history.
await stagenet.removeTrackedItem(totalSupply.id);Returns { id, removed: true }.
Where the history is stored
Recorded values live on your Stagenet (server-side) and are visible in the address’s Workspace. The SDK never holds the history in memory — every getTrackedItemValues call hits the Stagenet.