# Introduction

Freighter is a non-custodial wallet for the Stellar network, available as a browser extension and a mobile app. Non-custodial means your users hold their own keys — your dapp never sees or stores private keys. This guide walks you through integrating Freighter so your users can connect their wallets, sign transactions, and interact with Soroban smart contracts.

## What You Can Build

With Freighter, your dapp can connect a user's wallet with a single call — no signup form, no password, just a familiar wallet popup and an immediate public key. From there you can read their active network, hand off transactions for signing, and submit to the network.

For dapps that integrate with smart contracts, Freighter also handles authorization entry signing for smart contract calls and arbitrary message signing for account verification.

## How It Works

The integration model is simple: your dapp talks to the wallet, and the wallet talks to the user.

For **desktop browsers**, you integrate with the Freighter extension through a lightweight JavaScript library. It injects itself into the page, and your dapp calls methods like "is the wallet connected?", "what's the user's address?", and "please sign this transaction."

For **mobile**, the integration works over WalletConnect v2. When a user opens your dapp in a mobile browser, WalletConnect presents a modal where they select Freighter as their wallet and approve the connection. On desktop, the same flow can display a QR code for the user to scan. Either way, a secure relay session is established and signing requests flow through the same pattern — your dapp proposes, the user reviews and approves in the wallet.

Both paths produce the same output: signed transactions you can submit to the Stellar network. So your backend and submission logic stay the same regardless of whether your user connected from a laptop or a phone.

