> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sqd.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate a squid to Portal

> Migrate your EVM or Solana Squid SDK indexer from gateway/RPC data sources to Portal, with real-time data support

This guide walks you through migrating EVM and Solana indexer setups that use `.setGateway()` (and optionally `.setRpc()`) over to Portal data sources. It works whether or not you have already migrated to a Portal for historical data.

<Tip>
  **Prefer to run this with Claude Code, Codex, or another AI coding agent?** Install the migration skill in your squid's directory:

  ```bash theme={"system"}
  npx skills add subsquid-labs/skills/squid-sdk/migrate-to-portal
  ```

  Then open your agent in the same directory and ask it to migrate. The agent should find the skill; if if 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](https://github.com/subsquid-labs/skills/blob/main/squid-sdk/migrate-to-portal/SKILL.md).
</Tip>

<Note>
  If you're reading this because you are been self-hosting any gateway-based squids after May 19, 2026 12:00 UTC and got an error requesting a gateway API key, be aware that migrating to Portals is the recommended, but not the only path available to you. The alternative is to register and [access gateways with API keys](/en/data/api-keys).
</Note>

## Prerequisites

* An existing squid based on
  * `@subsquid/evm-processor` on EVM: will typically use `.setGateway()` and `.setRpcEndpoint()`, in rare cases without either or with a `.setPortal()` - all of these cases are covered
  * `@subsquid/solana-stream` on Solana:  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](/en/data/networks/evm))
  * For a private Portal: get an URL from SQD or supply your own if self-hosting

## Migration steps

Pick your chain.

