@sorobuild/stellar-sdk
v2.19.0
Published
A lightweight SDK for **Soroban RPC** and **Horizon** that works in both **Node.js** and **browser** apps (React/Next.js/Vite, etc.). It simplifies _dApp development_ — no node proxies, no custom headers, no URL juggling. Just use a Project ID (browser)
Downloads
15
Readme
🌟 Sorobuild Stellar SDK
A lightweight SDK for Soroban RPC and Horizon that works in both Node.js and browser apps (React/Next.js/Vite, etc.).
It simplifies dApp development — no node proxies, no custom headers, no URL juggling. Just use a Project ID (browser) or API key (server) and Sorobuild will auto-route to the correct network endpoints.
Prefer full control? Supply your own serverUrl
✅ Features
- 🔌 Auto-routing to Soroban RPC + Horizon via
https://soro.buildwhen you pass a key - 🛰️ Custom endpoints via a
serverUrlobject (use your own RPC/Horizon URLs) - 🧭 Typed Horizon mini-clients with one
call()per resource and ergonomic pass-throughs - 🛡️ Runtime validation for common pitfalls (account IDs, hashes, ledger seq, etc.)
- 🌐 Works in Node.js, React, Next.js, Vite, etc.
📦 Installation
npm install @sorobuild/stellar-sdk
# or
yarn add @sorobuild/stellar-sdkNode 20+ is recommended (native
fetch). For older Node versions, install a fetch polyfill (e.g.undici) before using the SDK.
🚀 Instantiation Options
You can initialize the SDK in one of two ways:
Option A — Smart routing with key (uses soro.build)
The
keymay be either a Project ID (for browser apps) or an API Key (for Node/server apps).
Sorobuild automatically detects the environment. You can pass the same fieldkeyin both contexts.
import { StellarServers } from "@sorobuild/stellar-sdk";
const servers = new StellarServers({
key: "<your_project_or_api_key>",
});
const { RpcServer, HorizonServer } = servers;
// Example:
const rpc = RpcServer("TESTNET", format?); // routes to soro.build Soroban RPC for TESTNET
const hz = HorizonServer("PUBLIC"); // routes to soro.build Horizon for PUBLIC- When you provide a key, the SDK uses soro.build nodes and smart routing based on the network you pass to
RpcServer("TESTNET"|"PUBLIC")/HorizonServer("TESTNET"|"PUBLIC").
Option B — Your own endpoints via serverUrl
import { StellarServers } from "@sorobuild/stellar-sdk";
const serverUrl = {
rpc: {
testnet: `https://your-rpc-testnet.com`,
public: `https://your-rpc-public.com`,
},
horizon: {
testnet: `your-horizon-testnet.com`,
public: `https://your-horizon-public.com`,
},
};
const servers = new StellarServers({ serverUrl });
const { RpcServer, HorizonServer } = servers;
// Example:
const rpc = RpcServer("TESTNET", format?); // uses your serverUrl.rpc.testnet
const hz = HorizonServer("PUBLIC"); // uses your serverUrl.horizon.public- format:
RpcServeralso takes an optional response format parameter — one of "base64", "json", or **"parsed"`.
Constructor signature
type ServerUrlConfig = {
rpc: { testnet: string; public: string };
horizon: { testnet: string; public: string };
};
new StellarServers(options: { key?: string; serverUrl?: ServerUrlConfig });Provide exactly one of
keyorserverUrl. If both are provided,StellarServerswill throw an error.
🏭 Server factories & formats
The SDK exposes factory functions to get configured clients per network:
// RpcServer(network, format?)
type RpcFormat = "base64" | "json" | "parsed";
const rpc = RpcServer("TESTNET", "base64"); // default if omitted
// HorizonServer(network)
const hz = HorizonServer("PUBLIC");RpcServer(network, format)
network:"TESTNET"or"PUBLIC"format(optional; default"base64"):"base64"– responses include base64 XDR strings (fastest, smallest)"json"– SDK setsxdrFormat: "json"for applicable RPC calls (JSON-encoded XDR)"parsed"– SDK applies Sorobuild’s built-in XDR parser and returns normalized objects where supported
HorizonServer(network)
network:"TESTNET"or"PUBLIC"(Horizon is JSON-only; no format param)
🧭 Node vs Browser Imports
Node.js (CommonJS)
// Node (require)
const { StellarServers } = require("@sorobuild/stellar-sdk");
const servers = new StellarServers({ key: "<your_api_key>" }); // or { serverUrl }
const rpc = servers.RpcServer("TESTNET");
(async () => {
const health = await rpc.getHealth();
console.log("health:", health);
})();Browser / ESM (TypeScript / modern bundlers)
// Browser / ESM (import)
import { StellarServers } from "@sorobuild/stellar-sdk";
const servers = new StellarServers({ key: "<your_project_id>" }); // or { serverUrl }
const hz = servers.HorizonServer("PUBLIC");
const assets = await hz.getAssets({ order: "desc", limit: 50 });🔧 Soroban RPC — Common calls
const rpc = RpcServer("TESTNET", "json");
// Health / network info
await rpc.getHealth();
await rpc.getFeeStats();
await rpc.getNetwork();
await rpc.getVersionInfo();
await rpc.getLatestLedger();
// Transactions
await rpc.getTransaction("<tx_hash>");
await rpc.getTransactions({ startLedger: 50_000, limit: 1000 }); // or { cursor, limit }
// Events (requires filters; 1–5)
await rpc.getEvents({
startLedger: 1_190_000,
type: "contract",
contractIds: ["CD..."],
topics: [["AAAADwAAAAh0cmFuc2Zlcg==", "*", "*", "*"]],
limit: 200,
});
// Simulate / Send
await rpc.simulateTransaction(
"<base64_envelope_xdr>",
/* instructionLeeway? */ 3_000_000
);
await rpc.sendTransaction("<base64_envelope_xdr>");
// NOTE: sendTransaction auto-resolves.
// If the RPC initially returns PENDING, the SDK will poll `getTransaction`
// until the transaction settles (SUCCESS/FAILED) and then return the resolved response.
// You do NOT need to implement manual polling yourself.
// Ledger entries
await rpc.getLedgerEntries({
keys: ["<base64_scval_key>", "<base64_contract_data_key>"],
});Notes
- Parameter rules enforced (e.g.,
cursorXORstartLedger,limitbounds, max filters). - When
formatis"json", the SDK automatically usesJSON encoding for XDRswhere supported. - When
formatis"parsed", the SDK applies Sorobuild’s built-in"XDR parser"and returns normalized JSON objects for supported RPC responses.
🌍 Horizon — What’s included
Typed mini-clients with a single call() each + pass-throughs on your wrapper.
All pass-throughs have rich JSDoc and clamp limit ≤ 200.
Resources & Pass-throughs
- Accounts —
getAccounts,getAccount,getAccountTransactions,getAccountOperations,getAccountPayments,getAccountEffects,getAccountOffers,getAccountTrades,getAccountData,getAccountDataText - Claimable Balances —
getClaimableBalances,getClaimableBalance,getClaimableBalanceTransactions,getClaimableBalanceOperations - Ledgers —
getLedgers,getLedger,getLedgerTransactions,getLedgerOperations,getLedgerPayments,getLedgerEffects - Liquidity Pools —
getLiquidityPools,getLiquidityPool,getLiquidityPoolTransactions,getLiquidityPoolOperations,getLiquidityPoolTrades,getLiquidityPoolEffects - Offers —
getOffers,getOffer,getOfferTrades - Operations —
getOperations,getOperation,getOperationEffects - Payments —
getPayments - Transactions —
getTransactions,getTransaction,getTransactionOperations,getTransactionEffects,submitTransaction,submitTransactionAsync - Assets —
getAssets - Effects (top-level) —
getEffects - Trades (top-level) —
getTrades
Validation & Guards
- Account IDs →
G…(StrKey.isValidEd25519PublicKey) - Transaction / Balance / LP IDs → 64-hex
- Offer / Operation IDs → positive integer
- Ledger sequence → positive integer
/accountsrequires at least one of signer | asset | sponsorlimitclamped to 200submitTransaction*requirestxbase64 envelope XDR
🧩 Horizon usage examples
const hz = HorizonServer("TESTNET");
/** Accounts */
await hz.getAccounts({ signer: "G...SIGNER", order: "desc", limit: 50 });
await hz.getAccount("G...ACCOUNT");
await hz.getAccountTransactions("G...ACCOUNT", {
include_failed: true,
limit: 100,
});
await hz.getAccountDataText("G...ACCOUNT", "profile:name"); // base64->utf8
/** Claimable balances */
await hz.getClaimableBalances({ claimant: "G...CLAIMANT", limit: 50 });
/** Ledgers */
await hz.getLedgers({ order: "desc", limit: 100 });
/** Liquidity pools */
await hz.getLiquidityPools({ reserves: ["XLM", "USD:G..."] });
/** Offers */
await hz.getOffers({ selling: "XLM", buying: "USD:G...", limit: 60 });
/** Operations / Payments */
await hz.getOperations({ include_failed: true, limit: 100 });
await hz.getPayments({ order: "desc", limit: 100 });
/** Transactions */
await hz.getTransactions({ include_failed: true, order: "desc", limit: 100 });
await hz.submitTransaction("<base64_envelope_xdr>", {
skip_memo_required_check: true,
});
/** Assets / Effects / Trades */
await hz.getAssets({ asset_code: "USD" });
await hz.getEffects({ cursor: "now" });
await hz.getTrades({
base_asset: "XLM",
counter_asset: "USD:G...",
order: "desc",
limit: 100,
});🔐 Project ID & API Key Usage
The SDK uses a single key field, which can represent either a projectId or an apiKey, depending on your environment:
🔓 Client Applications (e.g., React, Next.js)
- Use a
projectId - Safe to expose publicly (not a secret)
- Only works on whitelisted domains
- Requests from unauthorized origins will be blocked
- Use in development mode during testing — allows requests from localhost without domain restrictions.
🔒 Server Applications (e.g., Node.js, Express)
- Use an
apiKey - Must be kept secret
- Works from server environments only, no domain restrictions
Need a project key?
👉 contact us via [email protected]
👉 While the rpc portal is still in development, we’ll set up a test key for you if you need one. When it goes live, you’ll be able to create an account and manage your project keys and domains with ease.
🧠 FAQ
Q: Should I pass key or serverUrl?
A: Use key to leverage soro.build smart routing (simple, managed). Use serverUrl if you have your own endpoints (full control).
Q: Can I pass both?
A: Pass one. If you pass both, StellarServers will throw an error. In a future update, we’ll support passing both with an option to specify which takes precedence.
Q: How do I use this in Node vs Browser?
A: Node can require(...); browsers/ESM should import .... See Node vs Browser Imports.
📜 License
MIT © Sorobuild