If you want to support multiple Stellar wallets — not just Freighter — take a look at [Stellar Wallets Kit](https://stellarwalletskit.dev/). It provides a unified interface across Stellar wallets, including browser extensions and WalletConnect-based mobile wallets, so your users can connect with whichever wallet they prefer.

> **Note:** Stellar Wallets Kit's WalletConnect module currently only exposes `stellar_signXDR` and `stellar_signAndSubmitXDR`. If your dapp needs `stellar_signMessage` or `stellar_signAuthEntry` against Freighter Mobile, integrate WalletConnect directly for now (tracked in [stellar/freighter-mobile#815](https://github.com/stellar/freighter-mobile/issues/815)).

## Why Freighter

**Built and maintained by SDF.** Freighter is developed by the Stellar Development Foundation, the same team behind the core Stellar protocol and SDKs.

**Complete Stellar support.** Transaction signing, Soroban authorization entry signing with contract invocation inspection, SEP-53 message signing, contract token management, and Blockaid transaction scanning.

**Desktop and mobile.** Browser extension for desktop, WalletConnect for mobile. Both paths produce the same signed output, so your dapp supports both with minimal branching.

## Get Started

**Desktop** — integrate with the Freighter browser extension using `@stellar/freighter-api`. See the [Extension Guide](/extension-freighter-api/installation).

**Mobile** — connect to Freighter Mobile over WalletConnect v2. See the [Mobile Guide](/mobile-walletconnect/mobile).

**Both** — if your dapp needs to support desktop and mobile users, use both integration paths. Both produce the same output (signed XDR), so your submission logic stays the same regardless of how the user connected.

## Quick links

| I want to...         | Extension                                                     | Mobile                                             |
| -------------------- | ------------------------------------------------------------- | -------------------------------------------------- |
| Install / set up     | [Installation](/extension-freighter-api/installation)         | [Installation](/mobile-walletconnect/installation) |
| Connect to Freighter | [Connecting](/extension-freighter-api/connecting)             | [Connecting](/mobile-walletconnect/connecting)     |
| Sign a transaction   | [Signing](/extension-freighter-api/signing)                   | [Signing](/mobile-walletconnect/signing)           |
| Add a token          | [Token Management](/extension-freighter-api/token-management) | —                                                  |
| Try it out           | [Playground ↗](https://play.freighter.app)                    | [Playground ↗](https://play.freighter.app)         |

## Resources

* [GitHub — Freighter Extension](https://github.com/stellar/freighter)
* [GitHub — Freighter Mobile](https://github.com/stellar/freighter-mobile)
* [Freighter on Chrome Web Store](https://chromewebstore.google.com/detail/freighter/bcacfldlkkdogcmkkibnjlakofdplcbk)
* [Freighter on Firefox Add-ons](https://addons.mozilla.org/en-US/firefox/addon/freighter/)
* [Stellar Developer Docs](https://developers.stellar.org)
* [WalletConnect v2 Docs](https://docs.walletconnect.network/app-sdk/overview)


# Installation

Get up and running with Freighter by installing the browser extension and choosing the right integration method for your app.

## Prerequisites

1. Install the Freighter browser extension from the [Chrome Web Store](https://chromewebstore.google.com/detail/freighter/bcacfldlkkdogcmkkibnjlakofdplcbk) or [Firefox Add-ons](https://addons.mozilla.org/en-US/firefox/addon/freighter/).
2. Choose an integration method below based on your project setup.

## Choose Your Integration

### npm / yarn

Use this when building with a bundler (Webpack, Vite, etc.) in a React, Next.js, or other modern JS framework.

**Ideal for:**

* React, Vue, Svelte, or Angular applications
* Server-side rendered apps (Next.js, Nuxt)
* Any project using a JavaScript bundler

```bash
npm install @stellar/freighter-api
```

```bash
yarn add @stellar/freighter-api
```

Then import in your code. You can import the entire library:

```javascript
import freighterApi from "@stellar/freighter-api";
```

Or import only what you need:

```javascript
import {
  isConnected,
  getAddress,
  signAuthEntry,
  signTransaction,
  signMessage,
  addToken,
} from "@stellar/freighter-api";
```

> See the [Extension Integration](/extension-freighter-api/extension) for the full API reference.

### CDN (script tag)

Use this for plain HTML pages or projects without a build step. The library is loaded directly in the browser.

**Ideal for:**

* Static HTML sites
* Prototypes and quick experiments
* Projects without a bundler

Add to your `<head>`:

```html
<script src="https://unpkg.com/@stellar/freighter-api/build/index.min.js"></script>
```

This always loads the latest version automatically. To pin a specific version:

```html
<script src="https://unpkg.com/@stellar/freighter-api@6.0.1/build/index.min.js"></script>
```

Then access the API via `window.freighterApi`:

```javascript
const { address } = await window.freighterApi.requestAccess();
```

## Next steps

| I want to...                | Go to                                                         |
| --------------------------- | ------------------------------------------------------------- |
| Connect my app to Freighter | [Connecting](/extension-freighter-api/connecting)             |
| Sign a transaction          | [Signing](/extension-freighter-api/signing)                   |
| Add a token                 | [Token Management](/extension-freighter-api/token-management) |


# Overview

Integrate Freighter into your web application using `@stellar/freighter-api`. This library lets you send and receive data from a user's Freighter extension.

`@stellar/freighter-api` adheres to the [SEP-43](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0043.md) standard for wallet interfaces on Stellar, and also offers additional methods beyond the spec such as `getNetworkDetails`, `addToken`, and `WatchWalletChanges`.

## Error Type

All methods return an optional `error` field of type `FreighterApiError`:

```typescript
interface FreighterApiError {
  code: number;
  message: string;
  ext?: string[];
}
```

## API Reference

| Category                                                      | Description                                         |
| ------------------------------------------------------------- | --------------------------------------------------- |
| [Connecting](/extension-freighter-api/connecting)             | Detect Freighter, check permissions, request access |
| [Reading Data](/extension-freighter-api/reading-data)         | Get the user's address and network configuration    |
| [Signing](/extension-freighter-api/signing)                   | Sign transactions, auth entries, and messages       |
| [Token Management](/extension-freighter-api/token-management) | Add contract tokens to the user's wallet            |
| [Watching Changes](/extension-freighter-api/watching-changes) | Monitor wallet state changes in real time           |


# Connecting

Detect whether a user has Freighter installed, check if your app is authorized, and request access to the user's public key.

## Detecting Freighter

### `isConnected()`

Check if the user has Freighter installed in their browser.

**Returns:** `Promise<{ isConnected: boolean } & { error?: FreighterApiError }>`

```typescript
import { isConnected } from "@stellar/freighter-api";

const result = await isConnected();

if (result.isConnected) {
  console.log("Freighter is installed");
}
```

{% hint style="info" %}
`isConnected()` checks whether the Freighter extension is installed. Note that it returns `false` for any user without the extension — not just mobile users. To detect Freighter Mobile's in-app browser specifically, check for `window.stellar?.platform === "mobile"`.
{% endhint %}

## Checking Authorization

### `isAllowed()`

Check if the user has previously authorized your app to receive data from Freighter.

**Returns:** `Promise<{ isAllowed: boolean } & { error?: FreighterApiError }>`

```typescript
import { isAllowed } from "@stellar/freighter-api";

const result = await isAllowed();

if (result.isAllowed) {
  console.log("App is on the Allow List");
}
```

## Requesting Authorization

### `setAllowed()`

Prompt the user to authorize your app and add it to Freighter's **Allow List**. Once approved, the extension can provide user data without additional prompts.

**Returns:** `Promise<{ isAllowed: boolean } & { error?: FreighterApiError }>`

```typescript
import { setAllowed } from "@stellar/freighter-api";

const result = await setAllowed();

if (result.isAllowed) {
  console.log("App added to Allow List");
}
```

{% hint style="info" %}
If the user has already authorized your app, `setAllowed()` resolves immediately with `{ isAllowed: true }`.
{% endhint %}

## Requesting Access

### `requestAccess()`

Prompt the user to add your dapp to Freighter's Allow List and return their public key in one call. This is the recommended way to connect — it combines authorization and data retrieval in a single step.

**Returns:** `Promise<{ address: string } & { error?: FreighterApiError }>`

If the user has previously authorized your app, the public key is returned immediately without a popup.

```typescript
import { isConnected, requestAccess } from "@stellar/freighter-api";

// Always check isConnected first
const connectResult = await isConnected();
if (!connectResult.isConnected) {
  console.error("Freighter is not installed");
  return;
}

const accessResult = await requestAccess();
if (accessResult.error) {
  console.error("Access denied:", accessResult.error.message);
} else {
  console.log("Public key:", accessResult.address);
}
```

{% hint style="warning" %}
Always check `isConnected()` before calling `requestAccess()` to ensure the extension is available.
{% endhint %}

## Next steps

Once connected, you can [read the user's address and network](/extension-freighter-api/reading-data) or [sign transactions](/extension-freighter-api/signing).


# Reading Data

Retrieve the user's public key and network configuration from Freighter.

{% hint style="info" %}
These methods require that the user has previously authorized your app via [`requestAccess()`](/extension-freighter-api/connecting#requesting-access) or [`setAllowed()`](/extension-freighter-api/connecting#requesting-authorization).
{% endhint %}

## Getting the User's Address

### `getAddress()`

A lightweight way to retrieve the user's public key. Unlike `requestAccess()`, this will **not** prompt the user — it returns an empty string if the app isn't authorized or Freighter isn't connected.

**Returns:** `Promise<{ address: string } & { error?: FreighterApiError }>`

```typescript
import { getAddress } from "@stellar/freighter-api";

const { address, error } = await getAddress();

if (error) {
  console.error(error.message);
} else {
  console.log("Public key:", address);
}
```

## Getting the Network

### `getNetwork()`

Get the name and passphrase of the network the user has selected in Freighter.

**Returns:** `Promise<{ network: string; networkPassphrase: string } & { error?: FreighterApiError }>`

Possible `network` values: `PUBLIC`, `TESTNET`, `FUTURENET`, or `STANDALONE` (custom networks).

```typescript
import { getNetwork } from "@stellar/freighter-api";

const { network, networkPassphrase, error } = await getNetwork();

if (!error) {
  console.log("Network:", network);           // e.g., "TESTNET"
  console.log("Passphrase:", networkPassphrase);
}
```

### `getNetworkDetails()`

Get comprehensive network configuration including the Horizon URL, passphrase, and Soroban RPC URL.

**Returns:** `Promise<{ network: string; networkUrl: string; networkPassphrase: string; sorobanRpcUrl?: string } & { error?: FreighterApiError }>`

```typescript
import { getNetworkDetails } from "@stellar/freighter-api";

const details = await getNetworkDetails();

if (!details.error) {
  console.log("Network:", details.network);            // e.g., "TESTNET"
  console.log("Horizon:", details.networkUrl);          // e.g., "https://horizon-testnet.stellar.org"
  console.log("Passphrase:", details.networkPassphrase);
  console.log("Soroban RPC:", details.sorobanRpcUrl);   // e.g., "https://soroban-testnet.stellar.org"
}
```

{% hint style="info" %}
Use `getNetworkDetails()` instead of `getNetwork()` when working with Soroban smart contracts or when you need specific network endpoints.
{% endhint %}


# Signing

Sign transactions, authorization entries, and arbitrary messages using the user's Freighter wallet.

## Signing a Transaction

### `signTransaction()`

Pass a transaction XDR string to Freighter for the user to review and sign.

```
signTransaction(xdr: string, opts?: {
  network?: string,
  networkPassphrase?: string,
  address?: string
}) -> Promise<{ signedTxXdr: string; signerAddress: string } & { error?: FreighterApiError }>
```

The user will review the transaction details before signing.

**Parameters**

| Parameter                | Type     | Description                                                                                 |
| ------------------------ | -------- | ------------------------------------------------------------------------------------------- |
| `xdr`                    | `string` | **Required.** Base64-encoded transaction XDR.                                               |
| `opts.network`           | `string` | Network name (maps to `Networks` enum in `@stellar/stellar-sdk`).                           |
| `opts.networkPassphrase` | `string` | Custom passphrase. Ignored if `network` is also provided.                                   |
| `opts.address`           | `string` | Request a specific account's signature. Freighter will switch to that account if available. |

{% hint style="info" %}
Passing `network` or `networkPassphrase` lets Freighter warn the user if their wallet is configured to the wrong network.
{% endhint %}

**Example**

```typescript
import { signTransaction } from "@stellar/freighter-api";

const { signedTxXdr, signerAddress, error } = await signTransaction(xdr, {
  network: "TESTNET",
  address: "G...", // optional: request a specific account
});

if (error) {
  console.error("Signing failed:", error.message);
} else {
  console.log("Signed by:", signerAddress);
  console.log("Signed XDR:", signedTxXdr);
}
```

**Errors**

| Condition               | Error message                                |
| ----------------------- | -------------------------------------------- |
| User rejected           | `"The user rejected this request."`          |
| Extension not installed | `"The wallet encountered an internal error"` |

## Signing an Auth Entry

### `signAuthEntry()`

Sign an [authorization entry preimage](https://github.com/stellar/js-stellar-base/blob/a9567e5843760bfb6a8b786592046aee4c9d38b2/types/next.d.ts#L6895) and receive the signed hash back as a base64 string. Used for [Soroban contract authorization](https://developers.stellar.org/docs/smart-contracts/guides/auth/authorization) flows.

```
signAuthEntry(entryXdr: string, opts: {
  address: string
}) -> Promise<{ signedAuthEntry: string | null; signerAddress: string } & { error?: FreighterApiError }>
```

See the [`authorizeEntry` helper](https://github.com/stellar/js-stellar-base/blob/e3d6fc3351e7d242b374c7c6057668366364a279/src/auth.js#L97) in `js-stellar-base` for how signed auth entries are used, or the [Soroban development documentation](https://developers.stellar.org/docs/smart-contracts) for wallet-side patterns.

**Example**

```typescript
import { signAuthEntry } from "@stellar/freighter-api";

const { signedAuthEntry, signerAddress, error } = await signAuthEntry(entryXdr, {
  address: "G...",
});

if (error) {
  console.error("Auth entry signing failed:", error.message);
} else {
  console.log("Signed auth entry:", signedAuthEntry);
}
```

**Errors**

| Condition     | Error message                       |
| ------------- | ----------------------------------- |
| User rejected | `"The user rejected this request."` |

## Signing a Message

### `signMessage()`

Sign an arbitrary string and receive a base64-encoded Ed25519 signature. Follows [SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md).

```
signMessage(message: string, opts: {
  address: string
}) -> Promise<{ signedMessage: string | null; signerAddress: string } & { error?: FreighterApiError }>
```

**Example**

```typescript
import { signMessage } from "@stellar/freighter-api";

const { signedMessage, signerAddress, error } = await signMessage(
  "Verify account ownership",
  { address: "G..." },
);

if (error) {
  console.error("Message signing failed:", error.message);
} else {
  console.log("Signature:", signedMessage);
}
```

**Errors**

| Condition     | Error message                       |
| ------------- | ----------------------------------- |
| User rejected | `"The user rejected this request."` |

## Full Example: Connect, Sign, and Submit

A complete flow from connection to Horizon submission:

```typescript
import { isConnected, requestAccess, signTransaction } from "@stellar/freighter-api";
import { Server, TransactionBuilder } from "@stellar/stellar-sdk";

// 1. Check Freighter is installed
const connectResult = await isConnected();
if (!connectResult.isConnected) {
  throw new Error("Freighter not found");
}

// 2. Request the user's public key
const accessResult = await requestAccess();
if (accessResult.error) {
  throw new Error(accessResult.error.message);
}

// 3. Sign the transaction
const xdr = "AAAAAgAAAAA..."; // your assembled transaction XDR
const signResult = await signTransaction(xdr, {
  network: "TESTNET",
  address: accessResult.address,
});
if (signResult.error) {
  throw new Error(signResult.error.message);
}

// 4. Submit to Horizon
const server = new Server("https://horizon-testnet.stellar.org");
const tx = TransactionBuilder.fromXDR(
  signResult.signedTxXdr,
  "Test SDF Network ; September 2015",
);
const response = await server.submitTransaction(tx);
console.log("Transaction submitted:", response.hash);
```


# Token Management

Unlike Stellar assets, contract tokens a user owns are not automatically displayed in Freighter — they must be explicitly added. This method lets your dapp prompt users to add a contract token directly, without the user having to find and add it in Freighter manually.

{% hint style="info" %}
In a future release, Freighter will auto-detect token transfers and display them automatically. Once that ships, `addToken()` will become a no-op.
{% endhint %}

## Adding a Token

### `addToken()`

Trigger an "add token" flow that displays token details in a modal for the user to review and approve.

```
addToken({ contractId: string, networkPassphrase?: string })
  -> Promise<{ contractId: string } & { error?: FreighterApiError }>
```

When called, Freighter loads the token's **symbol**, **name**, **decimals**, and **balance** from the contract and displays them for the user to verify before adding.

### Parameters

| Parameter           | Type     | Description                                           |
| ------------------- | -------- | ----------------------------------------------------- |
| `contractId`        | `string` | **Required.** The contract token ID (`C...` address). |
| `networkPassphrase` | `string` | Defaults to Mainnet passphrase if omitted.            |

### Example

```typescript
import { isConnected, addToken } from "@stellar/freighter-api";

const { isConnected: connected } = await isConnected();
if (!connected) return;

const result = await addToken({
  contractId: "CC...ABCD",
  networkPassphrase: "Test SDF Network ; September 2015", // optional
});

if (result.error) {
  console.error(result.error.message);
} else {
  console.log(`Token added: ${result.contractId}`);
}
```

{% hint style="info" %}
After the user approves, Freighter will automatically track the token's balance and display it alongside other account balances.
{% endhint %}


# Watching Changes

Monitor the user's Freighter wallet for address and network changes in real time.

## WatchWalletChanges

### `new WatchWalletChanges(timeout?: number)`

Creates a watcher that polls Freighter for changes at a configurable interval.

| Parameter | Type     | Default | Description                       |
| --------- | -------- | ------- | --------------------------------- |
| `timeout` | `number` | `3000`  | Polling interval in milliseconds. |

### `watch(callback)`

Start polling. The callback fires only when something has changed (address, network, or passphrase).

```
watch(callback: ({ address: string; network: string; networkPassphrase: string }) => void)
```

### `stop()`

Stop polling for changes.

### Example

```typescript
import { WatchWalletChanges } from "@stellar/freighter-api";

const watcher = new WatchWalletChanges(1000); // poll every second

watcher.watch((changes) => {
  console.log("Address:", changes.address);
  console.log("Network:", changes.network);
  console.log("Passphrase:", changes.networkPassphrase);
});

// Stop after 30 seconds
setTimeout(() => watcher.stop(), 30000);
```

{% hint style="info" %}
The callback only fires when a value actually changes — it won't emit duplicate events.
{% endhint %}


# Installation

Get up and running with WalletConnect so your dapp can connect to Freighter Mobile.

## Prerequisites

1. Sign up at [WalletConnect Dashboard](https://dashboard.walletconnect.com/) and create a project to get your **Project ID**.
2. Choose an installation method below.

## npm / yarn

```bash
npm install @walletconnect/universal-provider @reown/appkit
```

```bash
yarn add @walletconnect/universal-provider @reown/appkit
```

Then initialize the provider and modal:

```typescript
import { UniversalProvider } from "@walletconnect/universal-provider";
import { createAppKit } from "@reown/appkit/core";
import { mainnet } from "@reown/appkit/networks";

const projectId = "YOUR_PROJECT_ID";

// Provider — handles the WalletConnect protocol
const provider = await UniversalProvider.init({
  projectId,
  metadata: {
    name: "My Stellar Dapp",
    description: "A dapp that integrates with Freighter Mobile",
    url: "https://my-dapp.com",
    icons: ["https://my-dapp.com/icon.png"],
  },
});

// Modal — displays the QR code and wallet list.
// AppKit requires at least one network. Stellar is not built-in,
// so we pass a placeholder. With manualWCControl the modal won't
// use it for chain switching.
const modal = createAppKit({
  projectId,
  networks: [mainnet],
  universalProvider: provider,
  manualWCControl: true,
});
```

## Next steps

| I want to...                           | Go to                                                            |
| -------------------------------------- | ---------------------------------------------------------------- |
| Connect to Freighter Mobile            | [Connecting](/mobile-walletconnect/connecting)                   |
| Sign a transaction                     | [Signing](/mobile-walletconnect/signing)                         |
| Feature Freighter upfront in the modal | [Featuring Freighter](/mobile-walletconnect/featuring-freighter) |

## Alternatives

[Stellar Wallets Kit](https://stellarwalletskit.dev/) provides a multi-wallet interface that includes WalletConnect support, but currently only supports `stellar_signXDR` and `stellar_signAndSubmitXDR`. To use all four Freighter Mobile methods — including `stellar_signMessage` and `stellar_signAuthEntry` — implement the WalletConnect integration directly as shown in this guide.

{% hint style="info" %}
Older examples may use `@walletconnect/sign-client` directly. That approach still works, but `@walletconnect/universal-provider` is the actively documented package. For more details, see the [UniversalProvider docs](https://docs.reown.com/advanced/providers/universal) and the [AppKit modal migration guide](https://docs.reown.com/appkit/upgrade/wcm).
{% endhint %}


# Overview

Connect your dapp to Freighter Mobile using the [WalletConnect v2](https://docs.walletconnect.network/app-sdk/overview) protocol. All methods follow the standard JSON-RPC 2.0 request/response format over a WalletConnect session.

## Supported Chains

| Network          | Chain ID          |
| ---------------- | ----------------- |
| Mainnet (PUBLIC) | `stellar:pubnet`  |
| Testnet          | `stellar:testnet` |

## API Reference

| Method                                                                               | Description                                                                                                                                         |
| ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`stellar_signXDR`](/mobile-walletconnect/signing#stellar_signxdr)                   | Sign a transaction and return the signed XDR                                                                                                        |
| [`stellar_signAndSubmitXDR`](/mobile-walletconnect/signing#stellar_signandsubmitxdr) | Sign and submit a transaction to Horizon                                                                                                            |
| [`stellar_signMessage`](/mobile-walletconnect/signing#stellar_signmessage)           | Sign an arbitrary UTF-8 message ([SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md))                           |
| [`stellar_signAuthEntry`](/mobile-walletconnect/signing#stellar_signauthentry)       | Sign a Soroban authorization entry preimage (as defined in [SEP-43](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0043.md)) |

## Error Handling

All error responses use JSON-RPC 2.0 format with code `5000`:

```json
{
  "id": 1234567890,
  "jsonrpc": "2.0",
  "error": {
    "code": 5000,
    "message": "User rejected the request"
  }
}
```

{% hint style="warning" %}
Freighter Mobile validates that the `chainId` in the request matches the wallet's active network. If the user is on the wrong network, the request is rejected. Always specify `chainId` as a separate argument to `provider.request()`, not inside `params`.
{% endhint %}

## Security

Freighter Mobile uses **Blockaid** scanning to protect users:

| Scan type            | When it runs                                                  |
| -------------------- | ------------------------------------------------------------- |
| **Site scan**        | Once during WalletConnect session connection                  |
| **Transaction scan** | For `stellar_signXDR` and `stellar_signAndSubmitXDR` requests |

`stellar_signMessage` and `stellar_signAuthEntry` do not trigger additional scans — the site was already scanned during connection.

## See also

* [WalletConnect v2 Docs](https://docs.walletconnect.network/app-sdk/overview)
* [Stellar Smart Contracts Auth](https://developers.stellar.org/docs/smart-contracts/guides/auth/authorization)
* [SEP-53: Message Signing](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md)
* [SEP-43: Auth Entry Signing](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0043.md)


# Connecting

Establish a WalletConnect session with Freighter Mobile, handle session lifecycle events, and disconnect.

Assumes `provider` and `modal` are initialized as shown in [Installation](/mobile-walletconnect/installation).

## Creating a Session

Call `modal.open()` to show the wallet selection modal, then `provider.connect()` to start the pairing. The modal displays a QR code and a list of wallets — when the user selects Freighter or scans the QR code, the connection is established automatically. `connect()` resolves once the wallet approves.

```typescript
// Open the modal (shows a spinner, then the QR code and wallet list)
modal.open();

// Connect — resolves when the wallet approves
const session = await provider.connect({
  namespaces: {
    stellar: {
      methods: [
        "stellar_signXDR",
        "stellar_signAndSubmitXDR",
        "stellar_signMessage",
        "stellar_signAuthEntry",
      ],
      chains: ["stellar:pubnet"],
      events: ["accountsChanged"],
    },
  },
});

if (!session) {
  throw new Error("Connection failed");
}

modal.close();
console.log("Connected! Session topic:", session.topic);
```

The connected session is also accessible at any time via `provider.session`.

Accounts are stored in `session.namespaces.stellar.accounts` as `"stellar:pubnet:G..."` strings — split on `:` to extract the public key:

```typescript
const publicKey = session.namespaces.stellar.accounts[0].split(":")[2];
```

{% hint style="info" %}
Use `namespaces` for methods your dapp needs to function. Use `optionalNamespaces` for methods that enhance the experience but aren't essential.

**Note:** `UniversalProvider` treats `namespaces` as optional at the protocol level on the first connection. After the wallet approves, the approved namespaces are persisted and become required on subsequent connections. To be safe, always verify the approved methods after connecting:

```typescript
const methods = session.namespaces.stellar?.methods || [];
if (!methods.includes("stellar_signXDR")) {
  throw new Error("Wallet does not support required methods");
}
```

{% endhint %}

## Handling Events

Listen for session lifecycle events to keep your UI in sync:

```typescript
// Session was disconnected by the wallet
provider.on("session_delete", ({ topic }) => {
  console.log("Session deleted:", topic);
});

// Session expired
provider.on("session_expire", ({ topic }) => {
  console.log("Session expired:", topic);
});
```

## Disconnecting

When the user wants to disconnect:

```typescript
await provider.disconnect();
```

## Next steps

Once connected, you can [sign transactions, messages, and auth entries](/mobile-walletconnect/signing).


# Signing

Sign transactions, messages, and Soroban authorization entries via WalletConnect.

Assumes `provider` and `modal` are initialized as shown in [Installation](/mobile-walletconnect/installation).

{% hint style="info" %}
The WalletConnect RPC methods use different response field names than the extension API. For example, `stellar_signXDR` returns `signedXDR` while the extension's `signTransaction()` returns `signedTxXdr`. See the response tables below for each method's exact fields.
{% endhint %}

## Signing a Transaction

### `stellar_signXDR`

Sign a transaction and return the signed XDR. The transaction is **not** submitted to the network — your dapp is responsible for submission.

**Parameters**

| Parameter | Type     | Description                                                                             |
| --------- | -------- | --------------------------------------------------------------------------------------- |
| `xdr`     | `string` | **Required.** Base64-encoded `TransactionEnvelope` or `FeeBumpTransactionEnvelope` XDR. |

**Response**

| Field       | Type     | Description                            |
| ----------- | -------- | -------------------------------------- |
| `signedXDR` | `string` | Base64-encoded signed transaction XDR. |

```typescript
const result = await provider.request(
  {
    method: "stellar_signXDR",
    params: { xdr: "AAAAAgAAAAA..." },
  },
  "stellar:pubnet",
);

console.log("Signed XDR:", result.signedXDR);
```

**Errors**

| Condition            | Error message                                |
| -------------------- | -------------------------------------------- |
| Chain not supported  | `"Unsupported chain: <chainId>"`             |
| Wrong active network | `"Please switch to <network> and try again"` |
| XDR parse failure    | `"Failed to sign transaction"`               |
| User rejected        | `"User rejected the request"`                |

***

### `stellar_signAndSubmitXDR`

Sign a transaction **and** submit it to Horizon in one step.

**Parameters**

| Parameter | Type     | Description                                                                             |
| --------- | -------- | --------------------------------------------------------------------------------------- |
| `xdr`     | `string` | **Required.** Base64-encoded `TransactionEnvelope` or `FeeBumpTransactionEnvelope` XDR. |

**Response**

| Field    | Type     | Description                                               |
| -------- | -------- | --------------------------------------------------------- |
| `status` | `string` | `"success"` when the transaction is signed and submitted. |

```typescript
const result = await provider.request(
  {
    method: "stellar_signAndSubmitXDR",
    params: { xdr: "AAAAAgAAAAA..." },
  },
  "stellar:pubnet",
);

console.log("Status:", result.status); // "success"
```

**Errors**

| Condition                  | Error message                                |
| -------------------------- | -------------------------------------------- |
| Chain not supported        | `"Unsupported chain: <chainId>"`             |
| Wrong active network       | `"Please switch to <network> and try again"` |
| XDR parse / sign failure   | `"Failed to sign transaction"`               |
| Horizon submission failure | `"Failed to submit transaction"`             |
| User rejected              | `"User rejected the request"`                |

{% hint style="info" %}
Use **`stellar_signXDR`** when you need to inspect or modify the signed transaction before submission. Use **`stellar_signAndSubmitXDR`** for a simpler flow where Freighter handles everything.
{% endhint %}

***

## Signing a Message

### `stellar_signMessage`

Sign an arbitrary UTF-8 text message with the wallet's active key, following [SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md).

**Parameters**

| Parameter | Type     | Constraints                     | Description                    |
| --------- | -------- | ------------------------------- | ------------------------------ |
| `message` | `string` | Non-empty, max 1024 UTF-8 bytes | The plaintext message to sign. |

**Response**

| Field       | Type     | Description                                    |
| ----------- | -------- | ---------------------------------------------- |
| `signature` | `string` | Base64-encoded Ed25519 signature (per SEP-53). |

```typescript
const result = await provider.request(
  {
    method: "stellar_signMessage",
    params: { message: "Please sign to verify account ownership" },
  },
  "stellar:pubnet",
);

console.log("Signature:", result.signature);
```

**Errors**

| Condition                     | Error message                  |
| ----------------------------- | ------------------------------ |
| Missing or non-string message | `"Invalid message"`            |
| Empty message                 | `"Cannot sign empty message"`  |
| Message exceeds 1 KB          | `"Message too long (max 1KB)"` |
| Signing failed                | `"Failed to sign message"`     |
| User rejected                 | `"User rejected the request"`  |

***

## Signing an Auth Entry

### `stellar_signAuthEntry`

Sign a Soroban authorization entry preimage for multi-auth and custom-account smart contract workflows. Follows [SEP-43](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0043.md).

Your dapp constructs the `HashIdPreimage` (network ID + nonce + expiry + invocation), and Freighter hashes it with SHA-256 and signs the digest.

{% hint style="warning" %}
Freighter Mobile performs a **Blockaid site scan** during the initial WalletConnect session connection — no additional scan runs at auth-entry signing time. Review the contract address, function name, and subinvocations displayed in the UI before confirming.
{% endhint %}

**Parameters**

| Parameter  | Type     | Constraints               | Description                                                                                                                                                         |
| ---------- | -------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `entryXdr` | `string` | Non-empty, non-whitespace | Base64-encoded `HashIdPreimage` XDR (envelope type `envelopeTypeSorobanAuthorization`). Build from the `SorobanAuthorizationEntry` returned by contract simulation. |

**Response**

| Field             | Description                                                                |
| ----------------- | -------------------------------------------------------------------------- |
| `signedAuthEntry` | Base64-encoded 64-byte Ed25519 signature over `SHA-256(preimage)`.         |
| `signerAddress`   | Stellar public key (G-address) of the account that produced the signature. |

```typescript
const result = await provider.request(
  {
    method: "stellar_signAuthEntry",
    params: { entryXdr: "AAAAAQ..." },
  },
  "stellar:pubnet",
);

console.log("Signature:", result.signedAuthEntry);
console.log("Signer:", result.signerAddress);
```

**Errors**

| Condition                                          | Error message                                      |
| -------------------------------------------------- | -------------------------------------------------- |
| Missing, non-string, or whitespace-only `entryXdr` | `"Invalid authorization entry"`                    |
| XDR parse failure                                  | `"Failed to process auth entry"`                   |
| `networkId` doesn't match active network           | `"Authorization entry is for a different network"` |
| User rejected                                      | `"User rejected the request"`                      |

## Full Example: Connect, Sign, and Submit

A complete flow from connection to transaction signing. The AppKit modal displays both a QR code and a list of wallets. When a user opens your dapp in Freighter Mobile's in-app browser, they select Freighter from the wallet list to deep-link into the connection approval — no QR code scanning needed. From an external browser, the user scans the QR code with their phone instead.

```typescript
import { UniversalProvider } from "@walletconnect/universal-provider";
import { createAppKit } from "@reown/appkit/core";
import { mainnet } from "@reown/appkit/networks";

const projectId = "YOUR_PROJECT_ID";

// 1. Initialize provider and modal
const provider = await UniversalProvider.init({
  projectId,
  metadata: {
    name: "My Stellar Dapp",
    description: "A dapp that integrates with Freighter Mobile",
    url: "https://my-dapp.com",
    icons: ["https://my-dapp.com/icon.png"],
  },
});

// AppKit requires at least one network. Stellar is not built-in,
// so we pass a placeholder. With manualWCControl the modal won't
// use it for chain switching.
const modal = createAppKit({
  projectId,
  networks: [mainnet],
  universalProvider: provider,
  manualWCControl: true,
});

// 2. Open the modal and connect
modal.open();

const session = await provider.connect({
  namespaces: {
    stellar: {
      methods: ["stellar_signXDR", "stellar_signAndSubmitXDR", "stellar_signMessage", "stellar_signAuthEntry"],
      chains: ["stellar:pubnet"],
      events: ["accountsChanged"],
    },
  },
});

if (!session) {
  throw new Error("Connection failed");
}

modal.close();

// 3. Sign a transaction
const xdr = "AAAAAgAAAAA..."; // your assembled transaction XDR
const result = await provider.request(
  {
    method: "stellar_signXDR",
    params: { xdr },
  },
  "stellar:pubnet",
);

console.log("Signed XDR:", result.signedXDR);
```


# Featuring Freighter

When a user opens the WalletConnect / Reown modal in your dapp, wallets in the **featured row** are shown first — most users pick from there without scrolling or using the search bar. If your dapp is built for Stellar, featuring Freighter makes it discoverable upfront and improves conversion for Stellar users.

## Freighter's WalletConnect Explorer ID

```
997a355c8f682468706a76cff1b004a7115f505fb962dac54b6e9b442dd1c380
```

This is the **public** identifier of Freighter's listing in the [WalletGuide Explorer](https://walletguide.walletconnect.network/) (a.k.a. Reown Explorer). It is not the same as your dapp's WalletConnect **Project ID** — Project IDs identify the dapp/wallet account on the WalletConnect relay, while Explorer IDs identify a public wallet listing in the directory.

The Explorer ID can change if Freighter's listing is rotated (e.g. after a security key rotation on our end). If Freighter ever stops appearing in the featured row, look up the current ID using one of the following:

* **UI:** Open [WalletGuide](https://walletguide.walletconnect.network/) → search "Freighter" → click the card → copy the `Wallet ID` field from the detail view.
* **API:** `curl "https://explorer-api.walletconnect.com/v3/wallets?projectId=<your-wc-project-id>&search=freighter"` and read `id` from the Freighter entry in the response. Use your own WalletConnect **Project ID** here — the one you got from the [WalletConnect Dashboard](https://dashboard.walletconnect.com/) during [Installation](/mobile-walletconnect/installation). The `projectId` param is just used for rate-limiting and must be a valid registered project ID, but it does not filter results — every valid project ID returns the same global wallet directory, so Freighter will appear in the response regardless of which valid ID you use.

## Option A — Direct WalletConnect / Reown integration

If you set up the modal directly with `@reown/appkit` (as in [Installation](/mobile-walletconnect/installation)), pass `featuredWalletIds` to `createAppKit`:

```typescript
import { createAppKit } from "@reown/appkit/core";
import { mainnet } from "@reown/appkit/networks";

const modal = createAppKit({
  projectId,
  networks: [mainnet],
  universalProvider: provider,
  manualWCControl: true,
  featuredWalletIds: [
    "997a355c8f682468706a76cff1b004a7115f505fb962dac54b6e9b442dd1c380", // Freighter
  ],
});
```

You can include other wallet IDs in the same array — they appear in the featured row in the order you list them.

## Option B — Stellar Wallets Kit

[Stellar Wallets Kit](https://stellarwalletskit.dev/) (SWK) includes Freighter in `featuredWalletIds` by default for its `WalletConnectModule`, so on recent versions of the kit you get featured placement automatically — no extra config needed.

If you're on an older kit version (or the kit's default has gone stale), you can override the value via `appKitOptions.featuredWalletIds`:

```typescript
import { WalletConnectModule } from "@creit-tech/stellar-wallets-kit/modules/wallet-connect";

const wc = new WalletConnectModule({
  // ...your usual config (projectId, allowedChains, ...)
  appKitOptions: {
    featuredWalletIds: [
      "997a355c8f682468706a76cff1b004a7115f505fb962dac54b6e9b442dd1c380", // Freighter
    ],
  } as unknown as ConstructorParameters<typeof WalletConnectModule>[0]["appKitOptions"],
});
```

The cast is needed because `appKitOptions` is typed as the full `CreateAppKit` shape, but the kit only forwards a subset of those options to the underlying modal — so passing just `featuredWalletIds` is correct at runtime but doesn't satisfy the static type. Prefer the narrow `as unknown as ConstructorParameters<...>` cast over `as any`: the runtime behavior is identical, but the assertion stays scoped to this property value rather than erasing type-checking everywhere `appKitOptions` is consumed.


# Third-Party Integrations

Freighter connects to third-party services to provide a better user experience. As an open source project, we document these integrations so other wallet developers can adopt similar patterns.

## Multi-Wallet Support

If your dapp needs to support multiple Stellar wallets beyond Freighter, [Stellar Wallets Kit](https://stellarwalletskit.dev/) provides a unified interface for connecting to any Stellar wallet. It handles wallet detection, connection, and signing through a single API, so your users can choose whichever wallet they prefer.

## Available Integrations

| Integration                                                                                                   | Description                                      |
| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| [Stellar Wallets Kit](https://stellarwalletskit.dev/)                                                         | Unified multi-wallet interface for Stellar dapps |
| [Soroswap](https://github.com/stellar/freighter/blob/master/extension/INTEGRATING_SOROSWAP.MD)                | DEX integration for token swaps                  |
| [Hardware Wallets](https://github.com/stellar/freighter/blob/master/extension/INTEGRATING_HARDWARE_WALLET.MD) | Ledger and other hardware wallet support         |


