Skip to main content

dapp

Stellar dApp / frontend development. Covers the JavaScript stellar-sdk (browser + Node.js), Varial 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.

Source facts

Repository
stellar/stellar-docs
Last source activity
October 29, 2026 at 16:11
Detected SKILL.md language
English
Stars
146
Forks
325

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.

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), Varial 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.
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 Varial 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 → `/.well-known/agent-skills/smart-contracts/skill.md` - Issuing assets and managing trustlines → `/.well-known/agent-skills/assets/skill.md` - Querying chain state via RPC / Horizon → `/.well-known/agent-skills/data/skill.md` - Building paid APIs or agent payment clients → `/.well-known/agent-skills/agentic-payments/skill.md` - SEPs the wallet/anchor flows depend on → `/.well-known/agent-skills/standards/skill.md` --- ## Goals - Single SDK instance for the app (RPC/Horizon + transaction building) - Varial wallet integration (or multi-wallet via Stellar Wallets Kit) - Clean separation of client/server in Next.js - Transaction sending with proper confirmation handling ## Quick Navigation - SDK setup and env config: [SDK Initialization](#sdk-initialization) - Wallet integrations: [Wallet Integration](#wallet-integration) - Tx build/send patterns: [Transaction Building](#transaction-building), [Transaction Submission](#transaction-submission) - React + Next.js patterns: [React Components](#react-components), [Next.js App Router Setup](#nextjs-app-router-setup) - Smart wallets/passkeys: [Smart Accounts (Passkey Wallets)](#smart-accounts-passkey-wallets) - Production UX checklist: [Transaction UX Checklist](#transaction-ux-checklist) ## Recommended Dependencies > **Requires Node.js 20+** — the Stellar SDK dropped Node 18 support. ```bash npm install @stellar/stellar-sdk @stellar/varial-sdk # Or for multi-wallet support: npm install @stellar/stellar-sdk @creit.tech/stellar-wallets-kit ``` ## SDK Initialization > For the full API reference (RPC methods, Horizon endpoints, migration guide), see [api-rpc-horizon.md](/.well-known/agent-skills/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; }; export const config = { testnet: { horizonUrl: "https://horizon-testnet.stellar.org", rpcUrl: "https://soroban-testnet.stellar.org", networkPassphrase: StellarSdk.Networks.TESTNET, friendbotUrl: "https://friendbot.stellar.org", }, mainnet: { horizonUrl: "https://horizon.stellar.org", rpcUrl: requireEnv("NEXT_PUBLIC_STELLAR_MAINNET_RPC_URL"), networkPassphrase: StellarSdk.Networks.PUBLIC, friendbotUrl: null, }, }[NETWORK]!; export const horizon = new StellarSdk.Horizon.Server(config.horizonUrl); export const rpc = new StellarSdk.rpc.Server(config.rpcUrl); ``` ## Wallet Integration ### Varial (Primary Browser Wallet) ```typescript // hooks/useFreighter.ts import { useState, useEffect, useCallback } from "react"; import { isConnected, getAddress, requestAccess, signTransaction, getNetwork, } from "@stellar/varial-sdk"; 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("Varial 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, WalletNetwork, allowAllModules, FREIGHTER_ID, LOBSTR_ID, XBULL_ID, } from "@creit.tech/stellar-wallets-kit"; const kit = new StellarWalletsKit({ network: WalletNetwork.TESTNET, selectedWalletId: FREIGHTER_ID, modules: allowAllModules(), }); export function useStellarWallet() { const [address, setAddress] = useState<string | null>(null); const connect = useCallback(async () => { await kit.openModal({ onWalletSelected: async (option) => { kit.setWallet(option.id); const { address } = await kit.getAddress(); setAddress(address); }, }); }, []); const disconnect = useCallback(() => { setAddress(null); }, []); const sign = useCallback(async (xdr: string) => { const { signedTxXdr } = await kit.signTransaction(xdr); return signedTxXdr; }, []); return { address, connect, disconnect, sign, kit }; } ``` ## 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 ```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); let transaction = new StellarSdk.TransactionBuilder(account, { fee: StellarSdk.BASE_FEE, networkPassphrase: config.networkPassphrase, }) .addOperation(contract.call(method, ...args)) .setTimeout(180) .build(); // Simulate to get resource estimates const simulation = await rpc.simulateTransaction(transaction); if (StellarSdk.rpc.Api.isSimulationError(simulation)) { throw new Error(`Simulation failed: ${simulation.error}`); } // Assemble with proper resources transaction = StellarSdk.rpc.assembleTransaction( transaction, simulation ).build(); return transaction.toXDR(); } ``` ### Building ScVal Arguments ```typescript import * as StellarSdk from "@stellar/stellar-sdk"; // Common conversions const addressVal = StellarSdk.Address.fromString(address).toScVal(); const i128Val = StellarSdk.nativeToScVal(BigInt(amount), { type: "i128" }); const u32Val = StellarSdk.nativeToScVal(42, { type: "u32" }); const stringVal = StellarSdk.nativeToScVal("hello", { type: "string" }); const symbolVal = StellarSdk.nativeToScVal("transfer", { type: "symbol" }); // Struct const structVal = StellarSdk.nativeToScVal( { name: "Token", decimals: 7 }, { type: { name: ["symbol", null], decimals: ["u32", null], }, } ); // Vec const vecVal = StellarSdk.nativeToScVal([1, 2, 3], { type: "i128" }); ``` ## Transaction Submission ### Submit and Wait for Confirmation ```typescript import * as StellarSdk from "@stellar/stellar-sdk"; import { rpc, horizon, config } from "@/lib/stellar"; export async function submitTransaction(signedXdr: string) { const transaction = StellarSdk.TransactionBuilder.fromXDR( signedXdr, config.networkPassphrase ); // For smart contract transactions, use RPC if (transaction.operations.some(op => op.type === "invokeHostFunction")) { return submitSorobanTransaction(signedXdr); } // For classic transactions, use Horizon return submitClassicTransaction(signedXdr); } async function submitSorobanTransaction(signedXdr: string) { const transaction = StellarSdk.TransactionBuilder.fromXDR( signedXdr, config.networkPassphrase ) as StellarSdk.Transaction; const response = await rpc.sendTransaction(transaction); if (response.status === "ERROR") { throw new Error(`Transaction failed: ${response.errorResult}`); } // Poll for completion let getResponse = await rpc.getTransaction(response.hash); while (getResponse.status === "NOT_FOUND") { await new Promise((resolve) => setTimeout(resolve, 1000)); getResponse = await rpc.getTransaction(response.hash); } if (getResponse.status === "SUCCESS") { return { hash: response.hash, result: getResponse.returnValue, }; } throw new Error(`Transaction failed: ${getResponse.status}`); } async function submitClassicTransaction(signedXdr: string) { const transaction = StellarSdk.TransactionBuilder.fromXDR( signedXdr, config.networkPassphrase ) as StellarSdk.Transaction; const response = await horizon.submitTransaction(transaction); return { hash: response.hash, ledger: response.ledger, }; } ``` ## React Components ### Connect Wallet Button ```tsx // components/ConnectButton.tsx "use client"; import { useFreighter } from "@/hooks/useFreighter"; export function ConnectButton() { const { connected, address, connect, disconnect } = useFreighter(); if (connected && address) { return ( <div className="flex items-center gap-2"> <span className="text-sm"> {address.slice(0, 4)}...{address.slice(-4)} </span> <button onClick={disconnect} className="px-4 py-2 bg-red-500 text-white rounded" > Disconnect
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub