Migration skills
Download source-backed SKILL.md artifacts for Voyager migration and accounting point-in-time balances.
Download source-backed SKILL.md artifacts for Voyager migration and accounting point-in-time balances.
Use this page when a client wants an agent to migrate from Voyager-style calls to Starkscan without guessing routes.
The artifacts below are intentionally narrow. They read like executable migration contracts, not generic prompts. Each skill tells the agent which Starkscan routes are certified, beta, provider-blocked, or unsupported, and points back to the published starkscan-openapi.yaml before any code generation.
| Skill | Best for | Artifact |
|---|---|---|
| Accounting point-in-time balances | Accountants that need one token balance for one account at a calendar close. | /skills/accounting-point-in-time-balances/SKILL.md |
| Voyager migration | Adapters that need to replace a bounded Voyager workflow with Starkscan REST routes and keep unsupported/RPC gaps explicit. | /skills/voyager-migration/SKILL.md |
Install the artifact by giving the SKILL.md file to Claude, Codex, Cursor, or any agent runtime that supports portable task instructions. If the runtime does not support skills directly, paste only the relevant sections into the migration task and keep the OpenAPI link attached.
The current accounting close workflow is already possible with two Starkscan calls:
export STARKSCAN_BASE_URL="https://starkscan.co/api"
export STARKSCAN_CHAIN="SN_MAIN"
export STARKSCAN_API_KEY="<api_key>"
export STARKSCAN_TIMESTAMP="2025-12-31T23:59:59Z"
export STARKSCAN_TIMESTAMP_QUERY="2025-12-31T23%3A59%3A59Z"
export STARKSCAN_TOKEN="<token_contract_address>"
export STARKSCAN_ACCOUNT="<account_address>"
curl -sS \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"$STARKSCAN_BASE_URL/v1/$STARKSCAN_CHAIN/block-at-timestamp?timestamp=$STARKSCAN_TIMESTAMP_QUERY&closest=before"
Use the returned block.blockNumber as the block_tag:
curl -sS \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"$STARKSCAN_BASE_URL/v1/$STARKSCAN_CHAIN/token/$STARKSCAN_TOKEN/balance-of/$STARKSCAN_ACCOUNT?block_tag=<block.blockNumber>"
For 2025-12-31T23:59:59Z, Starkscan's correctness gate verified block 5007537 against RPC. The helper response also includes previousBlock, nextBlock, and source=indexed_blocks so an accountant can keep the bracket evidence with the report.
Use this matrix before generating migration code. The state must stay attached to every replaced call so clients know whether they are using a certified REST route, a beta REST route, a provider-blocked RPC surface, or no Starkscan replacement yet.
| Voyager-style need | Starkscan replacement | State | Migration note |
|---|---|---|---|
| Health / host reachability | GET /v1/{chain}/status | certified | Use this as the first smoke for every integration. |
| Exact token balance | GET /v1/{chain}/token/{token}/balance-of/{address}?block_tag={block} | certified | Use exact token and account addresses; pass a concrete block for accounting. |
| Timestamp to block | GET /v1/{chain}/block-at-timestamp?timestamp={time}&closest=before | certified | Use as the helper before exact historical balance checks for one known token/account. |
| One transaction | GET /v1/{chain}/tx/{tx_hash} | certified | Use for transaction detail and support links. |
| Contract metadata | GET /v1/{chain}/contract/{address} | beta | Indexed-only class/deployment/token metadata. Nullable token fields mean not identified in indexed token metadata. |
| Wallet transactions | GET /v1/{chain}/address/{address}/transactions?limit={n}&cursor={nextCursor} | beta | Cursor-based; preserve nextCursor exactly. |
| Address token transfers | GET /v1/{chain}/address/{address}/transfers?direction=any&limit={n}&cursor={nextCursor} | beta | Wallet-first transfer rows across all indexed tokens. Use direction=any, in, or out; any dedupes self-transfers while in and out keep the explicit directional view. Direction, token, block, cursor, and limit filters are applied before pagination. |
| Token transfer rows | GET /v1/{chain}/token/{token}/transfers?address={address}&limit={n}&cursor={nextCursor} | beta | Use when the token contract is known. Do not enumerate token contracts client-side for wallet history. |
| Storage reads, class reads, nonce, calls, fee, simulation | Keep existing Starknet RPC provider for now | provider-blocked | Starkscan JSON-RPC exists for gated proof, but do not migrate customer provider traffic until a mainnet 0.10.2+ upstream preflight and provider-readiness attestation land. |
| Writes, WebSocket subscriptions, broad Voyager parity | No current-pilot replacement | unsupported | Keep the existing provider path until Starkscan RPC formally launches that surface. |
| Task | Route | State |
|---|---|---|
| Health check | GET /v1/{chain}/status | certified |
| Timestamp to block | GET /v1/{chain}/block-at-timestamp?timestamp={time}&closest=before | certified |
| Exact token balance | GET /v1/{chain}/token/{token}/balance-of/{address}?block_tag={block} | certified |
| One transaction | GET /v1/{chain}/tx/{tx_hash} | certified |
| Contract metadata | GET /v1/{chain}/contract/{address} | beta |
| Wallet transactions | GET /v1/{chain}/address/{address}/transactions?limit={n}&cursor={nextCursor} | beta |
| Address token transfers | GET /v1/{chain}/address/{address}/transfers?direction=any&limit={n}&cursor={nextCursor} | beta |
| Token transfer rows | GET /v1/{chain}/token/{token}/transfers?address={address}&limit={n}&cursor={nextCursor} | beta |
| Storage reads / class reads / nonce / simulation | Keep existing Starknet RPC provider for now | provider-blocked |
Current gaps must stay visible in migration plans. A standalone REST storage
route, migration-ready Starkscan JSON-RPC, and broad Voyager parity are not
certified launch surfaces. If an agent asks for a route that is not listed here
or in the OpenAPI artifact, it should answer unsupported or
provider-blocked instead of inventing an endpoint.
Use this when a Voyager workflow needs indexed deployment/class/token identity for a known address. This route is beta and indexed-only; it does not probe ABI or RPC on the request path.
curl -sS \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"$STARKSCAN_BASE_URL/v1/$STARKSCAN_CHAIN/contract/0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
{
"chainId": "SN_MAIN",
"address": "0x40337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a",
"classHash": "0x30b8...",
"deployedAtBlock": 9795720,
"deployedAtTx": "0x...",
"createdOnIso": "2026-05-18T00:00:00.000Z",
"deployedByAddress": "0x...",
"isAccount": false,
"isToken": null,
"tokenKind": null,
"alias": null,
"metadataCompleteness": true,
"source": "indexed_read_model"
}
Token fields are either identified together (isToken: true, non-null tokenKind) or absent together (isToken: null, tokenKind: null, alias: null).
X-Starkscan-Api-Key; do not paste the full key into chats, screenshots, tickets, or generated reports.closest=before for "as of this instant" accounting semantics.USDC when correctness matters.latest for historical accounting close.X-Request-Id, X-Starkscan-Route-Class, rate-limit headers, HTTP status, and latency for support./starkscan-openapi.yaml before generating SDK code or adding a route to a migration matrix.curl -sS \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"$STARKSCAN_BASE_URL/v1/$STARKSCAN_CHAIN/status"
BLOCK_RESPONSE="$(curl -sS \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"$STARKSCAN_BASE_URL/v1/$STARKSCAN_CHAIN/block-at-timestamp?timestamp=2025-12-31T23%3A59%3A59Z&closest=before")"
printf '%s\n' "$BLOCK_RESPONSE"
BLOCK_NUMBER="$(printf '%s' "$BLOCK_RESPONSE" | python3 -c 'import json, sys; print(json.load(sys.stdin)["block"]["blockNumber"])')"
curl -sS \
-H "X-Starkscan-Api-Key: $STARKSCAN_API_KEY" \
"$STARKSCAN_BASE_URL/v1/$STARKSCAN_CHAIN/token/$STARKSCAN_TOKEN/balance-of/$STARKSCAN_ACCOUNT?block_tag=$BLOCK_NUMBER"
Use the TypeScript SDK for the certified balance route after the timestamp helper resolves the block:
import { createStarkscanClient } from "@starkscan/sdk";
const baseUrl = process.env.STARKSCAN_BASE_URL ?? "https://starkscan.co/api";
const chainId = process.env.STARKSCAN_CHAIN ?? "SN_MAIN";
const apiKey = process.env.STARKSCAN_API_KEY;
const token = process.env.STARKSCAN_TOKEN;
const account = process.env.STARKSCAN_ACCOUNT;
const timestamp = "2025-12-31T23:59:59Z";
if (!apiKey || !token || !account) {
throw new Error("STARKSCAN_API_KEY, STARKSCAN_TOKEN, and STARKSCAN_ACCOUNT are required");
}
const blockResponse = await fetch(
`${baseUrl}/v1/${chainId}/block-at-timestamp?timestamp=${encodeURIComponent(timestamp)}&closest=before`,
{ headers: { "X-Starkscan-Api-Key": apiKey } },
);
const { block } = (await blockResponse.json()) as { block: { blockNumber: number } };
const starkscan = createStarkscanClient({ apiKey, baseUrl, chainId });
const balance = await starkscan.tokenBalanceOf(token, account, String(block.blockNumber));
console.log({ blockNumber: block.blockNumber, balance });
Use the CLI for a package-level smoke after the block number is known:
npx -y @starkscan/cli@alpha \
--base-url "$STARKSCAN_BASE_URL" \
--chain "$STARKSCAN_CHAIN" \
--api-key "$STARKSCAN_API_KEY" \
doctor
npx -y @starkscan/cli@alpha \
--base-url "$STARKSCAN_BASE_URL" \
--chain "$STARKSCAN_CHAIN" \
--api-key "$STARKSCAN_API_KEY" \
--output-format json \
token-balance-of "$STARKSCAN_ACCOUNT" "$STARKSCAN_TOKEN" \
--block-tag "$BLOCK_NUMBER"
The safe builder behavior is constrained:
SKILL.md, curl commands, TypeScript SDK notes, Python smoke, and a route-state table only for matched routes.unsupported, beta, or provider-blocked with the reason.This prevents a migration agent from converting "missing docs" into fake Starkscan routes.