Skip to Content
SDK & CLISDKState Tracking

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

FieldTypeDescription
addressstringThe address the item is anchored to (see per-type notes below)
namestringDisplay name
typestringOne of native-token-balance, erc20-balance, function-result, storage-variable
functionAbistring?JSON ABI fragment or human-readable signature. Required for erc20-balance and function-result.
functionArgsstring?JSON-encoded args array. For erc20-balance: '["<holder>"]'. For storage-variable: '["<32-byte slot hex>"]'.
functionOutputPathstring?Dot-separated path into a struct/tuple return (e.g. info.balance). Omit to store the full return.
recordTriggerstring?every-tx (default), every-n-blocks, or cron
trackingFrequencynumber?Required for every-n-blocks (blocks) and cron (seconds)
tagsstring[]?Free-form tags for filtering

address per type:

  • native-token-balance, storage-variable — the address whose balance / storage to read
  • erc20-balance — the token contract; the holder goes in functionArgs
  • function-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); }
FieldTypeDescription
addressstring?Filter to items anchored to this address
tagstring?Filter to items carrying this tag
withValuesboolean?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);
FieldTypeDescription
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.
maxBlockNumbernumberReturn 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.

Last updated on