Skip to main content

dapp

Stellar dApp / frontend development. Covers the JavaScript stellar-sdk (browser + Node.js), Freighter wallet, Stellar Wallets Kit (multi-wallet), Wallet Standard, smart accounts with passkeys, transaction building / signing / submission, smart contract invocation from the client, simulation, and error handling. Use when building a React/Next.js/Node.js app that talks to Stellar — classic operations or smart contracts. Blux is also an option.

Source facts

Repository
stellar/stellar-dev-skill
Last source activity
October 7, 2026 at 16:55
Detected SKILL.md language
English
Stars
53
Forks
64

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
4 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
dapp
description
Stellar dApp / frontend development. Covers the JavaScript stellar-sdk (browser + Node.js), Freighter wallet, Stellar Wallets Kit (multi-wallet), Wallet Standard, smart accounts with passkeys, transaction building / signing / submission, smart contract invocation from the client, simulation, and error handling. Use when building a React/Next.js/Node.js app that talks to Stellar — classic operations or smart contracts. Blux is also an option.
user-invocable
true
argument-hint
[dapp task]
# Stellar dApp / Frontend Client-side development with `@stellar/stellar-sdk`, wallet connection, signing, and submitting transactions. Covers both classic Stellar operations and smart contract invocation from the browser or Node.js. ## When to use this skill - Connecting Freighter or other wallets via Stellar Wallets Kit - Building, simulating, signing, and submitting transactions - Invoking Stellar smart contracts from a frontend - Implementing smart accounts with passkeys - Handling network passphrases (Mainnet / Testnet / local) ## Related skills - Writing the contract being invoked → `../smart-contracts/SKILL.md` - Issuing assets and managing trustlines → `../assets/SKILL.md` - Querying chain state via RPC / Horizon → `../data/SKILL.md` - Building paid APIs or agent payment clients → `../agentic-payments/SKILL.md` - SEPs the wallet/anchor flows depend on → `../standards/SKILL.md` --- ## Goals - Single SDK instance for the app (RPC/Horizon + transaction building) - Freighter wallet integration (or multi-wallet via Stellar Wallets Kit) - Clean separation of client/server in Next.js - Transaction sending with proper confirmation handling ## Read the file that matches the task This file covers SDK setup, wallet connection, and transaction build/sign/submit. The deep dives live alongside it: | Task | File | |------|------| | SDK setup and env config | [SDK Initialization](#sdk-initialization) (below) | | Wallet integrations (Freighter, Wallets Kit) | [Wallet Integration](#wallet-integration) (below) | | Tx build/send patterns | [Transaction Building](#transaction-building), [Transaction Submission](#transaction-submission) (below) | | Connect-wallet button, payment form, Next.js App Router wiring | [react.md](react.md) | | Account balances, contract reads (`queryContract`), raw ledger entries | [data-fetching.md](data-fetching.md) | | Passkey smart wallets (Smart Account Kit), gasless tx via OpenZeppelin Relayer | [smart-accounts.md](smart-accounts.md) | | Production UX checklist | [Transaction UX Checklist](#transaction-ux-checklist) (below) | ## Recommended Dependencies > **Requires Node.js 22+.** As of SDK v16, Node 22 is the minimum (older Node produces an `EBADENGINE` warning). v16 also folded `@stellar/stellar-base` into `@stellar/stellar-sdk`, is ESM-first, and uses native `fetch` instead of axios. If you still import `@stellar/stellar-base` directly, switch the import to `@stellar/stellar-sdk` and uninstall the base package (keeping both breaks `instanceof` checks). See the [migration guide](https://stellar.github.io/js-stellar-sdk/guides/00-migration). ```bash npm install @stellar/stellar-sdk @stellar/freighter-api # Or for multi-wallet support — Wallets Kit v2 is distributed on JSR, not npm: npx jsr add @creit-tech/stellar-wallets-kit ``` > **Sourcing:** SDK mechanics below (init, transaction building, contract invocation, submission, data fetching, error handling) track the official [JS SDK docs](https://stellar.github.io/js-stellar-sdk/) (which also publish [`llms.txt`](https://stellar.github.io/js-stellar-sdk/llms.txt) / [`llms-full.txt`](https://stellar.github.io/js-stellar-sdk/llms-full.txt) bundles for agents). Wallet integrations (Freighter, Stellar Wallets Kit), passkey smart accounts, and the OpenZeppelin relayer are separate packages, not part of the JS SDK — verify those against their own upstream docs. ## SDK Initialization > For the full API reference (RPC methods, Horizon endpoints, migration guide), see the [data skill](../data/SKILL.md). ### Basic Setup ```typescript import * as StellarSdk from "@stellar/stellar-sdk"; // For Testnet const testnetServer = new StellarSdk.Horizon.Server("https://horizon-testnet.stellar.org"); const testnetRpc = new StellarSdk.rpc.Server("https://soroban-testnet.stellar.org"); const testnetNetworkPassphrase = StellarSdk.Networks.TESTNET; // For Mainnet const mainnetServer = new StellarSdk.Horizon.Server("https://horizon.stellar.org"); const mainnetRpcUrl = process.env.NEXT_PUBLIC_STELLAR_MAINNET_RPC_URL; if (!mainnetRpcUrl) throw new Error("Missing NEXT_PUBLIC_STELLAR_MAINNET_RPC_URL"); const mainnetRpc = new StellarSdk.rpc.Server(mainnetRpcUrl); // set from your chosen RPC provider const mainnetNetworkPassphrase = StellarSdk.Networks.PUBLIC; ``` ### Environment Configuration > Use a provider-specific mainnet RPC URL (see: https://developers.stellar.org/docs/data/apis/rpc/providers). ```typescript // lib/stellar.ts import * as StellarSdk from "@stellar/stellar-sdk"; const NETWORK = process.env.NEXT_PUBLIC_STELLAR_NETWORK || "testnet"; const requireEnv = (name: string): string => { const value = process.env[name]; if (!value) throw new Error(`Missing required env var: ${name}`); return value; }; function getConfig(network: string) { switch (network) { case "testnet": return { horizonUrl: "https://horizon-testnet.stellar.org", rpcUrl: "https://soroban-testnet.stellar.org", networkPassphrase: StellarSdk.Networks.TESTNET, friendbotUrl: "https://friendbot.stellar.org" as string | null, }; case "mainnet": return { horizonUrl: "https://horizon.stellar.org", // Resolved lazily so testnet runs don't require the mainnet env var rpcUrl: requireEnv("NEXT_PUBLIC_STELLAR_MAINNET_RPC_URL"), networkPassphrase: StellarSdk.Networks.PUBLIC, friendbotUrl: null, }; default: throw new Error(`Unknown network: ${network}`); } } export const config = getConfig(NETWORK); export const horizon = new StellarSdk.Horizon.Server(config.horizonUrl); export const rpc = new StellarSdk.rpc.Server(config.rpcUrl); ``` ## Wallet Integration ### Freighter (Primary Browser Wallet) ```typescript // hooks/useFreighter.ts import { useState, useEffect, useCallback } from "react"; import { isConnected, getAddress, requestAccess, signTransaction, getNetwork, } from "@stellar/freighter-api"; export function useFreighter() { const [connected, setConnected] = useState(false); const [address, setAddress] = useState<string | null>(null); const [network, setNetwork] = useState<string | null>(null); useEffect(() => { checkConnection(); }, []); const checkConnection = async () => { const { isConnected: installed, error } = await isConnected(); if (error || !installed) return; // getAddress returns address: "" until the app has been granted access, // so a non-empty address means we're already authorized. const { address: addr, error: addressError } = await getAddress(); if (addressError || !addr) return; const { network: net, error: networkError } = await getNetwork(); if (networkError) return; setConnected(true); setAddress(addr); setNetwork(net); }; const connect = useCallback(async () => { const { isConnected: installed, error } = await isConnected(); if (error || !installed) { throw new Error("Freighter extension not installed"); } // requestAccess prompts the user and returns the granted address. const { address: addr, error: accessError } = await requestAccess(); if (accessError) throw new Error(accessError.message); const { network: net, error: networkError } = await getNetwork(); if (networkError) throw new Error(networkError.message); setConnected(true); setAddress(addr); setNetwork(net); return addr; }, []); const disconnect = useCallback(() => { setConnected(false); setAddress(null); setNetwork(null); }, []); const sign = useCallback( async (xdr: string, networkPassphrase: string) => { if (!connected) throw new Error("Wallet not connected"); const { signedTxXdr, error } = await signTransaction(xdr, { networkPassphrase, }); if (error) throw new Error(error.message); return signedTxXdr; }, [connected] ); return { connected, address, network, connect, disconnect, sign }; } ``` ### Stellar Wallets Kit (Multi-Wallet) ```typescript // hooks/useStellarWallet.ts import { useState, useCallback } from "react"; import { StellarWalletsKit, Networks } from "@creit-tech/stellar-wallets-kit"; import { defaultModules } from "@creit-tech/stellar-wallets-kit/modules/utils"; // v2 is a static singleton: init once at module load, then call static methods — // there is no instance to construct or pass around. // defaultModules() loads every wallet that needs no extra setup; modules with // prerequisites (WalletConnect, Ledger, Trezor) must be imported and added explicitly. StellarWalletsKit.init({ modules: defaultModules(), network: Networks.TESTNET, }); export function useStellarWallet() { const [address, setAddress] = useState<string | null>(null); const connect = useCallback(async () => { // authModal() opens the wallet picker, sets the chosen module active, // and returns the address — one call replaces v1's openModal callback dance. const { address } = await StellarWalletsKit.authModal(); setAddress(address); }, []); const disconnect = useCallback(async () => { await StellarWalletsKit.disconnect(); setAddress(null); }, []); const sign = useCallback(async (xdr: string) => { const { signedTxXdr } = await StellarWalletsKit.signTransaction(xdr); return signedTxXdr; }, []); return { address, connect, disconnect, sign }; } ``` > **Migrating from v1?** (noted July 2026) v1 lived on npm under the dotted scope `@creit.tech/stellar-wallets-kit`, with `new StellarWalletsKit({...})`, `allowAllModules()`, and `openModal({ onWalletSelected })`. v2 moved to JSR under `@creit-tech/stellar-wallets-kit`, made the kit fully static, replaced `allowAllModules()` with `defaultModules()`, and folded wallet selection + address fetch into `authModal()`. npm parity is maintained for now, but the maintainers say npm updates will eventually stop — install from JSR. Pre-selecting a wallet (`setWallet(FREIGHTER_ID)`) still works; the ID constants now live in per-wallet module subpaths like `@creit-tech/stellar-wallets-kit/modules/freighter`. You can also use [Blux](https://blux.cc) for wallet integration and contract calls. See [react.md](react.md) and [data-fetching.md](data-fetching.md) for examples. ## Transaction Building ### Basic Payment ```typescript import * as StellarSdk from "@stellar/stellar-sdk"; import { horizon, config } from "@/lib/stellar"; export async function buildPaymentTx( sourceAddress: string, destinationAddress: string, amount: string, asset: StellarSdk.Asset = StellarSdk.Asset.native() ) { const account = await horizon.loadAccount(sourceAddress); const transaction = new StellarSdk.TransactionBuilder(account, { fee: StellarSdk.BASE_FEE, networkPassphrase: config.networkPassphrase, }) .addOperation( StellarSdk.Operation.payment({ destination: destinationAddress, asset: asset, amount: amount, }) ) .setTimeout(180) .build(); return transaction.toXDR(); } ``` ### Smart Contract Invocation (`contract.Client`) The canonical way to call a Stellar smart contract from JS is the `contract.Client`, not hand-built `Contract.call` + `assembleTransaction`. The client reads the contract's interface from the network, so each method is callable by name and returns an `AssembledTransaction`. You get a native JS result and don't build ScVals by hand. ```typescript import { contract } from "@stellar/stellar-sdk"; import { config } from "@/lib/stellar"; // Describe just the methods you call. `Client.from<T>()` uses this to type // the returned client, so calls are checked and autocompleted — no codegen. // For a contract with many methods, generate this interface from its spec // with the SDK's binding CLI instead of writing it by hand. interface CounterContract { increment: ( options?: contract.MethodOptions, ) => Promise<contract.AssembledTransaction<number>>; } // `signTransaction` comes from the wallet (e.g. Freighter/Wallets Kit in the // browser). `contract.basicNodeSigner(keypair, networkPassphrase)` is the // Node equivalent for scripts and tests. export async function getCounterClient( contractId: string, publicKey: string, signTransaction: contract.ClientOptions["signTransaction"], ) { return contract.Client.from<CounterContract>({ contractId, rpcUrl: config.rpcUrl, networkPassphrase: config.networkPassphrase, publicKey, signTransaction, }); } // Preview (free simulation) then sign + send to apply on-chain. export async function increment(client: contract.Client & CounterContract) { const tx = await client.increment(); console.log("preview:", tx.result); // predicted return value, no signature const sent = await tx.signAndSend(); // submits and polls to completion return sent.result; } ``` `AssembledTransaction` also supports fine-grained control (`{ fee, simulate, timeoutInSeconds }` as a second arg) and multi-party auth via `tx.needsNonInvokerSigningBy()` / `tx.signAuthEntries()`. See [Invoke a Contract](https://stellar.github.io/js-stellar-sdk/guides/06-invoke-a-contract) and [Authorize a Contract Call](https://stellar.github.io/js-stellar-sdk/guides/07-contract-auth). <details> <summary><b>Advanced: low-level invocation without a client</b></summary> Use this only when you need direct control over the transaction (e.g. batching a contract call with classic operations). Otherwise prefer `contract.Client` above. ```typescript import * as StellarSdk from "@stellar/stellar-sdk"; import { rpc, config } from "@/lib/stellar"; export async function invokeContract( sourceAddress: string, contractId: string, method: string, args: StellarSdk.xdr.ScVal[] ) { const account = await rpc.getAccount(sourceAddress); const contract = new StellarSdk.Contract(contractId); const transaction = new StellarSdk.TransactionBuilder(account, {
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub