npm package: @sorobuild/stellar-sdk
Description: 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)
Latest version: 2.19.0

# 🌟 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.build` when you pass a **key**
- 🛰️ **Custom endpoints** via a `serverUrl` object (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

```bash
npm install @sorobuild/stellar-sdk
# or
yarn add @sorobuild/stellar-sdk
```

> Node 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 `key` may 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 field `key` in both contexts.

```ts
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`

```ts
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**: `RpcServer` also takes an optional response format parameter — one of **"base64"**, **"json"**, or \*\*"parsed"`.

**Constructor signature**

```ts
type ServerUrlConfig = {
  rpc:     { testnet: string; public: string };
  horizon: { testnet: string; public: string };
};

new StellarServers(options: { key?: string; serverUrl?: ServerUrlConfig });
```

> Provide **exactly one** of `key` **or** `serverUrl`. If both are provided, `StellarServers` will throw an error.

---

## 🏭 Server factories & formats

The SDK exposes **factory functions** to get configured clients per network:

```ts
// 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 sets `xdrFormat: "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)

```js
// 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)

```ts
// 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

```ts
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., `cursor` XOR `startLedger`, `limit` bounds, max filters).
- When `format` is `"json"` , the SDK automatically uses `JSON encoding for XDRs` where supported.
- When `format` is `"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**
- `/accounts` requires at least one of **signer | asset | sponsor**
- `limit` clamped to **200**
- `submitTransaction*` requires **`tx` base64 envelope XDR**

---

## 🧩 Horizon usage examples

```ts
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 [info@soro.build](mailto:info@soro.build)
👉 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](#-node-vs-browser-imports).

---

## 📜 License

MIT © Sorobuild