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..2e6616d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,56 @@ -# freighter-developer-docs -Freighter wallet developer documentation +# Welcome to Freighter + +Freighter is a non-custodial wallet for the Stellar network, available as a browser extension and a mobile app. Non-custodial means your users hold their own keys — your dapp never sees or stores private keys. This guide walks you through integrating Freighter so your users can connect their wallets, sign transactions, and interact with Soroban smart contracts. + +## What You Can Build + +With Freighter, your dapp can connect a user's wallet with a single call — no signup form, no password, just a familiar wallet popup and an immediate public key. From there you can read their active network, hand off transactions for signing, and submit to the network. + +For dapps that integrate with smart contracts, Freighter also handles authorization entry signing for smart contract calls and arbitrary message signing for account verification. + +## How It Works + +The integration model is simple: your dapp talks to the wallet, and the wallet talks to the user. + +For **desktop browsers**, you integrate with the Freighter extension through a lightweight JavaScript library. It injects itself into the page, and your dapp calls methods like "is the wallet connected?", "what's the user's address?", and "please sign this transaction." + +For **mobile**, the integration works over WalletConnect v2. When a user opens your dapp in a mobile browser, WalletConnect presents a modal where they select Freighter as their wallet and approve the connection. On desktop, the same flow can display a QR code for the user to scan. Either way, a secure relay session is established and signing requests flow through the same pattern — your dapp proposes, the user reviews and approves in the wallet. + +Both paths produce the same output: signed transactions you can submit to the Stellar network. So your backend and submission logic stay the same regardless of whether your user connected from a laptop or a phone. + +If you want to support multiple Stellar wallets — not just Freighter — take a look at [Stellar Wallets Kit](https://stellarwalletskit.dev/). It provides a unified interface across Stellar wallets, including browser extensions and WalletConnect-based mobile wallets, so your users can connect with whichever wallet they prefer. + +> **Note:** Stellar Wallets Kit's WalletConnect module currently only exposes `stellar_signXDR` and `stellar_signAndSubmitXDR`. If your dapp needs `stellar_signMessage` or `stellar_signAuthEntry` against Freighter Mobile, integrate WalletConnect directly for now (tracked in [stellar/freighter-mobile#815](https://github.com/stellar/freighter-mobile/issues/815)). + +## Why Freighter + +**Built and maintained by SDF.** Freighter is developed by the Stellar Development Foundation, the same team behind the core Stellar protocol and SDKs. + +**Complete Stellar support.** Transaction signing, Soroban authorization entry signing with contract invocation inspection, SEP-53 message signing, contract token management, and Blockaid transaction scanning. + +**Desktop and mobile.** Browser extension for desktop, WalletConnect for mobile. Both paths produce the same signed output, so your dapp supports both with minimal branching. + +## Get Started + +**Desktop** — integrate with the Freighter browser extension using `@stellar/freighter-api`. See the [Extension Guide](extension/installation.md). + +**Mobile** — connect to Freighter Mobile over WalletConnect v2. See the [Mobile Guide](mobile/README.md). + +**Both** — if your dapp needs to support desktop and mobile users, use both integration paths. Both produce the same output (signed XDR), so your submission logic stays the same regardless of how the user connected. + +## Quick links + +| I want to... | Extension | Mobile | +| ------------------------------------- | -------------------------------------------------- | ---------------------------------------------------- | +| Install / set up | [Installation](extension/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 + +- [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.network/app-sdk/overview) diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..083da4b --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,24 @@ +# Table of contents + +* [Introduction](README.md) + +## Extension (freighter-api) + +* [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) + +* [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/extension/README.md b/extension/README.md new file mode 100644 index 0000000..e4edbc6 --- /dev/null +++ b/extension/README.md @@ -0,0 +1,27 @@ +# 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. + +`@stellar/freighter-api` adheres to the [SEP-43](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0043.md) standard for wallet interfaces on Stellar, and also offers additional methods beyond the spec such as `getNetworkDetails`, `addToken`, and `WatchWalletChanges`. + +## Error Type + +All methods return an optional `error` field of type `FreighterApiError`: + +```typescript +interface FreighterApiError { + code: number; + message: string; + ext?: string[]; +} +``` + +## API Reference + +| Category | Description | +| --- | --- | +| [Connecting](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 contract tokens to the user's wallet | +| [Watching Changes](watching-changes.md) | Monitor wallet state changes in real time | diff --git a/extension/connecting.md b/extension/connecting.md new file mode 100644 index 0000000..fa69097 --- /dev/null +++ b/extension/connecting.md @@ -0,0 +1,101 @@ +# 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?: FreighterApiError }>` + +```typescript +import { isConnected } from "@stellar/freighter-api"; + +const result = await isConnected(); + +if (result.isConnected) { + console.log("Freighter is installed"); +} +``` + +{% hint style="info" %} +`isConnected()` checks whether the Freighter extension is installed. Note that it returns `false` for any user without the extension — not just mobile users. To detect Freighter Mobile's in-app browser specifically, check for `window.stellar?.platform === "mobile"`. +{% endhint %} + +## Checking Authorization + +### `isAllowed()` + +Check if the user has previously authorized your app to receive data from Freighter. + +**Returns:** `Promise<{ isAllowed: boolean } & { error?: FreighterApiError }>` + +```typescript +import { isAllowed } from "@stellar/freighter-api"; + +const result = await isAllowed(); + +if (result.isAllowed) { + console.log("App is on the Allow List"); +} +``` + +## Requesting Authorization + +### `setAllowed()` + +Prompt the user to authorize your app and add it to Freighter's **Allow List**. Once approved, the extension can provide user data without additional prompts. + +**Returns:** `Promise<{ isAllowed: boolean } & { error?: FreighterApiError }>` + +```typescript +import { setAllowed } from "@stellar/freighter-api"; + +const result = await setAllowed(); + +if (result.isAllowed) { + console.log("App added to Allow List"); +} +``` + +{% hint style="info" %} +If the user has already authorized your app, `setAllowed()` resolves immediately with `{ isAllowed: true }`. +{% endhint %} + +## Requesting Access + +### `requestAccess()` + +Prompt the user to add your dapp to Freighter's Allow List and return their public key in one call. This is the recommended way to connect — it combines authorization and data retrieval in a single step. + +**Returns:** `Promise<{ address: string } & { error?: FreighterApiError }>` + +If the user has previously authorized your app, the public key is returned immediately without a popup. + +```typescript +import { isConnected, requestAccess } from "@stellar/freighter-api"; + +// Always check isConnected first +const connectResult = await isConnected(); +if (!connectResult.isConnected) { + console.error("Freighter is not installed"); + return; +} + +const accessResult = await requestAccess(); +if (accessResult.error) { + console.error("Access denied:", accessResult.error.message); +} else { + console.log("Public key:", accessResult.address); +} +``` + +{% hint style="warning" %} +Always check `isConnected()` before calling `requestAccess()` to ensure the extension is available. +{% endhint %} + +## Next steps + +Once connected, you can [read the user's address and network](reading-data.md) or [sign transactions](signing.md). diff --git a/extension/installation.md b/extension/installation.md new file mode 100644 index 0000000..d31d61f --- /dev/null +++ b/extension/installation.md @@ -0,0 +1,85 @@ +# 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. You can import the entire library: + +```javascript +import freighterApi from "@stellar/freighter-api"; +``` + +Or import only what you need: + +```javascript +import { + isConnected, + getAddress, + signAuthEntry, + signTransaction, + signMessage, + addToken, +} from "@stellar/freighter-api"; +``` + +> See the [Extension Integration](../extension/) 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](../extension/connecting.md) | +| Sign a transaction | [Signing](../extension/signing.md) | +| Add a token | [Token Management](../extension/token-management.md) | diff --git a/extension/reading-data.md b/extension/reading-data.md new file mode 100644 index 0000000..e322cc3 --- /dev/null +++ b/extension/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?: FreighterApiError }>` + +```typescript +import { getAddress } from "@stellar/freighter-api"; + +const { address, error } = await getAddress(); + +if (error) { + console.error(error.message); +} else { + console.log("Public key:", address); +} +``` + +## Getting the Network + +### `getNetwork()` + +Get the name and passphrase of the network the user has selected in Freighter. + +**Returns:** `Promise<{ network: string; networkPassphrase: string } & { error?: FreighterApiError }>` + +Possible `network` values: `PUBLIC`, `TESTNET`, `FUTURENET`, or `STANDALONE` (custom networks). + +```typescript +import { getNetwork } from "@stellar/freighter-api"; + +const { network, networkPassphrase, error } = await getNetwork(); + +if (!error) { + console.log("Network:", network); // e.g., "TESTNET" + console.log("Passphrase:", networkPassphrase); +} +``` + +### `getNetworkDetails()` + +Get comprehensive network configuration including the Horizon URL, passphrase, and Soroban RPC URL. + +**Returns:** `Promise<{ network: string; networkUrl: string; networkPassphrase: string; sorobanRpcUrl?: string } & { error?: FreighterApiError }>` + +```typescript +import { getNetworkDetails } from "@stellar/freighter-api"; + +const details = await getNetworkDetails(); + +if (!details.error) { + console.log("Network:", details.network); // e.g., "TESTNET" + console.log("Horizon:", details.networkUrl); // e.g., "https://horizon-testnet.stellar.org" + console.log("Passphrase:", details.networkPassphrase); + console.log("Soroban RPC:", details.sorobanRpcUrl); // e.g., "https://soroban-testnet.stellar.org" +} +``` + +{% hint style="info" %} +Use `getNetworkDetails()` instead of `getNetwork()` when working with Soroban smart contracts or when you need specific network endpoints. +{% endhint %} diff --git a/extension/signing.md b/extension/signing.md new file mode 100644 index 0000000..d1376fe --- /dev/null +++ b/extension/signing.md @@ -0,0 +1,168 @@ +# Signing + +Sign transactions, authorization entries, and arbitrary messages using the user's Freighter wallet. + +## Signing a Transaction + +### `signTransaction()` + +Pass a transaction XDR string to Freighter for the user to review and sign. + +``` +signTransaction(xdr: string, opts?: { + network?: string, + networkPassphrase?: string, + address?: string +}) -> Promise<{ signedTxXdr: string; signerAddress: string } & { error?: FreighterApiError }> +``` + +The user will review the transaction details before signing. + +**Parameters** + +| Parameter | Type | Description | +| --- | --- | --- | +| `xdr` | `string` | **Required.** Base64-encoded transaction XDR. | +| `opts.network` | `string` | Network name (maps to `Networks` enum in `@stellar/stellar-sdk`). | +| `opts.networkPassphrase` | `string` | Custom passphrase. Ignored if `network` is also provided. | +| `opts.address` | `string` | Request a specific account's signature. Freighter will switch to that account if available. | + +{% hint style="info" %} +Passing `network` or `networkPassphrase` lets Freighter warn the user if their wallet is configured to the wrong network. +{% endhint %} + +**Example** + +```typescript +import { signTransaction } from "@stellar/freighter-api"; + +const { signedTxXdr, signerAddress, error } = await signTransaction(xdr, { + network: "TESTNET", + address: "G...", // optional: request a specific account +}); + +if (error) { + console.error("Signing failed:", error.message); +} else { + console.log("Signed by:", signerAddress); + console.log("Signed XDR:", signedTxXdr); +} +``` + +**Errors** + +| Condition | Error message | +| --- | --- | +| User rejected | `"The user rejected this request."` | +| Extension not installed | `"The wallet encountered an internal error"` | + +## Signing an Auth Entry + +### `signAuthEntry()` + +Sign an [authorization entry preimage](https://github.com/stellar/js-stellar-base/blob/a9567e5843760bfb6a8b786592046aee4c9d38b2/types/next.d.ts#L6895) and receive the signed hash back as a base64 string. Used for [Soroban contract authorization](https://developers.stellar.org/docs/smart-contracts/guides/auth/authorization) flows. + +``` +signAuthEntry(entryXdr: string, opts: { + address: string +}) -> Promise<{ signedAuthEntry: string | null; signerAddress: string } & { error?: FreighterApiError }> +``` + +See the [`authorizeEntry` helper](https://github.com/stellar/js-stellar-base/blob/e3d6fc3351e7d242b374c7c6057668366364a279/src/auth.js#L97) in `js-stellar-base` for how signed auth entries are used, or the [Soroban development documentation](https://developers.stellar.org/docs/smart-contracts) for wallet-side patterns. + +**Example** + +```typescript +import { signAuthEntry } from "@stellar/freighter-api"; + +const { signedAuthEntry, signerAddress, error } = await signAuthEntry(entryXdr, { + address: "G...", +}); + +if (error) { + console.error("Auth entry signing failed:", error.message); +} else { + console.log("Signed auth entry:", signedAuthEntry); +} +``` + +**Errors** + +| Condition | Error message | +| --- | --- | +| User rejected | `"The user rejected this request."` | + +## Signing a Message + +### `signMessage()` + +Sign an arbitrary string and receive a base64-encoded Ed25519 signature. Follows [SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md). + +``` +signMessage(message: string, opts: { + address: string +}) -> Promise<{ signedMessage: string | null; signerAddress: string } & { error?: FreighterApiError }> +``` + +**Example** + +```typescript +import { signMessage } from "@stellar/freighter-api"; + +const { signedMessage, signerAddress, error } = await signMessage( + "Verify account ownership", + { address: "G..." }, +); + +if (error) { + console.error("Message signing failed:", error.message); +} else { + console.log("Signature:", signedMessage); +} +``` + +**Errors** + +| Condition | Error message | +| --- | --- | +| User rejected | `"The user rejected this request."` | + +## Full Example: Connect, Sign, and Submit + +A complete flow from connection to Horizon submission: + +```typescript +import { isConnected, requestAccess, signTransaction } from "@stellar/freighter-api"; +import { Server, TransactionBuilder } from "@stellar/stellar-sdk"; + +// 1. Check Freighter is installed +const connectResult = await isConnected(); +if (!connectResult.isConnected) { + throw new Error("Freighter not found"); +} + +// 2. Request the user's public key +const accessResult = await requestAccess(); +if (accessResult.error) { + throw new Error(accessResult.error.message); +} + +// 3. Sign the transaction +const xdr = "AAAAAgAAAAA..."; // your assembled transaction XDR +const signResult = await signTransaction(xdr, { + network: "TESTNET", + address: accessResult.address, +}); +if (signResult.error) { + throw new Error(signResult.error.message); +} + +// 4. Submit to Horizon +const server = new Server("https://horizon-testnet.stellar.org"); +const tx = TransactionBuilder.fromXDR( + signResult.signedTxXdr, + "Test SDF Network ; September 2015", +); +const response = await server.submitTransaction(tx); +console.log("Transaction submitted:", response.hash); +``` diff --git a/extension/token-management.md b/extension/token-management.md new file mode 100644 index 0000000..a3a8bde --- /dev/null +++ b/extension/token-management.md @@ -0,0 +1,51 @@ +# Token Management + +Unlike Stellar assets, contract tokens a user owns are not automatically displayed in Freighter — they must be explicitly added. This method lets your dapp prompt users to add a contract token directly, without the user having to find and add it in Freighter manually. + +{% hint style="info" %} +In a future release, Freighter will auto-detect token transfers and display them automatically. Once that ships, `addToken()` will become a no-op. +{% endhint %} + +## Adding a Token + +### `addToken()` + +Trigger an "add token" flow that displays token details in a modal for the user to review and approve. + +``` +addToken({ contractId: string, networkPassphrase?: string }) + -> Promise<{ contractId: string } & { error?: FreighterApiError }> +``` + +When called, Freighter loads the token's **symbol**, **name**, **decimals**, and **balance** from the contract and displays them for the user to verify before adding. + +### Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| `contractId` | `string` | **Required.** The contract token ID (`C...` address). | +| `networkPassphrase` | `string` | Defaults to Mainnet passphrase if omitted. | + +### Example + +```typescript +import { isConnected, addToken } from "@stellar/freighter-api"; + +const { isConnected: connected } = await isConnected(); +if (!connected) return; + +const result = await addToken({ + contractId: "CC...ABCD", + networkPassphrase: "Test SDF Network ; September 2015", // optional +}); + +if (result.error) { + console.error(result.error.message); +} else { + console.log(`Token added: ${result.contractId}`); +} +``` + +{% hint style="info" %} +After the user approves, Freighter will automatically track the token's balance and display it alongside other account balances. +{% endhint %} diff --git a/extension/watching-changes.md b/extension/watching-changes.md new file mode 100644 index 0000000..536f2e2 --- /dev/null +++ b/extension/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..77cc2c6 --- /dev/null +++ b/index.html @@ -0,0 +1,24 @@ + + + + +