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

# addPhantomRedirectSolanaExtension

> Enable Phantom wallet support via deep link redirect on mobile

Adds support for connecting and signing with [Phantom](https://phantom.app) on mobile using Phantom's
[deep link redirect protocol](https://docs.phantom.app/phantom-deeplinks/deeplinks-ios-and-android).
This is the recommended integration method for mobile web apps that want to use Phantom on iOS and
Android without relying on browser extension injection.

The extension handles the full redirect lifecycle: building encrypted deep links, waiting for Phantom
to redirect back to your app, and completing the operation.

## Installation

```bash theme={"system"}
npm install @dynamic-labs-sdk/solana
```

## Usage

> [tab] JavaScript
>
> Call `addPhantomRedirectSolanaExtension` once during app initialization, passing the current URL so
> the extension can detect and complete any in-progress Phantom redirect:
>
> ```javascript theme={"system"}
> import { createDynamicClient } from '@dynamic-labs-sdk/client';
> import { addPhantomRedirectSolanaExtension } from '@dynamic-labs-sdk/solana';
>
> const client = createDynamicClient({
>   environmentId: 'YOUR_ENVIRONMENT_ID',
> });
>
> addPhantomRedirectSolanaExtension(
>   {
>     onCloseTab: () => window.close(),
>     url: new URL(window.location.href),
>   },
>   client
> );
> ```

> [tab] React
>
> In React, call `addPhantomRedirectSolanaExtension` at module level in your client setup file — it must run on every page load so the extension can detect and complete any in-progress Phantom redirect:
>
> ```tsx theme={"system"}
> // dynamicClient.ts
> import { createDynamicClient, initializeClient } from '@dynamic-labs-sdk/client';
> import { addSolanaWalletStandardExtension } from '@dynamic-labs-sdk/solana/walletStandard';
> import { addPhantomRedirectSolanaExtension } from '@dynamic-labs-sdk/solana';
>
> export const dynamicClient = createDynamicClient({
>   environmentId: 'YOUR_ENVIRONMENT_ID',
> });
>
> addSolanaWalletStandardExtension();
>
> // Must run on every page load — pass the current URL so the extension
> // can detect and complete any in-progress Phantom redirect
> addPhantomRedirectSolanaExtension({
>   onCloseTab: () => window.close(),
>   url: new URL(window.location.href),
> });
>
> void initializeClient();
> ```
>
> The module is imported once when the app loads, so the extension automatically detects return redirects from Phantom on every navigation.

## Parameters

| Parameter | Type | Description |
| - | - | - |
| `url` | `URL` | The current page URL. Used to detect and complete a pending Phantom redirect, and to derive the base redirect URL for outgoing deep links. Pass `new URL(window.location.href)` on every page load. |
| `onCloseTab` | `() => void` | Callback invoked when the SDK detects that this page is running in the clone tab Phantom opened, and the tab has finished delivering its result. Call `window.close()` for the default behavior, or provide a custom handler if you need different behavior (e.g. navigating to a fallback route). Pass a no-op if you handle navigation yourself. See [Why the clone tab exists](#why-the-clone-tab-exists). |
| `disableAutoRedirectCompletion` | `boolean` (optional) | When `true`, the extension will not automatically detect and complete pending redirects on load. Defaults to `false`. |
| `client` | `DynamicClient` (optional) | The Dynamic client instance. Only required when using multiple clients. |

## How it works

Phantom's redirect protocol works as follows:

1. Your app calls a standard wallet operation (connect, sign, send, disconnect) on the Phantom redirect provider
2. The SDK builds an encrypted deep link and redirects the user to the Phantom mobile app
3. The user approves the request inside Phantom
4. Phantom redirects back to your app. For connect and sign operations, the redirect URL includes an
   encrypted response. For disconnect, Phantom redirects back with no query parameters.
5. On the next page load, `addPhantomRedirectSolanaExtension` detects the redirect,
   decrypts the response (if present), and completes the operation

All payloads between your app and Phantom are encrypted end-to-end using NaCl box encryption with
ephemeral key pairs.

### Why the clone tab exists

On mobile browsers, Phantom does not return the user to the tab that started the operation. It opens
the redirect URL in a **new tab**, a clone of your app. That clone tab is the one carrying Phantom's
encrypted response, so it is the one that has to complete the operation, while the promise the user
is waiting on lives in the original tab.

The SDK bridges the two:

1. The clone tab decrypts Phantom's response and broadcasts the result to the original tab over the
   `BroadcastChannel` API, which resolves the pending promise there.
2. The clone tab then recognizes that it is a clone. Each tab has its own JavaScript context, so only
   the tab that started the operation has the origin marker set. A tab without it can only be a clone.
3. Since the original tab already has the result, the clone has nothing left to do, and the SDK emits
   `phantomRedirectCloseTab`, which invokes your `onCloseTab`. Closing the clone returns the user to
   the original tab with the operation already resolved.

The callback does not fire in native mobile contexts (React Native, Capacitor). There the deep link is
delivered to the same JavaScript context that started the operation, so no clone tab is ever created
and the tab that started the operation stays open. It also does not fire when
`disableAutoRedirectCompletion` is `true`, since the extension only listens for the close request
while it owns redirect completion.

## Supported operations

Once registered, the Phantom redirect provider supports all standard Solana wallet operations:

* `connect` — request wallet connection
* `disconnect` — end the wallet session
* `signMessage` — sign an arbitrary message
* `signTransaction` — sign a transaction
* `signAllTransactions` — sign multiple transactions
* `signAndSendTransaction` — sign and submit a transaction

These are used via the same functions as any other Solana wallet in Dynamic — see
[Signing and sending transactions](https://docs.dynamic.xyz/docs/javascript/reference/solana/signing-sending-transactions.md).

## Connect and verify on mobile

> [!WARNING]
>
> `connectAndVerifyWithWalletProvider` is **not supported** with Phantom redirect on mobile. It will
> throw a `DeeplinkConnectAndVerifyUnsupportedError`.

On mobile, `connectAndVerifyWithWalletProvider` triggers two sequential deep links — one to connect
and another to sign for verification. iOS will not honor the second deep link because it is no longer
tied to a user gesture. Android may handle this differently, but this restriction applies to all
mobile platforms to ensure a stable experience and prevent production-only errors.

Instead, use `connectWithWalletProvider` and `verifyWalletAccount` as two separate user actions:

```javascript theme={"system"}
import {
  connectWithWalletProvider,
  verifyWalletAccount,
  DeeplinkConnectAndVerifyUnsupportedError,
} from '@dynamic-labs-sdk/client';

// Step 1: Connect the wallet (first user tap)
const walletAccount = await connectWithWalletProvider({
  walletProviderKey: 'phantomsol:deepLink',
  addToDynamicWalletAccounts: false,
});

// Step 2: Verify ownership from a separate user action (e.g. a "Verify Ownership" button)
await verifyWalletAccount({ walletAccount });
```

Alternatively, you can catch the error from `connectAndVerifyWithWalletProvider` and fall back to
the two-step flow:

```javascript theme={"system"}
import {
  connectAndVerifyWithWalletProvider,
  connectWithWalletProvider,
  verifyWalletAccount,
  DeeplinkConnectAndVerifyUnsupportedError,
} from '@dynamic-labs-sdk/client';

try {
  await connectAndVerifyWithWalletProvider({
    walletProviderKey: key,
  });
} catch (error) {
  if (error instanceof DeeplinkConnectAndVerifyUnsupportedError) {
    // Mobile deep link provider — connect only, then verify separately
    const walletAccount = await connectWithWalletProvider({
      walletProviderKey: key,
      addToDynamicWalletAccounts: false,
    });

    // Present a "Verify Ownership" button to the user, then:
    await verifyWalletAccount({ walletAccount });
  } else {
    throw error;
  }
}
```

See [Connecting and Verifying a Wallet](https://docs.dynamic.xyz/docs/javascript/reference/wallets/connect-and-verify-wallet.md)
for more details on the connect and verify flow.

## Setting a redirect URL

Phantom redirects back to your app after the user approves or rejects a request. Make sure your app
handles the redirect URL on the page that calls `addPhantomRedirectSolanaExtension`, so the extension
can complete the pending operation automatically.

```javascript theme={"system"}
import { addPhantomRedirectSolanaExtension } from '@dynamic-labs-sdk/solana';
// On every page load, pass the current URL
addPhantomRedirectSolanaExtension(
  {
    onCloseTab: () => window.close(),
    url: new URL(window.location.href),
  },
  client
);

// If Phantom redirected here with a response, the extension completes it automatically.
// If not, it simply registers the provider for future use.
```

## Limitations

* Requires the user to have the Phantom mobile app installed
* The redirect flow causes a full page navigation — ensure your app state is restored on return
* Each operation generates a new ephemeral encryption key pair; sessions do not persist across devices
* Only supported on Solana
* `connectAndVerifyWithWalletProvider` is not supported on mobile — see [Connect and verify on mobile](#connect-and-verify-on-mobile) above

## Related functions

* [addSolanaExtension](https://docs.dynamic.xyz/docs/javascript/reference/solana/adding-solana-extensions.md) - Register standard Solana wallet providers
* [Connecting and Verifying a Wallet](https://docs.dynamic.xyz/docs/javascript/reference/wallets/connect-and-verify-wallet.md) - Connect and verify wallet accounts
* [signing-sending-transactions](https://docs.dynamic.xyz/docs/javascript/reference/solana/signing-sending-transactions.md) - Sign and send transactions with any Solana wallet


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