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:
- Which token contracts might matter for this wallet?
- 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=25Discovery 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
| Scope | Use when | Coverage meaning |
|---|---|---|
explicit | your wallet already owns a reviewed token list | complete only for the supplied list |
discovered | you want transfer-evidence candidates | bounded standard-fungible evidence |
discovered_plus_registry | you want evidence plus reviewed known assets | bounded 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_completeis the wallet-home-screen default. If any requested value cannot be verified at the selected block, the request fails with HTTP503andRetry-After; it does not return a partial success body.verified_partialreturns successful values plus typedtimeout,unsupported, orerrorstatuses. It always setspartial=trueandwalletSafe=falsewhen 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_l2resolves the latest accepted Starknet block, then pins its hash.l1_acceptedresolves Starkscan's current L1-accepted Starknet watermark, then pins its hash.explicitrequires exactly oneblockHashor non-negativeblockNumber.
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 25The 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.