Skip to main content
This guide walks you through migrating a Solana indexer setup that uses .setGateway() (and optionally .setRpc()) over to a Portal data source. It works whether or not you have already migrated to a Portal for historical data.
Prefer to run this with Claude Code, Codex, or another AI coding agent? Install the migration skill in your squid’s directory:
Then open your agent in the same directory and ask it to migrate. The agent should find the skill; if it doesn’t, stop it and tell it where to look in plain English (e.g. at ./.agents/skills/migrate-to-portal if you installed the skill project-scoped for Claude).The skill detects your chain and follows the same steps as this guide. Skill source: subsquid-labs/skills/squid-sdk/migrate-to-portal.
If you self-host and hit a gateway API-key error after May 19, 2026 12:00 UTC, note that the Solana gateway is itself being retired — it shrinks to the last 30 days of data on June 1, 2026 and is scheduled for full removal later this year. Adding a key only buys a little time; Portal is the durable path, so migrate now.

Prerequisites

  • An existing squid based on @subsquid/solana-stream: using .setGateway() and/or .setRpc()
  • Node.js 22+ and npm installed
  • An SQD Portal URL for your dataset:
    • For public Portal: https://portal.sqd.dev/datasets/<slug> (find your slug on the networks page)
    • For a private Portal: get an URL from SQD or supply your own if self-hosting

Migration steps

Jump to: Reference template · Common errors
1

Stop using the RPC client

Remove the SolanaRpcClient import and the .setRpc({...}) call from your data source. Your project still compiles on your current @subsquid/solana-stream version after this edit; once you bump to ^1.x.x in the next step (where SolanaRpcClient and .setRpc() disappear), you avoid a broken intermediate state.
2

Upgrade SDK packages

From your squid’s folder, upgrade every @subsquid/* package to its latest release:
This should bump @subsquid/solana-stream to a 1.x.x version.Then install:
3

Switch the data source to Portal

Replace the gateway URL with the Portal URL:
(The SolanaRpcClient import and .setRpc({...}) call are already gone from Step 1.)
If your project re-exports Fields/Block/Transaction/Context aliases that handlers consume, re-derive them against the new packages:
Use the solana-example template as a reference if your squid has unusual aliases.
Previously, using a gateway data source only (without RPC) enabled the regime when the squid only consumes and processes finalized data. This introduces a significant delay between the data appearing on chain and in the indexer, but allows you to forward data to append-only destinations, which is not possible with real time data. To enable this regime in Portal-powered squids, explicitly disable hot blocks support in the target database, e.g.
A data source streaming into such a target will automatically switch to ingesting from /finalized-stream instead of /stream.
4

Keep block ranges in slots

DataSourceBuilder.setBlockRange() uses the same slot-number coordinate returned as block.header.number. Keep existing slot ranges unchanged. There is no separate contiguous Portal-height coordinate for this API.
5

Replace `block.header.slot` reads

If your handler code reads the slot field of a block header, rename it to .number:
6

Expand the field selection

tokenBalance.preMint / postMint are the #1 silent break on real Solana migrations. DEX squids that read these to recover swap-side mints will hit Property 'preMint' does not exist on type 'TokenBalance' until you add them to .setFields().tokenBalance. Pre-1.x @subsquid/solana-stream merged them in for free; the current release returns only the fields you list.
transaction.accountKeys was never in the old SDK’s defaults either — add it if you read the fee payer via tx.accountKeys[0].
block.header.number is always available and contains the slot. The current Portal source does not return height, even though @subsquid/[email protected] accepts it in the TypeScript field selection. Migrate height reads to number.
Add instruction/log/balance/reward entries the same way. TypeScript errors at compile time will point you at any field still missing from the selection.
7

Smoke-test locally

Build and run the squid locally before deploying. If the first batch lands without TypeScript or missing-field runtime errors, you’re ready to deploy:
8

Re-sync the squid

We highly recommend that all squids migrated to Portal are re-synced. This allows you to make sure that everything works as expected for the whole length of the chain and catch any bugs early.If your squid is deployed to the Cloud, follow the zero-downtime update procedure:
  1. Deploy your squid into a new slot.
  2. Wait for it to sync, observing the improved data fetching.
  3. Assign your production tag to the new deployment to redirect the GraphQL requests there.
See the slots and tags guide for details.Can’t afford a re-sync? See the manual workaround (not recommended; edits the status schema directly).

Reference template (Solana)

If you want to diff against a known-good shape, this is the canonical post-migration template:

solana-example@master

Portal-based Solana indexer with the field selection, hot-block support, and types this guide produces

Next steps (Solana)

Portal Solana API

Complete Portal API documentation for Solana

Solana examples

More examples of Portal API usage on Solana