<Tabs>
  <Tab title="EVM" icon="ethereum">
    **Jump to:** [Reference templates](#reference-templates-evm) · [Common errors](#common-errors-evm)

    <Steps>
      <Step title="Install new packages">
        Replace the old processor package with the new packages:

        ```bash theme={"system"}
        npm uninstall @subsquid/evm-processor @subsquid/archive-registry
        npm i @subsquid/evm-stream @subsquid/evm-objects @subsquid/batch-processor @subsquid/logger
        ```

        <Note>
          `@subsquid/archive-registry` and `lookupArchive` are deprecated. Remove the package and all `lookupArchive(...)` calls (we cover the data source replacement in Step 3).
        </Note>

        If your handler imports `decodeHex` or `assertNotNull` from `@subsquid/evm-processor`, move those imports too. They are exported only by their utility packages now (both pulled in transitively by `@subsquid/evm-stream` and `@subsquid/batch-processor`, so no extra install needed):

        ```diff theme={"system"}
        -import {decodeHex, assertNotNull} from '@subsquid/evm-processor'
        +import {decodeHex} from '@subsquid/util-internal-hex'
        +import {assertNotNull} from '@subsquid/util-internal'
        ```
      </Step>

      <Step title="Switch the processor to a Portal data source">
        In `src/processor.ts` or `src/main.ts`, swap the processor imports and initialization:

        ```diff theme={"system"}
        -import {EvmBatchProcessor} from '@subsquid/evm-processor'
        +import {DataSourceBuilder} from '@subsquid/evm-stream'
        +import {augmentBlock} from '@subsquid/evm-objects'
        +import {createLogger} from '@subsquid/logger'
        ```

        ```diff theme={"system"}
        -const processor = new EvmBatchProcessor()
        -  .setGateway('https://v2.archive.subsquid.io/network/ethereum-mainnet')
        -  .setRpcEndpoint('https://rpc.ankr.com/eth')
        -  .setFinalityConfirmation(75)
        +const dataSource = new DataSourceBuilder()
        +  .setPortal('https://portal.sqd.dev/datasets/ethereum-mainnet')
           // ... .addLog(...), .setFields(...), etc.
        +  .build()
        ```

        RPC endpoint and finality confirmation settings are no longer needed.

        <Accordion title="If your squid still uses the older `.setDataSource({archive, chain})` shape">
          Squids untouched since 2024 may use the pre-`setGateway` API:

          ```typescript theme={"system"}
          .setDataSource({
            archive: lookupArchive('eth-mainnet'),
            chain: 'https://rpc.ankr.com/eth',
          })
          ```

          The destination is the same. Replace the whole block with `.setPortal(...)` and drop the `lookupArchive` import (`@subsquid/archive-registry` is gone, see Step 1).
        </Accordion>

        <Accordion title="Have data & context type aliases exported alongside the processor object? Rewrite them here.">
          The data types are now split across two packages: `@subsquid/evm-stream` exports `FieldSelection`; `@subsquid/evm-objects` exports the augmented block data types (`BlockHeader`, `Block` — previously `BlockData` — `Log`, `Transaction`, `Trace`, `StateDiff`). Three changes to be aware of:

          1. `BlockData<F>` is renamed to `Block<F>`. The single-block type previously called `Block` (just the header) is now `BlockHeader<F>`.
          2. The upstream `DataHandlerContext` lives in `@subsquid/batch-processor`. Its generic arguments are **flipped** (`DataHandlerContext<Block, Store>`, not `<Store, Fields>`) and it has only `{store, blocks, isHead}`. Add `log` (and `_chain` if you make RPC calls) yourself.
          3. The `Fields` type alias should be derived from the same `fields` constant you pass to `.setFields()`.

          ```diff theme={"system"}
          -import {
          -  BlockHeader,
          -  DataHandlerContext,
          -  EvmBatchProcessor,
          -  Log as _Log,
          -  Transaction as _Transaction,
          -  BlockData as _BlockData,
          -  FieldSelection,
          -} from '@subsquid/evm-processor'
          +import * as evmObjects from '@subsquid/evm-objects'
          +import {DataSourceBuilder, FieldSelection} from '@subsquid/evm-stream'
          +import type {DataHandlerContext as BaseDataHandlerContext} from '@subsquid/batch-processor'
          +import type {Logger} from '@subsquid/logger'

           const fields = {
             log: { transactionHash: true },
           } satisfies FieldSelection

           export type Fields = typeof fields
          -export type Block = BlockHeader<Fields>
          -export type BlockData = _BlockData<Fields>
          -export type Log = _Log<Fields>
          -export type Transaction = _Transaction<Fields>
          -export type ProcessorContext<Store> = DataHandlerContext<Store, Fields>
          +export type Block = evmObjects.BlockHeader<Fields>
          +export type BlockData = evmObjects.Block<Fields>
          +export type Log = evmObjects.Log<Fields>
          +export type Transaction = evmObjects.Transaction<Fields>
          +export type DataHandlerContext<Store> = BaseDataHandlerContext<BlockData, Store> & {
          +  log: Logger
          +  // include `_chain` here if you make direct RPC calls (see the optional step below)
          +}
          ```
        </Accordion>

        <Accordion title="If your squid uses `.setPortal()` alongside `.setRpcEndpoint()` (Portal beta participants)">
          Treat the `.setPortal` call in the same way as you would a `.setGateway` call - remove it alongside the processor initialization and set up a new one.
        </Accordion>

        <Accordion title="If your squid is not using real time data (.setGateway without .setRpcEndpont)">
          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.

          ```ts theme={"system"}
          const db = new TypeormDatabase({supportHotBlocks: false})
          ```

          A data source streaming into such a target will automatically switch to ingesting from [`/finalized-stream`](/en/api/evm/finalized-stream) instead of [`/stream`](/en/api/evm/stream).
        </Accordion>
      </Step>

      <Step title="Expand the field selection">
        <Warning>
          **Every field your handler reads must be listed in `.setFields()`.** With `EvmBatchProcessor`, common fields like `log.address`, `log.topics`, `log.data`, `block.timestamp` and `transaction.from/to/hash` were merged in from a built-in default set. `DataSourceBuilder` fetches only the fields you list, and TypeScript will reject any unlisted access.
        </Warning>

        A minimum set typical for an EVM logs indexer:

        ```typescript theme={"system"}
        const fields = {
          block: { timestamp: true },
          log: {
            address: true,
            topics: true,
            data: true,
            transactionHash: true, // and anything else you were already selecting
          },
        } satisfies FieldSelection
        ```

        Add `transaction`/`trace`/`stateDiff` entries as needed. TypeScript errors at compile time will point you at any field still missing.

        The top-level `block.height` and `block.timestamp` shortcuts are also gone. Read them from `block.header.number` and `block.header.timestamp`. `block.header.height` still exists but is `@deprecated`.

        <Note>
          If your squid is from pre-`@subsquid/evm-processor@1.0.0` era, rename `evmLog` to `log`.
        </Note>
      </Step>

      <Step title="Update data requests">
        Rewrite data requests using the new `where-include-range` syntax:

        ```diff theme={"system"}
         .addLog({
        -  address: [USDC_CONTRACT_ADDRESS],
        -  topic0: [usdcAbi.events.Transfer.topic],
        -  transaction: true, // include the parent transaction
        +  where: {
        +    address: [USDC_CONTRACT_ADDRESS],
        +    topic0: [usdcAbi.events.Transfer.topic],
        +  },
        +  include: {
        +    transaction: true, // include the parent transaction
        +  },
           range: { from: 6_082_465 },
         })
        ```
      </Step>

      <Step title="Update the run function">
        Replace `processor.run()` with the unified `run` function. Add the import, create a logger, and enrich the context with `augmentBlock`:

        ```diff theme={"system"}
        +import {run} from '@subsquid/batch-processor'

        +const logger = createLogger('sqd:processor:mapping')

        -processor.run(db, async (ctx) => {
        +run(dataSource, db, async (simpleCtx) => {
        +  const ctx = {
        +    ...simpleCtx,
        +    blocks: simpleCtx.blocks.map(augmentBlock),
        +    log: logger,
        +  }
           // ... handler body
         })
        ```

        <Accordion title="Used `.setPrometheusPort()` / `.setPrometheusServer()`? Move them here.">
          `PrometheusServer` is now passed to `run()` via the `prometheus` option instead of being attached to the processor. Construct one and hand it over; the runner still attaches the built-in `sqd_processor_*` metrics and calls `.serve()` itself.

          ```diff theme={"system"}
          -processor.setPrometheusPort(3000)
          -processor.setPrometheusServer(myServer)
          +import {PrometheusServer} from '@subsquid/batch-processor'
          +
          +const prometheus = new PrometheusServer()
          +prometheus.setPort(3000)
          +// prometheus.addMetricsSink({ register(registry) { /* register custom prom-client metrics */ } })
          +
          +run(dataSource, db, async (simpleCtx) => { /* ... */ }, {prometheus})
          ```

          `setPort()` overrides `PROCESSOR_PROMETHEUS_PORT` / `PROMETHEUS_PORT`. For custom metrics, register them on the server's registry via `addMetricsSink()` so they share the `/metrics` endpoint with `sqd_processor_*` — see [this branch](https://github.com/subsquid-labs/squid-evm-rt-template/tree/example/custom-metrics) for a worked example.
        </Accordion>
      </Step>

      <Step title="Add an RPC client">
        <Badge color="gray">Optional</Badge> Only do this if your batch handler makes [direct RPC calls](/en/sdk/squid-sdk/resources/tools/typegen/state-queries?typegen=evm) (contract state queries). Otherwise skip.

        Install the client, initialize an `RpcClient`, and enrich the batch context with a `_chain` field:

        ```bash theme={"system"}
        npm i @subsquid/rpc-client
        ```

        ```typescript theme={"system"}
        import {RpcClient} from '@subsquid/rpc-client'

        const rpcClient = new RpcClient({
          url: 'https://my_rpc_url',
          rateLimit: 100,
        })
        ```

        ```diff theme={"system"}
          const ctx = {
            ...simpleCtx,
            blocks: simpleCtx.blocks.map(augmentBlock),
            log: logger,
        +    _chain: {
        +      client: rpcClient
        +    }
          }
        ```

        If you keep a `DataHandlerContext<Store>` alias in `src/processor.ts`, extend it so `_chain` is part of the type your handlers see:

        ```diff theme={"system"}
         export type DataHandlerContext<Store> = BaseDataHandlerContext<BlockData, Store> & {
           log: Logger
        +  _chain: { client: RpcClient }
         }
        ```

        Contract state queries then work as before:

        ```typescript theme={"system"}
        const usdcContract = new usdcAbi.Contract(ctx, blocks[0].header, USDC_CONTRACT_ADDRESS)
        const decimals = await usdcContract.decimals()
        ```
      </Step>

      <Step title="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:

        ```bash theme={"system"}
        npm run build
        npx squid-typeorm-migration apply
        node lib/main.js
        ```
      </Step>

      <Step title="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](/en/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](/en/cloud/resources/slots-and-tags#zero-downtime-updates) for details.

        Can't afford a re-sync? Simply [re-deploy your squid](/en/sdk/squid-sdk/squid-cli/deploy) without resetting its database.
      </Step>
    </Steps>

    <Accordion title="Common errors (EVM)">
      A few canonical TypeScript errors map back to specific steps above. If you hit one, jump to the fix:

      | Symptom                                                                                                  | Root cause                                                                               | Fix                                                                                                                                                                                      |
      | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
      | `Property 'transactionHash' does not exist on type 'Log'` (used `log` key)                               | Forgot to list `transactionHash: true` under `.setFields().log`                          | Add `transactionHash: true` (and any other fields you read) under `.setFields().log`                                                                                                     |
      | `Property 'height' does not exist on type 'BlockHeader'` (or `'timestamp'`)                              | Top-level `block.height`/`block.timestamp` shortcuts removed                             | Use `block.header.number` / `block.header.timestamp`. `block.header.height` still works (deprecated) if `.height` is baked into entity columns and you want a literal rename.            |
      | `'apiKey' does not exist in type 'GatewaySettings'`                                                      | You're on `evm-processor < 1.30.0` and tried to add `apiKey` before bumping the package. | Either (a) migrate to `evm-stream` / `.setPortal('...')`: Portal needs no API key today. (b) Bump `@subsquid/evm-processor` to `^1.30.0` to get the `apiKey` field on `GatewaySettings`. |
      | `Module '"@subsquid/evm-processor"' has no exported member 'decodeHex'`                                  | `decodeHex` is only exported by `@subsquid/util-internal-hex` now                        | See Step 1 *Install new packages*                                                                                                                                                        |
      | `Module '"@subsquid/evm-processor"' has no exported member 'assertNotNull'`                              | `assertNotNull` is only exported by `@subsquid/util-internal` now                        | Same step                                                                                                                                                                                |
      | `Module '"@subsquid/archive-registry"' has no exported member 'lookupArchive'` (or `Cannot find module`) | Package removed entirely                                                                 | Uninstall `@subsquid/archive-registry` and replace the whole data source block with `.setPortal(...)`                                                                                    |
      | `Object literal may only specify known properties, and 'evmLog' does not exist`                          | `.setFields()` key renamed in `evm-processor@^1.21.0`                                    | Rename `evmLog` to `log` in `.setFields()` (see *Expand the field selection*)                                                                                                            |
      | `Property 'transactionHash' does not exist on type 'Log'` (used `evmLog` key)                            | `evmLog` key no longer projects `transactionHash` onto `Log`                             | Rename `evmLog` to `log` in `.setFields()`                                                                                                                                               |
    </Accordion>

    ### Reference templates (EVM)

    If you want to diff against a known-good shape, these are the canonical post-migration templates:

    <CardGroup cols={2}>
      <Card title="squid-evm-rt-template@with-logger" icon="github" href="https://github.com/subsquid-labs/squid-evm-rt-template/tree/with-logger">
        Portal data source, augmented context, no direct RPC calls
      </Card>

      <Card title="squid-evm-rt-template@with-rpc-client" icon="github" href="https://github.com/subsquid-labs/squid-evm-rt-template/tree/with-rpc-client">
        Same as above plus an `RpcClient` wired into the context for state queries
      </Card>
    </CardGroup>

    ### Next steps (EVM)

    <CardGroup cols={2}>
      <Card title="Portal EVM API" icon="book-open" href="/en/portal/evm/overview">
        Complete Portal API documentation for EVM
      </Card>

      <Card title="EVM examples" icon="code" href="/en/portal/evm/examples/query-logs">
        More examples of Portal API usage on EVM
      </Card>
    </CardGroup>
  </Tab>

  <Tab title="Solana" icon="globe">
    **Jump to:** [Reference template](#reference-template-solana) · [Common errors](#common-errors-solana) · [Height-to-slot converter](/en/sdk/migration/height-to-slot)

    <Steps>
      <Step title="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.

        ```diff theme={"system"}
        -import {DataSourceBuilder, SolanaRpcClient} from '@subsquid/solana-stream'
        +import {DataSourceBuilder} from '@subsquid/solana-stream'
        ```

        ```diff theme={"system"}
           const dataSource = new DataSourceBuilder()
             .setGateway('https://v2.archive.subsquid.io/network/solana-mainnet')
        -    .setRpc({
        -      client: new SolanaRpcClient({
        -        url: process.env.SOLANA_NODE
        -      })
        -    })
        ```
      </Step>

      <Step title="Upgrade SDK packages">
        From your squid's folder, upgrade every `@subsquid/*` package to its latest release:

        ```bash theme={"system"}
        npx --yes npm-check-updates --filter "@subsquid/*" --target "@latest" --upgrade
        ```

        This should bump `@subsquid/solana-stream` to a `1.x.x` version.

        Then install:

        <Tabs>
          <Tab title="NPM">
            ```bash theme={"system"}
            npm install
            ```
          </Tab>

          <Tab title="Yarn">
            ```bash theme={"system"}
            yarn install
            ```
          </Tab>

          <Tab title="PNPM">
            ```bash theme={"system"}
            pnpm install
            ```
          </Tab>
        </Tabs>
      </Step>

      <Step title="Switch the data source to Portal">
        Replace the gateway URL with the Portal URL:

        ```diff theme={"system"}
        +  .setPortal({
        +    url: 'https://portal.sqd.dev/datasets/solana-mainnet',
        +      http: {
        +        retryAttempts: Infinity
        +      }
        +   })
        -  .setGateway('https://v2.archive.subsquid.io/network/solana-mainnet')
        ```

        (The `SolanaRpcClient` import and `.setRpc({...})` call are already gone from Step 1.)

        <Accordion title="Have data & context type aliases exported alongside the processor object? Rewrite them here.">
          If your project re-exports `Fields`/`Block`/`Transaction`/`Context` aliases that handlers consume, re-derive them against the new packages:

          ```diff theme={"system"}
          -import {DataSourceBuilder, FieldSelection, Block as _Block, Transaction as _Transaction, Instruction as _Instruction} from '@subsquid/solana-stream'
          +import {DataSourceBuilder, FieldSelection} from '@subsquid/solana-stream'
          +import * as solanaObjects from '@subsquid/solana-objects'
          +import type {DataHandlerContext as BaseDataHandlerContext} from '@subsquid/batch-processor'
          +import type {Logger} from '@subsquid/logger'

           const fields = {
             block: {timestamp: true},
             transaction: {signatures: true, err: true, accountKeys: true},
           } satisfies FieldSelection

           export type Fields = typeof fields
          -export type Block = _Block<Fields>
          -export type Transaction = _Transaction<Fields>
          -export type Instruction = _Instruction<Fields>
          -export type Context = DataHandlerContext<Store, Fields>
          +export type BlockHeader = solanaObjects.BlockHeader<Fields>
          +export type Block = solanaObjects.Block<Fields>
          +export type Transaction = solanaObjects.Transaction<Fields>
          +export type Instruction = solanaObjects.Instruction<Fields>
          +export type DataHandlerContext<Store> = BaseDataHandlerContext<Block, Store> & {
          +  log: Logger
          +}
          ```

          Use the [solana-example template](https://github.com/subsquid-labs/solana-example/tree/master) as a reference if your squid has unusual aliases.
        </Accordion>

        <Accordion title="If your squid is not using real time data (.setGateway without .setRpcEndpont)">
          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.

          ```ts theme={"system"}
          const db = new TypeormDatabase({supportHotBlocks: false})
          ```

          A data source streaming into such a target will automatically switch to ingesting from [`/finalized-stream`](/en/api/evm/finalized-stream) instead of [`/stream`](/en/api/evm/stream).
        </Accordion>
      </Step>

      <Step title="Convert block heights to slot numbers">
        Solana datasets on Portal index by **slot number**, not block height. Replace any block-height literals (typically in `.setBlockRange({from: ...})`) with the corresponding slot:

        ```diff theme={"system"}
        +  .setBlockRange({from: 325000000})
        -  .setBlockRange({from: 303262650})
        ```

        Convert your height to a slot with the [bisection converter](/en/sdk/migration/height-to-slot).
      </Step>

      <Step title="Replace `block.header.slot` reads">
        If your handler code reads the `slot` field of a block header, rename it to `.number`:

        ```diff theme={"system"}
        -  slot: block.header.slot,
        +  slot: block.header.number,
        ```
      </Step>

      <Step title="Expand the field selection">
        <Warning>
          **`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.
        </Warning>

        `transaction.accountKeys` was never in the old SDK's defaults either — add it if you read the fee payer via `tx.accountKeys[0]`.

        <Accordion title="Full set of fields the old `@subsquid/solana-stream` used to merge in for free">
          | Object         | Fields merged in by the old SDK                                                                                                                                            |
          | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
          | `block`        | `timestamp`                                                                                                                                                                |
          | `transaction`  | `signatures`, `err`                                                                                                                                                        |
          | `instruction`  | `programId`, `accounts`, `data`, `isCommitted`                                                                                                                             |
          | `log`          | `programId`, `kind`, `message`                                                                                                                                             |
          | `balance`      | `pre`, `post`                                                                                                                                                              |
          | `tokenBalance` | the full `pre*`/`post*` family (`preMint`, `postMint`, `preOwner`, `postOwner`, `preAmount`, `postAmount`, `preDecimals`, `postDecimals`, `preProgramId`, `postProgramId`) |
          | `reward`       | `lamports`, `rewardType`                                                                                                                                                   |
        </Accordion>

        If you want to keep using the block height to stay compatible with your old code, request it explicitly. `block.header.number` (the slot) is always available, but `height` is not:

        ```diff theme={"system"}
           .setFields({
             block: { // block header fields
               timestamp: true,
        +      height: true
             },
        +    transaction: {
        +      signatures: true,
        +      err: true,
        +      accountKeys: true,  // needed for tx.accountKeys[0] (fee payer)
        +    },
        +    tokenBalance: {
        +      preMint: true,
        +      postMint: true,
        +      // add preAmount / postAmount / preDecimals / postDecimals / preOwner / postOwner as needed
        +    },
           })
        ```

        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.
      </Step>

      <Step title="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:

        ```bash theme={"system"}
        npm run build
        npx squid-typeorm-migration apply
        node lib/main.js
        ```
      </Step>

      <Step title="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](en/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](/en/cloud/resources/slots-and-tags#zero-downtime-updates) for details.

        Can't afford a re-sync? See the [manual workaround](/en/sdk/migration/solana-resync-workaround) (not recommended; edits the status schema directly).
      </Step>
    </Steps>

    <Accordion title="Common errors (Solana)">
      | Symptom                                                                                                | Root cause                                                                                                   | Fix                                                                                  |
      | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
      | `Property 'preMint' does not exist on type 'TokenBalance'` (or `postMint`/`preOwner`/`postAmount`/...) | Pre-1.x `solana-stream` merged the `tokenBalance.pre*`/`post*` family for free; the current release does not | Add the field to `.setFields().tokenBalance` (see *Expand the field selection*)      |
      | `Property 'timestamp' does not exist on type 'BlockHeader'`                                            | `block.timestamp` was in the old SDK's default set                                                           | Add `block: {timestamp: true}` to `.setFields()`                                     |
      | `Property 'signatures' does not exist on type 'Transaction'` (or `accountKeys`/`err`)                  | Same: was in the old SDK's defaults, no longer merged in                                                     | Add to `.setFields().transaction`                                                    |
      | `Property 'height' does not exist on type 'BlockHeader'`                                               | `block.header.height` is no longer implicit; the slot lives on `.number`                                     | Use `block.header.number`, or request `height` explicitly under `.setFields().block` |
      | `Module '"@subsquid/solana-stream"' has no exported member 'SolanaRpcClient'`                          | You bumped to `solana-stream@^1.x.x` but kept the import                                                     | Remove the import (see *Stop using the RPC client*)                                  |
      | `Property 'apiKey' does not exist in type 'GatewaySettings'`                                           | You're calling `.setGateway({url, apiKey})` on the new SDK                                                   | Portal uses `.setPortal('...')` and needs no API key today                           |
    </Accordion>

    ### Reference template (Solana)

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

    <Card title="solana-example@master" icon="github" href="https://github.com/subsquid-labs/solana-example/tree/master">
      Portal-based Solana indexer with the field selection, hot-block support, and types this guide produces
    </Card>

    ### Next steps (Solana)

    <CardGroup cols={2}>
      <Card title="Portal Solana API" icon="book-open" href="/en/portal/solana/overview">
        Complete Portal API documentation for Solana
      </Card>

      <Card title="Solana examples" icon="code" href="/en/portal/solana/examples/dex-swaps">
        More examples of Portal API usage on Solana
      </Card>
    </CardGroup>
  </Tab>
</Tabs>
