Starkscan

Migrate To Wallet State

Replace retired indexed token holdings with separate discovery and block-pinned wallet-state calls.

Migrate to wallet state

GET /v1/{chain}/address/{address}/token-holdings is retired. It returns HTTP 410 Gone, no holdings data, successor links, and this guide. POST /v1/{chain}/address/{address}/portfolio-live was removed.

The old routes mixed two different questions:

  1. Which token contracts might matter for this wallet?
  2. What is the token contract's authoritative balance now?

An index can answer the first question within a declared evidence scope. It cannot certify the second. Transfer-derived balances can retain phantom assets, miss non-standard changes, or look internally consistent while omitting tokens. Wallet integrations must keep discovery coverage separate from balance correctness.

New two-step contract

Use discovery when you do not already have a token list:

GET /v1/{chain}/address/{address}/assets/discovery?scope=discovered_plus_registry&limit=25

Discovery returns token candidates, evidence, a version-pinned cursor, and coverage. It returns no balance. Continue while hasMore=true. coverage.completeWithinScope describes the declared standard-fungible evidence scope only; coverage.globallyComplete remains false because non-standard or unregistered assets may be undiscoverable.

Then verify a bounded candidate page:

POST /v1/{chain}/query/wallet-state
Content-Type: application/json

{
  "ownerAddress": "0x...",
  "mode": "require_complete",
  "scope": "discovered_plus_registry",
  "limit": 25,
  "blockPreference": "latest_accepted_l2",
  "include": {
    "nonce": true,
    "classHash": true
  }
}

wallet-state resolves one immutable block hash, then reads every balance and optional account field from the dedicated RPC serving pool at that hash. Starkscan never substitutes an indexed balance and never interprets an error as zero.

Choose a scope

ScopeUse whenCoverage meaning
explicityour wallet already owns a reviewed token listcomplete only for the supplied list
discoveredyou want transfer-evidence candidatesbounded standard-fungible evidence
discovered_plus_registryyou want evidence plus reviewed known assetsbounded evidence plus the checked-in registry

For explicit, send tokenAddresses and do not send a cursor. For discovery scopes, omit tokenAddresses and pass the returned cursor when continuing.

Choose a correctness mode

  • require_complete is the wallet-home-screen default. If any requested value cannot be verified at the selected block, the request fails with HTTP 503 and Retry-After; it does not return a partial success body.
  • verified_partial returns successful values plus typed timeout, unsupported, or error statuses. It always sets partial=true and walletSafe=false when a value failed.

Only status="ok" with a non-null balanceRaw is a verified balance. Verified zeros are omitted from items and counted in verification.verifiedZeroHidden. An absent row caused by an error is never equivalent to zero.

walletSafe=true requires all three conditions:

  • every selected value was verified at block.blockHash;
  • the candidate page has no continuation;
  • discovery is complete within its declared scope.

It is not a claim of globally complete asset discovery.

Block preference

  • latest_accepted_l2 resolves the latest accepted Starknet block, then pins its hash.
  • l1_accepted resolves Starkscan's current L1-accepted Starknet watermark, then pins its hash.
  • explicit requires exactly one blockHash or non-negative blockNumber.

The response always echoes block.blockNumber, block.blockHash, and block.finalityStatus. Compare snapshots only when their block identity matches.

Pricing

Balances and prices have separate status. A non-zero asset with unavailable pricing remains visible. Its price.valueUsd is null, it increments valuation.unpricedNonZeroAssetCount, and it is excluded from valuation.totalUsd. It is never counted as zero dollars.

Capacity and billing

Phase 1 accepts one owner and at most 25 candidates per request. The 26-50 band remains unavailable until dedicated-pool and clean-window capacity certification plus a coordinated schema and client release. The partner is billed one bounded wallet-state operation. Starkscan still accounts for actual internal RPC work and enforces concurrency, timeout, and response-size limits.

SDK and CLI

const discovery = await starkscan.walletAssetDiscovery('0x...', {
  scope: 'discovered_plus_registry',
  limit: 25,
});

const state = await starkscan.walletState({
  ownerAddress: '0x...',
  mode: 'require_complete',
  scope: 'discovered_plus_registry',
  limit: 25,
  blockPreference: 'latest_accepted_l2',
});
starkscan wallet-asset-discovery 0xWALLET --limit 25
starkscan wallet-state 0xWALLET --mode require_complete --limit 25

The MCP replacements are wallet_asset_discovery and wallet_state. Installed clients should update to the coordinated SDK, CLI, and MCP release before the retirement deploy. If package-registry propagation is delayed, this page and the HTTP examples above remain the authoritative migration path.

On this page