From 6af05ebc33eee2f891e19b970774036487351231 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Thu, 9 Apr 2026 18:25:42 -0400 Subject: [PATCH 01/27] Add Freighter developer documentation Migrate docs from freighter-integration-docs, removing unused GitBook assets. Includes developer guide, mobile integration, and API reference. Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitbook.yaml | 5 + README.md | 48 +++++- SUMMARY.md | 24 +++ developer-guide/README.md | 36 +++++ developer-guide/connecting.md | 97 +++++++++++ developer-guide/installation.md | 72 +++++++++ developer-guide/reading-data.md | 71 ++++++++ developer-guide/signing.md | 153 ++++++++++++++++++ developer-guide/token-management.md | 47 ++++++ developer-guide/watching-changes.md | 46 ++++++ index.html | 24 +++ integrations/README.md | 10 ++ mobile/README.md | 64 ++++++++ mobile/connecting.md | 69 ++++++++ mobile/installation.md | 41 +++++ mobile/signing.md | 243 ++++++++++++++++++++++++++++ 16 files changed, 1048 insertions(+), 2 deletions(-) create mode 100644 .gitbook.yaml create mode 100644 SUMMARY.md create mode 100644 developer-guide/README.md create mode 100644 developer-guide/connecting.md create mode 100644 developer-guide/installation.md create mode 100644 developer-guide/reading-data.md create mode 100644 developer-guide/signing.md create mode 100644 developer-guide/token-management.md create mode 100644 developer-guide/watching-changes.md create mode 100644 index.html create mode 100644 integrations/README.md create mode 100644 mobile/README.md create mode 100644 mobile/connecting.md create mode 100644 mobile/installation.md create mode 100644 mobile/signing.md diff --git a/.gitbook.yaml b/.gitbook.yaml new file mode 100644 index 0000000..6acdd6a --- /dev/null +++ b/.gitbook.yaml @@ -0,0 +1,5 @@ +root: ./ + +structure: + readme: README.md + summary: SUMMARY.md diff --git a/README.md b/README.md index de5df88..23ee1e8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,46 @@ -# freighter-developer-docs -Freighter wallet developer documentation +# Welcome to Freighter + +Freighter is a non-custodial wallet for the Stellar network. It lets you manage accounts, sign transactions, and interact with Soroban smart contracts from both the browser extension and the mobile app. + +## Choose Your Integration + +### Extension + +Integrate with Freighter's browser extension using `@stellar/freighter-api`. Best for web apps that run in desktop browsers. + +**Ideal for:** + +- React, Vue, or Angular web apps +- Server-rendered apps (Next.js, Nuxt) +- Static sites via CDN + +> Get started with the [Extension Installation Guide](developer-guide/installation.md) + +### Mobile (WalletConnect) + +Connect to Freighter Mobile using the WalletConnect v2 protocol. Best for dapps that need to support mobile users. + +**Ideal for:** + +- Mobile-first dapps +- Cross-platform apps that support both desktop and mobile wallets +- Dapps using WalletConnect for multi-wallet support + +> Get started with the [Mobile Integration Guide](mobile/) + +## Quick links + +| I want to... | Extension | Mobile | +| ------------------------------------- | -------------------------------------------------- | ---------------------------------------------------- | +| Install / set up | [Installation](developer-guide/installation.md) | [Installation](mobile/installation.md) | +| Connect to Freighter | [Connecting](developer-guide/connecting.md) | [Connecting](mobile/connecting.md) | +| Sign a transaction | [Signing](developer-guide/signing.md) | [Signing](mobile/signing.md) | +| Add a token | [Token Management](developer-guide/token-management.md) | — | + +## Resources + +- [GitHub — Freighter Extension](https://github.com/stellar/freighter) +- [GitHub — Freighter Mobile](https://github.com/stellar/freighter-mobile) +- [Chrome Extension Store](https://chromewebstore.google.com/detail/freighter/bcacfldlkkdogcmkkibnjlakofdplcbk) +- [Stellar Developer Docs](https://developers.stellar.org) +- [WalletConnect v2 Docs](https://docs.walletconnect.com/) diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..467177b --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,24 @@ +# Table of contents + +* [Introduction](README.md) + +## Extension + +* [Installation](developer-guide/installation.md) +* [Overview](developer-guide/README.md) +* [Connecting](developer-guide/connecting.md) +* [Reading Data](developer-guide/reading-data.md) +* [Signing](developer-guide/signing.md) +* [Token Management](developer-guide/token-management.md) +* [Watching Changes](developer-guide/watching-changes.md) + +## Mobile (WalletConnect) + +* [Installation](mobile/installation.md) +* [Overview](mobile/README.md) +* [Connecting](mobile/connecting.md) +* [Signing](mobile/signing.md) + +## Integrations + +* [Third-Party Integrations](integrations/README.md) diff --git a/developer-guide/README.md b/developer-guide/README.md new file mode 100644 index 0000000..b4c4de2 --- /dev/null +++ b/developer-guide/README.md @@ -0,0 +1,36 @@ +# Developer Guide + +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`. + +## Importing + +Import the entire library: + +```javascript +import freighterApi from "@stellar/freighter-api"; +``` + +Or import only what you need: + +```javascript +import { + isConnected, + getAddress, + signAuthEntry, + signTransaction, + signBlob, + addToken, +} from "@stellar/freighter-api"; +``` + +## API Reference + +| Category | Description | +| --- | --- | +| [Connecting](connecting.md) | Detect Freighter, check permissions, request access | +| [Reading Data](reading-data.md) | Get the user's address and network configuration | +| [Signing](signing.md) | Sign transactions, auth entries, and messages | +| [Token Management](token-management.md) | Add Soroban tokens to the user's wallet | +| [Watching Changes](watching-changes.md) | Monitor wallet state changes in real time | diff --git a/developer-guide/connecting.md b/developer-guide/connecting.md new file mode 100644 index 0000000..f5bface --- /dev/null +++ b/developer-guide/connecting.md @@ -0,0 +1,97 @@ +# Connecting to Freighter + +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?: string }>` + +```typescript +import { isConnected } from "@stellar/freighter-api"; + +const result = await isConnected(); + +if (result.isConnected) { + console.log("Freighter is installed"); +} +``` + +## Checking Authorization + +### `isAllowed()` + +Check if the user has previously authorized your app to receive data from Freighter. + +**Returns:** `Promise<{ isAllowed: boolean } & { error?: string }>` + +```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 add your app to Freighter's **Allow List**. Once approved, the extension can provide user data without additional prompts. + +**Returns:** `Promise<{ isAllowed: boolean } & { error?: string }>` + +```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 `true`. +{% endhint %} + +## Requesting Access + +### `requestAccess()` + +Prompt the user for permission to access their public key. This is the **recommended** way to initiate a connection with Freighter — it handles both authorization and key retrieval in one call. + +**Returns:** `Promise<{ address: string } & { error?: string }>` + +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); +} 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](reading-data.md) or [sign transactions](signing.md). diff --git a/developer-guide/installation.md b/developer-guide/installation.md new file mode 100644 index 0000000..95fe136 --- /dev/null +++ b/developer-guide/installation.md @@ -0,0 +1,72 @@ +# 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](https://chromewebstore.google.com/detail/freighter/bcacfldlkkdogcmkkibnjlakofdplcbk) from the Chrome Web Store. +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: + +```typescript +import { isConnected, requestAccess, signTransaction } from "@stellar/freighter-api"; +``` + +> See the [Developer Guide](../developer-guide/) 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 ``: + +```html + +``` + +This always loads the latest version automatically. To pin a specific version: + +```html + +``` + +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](../developer-guide/connecting.md) | +| Sign a transaction | [Signing](../developer-guide/signing.md) | +| Add a token | [Token Management](../developer-guide/token-management.md) | diff --git a/developer-guide/reading-data.md b/developer-guide/reading-data.md new file mode 100644 index 0000000..514097b --- /dev/null +++ b/developer-guide/reading-data.md @@ -0,0 +1,71 @@ +# 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()`](connecting.md#requesting-access) or [`setAllowed()`](connecting.md#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?: string }>` + +```typescript +import { getAddress } from "@stellar/freighter-api"; + +const { address, error } = await getAddress(); + +if (error) { + console.error(error); +} 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?: string }>` + +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?: string }>` + +```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 %} diff --git a/developer-guide/signing.md b/developer-guide/signing.md new file mode 100644 index 0000000..68eea69 --- /dev/null +++ b/developer-guide/signing.md @@ -0,0 +1,153 @@ +# 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?: string }> +``` + +The user will be prompted to enter their password (if the extension doesn't currently hold the private key) and then review the transaction details before signing. + +{% hint style="warning" %} +The private key is cached for **5 minutes** after the user enters their password. The transaction must be reviewed and accepted within that window. +{% endhint %} + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `xdr` | `string` | **Required.** Base64-encoded transaction XDR. | +| `opts.network` | `string` | Network name (maps to `Networks` enum in `js-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); +} else { + console.log("Signed by:", signerAddress); + console.log("Signed XDR:", signedTxXdr); +} +``` + +## Signing an Auth Entry + +### `signAuthEntry()` + +Sign a Soroban [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?: string }> +``` + +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](soroban-development.md) page 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); +} else { + console.log("Signed auth entry:", signedAuthEntry); +} +``` + +## 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?: string }> +``` + +### 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); +} else { + console.log("Signature:", signedMessage); +} +``` + +## 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-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); +} + +// 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); +} + +// 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); +``` diff --git a/developer-guide/token-management.md b/developer-guide/token-management.md new file mode 100644 index 0000000..5508a52 --- /dev/null +++ b/developer-guide/token-management.md @@ -0,0 +1,47 @@ +# Token Management + +Add Soroban tokens to a user's Freighter wallet programmatically. + +## 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?: string }> +``` + +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 Soroban token contract ID. | +| `networkPassphrase` | `string` | Defaults to Pubnet's 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); +} 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 %} diff --git a/developer-guide/watching-changes.md b/developer-guide/watching-changes.md new file mode 100644 index 0000000..536f2e2 --- /dev/null +++ b/developer-guide/watching-changes.md @@ -0,0 +1,46 @@ +# Watching Wallet 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 %} diff --git a/index.html b/index.html new file mode 100644 index 0000000..5c7b106 --- /dev/null +++ b/index.html @@ -0,0 +1,24 @@ + + + + + Freighter Documentation + + + + +
+ + + + + + + diff --git a/integrations/README.md b/integrations/README.md new file mode 100644 index 0000000..cee387f --- /dev/null +++ b/integrations/README.md @@ -0,0 +1,10 @@ +# 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. + +## Available Integrations + +| Integration | Description | +| --- | --- | +| [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 | diff --git a/mobile/README.md b/mobile/README.md new file mode 100644 index 0000000..17733db --- /dev/null +++ b/mobile/README.md @@ -0,0 +1,64 @@ +# Mobile Integration + +Connect your dapp to Freighter Mobile using the [WalletConnect v2](https://docs.walletconnect.com/) protocol. All methods follow the standard JSON-RPC 2.0 request/response format over a WalletConnect session. + +## Supported Chains + +| Network | Chain ID | +| --- | --- | +| Mainnet | `stellar:pubnet` | +| Testnet | `stellar:testnet` | + +## API Reference + +| Method | Description | +| --- | --- | +| [`stellar_signXDR`](signing.md#stellar_signxdr) | Sign a transaction and return the signed XDR | +| [`stellar_signAndSubmitXDR`](signing.md#stellar_signandsubmitxdr) | Sign and submit a transaction to Horizon | +| [`stellar_signMessage`](signing.md#stellar_signmessage) | Sign an arbitrary UTF-8 message ([SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md)) | +| [`stellar_signAuthEntry`](signing.md#stellar_signauthentry) | Sign a Soroban authorization entry preimage ([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` in your request 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. + +## Supporting Both Extension and Mobile + +If your dapp needs to support both desktop (extension) and mobile users, the typical pattern is: + +1. **Detect the environment** — check if `window.freighterApi` is available for extension, otherwise offer WalletConnect +2. **Use WalletConnect as the fallback** — mobile users scan a QR code; desktop users without the extension can use the WalletConnect modal +3. **Unify your signing interface** — both paths produce the same output (signed XDR), so your submission logic stays the same + +## See also + +- [WalletConnect v2 Docs](https://docs.walletconnect.com/) +- [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) diff --git a/mobile/connecting.md b/mobile/connecting.md new file mode 100644 index 0000000..fc3ee51 --- /dev/null +++ b/mobile/connecting.md @@ -0,0 +1,69 @@ +# Connecting + +Establish a WalletConnect session with Freighter Mobile, handle session lifecycle events, and disconnect. + +## Creating a Session + +Request a connection with the Stellar namespace. This generates a URI you can display as a QR code or use as a deep link for mobile users. + +```typescript +const { uri, approval } = await client.connect({ + requiredNamespaces: { + stellar: { + methods: [ + "stellar_signXDR", + "stellar_signAndSubmitXDR", + "stellar_signMessage", + "stellar_signAuthEntry", + ], + chains: ["stellar:pubnet"], + events: ["accountsChanged"], + }, + }, +}); + +// Display `uri` as a QR code for the user to scan with Freighter Mobile +console.log("Scan this URI:", uri); + +// Wait for the user to approve in Freighter Mobile +const session = await approval(); +console.log("Connected! Session topic:", session.topic); +``` + +{% hint style="info" %} +Use `requiredNamespaces` for methods your dapp needs to function. Use `optionalNamespaces` for methods that enhance the experience but aren't essential. +{% endhint %} + +## Handling Events + +Listen for session lifecycle events to keep your UI in sync: + +```typescript +// Session was disconnected by the wallet +client.on("session_delete", ({ topic }) => { + console.log("Session deleted:", topic); +}); + +// Session expired +client.on("session_expire", ({ topic }) => { + console.log("Session expired:", topic); +}); +``` + +## Disconnecting + +When the user wants to disconnect: + +```typescript +await client.disconnect({ + topic: session.topic, + reason: { + code: 6000, + message: "User disconnected", + }, +}); +``` + +## Next steps + +Once connected, you can [sign transactions, messages, and auth entries](signing.md). diff --git a/mobile/installation.md b/mobile/installation.md new file mode 100644 index 0000000..c264306 --- /dev/null +++ b/mobile/installation.md @@ -0,0 +1,41 @@ +# Installation + +Get up and running with WalletConnect so your dapp can connect to Freighter Mobile. + +## Prerequisites + +1. Sign up at [WalletConnect Cloud](https://cloud.walletconnect.com/) and create a project to get your **Project ID**. +2. Choose an installation method below. + +## npm / yarn + +```bash +npm install @walletconnect/sign-client @walletconnect/types +``` + +```bash +yarn add @walletconnect/sign-client @walletconnect/types +``` + +Then initialize the client: + +```typescript +import SignClient from "@walletconnect/sign-client"; + +const client = await SignClient.init({ + projectId: "YOUR_PROJECT_ID", + 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"], + }, +}); +``` + +## Next steps + +| I want to... | Go to | +| ------------------------------------- | ---------------------------------------------- | +| Connect to Freighter Mobile | [Connecting](connecting.md) | +| Sign a transaction | [Signing](signing.md) | diff --git a/mobile/signing.md b/mobile/signing.md new file mode 100644 index 0000000..e15baad --- /dev/null +++ b/mobile/signing.md @@ -0,0 +1,243 @@ +# Signing + +Sign transactions, messages, and Soroban authorization entries via WalletConnect. + +## 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 client.request({ + topic: session.topic, + chainId: "stellar:pubnet", + request: { + method: "stellar_signXDR", + params: { + xdr: "AAAAAgAAAAA...", + }, + }, +}); + +console.log("Signed XDR:", result.signedXDR); +``` + +**Errors** + +| Condition | Error message | +| --- | --- | +| Chain not supported | `"Unsupported chain: "` | +| Wrong active network | `"Please switch to 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 client.request({ + topic: session.topic, + chainId: "stellar:pubnet", + request: { + method: "stellar_signAndSubmitXDR", + params: { + xdr: "AAAAAgAAAAA...", + }, + }, +}); + +console.log("Status:", result.status); // "success" +``` + +**Errors** + +| Condition | Error message | +| --- | --- | +| Chain not supported | `"Unsupported chain: "` | +| Wrong active network | `"Please switch to 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 client.request({ + topic: session.topic, + chainId: "stellar:pubnet", + request: { + method: "stellar_signMessage", + params: { + message: "Please sign to verify account ownership", + }, + }, +}); + +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** before showing the signing sheet. 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 client.request({ + topic: session.topic, + chainId: "stellar:pubnet", + request: { + method: "stellar_signAuthEntry", + params: { + entryXdr: "AAAAAQ...", + }, + }, +}); + +console.log("Signature:", result.signedAuthEntry); +console.log("Signer:", result.signerAddress); +``` + +### Building the `entryXdr` + +Here's how to construct the preimage from a contract simulation result and attach the signature back to the auth entry: + +```typescript +import { xdr, Networks, hash, Keypair } from "@stellar/stellar-sdk"; + +// 1. Get the SorobanAuthorizationEntry from simulation +const simulationResult = await server.simulateTransaction(tx); +const authEntry = simulationResult.result.auth[0]; + +// 2. Build the HashIdPreimage +const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization( + new xdr.HashIdPreimageSorobanAuthorization({ + networkId: hash(Buffer.from(Networks.PUBLIC)), + nonce: authEntry.credentials().address().nonce(), + signatureExpirationLedger: authEntry + .credentials() + .address() + .signatureExpirationLedger(), + invocation: authEntry.rootInvocation(), + }), +); + +const entryXdr = preimage.toXDR("base64"); + +// 3. Send via WalletConnect +const result = await walletKit.request({ + topic: session.topic, + chainId: "stellar:pubnet", + request: { + method: "stellar_signAuthEntry", + params: { entryXdr }, + }, +}); + +// 4. Attach the signature back to the auth entry +const signerRawKey = Keypair.fromPublicKey(result.signerAddress).rawPublicKey(); +const signatureScVal = xdr.ScVal.scvMap([ + new xdr.ScMapEntry({ + key: xdr.ScVal.scvSymbol("public_key"), + val: xdr.ScVal.scvBytes(signerRawKey), + }), + new xdr.ScMapEntry({ + key: xdr.ScVal.scvSymbol("signature"), + val: xdr.ScVal.scvBytes(Buffer.from(result.signedAuthEntry, "base64")), + }), +]); +authEntry + .credentials() + .address() + .signature(xdr.ScVal.scvVec([signatureScVal])); +``` + +**Errors** + +| Condition | Error message | +| --- | --- | +| Missing 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"` | From 2d87af70ef1094fe7c890da7caebc77dd42a8c40 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Thu, 9 Apr 2026 19:00:26 -0400 Subject: [PATCH 02/27] Address PR review feedback: fix broken links, wrong paths, and browser compat issues - Fix mobile/ link to mobile/README.md for GitBook/docsify compatibility - Replace undocumented signBlob with signMessage in import example - Standardize on @stellar/stellar-sdk (not js-stellar-sdk or stellar-sdk) - Fix broken soroban-development.md link to point to Stellar developer docs - Fix CDN paths from dist/ to build/ to resolve 404s - Correct setAllowed() return type description to { isAllowed: true } - Clarify chainId is a top-level WalletConnect request field, not inside params - Replace Buffer.from() with TextEncoder/atob for browser compatibility - Use consistent client.request() variable name in auth entry example - Pin docsify (4.13.1) and prismjs (1.29.0) CDN versions Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 2 +- developer-guide/README.md | 2 +- developer-guide/connecting.md | 2 +- developer-guide/installation.md | 4 ++-- developer-guide/signing.md | 6 +++--- index.html | 10 +++++----- mobile/README.md | 2 +- mobile/signing.md | 10 ++++++---- 8 files changed, 20 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 23ee1e8..e674aaa 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Connect to Freighter Mobile using the WalletConnect v2 protocol. Best for dapps - Cross-platform apps that support both desktop and mobile wallets - Dapps using WalletConnect for multi-wallet support -> Get started with the [Mobile Integration Guide](mobile/) +> Get started with the [Mobile Integration Guide](mobile/README.md) ## Quick links diff --git a/developer-guide/README.md b/developer-guide/README.md index b4c4de2..17abf4d 100644 --- a/developer-guide/README.md +++ b/developer-guide/README.md @@ -20,7 +20,7 @@ import { getAddress, signAuthEntry, signTransaction, - signBlob, + signMessage, addToken, } from "@stellar/freighter-api"; ``` diff --git a/developer-guide/connecting.md b/developer-guide/connecting.md index f5bface..abb0801 100644 --- a/developer-guide/connecting.md +++ b/developer-guide/connecting.md @@ -57,7 +57,7 @@ if (result.isAllowed) { ``` {% hint style="info" %} -If the user has already authorized your app, `setAllowed()` resolves immediately with `true`. +If the user has already authorized your app, `setAllowed()` resolves immediately with `{ isAllowed: true }`. {% endhint %} ## Requesting Access diff --git a/developer-guide/installation.md b/developer-guide/installation.md index 95fe136..84a99dd 100644 --- a/developer-guide/installation.md +++ b/developer-guide/installation.md @@ -48,13 +48,13 @@ Use this for plain HTML pages or projects without a build step. The library is l Add to your ``: ```html - + ``` This always loads the latest version automatically. To pin a specific version: ```html - + ``` Then access the API via `window.freighterApi`: diff --git a/developer-guide/signing.md b/developer-guide/signing.md index 68eea69..f8eb4d6 100644 --- a/developer-guide/signing.md +++ b/developer-guide/signing.md @@ -27,7 +27,7 @@ The private key is cached for **5 minutes** after the user enters their password | Parameter | Type | Description | | --- | --- | --- | | `xdr` | `string` | **Required.** Base64-encoded transaction XDR. | -| `opts.network` | `string` | Network name (maps to `Networks` enum in `js-stellar-sdk`). | +| `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. | @@ -65,7 +65,7 @@ signAuthEntry(entryXdr: string, opts: { }) -> Promise<{ signedAuthEntry: string | null; signerAddress: string } & { error?: string }> ``` -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](soroban-development.md) page for wallet-side patterns. +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 @@ -118,7 +118,7 @@ A complete flow from connection to Horizon submission: ```typescript import { isConnected, requestAccess, signTransaction } from "@stellar/freighter-api"; -import { Server, TransactionBuilder } from "stellar-sdk"; +import { Server, TransactionBuilder } from "@stellar/stellar-sdk"; // 1. Check Freighter is installed const connectResult = await isConnected(); diff --git a/index.html b/index.html index 5c7b106..efc6b11 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ Freighter Documentation - +
@@ -16,9 +16,9 @@ auto2top: true, } - - - - + + + + diff --git a/mobile/README.md b/mobile/README.md index 17733db..00ebcb1 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -34,7 +34,7 @@ All error responses use JSON-RPC 2.0 format with code `5000`: ``` {% 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` in your request params. +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. In WalletConnect v2, always specify `chainId` as a top-level field on the request, not inside `params`. {% endhint %} ## Security diff --git a/mobile/signing.md b/mobile/signing.md index e15baad..af91844 100644 --- a/mobile/signing.md +++ b/mobile/signing.md @@ -187,13 +187,14 @@ Here's how to construct the preimage from a contract simulation result and attac import { xdr, Networks, hash, Keypair } from "@stellar/stellar-sdk"; // 1. Get the SorobanAuthorizationEntry from simulation +// Assumes `server` (SorobanRpc.Server) and `tx` (Transaction) are already set up const simulationResult = await server.simulateTransaction(tx); const authEntry = simulationResult.result.auth[0]; // 2. Build the HashIdPreimage const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization( new xdr.HashIdPreimageSorobanAuthorization({ - networkId: hash(Buffer.from(Networks.PUBLIC)), + networkId: hash(new TextEncoder().encode(Networks.PUBLIC)), nonce: authEntry.credentials().address().nonce(), signatureExpirationLedger: authEntry .credentials() @@ -205,8 +206,8 @@ const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization( const entryXdr = preimage.toXDR("base64"); -// 3. Send via WalletConnect -const result = await walletKit.request({ +// 3. Send via WalletConnect (using the client from the connecting guide) +const result = await client.request({ topic: session.topic, chainId: "stellar:pubnet", request: { @@ -217,6 +218,7 @@ const result = await walletKit.request({ // 4. Attach the signature back to the auth entry const signerRawKey = Keypair.fromPublicKey(result.signerAddress).rawPublicKey(); +const signatureBytes = Uint8Array.from(atob(result.signedAuthEntry), (c) => c.charCodeAt(0)); const signatureScVal = xdr.ScVal.scvMap([ new xdr.ScMapEntry({ key: xdr.ScVal.scvSymbol("public_key"), @@ -224,7 +226,7 @@ const signatureScVal = xdr.ScVal.scvMap([ }), new xdr.ScMapEntry({ key: xdr.ScVal.scvSymbol("signature"), - val: xdr.ScVal.scvBytes(Buffer.from(result.signedAuthEntry, "base64")), + val: xdr.ScVal.scvBytes(signatureBytes), }), ]); authEntry From 90e65459e1e6726254f8f54e422139dc672f9eeb Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Thu, 9 Apr 2026 19:07:37 -0400 Subject: [PATCH 03/27] Fix error type: error is FreighterApiError object, not string The actual @stellar/freighter-api returns error as a FreighterApiError object with { code, message, ext? }, not a plain string. Updated all return type signatures and code examples to use error.message. Co-Authored-By: Claude Opus 4.6 (1M context) --- developer-guide/README.md | 12 ++++++++++++ developer-guide/connecting.md | 10 +++++----- developer-guide/reading-data.md | 6 +++--- developer-guide/signing.md | 16 ++++++++-------- developer-guide/token-management.md | 4 ++-- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/developer-guide/README.md b/developer-guide/README.md index 17abf4d..3fe4a86 100644 --- a/developer-guide/README.md +++ b/developer-guide/README.md @@ -25,6 +25,18 @@ import { } from "@stellar/freighter-api"; ``` +## 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 | diff --git a/developer-guide/connecting.md b/developer-guide/connecting.md index abb0801..5f1b597 100644 --- a/developer-guide/connecting.md +++ b/developer-guide/connecting.md @@ -8,7 +8,7 @@ Detect whether a user has Freighter installed, check if your app is authorized, Check if the user has Freighter installed in their browser. -**Returns:** `Promise<{ isConnected: boolean } & { error?: string }>` +**Returns:** `Promise<{ isConnected: boolean } & { error?: FreighterApiError }>` ```typescript import { isConnected } from "@stellar/freighter-api"; @@ -26,7 +26,7 @@ if (result.isConnected) { Check if the user has previously authorized your app to receive data from Freighter. -**Returns:** `Promise<{ isAllowed: boolean } & { error?: string }>` +**Returns:** `Promise<{ isAllowed: boolean } & { error?: FreighterApiError }>` ```typescript import { isAllowed } from "@stellar/freighter-api"; @@ -44,7 +44,7 @@ if (result.isAllowed) { Prompt the user to add your app to Freighter's **Allow List**. Once approved, the extension can provide user data without additional prompts. -**Returns:** `Promise<{ isAllowed: boolean } & { error?: string }>` +**Returns:** `Promise<{ isAllowed: boolean } & { error?: FreighterApiError }>` ```typescript import { setAllowed } from "@stellar/freighter-api"; @@ -66,7 +66,7 @@ If the user has already authorized your app, `setAllowed()` resolves immediately Prompt the user for permission to access their public key. This is the **recommended** way to initiate a connection with Freighter — it handles both authorization and key retrieval in one call. -**Returns:** `Promise<{ address: string } & { error?: string }>` +**Returns:** `Promise<{ address: string } & { error?: FreighterApiError }>` If the user has previously authorized your app, the public key is returned immediately without a popup. @@ -82,7 +82,7 @@ if (!connectResult.isConnected) { const accessResult = await requestAccess(); if (accessResult.error) { - console.error("Access denied:", accessResult.error); + console.error("Access denied:", accessResult.error.message); } else { console.log("Public key:", accessResult.address); } diff --git a/developer-guide/reading-data.md b/developer-guide/reading-data.md index 514097b..d740ec4 100644 --- a/developer-guide/reading-data.md +++ b/developer-guide/reading-data.md @@ -12,7 +12,7 @@ These methods require that the user has previously authorized your app via [`req 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?: string }>` +**Returns:** `Promise<{ address: string } & { error?: FreighterApiError }>` ```typescript import { getAddress } from "@stellar/freighter-api"; @@ -32,7 +32,7 @@ if (error) { Get the name and passphrase of the network the user has selected in Freighter. -**Returns:** `Promise<{ network: string; networkPassphrase: string } & { error?: string }>` +**Returns:** `Promise<{ network: string; networkPassphrase: string } & { error?: FreighterApiError }>` Possible `network` values: `PUBLIC`, `TESTNET`, `FUTURENET`, or `STANDALONE` (custom networks). @@ -51,7 +51,7 @@ if (!error) { Get comprehensive network configuration including the Horizon URL, passphrase, and Soroban RPC URL. -**Returns:** `Promise<{ network: string; networkUrl: string; networkPassphrase: string; sorobanRpcUrl?: string } & { error?: string }>` +**Returns:** `Promise<{ network: string; networkUrl: string; networkPassphrase: string; sorobanRpcUrl?: string } & { error?: FreighterApiError }>` ```typescript import { getNetworkDetails } from "@stellar/freighter-api"; diff --git a/developer-guide/signing.md b/developer-guide/signing.md index f8eb4d6..d6aa626 100644 --- a/developer-guide/signing.md +++ b/developer-guide/signing.md @@ -13,7 +13,7 @@ signTransaction(xdr: string, opts?: { network?: string, networkPassphrase?: string, address?: string -}) -> Promise<{ signedTxXdr: string; signerAddress: string } & { error?: string }> +}) -> Promise<{ signedTxXdr: string; signerAddress: string } & { error?: FreighterApiError }> ``` The user will be prompted to enter their password (if the extension doesn't currently hold the private key) and then review the transaction details before signing. @@ -46,7 +46,7 @@ const { signedTxXdr, signerAddress, error } = await signTransaction(xdr, { }); if (error) { - console.error("Signing failed:", error); + console.error("Signing failed:", error.message); } else { console.log("Signed by:", signerAddress); console.log("Signed XDR:", signedTxXdr); @@ -62,7 +62,7 @@ Sign a Soroban [authorization entry preimage](https://github.com/stellar/js-stel ``` signAuthEntry(entryXdr: string, opts: { address: string -}) -> Promise<{ signedAuthEntry: string | null; signerAddress: string } & { error?: 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. @@ -77,7 +77,7 @@ const { signedAuthEntry, signerAddress, error } = await signAuthEntry(entryXdr, }); if (error) { - console.error("Auth entry signing failed:", error); + console.error("Auth entry signing failed:", error.message); } else { console.log("Signed auth entry:", signedAuthEntry); } @@ -92,7 +92,7 @@ Sign an arbitrary string and receive a base64-encoded Ed25519 signature. Follows ``` signMessage(message: string, opts: { address: string -}) -> Promise<{ signedMessage: string | null; signerAddress: string } & { error?: string }> +}) -> Promise<{ signedMessage: string | null; signerAddress: string } & { error?: FreighterApiError }> ``` ### Example @@ -106,7 +106,7 @@ const { signedMessage, signerAddress, error } = await signMessage( ); if (error) { - console.error("Message signing failed:", error); + console.error("Message signing failed:", error.message); } else { console.log("Signature:", signedMessage); } @@ -129,7 +129,7 @@ if (!connectResult.isConnected) { // 2. Request the user's public key const accessResult = await requestAccess(); if (accessResult.error) { - throw new Error(accessResult.error); + throw new Error(accessResult.error.message); } // 3. Sign the transaction @@ -139,7 +139,7 @@ const signResult = await signTransaction(xdr, { address: accessResult.address, }); if (signResult.error) { - throw new Error(signResult.error); + throw new Error(signResult.error.message); } // 4. Submit to Horizon diff --git a/developer-guide/token-management.md b/developer-guide/token-management.md index 5508a52..c79921f 100644 --- a/developer-guide/token-management.md +++ b/developer-guide/token-management.md @@ -10,7 +10,7 @@ Trigger an "add token" flow that displays token details in a modal for the user ``` addToken({ contractId: string, networkPassphrase?: string }) - -> Promise<{ contractId: string } & { error?: 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. @@ -36,7 +36,7 @@ const result = await addToken({ }); if (result.error) { - console.error(result.error); + console.error(result.error.message); } else { console.log(`Token added: ${result.contractId}`); } From a01ff406c1a49a08de76c24c1dc38cbba6c45ab4 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Thu, 9 Apr 2026 19:09:38 -0400 Subject: [PATCH 04/27] Fix mobile signing docs to match freighter-mobile source - Blockaid site scan happens at connection time, not at auth-entry signing time - Add missing "non-string" condition to signAuthEntry error table Co-Authored-By: Claude Opus 4.6 (1M context) --- mobile/signing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/signing.md b/mobile/signing.md index af91844..fc82723 100644 --- a/mobile/signing.md +++ b/mobile/signing.md @@ -147,7 +147,7 @@ Sign a Soroban authorization entry preimage for multi-auth and custom-account sm 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** before showing the signing sheet. Review the contract address, function name, and subinvocations displayed in the UI before confirming. +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** @@ -239,7 +239,7 @@ authEntry | Condition | Error message | | --- | --- | -| Missing or whitespace-only `entryXdr` | `"Invalid authorization entry"` | +| 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"` | From 558da0b4e8b9d643f78289b88fd138305c297f92 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Thu, 9 Apr 2026 20:34:37 -0400 Subject: [PATCH 05/27] Rewrite introduction and add Stellar Wallets Kit integration - Rewrote README.md with What You Can Build, How It Works, and Why Freighter sections to walk developers through the integration experience without code samples - Added Stellar Wallets Kit (stellarwalletskit.dev) to the Introduction and Integrations page for multi-wallet support Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 28 +++++++++++++++++++++++++++- integrations/README.md | 5 +++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e674aaa..105ba94 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,32 @@ # Welcome to Freighter -Freighter is a non-custodial wallet for the Stellar network. It lets you manage accounts, sign transactions, and interact with Soroban smart contracts from both the browser extension and the mobile app. +Freighter is a non-custodial wallet for the Stellar network, available as a browser extension and a mobile app. This guide will walk you through integrating Freighter into your dapp so your users can connect their wallets, sign transactions, and interact with Soroban smart contracts — all without you ever touching a private key. + +## 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. The user reviews everything in the wallet's own UI and stays in full control. + +For Soroban dapps, 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. You never handle private keys, seed phrases, or encryption. Freighter manages all of that on the user's device. + +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." Every sensitive action goes through the extension's UI where the user has full visibility and control. + +For **mobile**, the integration works over WalletConnect v2. Your dapp generates a connection URI, the user scans a QR code with Freighter Mobile, and a secure relay session is established. From that point on, 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 so your users can connect with whichever wallet they prefer. + +## Why Freighter + +**Your users stay in control.** Every transaction, every signature, every connection is explicitly approved by the user in the wallet's own interface. There's no ambient access, no silent signing, and no stored sessions that persist beyond what the user expects. + +**You ship faster.** The API surface is small and intentional. Connecting a wallet, signing a transaction, and reading account data each take one function call. There's no complex state machine to manage, no WebSocket lifecycle to babysit, and no cryptography to implement yourself. + +**It works where your users are.** Desktop users get the browser extension experience they're used to from other ecosystems. Mobile users get QR-code-based WalletConnect flows that feel native. You can support both from the same dapp with minimal branching. ## Choose Your Integration diff --git a/integrations/README.md b/integrations/README.md index cee387f..51d6450 100644 --- a/integrations/README.md +++ b/integrations/README.md @@ -2,9 +2,14 @@ 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 | From a96a02ac0d6072d8c6bba62d29824bb370732acb Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Thu, 9 Apr 2026 20:38:54 -0400 Subject: [PATCH 06/27] Note that Stellar Wallets Kit is desktop-only Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 105ba94..c05ffa9 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ For **mobile**, the integration works over WalletConnect v2. Your dapp generates 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 so your users can connect with whichever wallet they prefer. +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 desktop wallets so your users can connect with whichever browser extension they prefer. Note that it currently supports desktop wallets only, not mobile. ## Why Freighter From fe49492fe87c013d30dd8a2701b65c2c1a371580 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Thu, 9 Apr 2026 21:19:48 -0400 Subject: [PATCH 07/27] Fix missed error.message in reading-data.md and docsify CDN 404 - reading-data.md: console.error(error) -> console.error(error.message) to match FreighterApiError object type (missed in earlier update) - index.html: docsify path dist/ -> lib/ to fix 404 Co-Authored-By: Claude Opus 4.6 (1M context) --- developer-guide/reading-data.md | 2 +- index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/developer-guide/reading-data.md b/developer-guide/reading-data.md index d740ec4..e322cc3 100644 --- a/developer-guide/reading-data.md +++ b/developer-guide/reading-data.md @@ -20,7 +20,7 @@ import { getAddress } from "@stellar/freighter-api"; const { address, error } = await getAddress(); if (error) { - console.error(error); + console.error(error.message); } else { console.log("Public key:", address); } diff --git a/index.html b/index.html index efc6b11..77cc2c6 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@ auto2top: true, } - + From aef2f2a99633779a5835ef9d469338fcf91afa9b Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 13:38:41 -0400 Subject: [PATCH 08/27] Update README.md Co-authored-by: Jake Urban <10968980+JakeUrban@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c05ffa9..4f610ad 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Freighter is a non-custodial wallet for the Stellar network, available as a brow 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. The user reviews everything in the wallet's own UI and stays in full control. -For Soroban dapps, Freighter also handles authorization entry signing for smart contract calls and arbitrary message signing for account verification. +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 From 8cea352cc8b24304f6bf2d7503f0363c48f07f32 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 13:39:56 -0400 Subject: [PATCH 09/27] Update developer-guide/signing.md Co-authored-by: Jake Urban <10968980+JakeUrban@users.noreply.github.com> --- developer-guide/signing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/developer-guide/signing.md b/developer-guide/signing.md index d6aa626..0d8a29b 100644 --- a/developer-guide/signing.md +++ b/developer-guide/signing.md @@ -57,7 +57,7 @@ if (error) { ### `signAuthEntry()` -Sign a Soroban [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. +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: { From af128907de599dfb3f56bda1256110f00c492dcb Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 13:54:45 -0400 Subject: [PATCH 10/27] Rename developer-guide/ to extension/ Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 10 +++++----- SUMMARY.md | 14 +++++++------- {developer-guide => extension}/README.md | 0 {developer-guide => extension}/connecting.md | 0 {developer-guide => extension}/installation.md | 8 ++++---- {developer-guide => extension}/reading-data.md | 0 {developer-guide => extension}/signing.md | 0 {developer-guide => extension}/token-management.md | 0 {developer-guide => extension}/watching-changes.md | 0 9 files changed, 16 insertions(+), 16 deletions(-) rename {developer-guide => extension}/README.md (100%) rename {developer-guide => extension}/connecting.md (100%) rename {developer-guide => extension}/installation.md (85%) rename {developer-guide => extension}/reading-data.md (100%) rename {developer-guide => extension}/signing.md (100%) rename {developer-guide => extension}/token-management.md (100%) rename {developer-guide => extension}/watching-changes.md (100%) diff --git a/README.md b/README.md index 4f610ad..8f99aeb 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Integrate with Freighter's browser extension using `@stellar/freighter-api`. Bes - Server-rendered apps (Next.js, Nuxt) - Static sites via CDN -> Get started with the [Extension Installation Guide](developer-guide/installation.md) +> Get started with the [Extension Installation Guide](extension/installation.md) ### Mobile (WalletConnect) @@ -58,10 +58,10 @@ Connect to Freighter Mobile using the WalletConnect v2 protocol. Best for dapps | I want to... | Extension | Mobile | | ------------------------------------- | -------------------------------------------------- | ---------------------------------------------------- | -| Install / set up | [Installation](developer-guide/installation.md) | [Installation](mobile/installation.md) | -| Connect to Freighter | [Connecting](developer-guide/connecting.md) | [Connecting](mobile/connecting.md) | -| Sign a transaction | [Signing](developer-guide/signing.md) | [Signing](mobile/signing.md) | -| Add a token | [Token Management](developer-guide/token-management.md) | — | +| Install / set up | [Installation](extension/installation.md) | [Installation](mobile/installation.md) | +| Connect to Freighter | [Connecting](extension/connecting.md) | [Connecting](mobile/connecting.md) | +| Sign a transaction | [Signing](extension/signing.md) | [Signing](mobile/signing.md) | +| Add a token | [Token Management](extension/token-management.md) | — | ## Resources diff --git a/SUMMARY.md b/SUMMARY.md index 467177b..0f0240e 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -4,13 +4,13 @@ ## Extension -* [Installation](developer-guide/installation.md) -* [Overview](developer-guide/README.md) -* [Connecting](developer-guide/connecting.md) -* [Reading Data](developer-guide/reading-data.md) -* [Signing](developer-guide/signing.md) -* [Token Management](developer-guide/token-management.md) -* [Watching Changes](developer-guide/watching-changes.md) +* [Installation](extension/installation.md) +* [Overview](extension/README.md) +* [Connecting](extension/connecting.md) +* [Reading Data](extension/reading-data.md) +* [Signing](extension/signing.md) +* [Token Management](extension/token-management.md) +* [Watching Changes](extension/watching-changes.md) ## Mobile (WalletConnect) diff --git a/developer-guide/README.md b/extension/README.md similarity index 100% rename from developer-guide/README.md rename to extension/README.md diff --git a/developer-guide/connecting.md b/extension/connecting.md similarity index 100% rename from developer-guide/connecting.md rename to extension/connecting.md diff --git a/developer-guide/installation.md b/extension/installation.md similarity index 85% rename from developer-guide/installation.md rename to extension/installation.md index 84a99dd..24b15ca 100644 --- a/developer-guide/installation.md +++ b/extension/installation.md @@ -33,7 +33,7 @@ Then import in your code: import { isConnected, requestAccess, signTransaction } from "@stellar/freighter-api"; ``` -> See the [Developer Guide](../developer-guide/) for the full API reference. +> See the [Developer Guide](../extension/) for the full API reference. ### CDN (script tag) @@ -67,6 +67,6 @@ const { address } = await window.freighterApi.requestAccess(); | I want to... | Go to | | ------------------------------------- | -------------------------------------------------------- | -| Connect my app to Freighter | [Connecting](../developer-guide/connecting.md) | -| Sign a transaction | [Signing](../developer-guide/signing.md) | -| Add a token | [Token Management](../developer-guide/token-management.md) | +| Connect my app to Freighter | [Connecting](../extension/connecting.md) | +| Sign a transaction | [Signing](../extension/signing.md) | +| Add a token | [Token Management](../extension/token-management.md) | diff --git a/developer-guide/reading-data.md b/extension/reading-data.md similarity index 100% rename from developer-guide/reading-data.md rename to extension/reading-data.md diff --git a/developer-guide/signing.md b/extension/signing.md similarity index 100% rename from developer-guide/signing.md rename to extension/signing.md diff --git a/developer-guide/token-management.md b/extension/token-management.md similarity index 100% rename from developer-guide/token-management.md rename to extension/token-management.md diff --git a/developer-guide/watching-changes.md b/extension/watching-changes.md similarity index 100% rename from developer-guide/watching-changes.md rename to extension/watching-changes.md From 968bb1fcad30f93c8c167ce8df2d134e4759db04 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 13:58:13 -0400 Subject: [PATCH 11/27] Reduce redundant non-custodial/user-control messaging in intro Define non-custodial once in the opening line and remove repeated references to private key handling and user control throughout the page. Addresses PR review feedback. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8f99aeb..40e4019 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # Welcome to Freighter -Freighter is a non-custodial wallet for the Stellar network, available as a browser extension and a mobile app. This guide will walk you through integrating Freighter into your dapp so your users can connect their wallets, sign transactions, and interact with Soroban smart contracts — all without you ever touching a private key. +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. The user reviews everything in the wallet's own UI and stays in full control. +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. You never handle private keys, seed phrases, or encryption. Freighter manages all of that on the user's device. +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." Every sensitive action goes through the extension's UI where the user has full visibility and control. +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. Your dapp generates a connection URI, the user scans a QR code with Freighter Mobile, and a secure relay session is established. From that point on, signing requests flow through the same pattern — your dapp proposes, the user reviews and approves in the wallet. @@ -22,8 +22,6 @@ If you want to support multiple Stellar wallets — not just Freighter — take ## Why Freighter -**Your users stay in control.** Every transaction, every signature, every connection is explicitly approved by the user in the wallet's own interface. There's no ambient access, no silent signing, and no stored sessions that persist beyond what the user expects. - **You ship faster.** The API surface is small and intentional. Connecting a wallet, signing a transaction, and reading account data each take one function call. There's no complex state machine to manage, no WebSocket lifecycle to babysit, and no cryptography to implement yourself. **It works where your users are.** Desktop users get the browser extension experience they're used to from other ecosystems. Mobile users get QR-code-based WalletConnect flows that feel native. You can support both from the same dapp with minimal branching. From c5600a3b0db0333847c08ab7234451da3107d190 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 13:58:54 -0400 Subject: [PATCH 12/27] Emphasize in-app browser WalletConnect flow over QR code scanning The primary mobile use case is the in-app browser where WalletConnect shows a wallet selection panel, not QR scanning. QR is now mentioned as a secondary desktop-to-mobile option. Addresses PR review feedback. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40e4019..0102396 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The integration model is simple: your dapp talks to the wallet, and the wallet t 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. Your dapp generates a connection URI, the user scans a QR code with Freighter Mobile, and a secure relay session is established. From that point on, signing requests flow through the same pattern — your dapp proposes, the user reviews and approves in the wallet. +For **mobile**, the integration works over WalletConnect v2. When a user opens your dapp in a mobile browser, WalletConnect presents a panel 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. From 54843892c3ce2d1d49e778dbee89fb28cc46d0d9 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 13:59:44 -0400 Subject: [PATCH 13/27] Fix Stellar Wallets Kit description: it supports mobile via WalletConnect Removes incorrect "desktop only" note. Addresses PR review feedback. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0102396..46afd97 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ For **mobile**, the integration works over WalletConnect v2. When a user opens y 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 desktop wallets so your users can connect with whichever browser extension they prefer. Note that it currently supports desktop wallets only, not mobile. +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. ## Why Freighter From 639263469ccd4916bfb5785d82e8aa74c40b1687 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 14:04:45 -0400 Subject: [PATCH 14/27] Rewrite Why Freighter to differentiate vs other Stellar wallets Focus on what sets Freighter apart from Lobstr, xBull, etc: SDF-maintained, most advanced feature set (custom tokens, auth entry inspection, message signing, Blockaid scanning), desktop + mobile. Addresses PR review feedback. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 46afd97..68f6a0d 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,11 @@ If you want to support multiple Stellar wallets — not just Freighter — take ## Why Freighter -**You ship faster.** The API surface is small and intentional. Connecting a wallet, signing a transaction, and reading account data each take one function call. There's no complex state machine to manage, no WebSocket lifecycle to babysit, and no cryptography to implement yourself. +**Built and maintained by SDF.** Freighter is developed by the Stellar Development Foundation, the same team behind the core Stellar protocol and SDKs. -**It works where your users are.** Desktop users get the browser extension experience they're used to from other ecosystems. Mobile users get QR-code-based WalletConnect flows that feel native. You can support both from the same dapp with minimal branching. +**Complete Stellar support.** Transaction signing, Soroban authorization entry signing with contract invocation inspection, SEP-53 message signing, custom 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. ## Choose Your Integration From e29fa23e5419098a2e277ed0157fb94ca6bf3043 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 14:09:07 -0400 Subject: [PATCH 15/27] Replace "Choose Your Integration" with straightforward Get Started links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extension is the only option for desktop, WalletConnect is the only option for mobile — there's no real choice. Simplified to direct links per platform. Addresses PR review feedback. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 68f6a0d..24cc076 100644 --- a/README.md +++ b/README.md @@ -28,31 +28,13 @@ If you want to support multiple Stellar wallets — not just Freighter — take **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. -## Choose Your Integration +## Get Started -### Extension +**Desktop** — integrate with the Freighter browser extension using `@stellar/freighter-api`. See the [Extension Guide](extension/installation.md). -Integrate with Freighter's browser extension using `@stellar/freighter-api`. Best for web apps that run in desktop browsers. +**Mobile** — connect to Freighter Mobile over WalletConnect v2. See the [Mobile Guide](mobile/README.md). -**Ideal for:** - -- React, Vue, or Angular web apps -- Server-rendered apps (Next.js, Nuxt) -- Static sites via CDN - -> Get started with the [Extension Installation Guide](extension/installation.md) - -### Mobile (WalletConnect) - -Connect to Freighter Mobile using the WalletConnect v2 protocol. Best for dapps that need to support mobile users. - -**Ideal for:** - -- Mobile-first dapps -- Cross-platform apps that support both desktop and mobile wallets -- Dapps using WalletConnect for multi-wallet support - -> Get started with the [Mobile Integration Guide](mobile/README.md) +If your dapp needs to support both, start with the extension guide and add WalletConnect as a fallback for mobile users. ## Quick links From bcc4c3607616c72353ef646e06eb4cb151de64f0 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 14:15:53 -0400 Subject: [PATCH 16/27] Remove recommendation label from requestAccess() Co-Authored-By: Claude Opus 4.6 (1M context) --- extension/connecting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extension/connecting.md b/extension/connecting.md index 5f1b597..3a6502c 100644 --- a/extension/connecting.md +++ b/extension/connecting.md @@ -42,7 +42,7 @@ if (result.isAllowed) { ### `setAllowed()` -Prompt the user to add your app to Freighter's **Allow List**. Once approved, the extension can provide user data without additional prompts. +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 }>` @@ -64,7 +64,7 @@ If the user has already authorized your app, `setAllowed()` resolves immediately ### `requestAccess()` -Prompt the user for permission to access their public key. This is the **recommended** way to initiate a connection with Freighter — it handles both authorization and key retrieval in one call. +Prompt the user for permission to access their public key. Like `setAllowed()`, this handles authorization — but also returns the public key in one call. **Returns:** `Promise<{ address: string } & { error?: FreighterApiError }>` From cbee6f2f0f40724860f8d4006329c8a09e5cb445 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 14:20:31 -0400 Subject: [PATCH 17/27] Add context for why dapps need addToken Co-Authored-By: Claude Opus 4.6 (1M context) --- extension/token-management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/token-management.md b/extension/token-management.md index c79921f..82ca41d 100644 --- a/extension/token-management.md +++ b/extension/token-management.md @@ -1,6 +1,6 @@ # Token Management -Add Soroban tokens to a user's Freighter wallet programmatically. +Add Soroban tokens to a user's Freighter wallet programmatically. This lets your dapp prompt users to track a token directly — without the user having to find and add the contract address in Freighter manually. ## Adding a Token From 2c68084059954e928257e8b66ac803238f205b1d Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 14:22:40 -0400 Subject: [PATCH 18/27] Align token terminology with Stellar developer docs Replace "Soroban token" and "custom token" with "contract token" to match developers.stellar.org nomenclature. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 2 +- extension/README.md | 2 +- extension/token-management.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 24cc076..3077b37 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ If you want to support multiple Stellar wallets — not just Freighter — take **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, custom token management, and Blockaid transaction scanning. +**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. diff --git a/extension/README.md b/extension/README.md index 3fe4a86..6ad9d5d 100644 --- a/extension/README.md +++ b/extension/README.md @@ -44,5 +44,5 @@ interface FreighterApiError { | [Connecting](connecting.md) | Detect Freighter, check permissions, request access | | [Reading Data](reading-data.md) | Get the user's address and network configuration | | [Signing](signing.md) | Sign transactions, auth entries, and messages | -| [Token Management](token-management.md) | Add Soroban tokens to the user's wallet | +| [Token Management](token-management.md) | Add contract tokens to the user's wallet | | [Watching Changes](watching-changes.md) | Monitor wallet state changes in real time | diff --git a/extension/token-management.md b/extension/token-management.md index 82ca41d..616ede8 100644 --- a/extension/token-management.md +++ b/extension/token-management.md @@ -1,6 +1,6 @@ # Token Management -Add Soroban tokens to a user's Freighter wallet programmatically. This lets your dapp prompt users to track a token directly — without the user having to find and add the contract address in Freighter manually. +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. ## Adding a Token @@ -19,7 +19,7 @@ When called, Freighter loads the token's **symbol**, **name**, **decimals**, and | Parameter | Type | Description | | --- | --- | --- | -| `contractId` | `string` | **Required.** The Soroban token contract ID. | +| `contractId` | `string` | **Required.** The contract token ID (`C...` address). | | `networkPassphrase` | `string` | Defaults to Pubnet's passphrase if omitted. | ### Example From be3d9884331c3cd0354f4b94a8471dd224105f63 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 14:25:26 -0400 Subject: [PATCH 19/27] Rename extension overview title to Extension Integration Co-Authored-By: Claude Opus 4.6 (1M context) --- extension/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/README.md b/extension/README.md index 6ad9d5d..659cf85 100644 --- a/extension/README.md +++ b/extension/README.md @@ -1,4 +1,4 @@ -# Developer Guide +# Extension Integration Integrate Freighter into your web application using `@stellar/freighter-api`. This library lets you send and receive data from a user's Freighter extension. From ded2da6fefc27f05dc56a3174197adfeec6aa1af Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 14:34:27 -0400 Subject: [PATCH 20/27] Move import examples from extension overview to installation page Addresses PR feedback that the overview references freighter-api imports before the reader has been through installation. Co-Authored-By: Claude Opus 4.6 (1M context) --- extension/README.md | 21 --------------------- extension/installation.md | 21 +++++++++++++++++---- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/extension/README.md b/extension/README.md index 659cf85..e4edbc6 100644 --- a/extension/README.md +++ b/extension/README.md @@ -4,27 +4,6 @@ Integrate Freighter into your web application using `@stellar/freighter-api`. Th `@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`. -## Importing - -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"; -``` - ## Error Type All methods return an optional `error` field of type `FreighterApiError`: diff --git a/extension/installation.md b/extension/installation.md index 24b15ca..d31d61f 100644 --- a/extension/installation.md +++ b/extension/installation.md @@ -27,13 +27,26 @@ npm install @stellar/freighter-api yarn add @stellar/freighter-api ``` -Then import in your code: +Then import in your code. You can import the entire library: -```typescript -import { isConnected, requestAccess, signTransaction } from "@stellar/freighter-api"; +```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 [Developer Guide](../extension/) for the full API reference. +> See the [Extension Integration](../extension/) for the full API reference. ### CDN (script tag) From b033a7397db30ad320366b91defaa96cf740b35c Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 14:37:48 -0400 Subject: [PATCH 21/27] Move dual-integration guidance to Get Started, remove inaccurate details Removed the "Supporting Both Extension and Mobile" section from the mobile overview (contained inaccurate window.freighterApi reference). Added a concise "Both" option to the Get Started section instead. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 2 +- mobile/README.md | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/README.md b/README.md index 3077b37..a2395af 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ If you want to support multiple Stellar wallets — not just Freighter — take **Mobile** — connect to Freighter Mobile over WalletConnect v2. See the [Mobile Guide](mobile/README.md). -If your dapp needs to support both, start with the extension guide and add WalletConnect as a fallback for mobile users. +**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 diff --git a/mobile/README.md b/mobile/README.md index 00ebcb1..b93817a 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -48,14 +48,6 @@ Freighter Mobile uses **Blockaid** scanning to protect users: `stellar_signMessage` and `stellar_signAuthEntry` do not trigger additional scans — the site was already scanned during connection. -## Supporting Both Extension and Mobile - -If your dapp needs to support both desktop (extension) and mobile users, the typical pattern is: - -1. **Detect the environment** — check if `window.freighterApi` is available for extension, otherwise offer WalletConnect -2. **Use WalletConnect as the fallback** — mobile users scan a QR code; desktop users without the extension can use the WalletConnect modal -3. **Unify your signing interface** — both paths produce the same output (signed XDR), so your submission logic stays the same - ## See also - [WalletConnect v2 Docs](https://docs.walletconnect.com/) From 14fcca7abd5158b4d0a37a79def97433469a59b5 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 14:42:36 -0400 Subject: [PATCH 22/27] Add hint that isConnected can detect desktop vs mobile Co-Authored-By: Claude Opus 4.6 (1M context) --- extension/connecting.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extension/connecting.md b/extension/connecting.md index 3a6502c..d23db0b 100644 --- a/extension/connecting.md +++ b/extension/connecting.md @@ -20,6 +20,10 @@ if (result.isConnected) { } ``` +{% hint style="info" %} +`isConnected()` can also be used to detect if a user is on desktop or mobile — if it returns `false`, the user is likely on a mobile device and you should use WalletConnect instead. +{% endhint %} + ## Checking Authorization ### `isAllowed()` From 53048a9802a20abe8c44b0e61b2b39abad40e548 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 14:50:44 -0400 Subject: [PATCH 23/27] Fix inconsistencies between extension and mobile docs 1. "dApp" -> "Dapp" in mobile/installation.md (consistent with rest) 2. Network table: "Mainnet" -> "Mainnet (PUBLIC)" to map to enum value 3. "Pubnet's passphrase" -> "Mainnet passphrase" in token-management 4. Mobile connecting title: "Connecting" -> "Connecting to Freighter Mobile" 5. Added note about different response field names between extension and mobile (signedTxXdr vs signedXDR, signedMessage vs signature) 6. Clarified SEP-43 reference in mobile API table 7. Added error tables to extension signing methods (matching mobile) 8. Aligned parameter/example headings to bold text (matching mobile) 9. SUMMARY.md: "Extension" -> "Extension (freighter-api)" for symmetry 10. Added full connect-sign-submit example to mobile signing Co-Authored-By: Claude Opus 4.6 (1M context) --- SUMMARY.md | 2 +- extension/signing.md | 27 ++++++++++++--- extension/token-management.md | 2 +- mobile/README.md | 4 +-- mobile/connecting.md | 2 +- mobile/installation.md | 2 +- mobile/signing.md | 65 +++++++++++++++++++++++++++++++++++ 7 files changed, 94 insertions(+), 10 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index 0f0240e..083da4b 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -2,7 +2,7 @@ * [Introduction](README.md) -## Extension +## Extension (freighter-api) * [Installation](extension/installation.md) * [Overview](extension/README.md) diff --git a/extension/signing.md b/extension/signing.md index 0d8a29b..1110ba0 100644 --- a/extension/signing.md +++ b/extension/signing.md @@ -22,7 +22,7 @@ The user will be prompted to enter their password (if the extension doesn't curr The private key is cached for **5 minutes** after the user enters their password. The transaction must be reviewed and accepted within that window. {% endhint %} -### Parameters +**Parameters** | Parameter | Type | Description | | --- | --- | --- | @@ -35,7 +35,7 @@ The private key is cached for **5 minutes** after the user enters their password Passing `network` or `networkPassphrase` lets Freighter warn the user if their wallet is configured to the wrong network. {% endhint %} -### Example +**Example** ```typescript import { signTransaction } from "@stellar/freighter-api"; @@ -53,6 +53,13 @@ if (error) { } ``` +**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()` @@ -67,7 +74,7 @@ signAuthEntry(entryXdr: string, opts: { 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 +**Example** ```typescript import { signAuthEntry } from "@stellar/freighter-api"; @@ -83,6 +90,12 @@ if (error) { } ``` +**Errors** + +| Condition | Error message | +| --- | --- | +| User rejected | `"The user rejected this request."` | + ## Signing a Message ### `signMessage()` @@ -95,7 +108,7 @@ signMessage(message: string, opts: { }) -> Promise<{ signedMessage: string | null; signerAddress: string } & { error?: FreighterApiError }> ``` -### Example +**Example** ```typescript import { signMessage } from "@stellar/freighter-api"; @@ -112,6 +125,12 @@ if (error) { } ``` +**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: diff --git a/extension/token-management.md b/extension/token-management.md index 616ede8..0f0dcb8 100644 --- a/extension/token-management.md +++ b/extension/token-management.md @@ -20,7 +20,7 @@ When called, Freighter loads the token's **symbol**, **name**, **decimals**, and | Parameter | Type | Description | | --- | --- | --- | | `contractId` | `string` | **Required.** The contract token ID (`C...` address). | -| `networkPassphrase` | `string` | Defaults to Pubnet's passphrase if omitted. | +| `networkPassphrase` | `string` | Defaults to Mainnet passphrase if omitted. | ### Example diff --git a/mobile/README.md b/mobile/README.md index b93817a..5514394 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -6,7 +6,7 @@ Connect your dapp to Freighter Mobile using the [WalletConnect v2](https://docs. | Network | Chain ID | | --- | --- | -| Mainnet | `stellar:pubnet` | +| Mainnet (PUBLIC) | `stellar:pubnet` | | Testnet | `stellar:testnet` | ## API Reference @@ -16,7 +16,7 @@ Connect your dapp to Freighter Mobile using the [WalletConnect v2](https://docs. | [`stellar_signXDR`](signing.md#stellar_signxdr) | Sign a transaction and return the signed XDR | | [`stellar_signAndSubmitXDR`](signing.md#stellar_signandsubmitxdr) | Sign and submit a transaction to Horizon | | [`stellar_signMessage`](signing.md#stellar_signmessage) | Sign an arbitrary UTF-8 message ([SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md)) | -| [`stellar_signAuthEntry`](signing.md#stellar_signauthentry) | Sign a Soroban authorization entry preimage ([SEP-43](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0043.md)) | +| [`stellar_signAuthEntry`](signing.md#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 diff --git a/mobile/connecting.md b/mobile/connecting.md index fc3ee51..bdd87de 100644 --- a/mobile/connecting.md +++ b/mobile/connecting.md @@ -1,4 +1,4 @@ -# Connecting +# Connecting to Freighter Mobile Establish a WalletConnect session with Freighter Mobile, handle session lifecycle events, and disconnect. diff --git a/mobile/installation.md b/mobile/installation.md index c264306..f02450e 100644 --- a/mobile/installation.md +++ b/mobile/installation.md @@ -25,7 +25,7 @@ import SignClient from "@walletconnect/sign-client"; const client = await SignClient.init({ projectId: "YOUR_PROJECT_ID", metadata: { - name: "My Stellar dApp", + name: "My Stellar Dapp", description: "A dapp that integrates with Freighter Mobile", url: "https://my-dapp.com", icons: ["https://my-dapp.com/icon.png"], diff --git a/mobile/signing.md b/mobile/signing.md index fc82723..beb4d2f 100644 --- a/mobile/signing.md +++ b/mobile/signing.md @@ -2,6 +2,10 @@ Sign transactions, messages, and Soroban authorization entries via WalletConnect. +{% 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` @@ -243,3 +247,64 @@ authEntry | 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. This handles both the **in-app browser** scenario (user opens your dapp inside Freighter Mobile) and the **external browser** scenario (user visits your dapp in a regular browser). + +```typescript +import SignClient from "@walletconnect/sign-client"; + +// 1. Detect if we're inside Freighter Mobile's in-app browser. +// When Freighter Mobile opens a dapp in its built-in browser, +// it injects a global object to identify itself. +const isFreighterInAppBrowser = + window.stellar?.provider === "freighter" && + window.stellar?.platform === "mobile"; + +// 2. Initialize WalletConnect client +const client = await SignClient.init({ + projectId: "YOUR_PROJECT_ID", + 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"], + }, +}); + +// 3. Create session +const { uri, approval } = await client.connect({ + requiredNamespaces: { + stellar: { + methods: ["stellar_signXDR", "stellar_signAndSubmitXDR"], + chains: ["stellar:pubnet"], + events: ["accountsChanged"], + }, + }, +}); + +if (isFreighterInAppBrowser) { + // In-app browser: the wallet is the browser host, so the session + // is approved automatically — no QR code or modal needed. +} else { + // External browser: display `uri` as a QR code for the user to + // scan, or pass it to a WalletConnect modal. + console.log("Scan this URI:", uri); +} + +const session = await approval(); + +// 4. Sign a transaction +const xdr = "AAAAAgAAAAA..."; // your assembled transaction XDR +const result = await client.request({ + topic: session.topic, + chainId: "stellar:pubnet", + request: { + method: "stellar_signXDR", + params: { xdr }, + }, +}); + +console.log("Signed XDR:", result.signedXDR); +``` From 3bcf74788760769ba12a914bbc2983acc781fc7e Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Fri, 10 Apr 2026 15:05:42 -0400 Subject: [PATCH 24/27] Fix isConnected hint and mobile full example requiredNamespaces - isConnected() hint: clarify it checks for extension installation, not mobile detection. Point to window.stellar?.platform for that. - Mobile signing full example: add all 4 methods to requiredNamespaces (was missing stellar_signMessage and stellar_signAuthEntry). Co-Authored-By: Claude Opus 4.6 (1M context) --- extension/connecting.md | 2 +- mobile/signing.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extension/connecting.md b/extension/connecting.md index d23db0b..3027f8f 100644 --- a/extension/connecting.md +++ b/extension/connecting.md @@ -21,7 +21,7 @@ if (result.isConnected) { ``` {% hint style="info" %} -`isConnected()` can also be used to detect if a user is on desktop or mobile — if it returns `false`, the user is likely on a mobile device and you should use WalletConnect instead. +`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 diff --git a/mobile/signing.md b/mobile/signing.md index beb4d2f..6acb14c 100644 --- a/mobile/signing.md +++ b/mobile/signing.md @@ -277,7 +277,7 @@ const client = await SignClient.init({ const { uri, approval } = await client.connect({ requiredNamespaces: { stellar: { - methods: ["stellar_signXDR", "stellar_signAndSubmitXDR"], + methods: ["stellar_signXDR", "stellar_signAndSubmitXDR", "stellar_signMessage", "stellar_signAuthEntry"], chains: ["stellar:pubnet"], events: ["accountsChanged"], }, From 7ececcd93462e3e0985fbd808a1ed1938136031f Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Mon, 13 Apr 2026 14:48:02 -0400 Subject: [PATCH 25/27] Apply Cassio's review feedback on WalletConnect docs - Fix WalletConnect docs links to point to docs.walletconnect.network/app-sdk/overview (the .com URL routes to WalletConnect Pay) - Rename WalletConnect Cloud to WalletConnect Dashboard with new dashboard.walletconnect.com URL - Note that Stellar Wallets Kit's WC module only supports 2 of 4 Freighter Mobile signing methods Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 6 ++++-- mobile/README.md | 4 ++-- mobile/installation.md | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a2395af..2e6616d 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,14 @@ The integration model is simple: your dapp talks to the wallet, and the wallet t 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 panel 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. +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. @@ -51,4 +53,4 @@ If you want to support multiple Stellar wallets — not just Freighter — take - [GitHub — Freighter Mobile](https://github.com/stellar/freighter-mobile) - [Chrome Extension Store](https://chromewebstore.google.com/detail/freighter/bcacfldlkkdogcmkkibnjlakofdplcbk) - [Stellar Developer Docs](https://developers.stellar.org) -- [WalletConnect v2 Docs](https://docs.walletconnect.com/) +- [WalletConnect v2 Docs](https://docs.walletconnect.network/app-sdk/overview) diff --git a/mobile/README.md b/mobile/README.md index 5514394..3a4d540 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -1,6 +1,6 @@ # Mobile Integration -Connect your dapp to Freighter Mobile using the [WalletConnect v2](https://docs.walletconnect.com/) protocol. All methods follow the standard JSON-RPC 2.0 request/response format over a WalletConnect session. +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 @@ -50,7 +50,7 @@ Freighter Mobile uses **Blockaid** scanning to protect users: ## See also -- [WalletConnect v2 Docs](https://docs.walletconnect.com/) +- [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) diff --git a/mobile/installation.md b/mobile/installation.md index f02450e..d291e1c 100644 --- a/mobile/installation.md +++ b/mobile/installation.md @@ -4,7 +4,7 @@ Get up and running with WalletConnect so your dapp can connect to Freighter Mobi ## Prerequisites -1. Sign up at [WalletConnect Cloud](https://cloud.walletconnect.com/) and create a project to get your **Project ID**. +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 From 91e848f2455d2fbb5cd04aba8c84019b590aabfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Marcos=20Goulart?= <3228151+CassioMG@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:48:27 -0700 Subject: [PATCH 26/27] Migrate mobile docs to @walletconnect/universal-provider (#2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Migrate mobile installation to @walletconnect/universal-provider Replace @walletconnect/sign-client with @walletconnect/universal-provider in installation instructions. Add Stellar Wallets Kit alternative note and link to Reown docs. Co-Authored-By: Claude Opus 4.6 (1M context) * Migrate mobile connecting to UniversalProvider Replace SignClient connect/event/disconnect patterns with UniversalProvider equivalents. Add public key extraction example. Co-Authored-By: Claude Opus 4.6 (1M context) * Migrate mobile signing examples to UniversalProvider Replace all client.request() calls with provider.request() pattern. Update full connect-sign-submit example. Co-Authored-By: Claude Opus 4.6 (1M context) * Remove isFreighterInAppBrowser branching from full example The in-app browser detection is a Stellar Wallets Kit implementation detail, not needed by dapp developers using UniversalProvider directly. Simplified the full example and clarified the in-app vs external browser UX difference in prose instead. Co-Authored-By: Claude Opus 4.6 (1M context) * Add AppKit modal for wallet selection and QR code display - Add @reown/appkit to installation alongside universal-provider - Initialize createAppKit with manualWCControl for standalone modal - Use modal.open({ uri }) in display_uri handler instead of console.log - Add links to Reown UniversalProvider and AppKit modal docs Co-Authored-By: Claude Opus 4.6 (1M context) * Clarify that wallet selection triggers deep-linking Co-Authored-By: Claude Opus 4.6 (1M context) * Clarify that QR code and wallet list are in the same modal Co-Authored-By: Claude Opus 4.6 (1M context) * Pass universalProvider to createAppKit Wire the provider to the modal via the universalProvider option, matching the pattern from the Reown migration guide. Co-Authored-By: Claude Opus 4.6 (1M context) * Fix review issues: modal API, imports, and cross-file clarity Critical: - Replace display_uri + modal.open({ uri }) with modal.open() before provider.connect() — AppKit handles URI display automatically when manualWCControl and universalProvider are set Important: - Add comment explaining mainnet placeholder in AppKit networks config - Fix README chainId hint to reference provider.request() argument - Add session null check after provider.connect() Suggestions: - Switch to named import { UniversalProvider } matching Reown docs - Add cross-file reference notes to connecting.md and signing.md Co-Authored-By: Claude Opus 4.6 (1M context) * Fix: use namespaces instead of requiredNamespaces for UniversalProvider UniversalProvider.ConnectParams does not accept requiredNamespaces — the correct field is namespaces. Passing requiredNamespaces was silently ignored, meaning the Stellar namespace would not be registered. Also added a note explaining that UniversalProvider treats namespaces as optional on the first connection (they become required on reconnections via storage). dApps should verify session.namespaces.stellar.methods after connecting. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- mobile/README.md | 2 +- mobile/connecting.md | 51 ++++++++++------ mobile/installation.md | 36 ++++++++++-- mobile/signing.md | 128 +++++++++++++++++++---------------------- 4 files changed, 125 insertions(+), 92 deletions(-) diff --git a/mobile/README.md b/mobile/README.md index 3a4d540..b9325f8 100644 --- a/mobile/README.md +++ b/mobile/README.md @@ -34,7 +34,7 @@ All error responses use JSON-RPC 2.0 format with code `5000`: ``` {% 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. In WalletConnect v2, always specify `chainId` as a top-level field on the request, not inside `params`. +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 diff --git a/mobile/connecting.md b/mobile/connecting.md index bdd87de..bf9dcac 100644 --- a/mobile/connecting.md +++ b/mobile/connecting.md @@ -2,13 +2,19 @@ Establish a WalletConnect session with Freighter Mobile, handle session lifecycle events, and disconnect. +Assumes `provider` and `modal` are initialized as shown in [Installation](installation.md). + ## Creating a Session -Request a connection with the Stellar namespace. This generates a URI you can display as a QR code or use as a deep link for mobile users. +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 -const { uri, approval } = await client.connect({ - requiredNamespaces: { +// 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", @@ -22,16 +28,33 @@ const { uri, approval } = await client.connect({ }, }); -// Display `uri` as a QR code for the user to scan with Freighter Mobile -console.log("Scan this URI:", uri); +if (!session) { + throw new Error("Connection failed"); +} -// Wait for the user to approve in Freighter Mobile -const session = await approval(); +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 `requiredNamespaces` for methods your dapp needs to function. Use `optionalNamespaces` for methods that enhance the experience but aren't essential. +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 @@ -40,12 +63,12 @@ Listen for session lifecycle events to keep your UI in sync: ```typescript // Session was disconnected by the wallet -client.on("session_delete", ({ topic }) => { +provider.on("session_delete", ({ topic }) => { console.log("Session deleted:", topic); }); // Session expired -client.on("session_expire", ({ topic }) => { +provider.on("session_expire", ({ topic }) => { console.log("Session expired:", topic); }); ``` @@ -55,13 +78,7 @@ client.on("session_expire", ({ topic }) => { When the user wants to disconnect: ```typescript -await client.disconnect({ - topic: session.topic, - reason: { - code: 6000, - message: "User disconnected", - }, -}); +await provider.disconnect(); ``` ## Next steps diff --git a/mobile/installation.md b/mobile/installation.md index d291e1c..80e8b48 100644 --- a/mobile/installation.md +++ b/mobile/installation.md @@ -10,20 +10,25 @@ Get up and running with WalletConnect so your dapp can connect to Freighter Mobi ## npm / yarn ```bash -npm install @walletconnect/sign-client @walletconnect/types +npm install @walletconnect/universal-provider @reown/appkit ``` ```bash -yarn add @walletconnect/sign-client @walletconnect/types +yarn add @walletconnect/universal-provider @reown/appkit ``` -Then initialize the client: +Then initialize the provider and modal: ```typescript -import SignClient from "@walletconnect/sign-client"; +import { UniversalProvider } from "@walletconnect/universal-provider"; +import { createAppKit } from "@reown/appkit/core"; +import { mainnet } from "@reown/appkit/networks"; -const client = await SignClient.init({ - projectId: "YOUR_PROJECT_ID", +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", @@ -31,6 +36,17 @@ const client = await SignClient.init({ 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 @@ -39,3 +55,11 @@ const client = await SignClient.init({ | ------------------------------------- | ---------------------------------------------- | | Connect to Freighter Mobile | [Connecting](connecting.md) | | Sign a transaction | [Signing](signing.md) | + +## 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 %} diff --git a/mobile/signing.md b/mobile/signing.md index 6acb14c..12e039f 100644 --- a/mobile/signing.md +++ b/mobile/signing.md @@ -2,6 +2,8 @@ Sign transactions, messages, and Soroban authorization entries via WalletConnect. +Assumes `provider` and `modal` are initialized as shown in [Installation](installation.md). + {% 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 %} @@ -25,16 +27,13 @@ Sign a transaction and return the signed XDR. The transaction is **not** submitt | `signedXDR` | `string` | Base64-encoded signed transaction XDR. | ```typescript -const result = await client.request({ - topic: session.topic, - chainId: "stellar:pubnet", - request: { +const result = await provider.request( + { method: "stellar_signXDR", - params: { - xdr: "AAAAAgAAAAA...", - }, + params: { xdr: "AAAAAgAAAAA..." }, }, -}); + "stellar:pubnet", +); console.log("Signed XDR:", result.signedXDR); ``` @@ -67,16 +66,13 @@ Sign a transaction **and** submit it to Horizon in one step. | `status` | `string` | `"success"` when the transaction is signed and submitted. | ```typescript -const result = await client.request({ - topic: session.topic, - chainId: "stellar:pubnet", - request: { +const result = await provider.request( + { method: "stellar_signAndSubmitXDR", - params: { - xdr: "AAAAAgAAAAA...", - }, + params: { xdr: "AAAAAgAAAAA..." }, }, -}); + "stellar:pubnet", +); console.log("Status:", result.status); // "success" ``` @@ -116,16 +112,13 @@ Sign an arbitrary UTF-8 text message with the wallet's active key, following [SE | `signature` | `string` | Base64-encoded Ed25519 signature (per SEP-53). | ```typescript -const result = await client.request({ - topic: session.topic, - chainId: "stellar:pubnet", - request: { +const result = await provider.request( + { method: "stellar_signMessage", - params: { - message: "Please sign to verify account ownership", - }, + params: { message: "Please sign to verify account ownership" }, }, -}); + "stellar:pubnet", +); console.log("Signature:", result.signature); ``` @@ -168,16 +161,13 @@ Freighter Mobile performs a **Blockaid site scan** during the initial WalletConn | `signerAddress` | Stellar public key (G-address) of the account that produced the signature. | ```typescript -const result = await client.request({ - topic: session.topic, - chainId: "stellar:pubnet", - request: { +const result = await provider.request( + { method: "stellar_signAuthEntry", - params: { - entryXdr: "AAAAAQ...", - }, + params: { entryXdr: "AAAAAQ..." }, }, -}); + "stellar:pubnet", +); console.log("Signature:", result.signedAuthEntry); console.log("Signer:", result.signerAddress); @@ -210,15 +200,14 @@ const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization( const entryXdr = preimage.toXDR("base64"); -// 3. Send via WalletConnect (using the client from the connecting guide) -const result = await client.request({ - topic: session.topic, - chainId: "stellar:pubnet", - request: { +// 3. Send via WalletConnect +const result = await provider.request( + { method: "stellar_signAuthEntry", params: { entryXdr }, }, -}); + "stellar:pubnet", +); // 4. Attach the signature back to the auth entry const signerRawKey = Keypair.fromPublicKey(result.signerAddress).rawPublicKey(); @@ -250,21 +239,18 @@ authEntry ## Full Example: Connect, Sign, and Submit -A complete flow from connection to transaction signing. This handles both the **in-app browser** scenario (user opens your dapp inside Freighter Mobile) and the **external browser** scenario (user visits your dapp in a regular browser). +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 SignClient from "@walletconnect/sign-client"; - -// 1. Detect if we're inside Freighter Mobile's in-app browser. -// When Freighter Mobile opens a dapp in its built-in browser, -// it injects a global object to identify itself. -const isFreighterInAppBrowser = - window.stellar?.provider === "freighter" && - window.stellar?.platform === "mobile"; - -// 2. Initialize WalletConnect client -const client = await SignClient.init({ - projectId: "YOUR_PROJECT_ID", +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", @@ -273,9 +259,21 @@ const client = await SignClient.init({ }, }); -// 3. Create session -const { uri, approval } = await client.connect({ - requiredNamespaces: { +// 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"], @@ -284,27 +282,21 @@ const { uri, approval } = await client.connect({ }, }); -if (isFreighterInAppBrowser) { - // In-app browser: the wallet is the browser host, so the session - // is approved automatically — no QR code or modal needed. -} else { - // External browser: display `uri` as a QR code for the user to - // scan, or pass it to a WalletConnect modal. - console.log("Scan this URI:", uri); +if (!session) { + throw new Error("Connection failed"); } -const session = await approval(); +modal.close(); -// 4. Sign a transaction +// 3. Sign a transaction const xdr = "AAAAAgAAAAA..."; // your assembled transaction XDR -const result = await client.request({ - topic: session.topic, - chainId: "stellar:pubnet", - request: { +const result = await provider.request( + { method: "stellar_signXDR", params: { xdr }, }, -}); + "stellar:pubnet", +); console.log("Signed XDR:", result.signedXDR); ``` From 70a7b7516996e9bf920e8d325faf4321363fe8c7 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Tue, 14 Apr 2026 17:25:06 -0400 Subject: [PATCH 27/27] Address Jake's review feedback - Clarify requestAccess() does full Allow List authorization, not just public key access - Remove internal password/caching details from signing docs (not actionable for dapps) - Add note that addToken() will become a no-op once auto-detect ships - Remove "Building the entryXdr" section from mobile signing (out of scope) Co-Authored-By: Claude Opus 4.6 (1M context) --- extension/connecting.md | 2 +- extension/signing.md | 6 +--- extension/token-management.md | 4 +++ mobile/signing.md | 55 ----------------------------------- 4 files changed, 6 insertions(+), 61 deletions(-) diff --git a/extension/connecting.md b/extension/connecting.md index 3027f8f..fa69097 100644 --- a/extension/connecting.md +++ b/extension/connecting.md @@ -68,7 +68,7 @@ If the user has already authorized your app, `setAllowed()` resolves immediately ### `requestAccess()` -Prompt the user for permission to access their public key. Like `setAllowed()`, this handles authorization — but also returns the public key in one call. +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 }>` diff --git a/extension/signing.md b/extension/signing.md index 1110ba0..d1376fe 100644 --- a/extension/signing.md +++ b/extension/signing.md @@ -16,11 +16,7 @@ signTransaction(xdr: string, opts?: { }) -> Promise<{ signedTxXdr: string; signerAddress: string } & { error?: FreighterApiError }> ``` -The user will be prompted to enter their password (if the extension doesn't currently hold the private key) and then review the transaction details before signing. - -{% hint style="warning" %} -The private key is cached for **5 minutes** after the user enters their password. The transaction must be reviewed and accepted within that window. -{% endhint %} +The user will review the transaction details before signing. **Parameters** diff --git a/extension/token-management.md b/extension/token-management.md index 0f0dcb8..a3a8bde 100644 --- a/extension/token-management.md +++ b/extension/token-management.md @@ -2,6 +2,10 @@ 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()` diff --git a/mobile/signing.md b/mobile/signing.md index 12e039f..e0013e6 100644 --- a/mobile/signing.md +++ b/mobile/signing.md @@ -173,61 +173,6 @@ console.log("Signature:", result.signedAuthEntry); console.log("Signer:", result.signerAddress); ``` -### Building the `entryXdr` - -Here's how to construct the preimage from a contract simulation result and attach the signature back to the auth entry: - -```typescript -import { xdr, Networks, hash, Keypair } from "@stellar/stellar-sdk"; - -// 1. Get the SorobanAuthorizationEntry from simulation -// Assumes `server` (SorobanRpc.Server) and `tx` (Transaction) are already set up -const simulationResult = await server.simulateTransaction(tx); -const authEntry = simulationResult.result.auth[0]; - -// 2. Build the HashIdPreimage -const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization( - new xdr.HashIdPreimageSorobanAuthorization({ - networkId: hash(new TextEncoder().encode(Networks.PUBLIC)), - nonce: authEntry.credentials().address().nonce(), - signatureExpirationLedger: authEntry - .credentials() - .address() - .signatureExpirationLedger(), - invocation: authEntry.rootInvocation(), - }), -); - -const entryXdr = preimage.toXDR("base64"); - -// 3. Send via WalletConnect -const result = await provider.request( - { - method: "stellar_signAuthEntry", - params: { entryXdr }, - }, - "stellar:pubnet", -); - -// 4. Attach the signature back to the auth entry -const signerRawKey = Keypair.fromPublicKey(result.signerAddress).rawPublicKey(); -const signatureBytes = Uint8Array.from(atob(result.signedAuthEntry), (c) => c.charCodeAt(0)); -const signatureScVal = xdr.ScVal.scvMap([ - new xdr.ScMapEntry({ - key: xdr.ScVal.scvSymbol("public_key"), - val: xdr.ScVal.scvBytes(signerRawKey), - }), - new xdr.ScMapEntry({ - key: xdr.ScVal.scvSymbol("signature"), - val: xdr.ScVal.scvBytes(signatureBytes), - }), -]); -authEntry - .credentials() - .address() - .signature(xdr.ScVal.scvVec([signatureScVal])); -``` - **Errors** | Condition | Error message |