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

# Migrate your accounts

> Step-by-step guide to move Grid email accounts onto keys you control

If your users have Grid email accounts, Grid manages their Privy and Turnkey keys. Before Grid shuts down, each account's signers must be swapped for keys you control. `@sqds/grid/migration` does this from inside your app: it asks Grid for the swap, checks Grid's transaction before anything is signed, has the user's Grid session sign it, and confirms on-chain that no Grid authority remains.

## Overview

* **One login per account**: the end user signs in once with their email code. Grid's paymaster pays for the swap, so the user needs no SOL.
* **Nothing is signed unchecked**: the kit decodes Grid's prepared transaction and refuses to sign unless it does exactly what you asked.
* **Self-custodial or custodial**: the new key can be the user's own wallet (the kit runs in your front end) or a key your backend holds (the kit runs on your server).

| | Self-custodial | Custodial |
| - | - | - |
| New key | The user's own wallet | A key your backend controls (your own Privy or Turnkey server wallet, an HSM) |
| Where the kit runs | Front end | Backend (Node) |
| The user does | Logs in once with an email code, then approves cleanup in their wallet | Logs in once with an email code |

## Migration Steps

**1. Install the packages**

The migration entry points load `@solana/kit` and the Smart Account SDK; your existing `@sqds/grid` import is unchanged.

```bash theme={null}
npm install @sqds/grid@^3.2.0 @solana/kit@^8.6.0 @sqds/smart-account-core
```

For React apps, also install the hook:

```bash theme={null}
npm install @sqds/grid-react@^2.1.0
```

> [!NOTE]
>
> **ESM only.** `@sqds/grid/migration` and `@sqds/grid-react/migration` are ES modules. From CommonJS, use `await import('@sqds/grid/migration')`.

**2. Check that your new key signs v1 transactions**

After the swap, every transaction goes through the Smart Account SDK, which sends Solana v1 transactions only. Confirm your wallet or key provider signs them before you migrate any user.

> [!WARNING]
>
> **Check this first.** If the new key cannot sign v1 transactions, the account still migrates, but you cannot use it afterwards until you can sign them.

**3. Log the user in to Grid**

The user's Grid session authorizes the swap. Log them in the way you do today and wrap the session with `gridSession`.

```typescript theme={null}
import { address, createSolanaRpc } from '@solana/kit';
import { GridClient } from '@sqds/grid';
import {
  checkMigrated,
  gridSession,
  planMigration,
  resolveSettingsAddress,
  runCleanup,
  runMigration,
} from '@sqds/grid/migration';
```

```typescript theme={null}
const grid = new GridClient({ environment: 'production', apiKey });
const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');

const sessionSecrets = await grid.generateSessionSecrets();
const user = await grid.initAuth({ email });
const auth = await grid.completeAuth({ user: user.data, otpCode, sessionSecrets });
const session = gridSession(grid, { sessionSecrets, session: auth.data.authentication });
```

**4. Plan the migration**

`planMigration` is read-only. It reads the account from the chain, rejects accounts the kit cannot migrate, and lists the steps that remain.

```typescript theme={null}
const vault = address(auth.data.address);
const settings = await resolveSettingsAddress(grid, vault);

const plan = await planMigration(rpc, {
  settings,
  vault,
  newSigners: [{ key: newSigner.address, permissions: ['initiate', 'vote', 'execute'] }],
  newThreshold: 1,
});
```

> [!TIP]
>
> **Keep the user's permissions.** Pass every permission the account needs (`initiate`, `vote`, `execute`) and a threshold your new keys can reach.

**5. Run the swap**

`runMigration` asks Grid for the swap, checks it, has the session sign it and submits it. Save the pending swap in `onBeforeSubmit` so a crash or refresh can never submit twice.

```typescript theme={null}
const outcome = await runMigration(rpc, session, plan, {
  onBeforeSubmit: (pending) => savePending(vault, pending),
});
```

**6. Handle the outcome, clean up and confirm**

On `confirmed`, remove Grid's standing-order spending limits with the new key, then confirm with `checkMigrated`. On `failed` or `expired`, nothing changed on-chain: plan and run again.

```typescript theme={null}
if (outcome.status === 'confirmed') {
  await runCleanup(rpc, plan, { signers: [newSigner], feePayer: newSigner });
  const result = await checkMigrated(rpc, plan);
  console.log(result.status, result.remaining);
} else if (outcome.status === 'unknown') {
  await savePending(vault, outcome.pending);
} else {
  console.log('Nothing changed on-chain; plan and run again', outcome.status);
}
```

If the outcome is `unknown`, keep `outcome.pending` and resolve it later. Never start a new swap while one is pending.

```typescript theme={null}
const outcome = await runMigration(rpc, session, plan, { pending });
```

**7. Rehearse on sandbox**

Before migrating production users, run the same code end to end with a test email: create the `GridClient` with `environment: 'sandbox'` and your sandbox API key, point `createSolanaRpc` at `https://api.devnet.solana.com`, and use a devnet key as the new signer. Confirm `checkMigrated` returns `done`, then send one transfer with the Smart Account SDK.

## React

`useGridMigration` runs the same steps from a component. It stores the pending swap in `localStorage` before anything is signed, so a page refresh never submits twice.

```typescript theme={null}
import { useMemo } from 'react';
import { createSolanaRpc } from '@solana/kit';
import { gridSession, resolveSettingsAddress } from '@sqds/grid/migration';
import { useGridMigration } from '@sqds/grid-react/migration';
```

```typescript theme={null}
const rpc = useMemo(() => createSolanaRpc('https://api.mainnet-beta.solana.com'), []);
const session = useMemo(
  () => gridSession(client, { sessionSecrets, session: authentication }),
  [sessionSecrets, authentication],
);
const migration = useGridMigration({ rpc, session, vault });
```

```typescript theme={null}
async function onMigrateClick() {
  await migration.planMigration({
    settings: await resolveSettingsAddress(client, vault),
    newSigners: [{ key: userWallet.address, permissions: ['initiate', 'vote', 'execute'] }],
    newThreshold: 1,
  });
  const outcome = await migration.runMigration();
  if (outcome.status !== 'confirmed') return;
  await migration.runCleanup({ signers: [userWallet], feePayer: userWallet });
  await migration.checkMigrated();
}
```

> [!NOTE]
>
> **Every step is user-triggered.** The hook runs nothing on mount. `phase`, `error.code` and `error.retryable` tell you what to show.

## Not using `@sqds/grid`?

If you call Grid over HTTP or from another backend, you can still install `@sqds/grid` and import only `@sqds/grid/migration`. Skip `gridSession` and pass `runMigration` your own `GridSession`, an object with two methods:

* `prepareSignerUpdate(vault, request)`: calls `PATCH /api/grid/v1/accounts/{address}` and returns Grid's `{ transaction, transactionSigners, kmsPayloads }`.
* `signAndSubmit(vault, prepared)`: signs the KMS payloads with the user's Grid session, calls `/submit` and returns the transaction signature. If it fails before anything reached `/submit`, throw a `GridMigrationError` with code `SIGNING_FAILED`.

> [!NOTE]
>
> **Need a single-file version?** Email [support@grid.squads.xyz](mailto:support@grid.squads.xyz) for `grid-migration.ts`, the same code as one file you can copy into your project.

## Next Steps

> [card] [Operate accounts without Grid](https://developers.squads.so/grid/v1/migration/after-grid.md)
>
> Replace each Grid call with the Smart Account SDK

> [card] [Troubleshooting](https://developers.squads.so/grid/v1/migration/troubleshooting.md)
>
> Error codes, outages and accounts the kit does not migrate


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.