unicity-sphere/sphere-sdkPublic

The SDK for autonomous economic agents. Give an agent an identity, a wallet, and the ability to find, negotiate with, and settle with other agents - peer-to-peer, with perfect privacy and ultra-fast finality

AI summary: Modular TypeScript SDK for the Unicity state transition network and wallet operations.

Stars
5.4K
+1 today
Forks
105
Watchers
32
Open issues
98
Open PRs
3
Contributors
~7
Commits
868
Branches
326

TypeScriptMITCreated Jan 27, 2026Last push 2d agoLatest release v0.8.0+-2 stars this week+-2 this month

Star history

since Apr 12, 2026
02K4KApr 2026May 2026Jun 2026Aug 2026
5.4K stars as of Aug 6, 2026, tracked back to Apr 12, 2026. Historical curve reconstructed from public GitHub event archives, calibrated to the current total.

Contribution activity

commits per day, last 52 weeks
AugSepOctNovDecJanFebMarAprMayJunJulAugMonWedFri2025-08-09: 0 commits2025-08-10: 0 commits2025-08-11: 0 commits2025-08-12: 0 commits2025-08-13: 0 commits2025-08-14: 0 commits2025-08-15: 0 commits2025-08-16: 0 commits2025-08-17: 0 commits2025-08-18: 0 commits2025-08-19: 0 commits2025-08-20: 0 commits2025-08-21: 0 commits2025-08-22: 0 commits2025-08-23: 0 commits2025-08-24: 0 commits2025-08-25: 0 commits2025-08-26: 0 commits2025-08-27: 0 commits2025-08-28: 0 commits2025-08-29: 0 commits2025-08-30: 0 commits2025-08-31: 0 commits2025-09-01: 0 commits2025-09-02: 0 commits2025-09-03: 0 commits2025-09-04: 0 commits2025-09-05: 0 commits2025-09-06: 0 commits2025-09-07: 0 commits2025-09-08: 0 commits2025-09-09: 0 commits2025-09-10: 0 commits2025-09-11: 0 commits2025-09-12: 0 commits2025-09-13: 0 commits2025-09-14: 0 commits2025-09-15: 0 commits2025-09-16: 0 commits2025-09-17: 0 commits2025-09-18: 0 commits2025-09-19: 0 commits2025-09-20: 0 commits2025-09-21: 0 commits2025-09-22: 0 commits2025-09-23: 0 commits2025-09-24: 0 commits2025-09-25: 0 commits2025-09-26: 0 commits2025-09-27: 0 commits2025-09-28: 0 commits2025-09-29: 0 commits2025-09-30: 0 commits2025-10-01: 0 commits2025-10-02: 0 commits2025-10-03: 0 commits2025-10-04: 0 commits2025-10-05: 0 commits2025-10-06: 0 commits2025-10-07: 0 commits2025-10-08: 0 commits2025-10-09: 0 commits2025-10-10: 0 commits2025-10-11: 0 commits2025-10-12: 0 commits2025-10-13: 0 commits2025-10-14: 0 commits2025-10-15: 0 commits2025-10-16: 0 commits2025-10-17: 0 commits2025-10-18: 0 commits2025-10-19: 0 commits2025-10-20: 0 commits2025-10-21: 0 commits2025-10-22: 0 commits2025-10-23: 0 commits2025-10-24: 0 commits2025-10-25: 0 commits2025-10-26: 0 commits2025-10-27: 0 commits2025-10-28: 0 commits2025-10-29: 0 commits2025-10-30: 0 commits2025-10-31: 0 commits2025-11-01: 0 commits2025-11-02: 0 commits2025-11-03: 0 commits2025-11-04: 0 commits2025-11-05: 0 commits2025-11-06: 0 commits2025-11-07: 0 commits2025-11-09: 0 commits2025-11-10: 0 commits2025-11-11: 0 commits2025-11-12: 0 commits2025-11-13: 0 commits2025-11-14: 0 commits2025-11-15: 0 commits2025-11-16: 0 commits2025-11-17: 0 commits2025-11-18: 0 commits2025-11-19: 0 commits2025-11-20: 0 commits2025-11-21: 0 commits2025-11-22: 0 commits2025-11-23: 0 commits2025-11-24: 0 commits2025-11-25: 0 commits2025-11-26: 0 commits2025-11-27: 0 commits2025-11-28: 0 commits2025-11-29: 0 commits2025-11-30: 0 commits2025-12-01: 0 commits2025-12-02: 0 commits2025-12-03: 0 commits2025-12-04: 0 commits2025-12-05: 0 commits2025-12-06: 0 commits2025-12-07: 0 commits2025-12-08: 0 commits2025-12-09: 0 commits2025-12-10: 0 commits2025-12-11: 0 commits2025-12-12: 0 commits2025-12-13: 0 commits2025-12-14: 0 commits2025-12-15: 0 commits2025-12-16: 0 commits2025-12-17: 0 commits2025-12-18: 0 commits2025-12-19: 0 commits2025-12-20: 0 commits2025-12-21: 0 commits2025-12-22: 0 commits2025-12-23: 0 commits2025-12-24: 0 commits2025-12-25: 0 commits2025-12-26: 0 commits2025-12-27: 0 commits2025-12-28: 0 commits2025-12-29: 0 commits2025-12-30: 0 commits2025-12-31: 0 commits2026-01-01: 0 commits2026-01-02: 0 commits2026-01-03: 0 commits2026-01-04: 0 commits2026-01-05: 0 commits2026-01-06: 0 commits2026-01-07: 0 commits2026-01-08: 0 commits2026-01-09: 0 commits2026-01-10: 0 commits2026-01-11: 0 commits2026-01-12: 0 commits2026-01-13: 0 commits2026-01-14: 0 commits2026-01-15: 0 commits2026-01-16: 0 commits2026-01-17: 0 commits2026-01-18: 0 commits2026-01-19: 0 commits2026-01-20: 0 commits2026-01-21: 0 commits2026-01-22: 0 commits2026-01-23: 0 commits2026-01-24: 0 commits2026-01-25: 0 commits2026-01-26: 0 commits2026-01-27: 14 commits2026-01-28: 30 commits2026-01-29: 10 commits2026-01-30: 0 commits2026-01-31: 0 commits2026-02-01: 0 commits2026-02-02: 10 commits2026-02-03: 15 commits2026-02-04: 20 commits2026-02-05: 17 commits2026-02-06: 4 commits2026-02-07: 3 commits2026-02-08: 1 commit2026-02-09: 12 commits2026-02-10: 27 commits2026-02-11: 39 commits2026-02-12: 20 commits2026-02-13: 21 commits2026-02-14: 11 commits2026-02-15: 16 commits2026-02-16: 16 commits2026-02-17: 5 commits2026-02-18: 0 commits2026-02-19: 8 commits2026-02-20: 3 commits2026-02-21: 0 commits2026-02-22: 0 commits2026-02-23: 0 commits2026-02-24: 0 commits2026-02-25: 5 commits2026-02-26: 7 commits2026-02-27: 0 commits2026-02-28: 9 commits2026-03-01: 5 commits2026-03-02: 2 commits2026-03-03: 7 commits2026-03-04: 11 commits2026-03-05: 0 commits2026-03-06: 0 commits2026-03-07: 3 commits2026-03-08: 0 commits2026-03-09: 0 commits2026-03-10: 3 commits2026-03-11: 0 commits2026-03-12: 3 commits2026-03-13: 4 commits2026-03-14: 1 commit2026-03-15: 3 commits2026-03-16: 0 commits2026-03-17: 4 commits2026-03-18: 6 commits2026-03-19: 1 commit2026-03-20: 2 commits2026-03-21: 1 commit2026-03-22: 0 commits2026-03-23: 0 commits2026-03-24: 3 commits2026-03-25: 0 commits2026-03-26: 0 commits2026-03-27: 0 commits2026-03-28: 0 commits2026-03-29: 2 commits2026-03-30: 9 commits2026-03-31: 0 commits2026-04-01: 0 commits2026-04-02: 0 commits2026-04-03: 0 commits2026-04-04: 0 commits2026-04-05: 0 commits2026-04-06: 7 commits2026-04-07: 0 commits2026-04-08: 0 commits2026-04-09: 0 commits2026-04-10: 0 commits2026-04-11: 0 commits2026-04-12: 0 commits2026-04-13: 0 commits2026-04-14: 0 commits2026-04-15: 0 commits2026-04-16: 0 commits2026-04-17: 8 commits2026-04-18: 0 commits2026-04-19: 0 commits2026-04-20: 0 commits2026-04-21: 0 commits2026-04-22: 0 commits2026-04-23: 0 commits2026-04-24: 0 commits2026-04-25: 0 commits2026-04-26: 0 commits2026-04-27: 0 commits2026-04-28: 0 commits2026-04-29: 0 commits2026-04-30: 0 commits2026-05-01: 0 commits2026-05-02: 0 commits2026-05-03: 0 commits2026-05-04: 4 commits2026-05-05: 0 commits2026-05-06: 0 commits2026-05-07: 2 commits2026-05-08: 7 commits2026-05-09: 0 commits2026-05-10: 0 commits2026-05-11: 0 commits2026-05-12: 8 commits2026-05-13: 3 commits2026-05-14: 0 commits2026-05-15: 0 commits2026-05-16: 0 commits2026-05-17: 0 commits2026-05-18: 0 commits2026-05-19: 0 commits2026-05-20: 0 commits2026-05-21: 0 commits2026-05-22: 0 commits2026-05-23: 0 commits2026-05-24: 0 commits2026-05-25: 0 commits2026-05-26: 0 commits2026-05-27: 0 commits2026-05-28: 0 commits2026-05-29: 0 commits2026-05-30: 0 commits2026-05-31: 0 commits2026-06-01: 0 commits2026-06-02: 0 commits2026-06-03: 0 commits2026-06-04: 16 commits2026-06-05: 16 commits2026-06-06: 17 commits2026-06-07: 8 commits2026-06-08: 13 commits2026-06-09: 6 commits2026-06-10: 31 commits2026-06-11: 14 commits2026-06-12: 12 commits2026-06-13: 0 commits2026-06-14: 0 commits2026-06-15: 9 commits2026-06-16: 23 commits2026-06-17: 2 commits2026-06-18: 7 commits2026-06-19: 3 commits2026-06-20: 0 commits2026-06-21: 0 commits2026-06-22: 0 commits2026-06-23: 0 commits2026-06-24: 4 commits2026-06-25: 2 commits2026-06-26: 11 commits2026-06-27: 0 commits2026-06-28: 0 commits2026-06-29: 0 commits2026-06-30: 2 commits2026-07-01: 0 commits2026-07-02: 0 commits2026-07-03: 6 commits2026-07-04: 0 commits2026-07-05: 4 commits2026-07-06: 0 commits2026-07-07: 0 commits2026-07-08: 2 commits2026-07-09: 6 commits2026-07-10: 3 commits2026-07-11: 2 commits2026-07-12: 2 commits2026-07-13: 0 commits2026-07-14: 2 commits2026-07-15: 12 commits2026-07-16: 2 commits2026-07-17: 1 commit2026-07-18: 2 commits2026-07-19: 4 commits2026-07-20: 4 commits2026-07-21: 0 commits2026-07-22: 0 commits2026-07-23: 2 commits2026-07-24: 0 commits2026-07-25: 0 commits2026-07-26: 0 commits2026-07-27: 5 commits2026-07-28: 11 commits2026-07-29: 10 commits2026-07-30: 8 commits2026-07-31: 3 commits2026-08-01: 0 commits2026-08-02: 0 commits2026-08-03: 0 commits2026-08-04: 0 commits2026-08-05: 0 commits2026-08-06: 0 commits2026-08-07: 0 commits2026-08-08: 0 commits
719 commits in the last yearLessMore

Signals and awards

derived from tracked data
  • Very active

    719 commits in 52 weeks

  • Permissive license

    MIT

  • Continuous integration

    Automated checks passing

What sphere-sdk does

The Sphere SDK provides a comprehensive set of tools for interacting with the Unicity network. It handles critical wallet functions like BIP39/BIP32 key management, concurrent-safe token transfers, and peer-to-peer atomic swaps. Additionally, it integrates communication protocols like NIP-29 for group messaging and a Connect Protocol for dApp interactions, making it a robust foundation for building Unicity-powered applications.

TypeScript developers building decentralized applications, wallets, or financial tools on the Unicity network. Requires understanding of cryptography and blockchain concepts.

  • Advanced Wallet Management: Supports HD address derivation and optional PBKDF2 encryption.
  • Engine-Certified Payments: Features a SpendQueue for concurrent-send safety and self-healing coin selection.
  • P2P Atomic Swaps: Facilitates secure token exchanges using an escrow and DM-based negotiation protocol.
  • Integrated Messaging: Uses Nostr relays for NIP-17 DMs and NIP-29 group chat with moderation.

Where teams use it

Building Unicity dApps

Developers use the SDK to integrate Unicity payments and state transitions into their applications.

Creating Custom Wallets

Provides the foundational cryptography and network interaction required to build a Unicity wallet.

Secure Peer-to-Peer Trading

Enables decentralized applications to facilitate trustless token swaps between users.

Getting started: npm install @unicitylabs/sphere-sdk

README

main branch

Sphere SDK

A modular TypeScript SDK for Unicity wallet operations (Unicity state transition network).

Features

  • Wallet Management - BIP39/BIP32 key derivation; optional password encryption (PBKDF2)
  • Payments - Engine-certified token transfers, delivered via the wallet-api mailbox (DeliveryProvider port); concurrent-send safety (SpendQueue); self-healing coin selection
  • Invoicing / Accounting (experimental — not production-ready, not enabled in the Sphere wallet) - On-chain invoice lifecycle with payment attribution, auto-return, privacy-preserving hashed invoice IDs; invoices travel as v2 data-token blobs (hex strings) — createInvoice() returns the blob, importInvoice() accepts it
  • Token Swaps - P2P atomic swaps via escrow with DM-based negotiation protocol
  • Payment Requests - Request payments with async response tracking
  • Market (Intents) - Signed intent bulletin board with semantic search and live feed
  • Group Chat - NIP-29 relay-based group messaging with moderation
  • Messaging (Nostr) - NIP-17 DMs + NIP-29 group chat and nametag publishing — messaging only; not the v2 payment rail
  • Multi-Address - HD address derivation (BIP32/BIP44)
  • Token Validation - Engine-based token verification (trust base + spent check via the v2 gateway)
  • Connect Protocol - dApp ↔ wallet communication via ConnectClient / ConnectHost (browser extension + popup)
  • CLI - Comprehensive command-line interface with shell auto-completion

Installation

npm install @unicitylabs/sphere-sdk

Quick Start Guides

Choose your platform:

Platform Guide Required Optional
Browser QUICKSTART-BROWSER.md SDK only IndexedDB storage
Node.js QUICKSTART-NODEJS.md SDK + ws File storage
CLI @unicity-sphere/cli Separate package -
dApp integration CONNECT.md SDK only Sphere extension

CLI (Command Line Interface)

The CLI has moved to a dedicated package: @unicity-sphere/cli.

npm install -g @unicity-sphere/cli
sphere --help

See docs/QUICKSTART-CLI.md for the full command reference.

Quick Start

v2 setup is two provider layers, not one. createBrowserProviders / createNodeProviders build only the base (storage + transport + oracle). You must then add the wallet-api rails with createWalletApiProviders — that is what gives the wallet its delivery (mailbox) and token-storage ports. Skipping it does not error; you silently get a wallet that cannot send or receive v2 transfers. This single step is what trips most integrators up.

import { Sphere } from '@unicitylabs/sphere-sdk';
import { createBrowserProviders } from '@unicitylabs/sphere-sdk/impl/browser';
import { createWalletApiProviders } from '@unicitylabs/sphere-sdk/impl/shared/wallet-api';

// 1. Base providers: storage + transport + oracle. `network` is REQUIRED here (no default).
//    The testnet2 gateway key is PUBLIC (not a secret); it is required at runtime for send/mint.
const base = createBrowserProviders({
  network: 'testnet',                                          // alias of testnet2 (networkId 4)
  oracle: { apiKey: 'sk_ddc3cfcc001e4a28ac3fad7407f99590' },   // public testnet2 key
});

// 2. Add the v2 wallet-api rails: mailbox delivery + server token storage + client.
//    Returns { ...base, delivery, walletApi, tokenStorage }. THIS is the step v1 docs omitted.
const providers = createWalletApiProviders(base, {
  baseUrl: 'https://wallet-api.unicity.network',   // your wallet-api deployment (testnet2)
  network: 'testnet2',
  deviceId: 'my-stable-device-id',                 // persist this to avoid re-auth each launch
});

// 3. Init the wallet (auto-creates one if none exists).
const { sphere, created, generatedMnemonic } = await Sphere.init({
  ...providers,
  autoGenerate: true,
});
if (created && generatedMnemonic) {
  console.log('SAVE THIS RECOVERY PHRASE:', generatedMnemonic);
}

// 4. Send — engine-driven, certified on-chain. The recipient needs a published identity
//    (chain pubkey), e.g. a registered Unicity ID; otherwise send fails with INVALID_RECIPIENT.
const result = await sphere.payments.send({
  recipient: '@alice',
  amount: '1000000',     // decimal STRING — never a JS number
  coinId: 'UCT',         // a symbol auto-resolves to its hex coinId
  memo: 'hello',
});
console.log(result.status);   // 'completed'
// result.deliveryPending === true is NORMAL, not a failure: the token is certified on-chain but
// the recipient's mailbox delivery was deferred and will land on retry (see "Send result" below).

// 5. Receive — incoming transfers arrive automatically via the delivery port (background poll +
//    wake). To drain explicitly (e.g. a CLI/batch app), call receive():
const { transfers } = await sphere.payments.receive(undefined, (t) => {
  console.log('received', t.amount, t.coinId);
});

console.log(await sphere.payments.getAssets());

What just happened (the provider model)

A v2 wallet is composed from swappable ports, layered in two steps:

Layer Built by Ports it supplies
Base createBrowserProviders / createNodeProviders storage (wallet state), transport (Nostr — messaging/nametags only), oracle (gateway/trust base)
wallet-api rails createWalletApiProviders(base, …) delivery (mailbox), walletApi (REST client), tokenStorage (server inventory)
  • Delivery is a port, not Nostr. In v2, transfers are certified on-chain by the token engine and the finished token is delivered through the wallet-api mailbox (WalletApiMailboxProvider). Nostr carries messaging/nametags — it does not move payments.
  • Custody. createWalletApiProviders uses server custody ('inventory'): the wallet-api holds your token inventory. For own-custody (your app keeps token storage, wallet-api is delivery-only), swap in createOwnStorageWalletApiProviders (custody 'external').
  • network placement. Required on createBrowserProviders/createNodeProviders (throws INVALID_CONFIG if absent); optional/informational on Sphere.init.

For manual/advanced provider wiring, see Custom Providers Configuration. For the deeper integration guide, see docs/INTEGRATION.md.

Send result (TransferResult)

send() resolves with a TransferResult:

Field Meaning
status 'completed' on success. ('pending' | 'submitted' | 'confirmed' | 'delivered' | 'failed' also exist for in-flight/terminal states.)
deliveryPending true when the spend is certified on-chain but the recipient's mailbox delivery was deferred (a full inbox / transient outage). This is success, not failure — the token is finalized and the finished blob is journaled and re-delivered automatically.
deliveryState 'landed' (delivered) or 'pending-delivery' (deferred, as above).

Treat status === 'completed' as sent. Use deliveryPending only to show a "delivery pending" hint — never as an error. A stale-but-spent source is self-healed (the next live coin is selected automatically).

Handling send() rejections — CERTIFICATION_UNCONFIRMED is NOT re-sendable (money-safety)

send() throws for genuine failures (INVALID_RECIPIENT, insufficient balance, a TransferConflictError lost race) and for one indeterminate case you must handle specially: a ProofUnconfirmedError (code: 'CERTIFICATION_UNCONFIRMED', mayHaveCertified: true). It means the spend may already be on-chain but the proof fetch was inconclusive — the SDK keeps the intent open and completes it later under the same transferId.

  • ⚠️ Never re-issue send() on CERTIFICATION_UNCONFIRMED. A fresh send() mints a new transferId on a different source, so the original resumes and the retry sends → the recipient is double-paid. Treat it as "sent, pending confirmation."
  • Recovery is resumeOpenIntents() — it replays the open intent under the same transferId (recovers the proof + delivery, or records the spend if a rival tx won; never a second spend). It runs automatically at session start (Sphere.init / Sphere.load / re-sign-in). A long-running bot that doesn't re-init should call sphere.payments.resumeOpenIntents() on startup and periodically — it returns { resumed, conflicted, failed }.
import { isSphereError } from '@unicitylabs/sphere-sdk';

try {
  const result = await sphere.payments.send({ recipient: '@bob', amount, coinId });
  // result.status === 'completed' (or result.deliveryPending === true) → sent
} catch (err) {
  if (isSphereError(err) && err.code === 'CERTIFICATION_UNCONFIRMED') {
    // Possibly already sent on-chain — DO NOT re-send. Resume finishes it
    // (auto at next sign-in, or: await sphere.payments.resumeOpenIntents()).
  } else {
    // genuine failure — safe to surface to the user / retry
  }
}

transferMode on TransferRequest is deprecated — accepted for backwards-compat but ignored (v2 has a single engine-driven path).

Network Configuration

The SDK ships network presets that configure all services automatically. network is required — there is no default:

Network Aggregator (gateway) Nostr Relay
testnet gateway.testnet2.unicity.network (v2) nostr-relay.testnet.unicity.network
testnet2 alias of testnet (same configuration) nostr-relay.testnet.unicity.network
mainnet aggregator.unicity.network (v1-era) relay.unicity.network (+ public relays)
dev dev-aggregator.dyndns.org (v1-era) nostr-relay.testnet.unicity.network

v1 → v2 cutover: testnet now points at testnet2, the v2 state-transition gateway network (network id 4, taken from the trust base; own testnet2 token registry). The old goggregator-test testnet spoke the removed v1 protocol and is gone. mainnet and dev still point at v1-era aggregators — wallet operations that move money (send, mintFungibleToken, invoices) fail loudly (AGGREGATOR_ERROR) on those networks until their gateways are cut over to the v2 protocol. The only supported transfer wire payload is the finished v2 token blob — incoming v1-era payloads are dropped with an explicit error log, so peers must run a >= 0.8 wallet to send to this wallet.

// Use testnet for all services
const providers = createBrowserProviders({ network: 'testnet' });

// Override specific services while using network preset
const providers = createBrowserProviders({
  network: 'testnet',
  oracle: { url: 'https://custom-gateway.example.com' }, // custom v2 gateway
});

API Key

The SDK bundles no default API key. Pass the gateway key via oracle: { apiKey } — without it, gateway requests are unauthenticated and money movement on testnet2 fails.

const providers = createBrowserProviders({
  network: 'testnet',
  oracle: { apiKey: 'sk_...' },
});

The testnet2 key is not a secret — it is published in .env.example and safe to keep in docs and client code. A mainnet key, by contrast, IS a secret: keep it in your deploy environment only.

Testnet2 endpoints (the values we build with)

The testnet preset wires most of these automatically — you only pass network, oracle.apiKey, and the wallet-api baseUrl. The full set, for reference and manual wiring:

What Value
Network testnet (alias testnet2), networkId 4
Aggregator / gateway (token engine) https://gateway.testnet2.unicity.network
Aggregator API key (public — not a secret) sk_ddc3cfcc001e4a28ac3fad7407f99590
wallet-api (delivery + token storage) https://wallet-api.unicity.network
Nostr relay (messaging / nametags) wss://nostr-relay.testnet.unicity.network
Group-chat relay (NIP-29) wss://sphere-relay.unicity.network
Token registry https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json

The aggregator key above is the testnet2 key only and is safe in client code; a mainnet key is a real secret. mainnet/dev still point at v1-era aggregators and cannot serve the v2 engine yet (AGGREGATOR_ERROR).

Price Provider (Optional)

Enable fiat price display by adding a price config. Currently supports CoinGecko API (free and pro tiers).

// With CoinGecko (free tier, no API key)
const providers = createBrowserProviders({
  network: 'testnet',
  price: { platform: 'coingecko' },
});

// With CoinGecko Pro
const providers = createBrowserProviders({
  network: 'testnet',
  price: { platform: 'coingecko', apiKey: 'CG-xxx' },
});

const { sphere } = await Sphere.init({ ...providers, autoGenerate: true });

// Total portfolio value in USD
const totalUsd = await sphere.payments.getFiatBalance();
// 1523.45

// Assets with price data
const assets = await sphere.payments.getAssets();
// [{ coinId, symbol, totalAmount, priceUsd: 97500, fiatValueUsd: 975.00, change24h: 2.3, ... }]

Without price config, getFiatBalance() returns null and price fields in getAssets() are null. All other functionality works normally. (getBalance() is the synchronous per-coin balance accessor — it returns Asset[] without price data.)

You can also set the price provider after initialization:

import { createPriceProvider } from '@unicitylabs/sphere-sdk';

sphere.setPriceProvider(createPriceProvider({
  platform: 'coingecko',
  apiKey: 'CG-xxx',
}));

Test Tokens on Testnet (Self-Mint)

There is no faucet. On testnet you top up your wallet by self-minting fungible tokens via the v2 token engine — mintFungibleToken(coinIdHex, amount) mints a finished token directly to this wallet:

import { getCoinIdBySymbol } from '@unicitylabs/sphere-sdk';

// Resolve the coin's hex id from the token registry (or pass a hex coinId directly)
const coinId = getCoinIdBySymbol('UCT');

const result = await sphere.payments.mintFungibleToken(coinId!, 1000n);
if (result.success) {
  console.log('Minted token:', result.tokenId);
} else {
  console.error('Mint failed:', result.error);
}

Note: Minting requires a working v2 oracle config (trust base + gateway URL + API key) — it fails with an error result otherwise. See API Key above.

Multi-Address Support

The SDK supports HD (Hierarchical Deterministic) wallets with multiple addresses:

// Get current address index
const currentIndex = sphere.getCurrentAddressIndex(); // 0

// Switch to a different address
await sphere.switchToAddress(1);
console.log(sphere.identity?.directAddress); // DIRECT://... (address at index 1)

// Register nametag for this address (independent per address)
await sphere.registerNametag('bob');

// Switch back to first address
await sphere.switchToAddress(0);

// Get nametag for specific address
const bobNametag = sphere.getNametagForAddress(1); // 'bob'

// Get all address nametags
const allNametags = sphere.getAllAddressNametags();
// Map { 0 => 'alice', 1 => 'bob' }

// Derive address without switching (for display/receiving)
const addr2 = sphere.deriveAddress(2);
console.log(addr2.address, addr2.publicKey);

Identity Properties

Important: The DIRECT address is the primary address for the Unicity network.

interface Identity {
  chainPubkey: string;         // 33-byte compressed secp256k1 public key
  directAddress?: string;      // DIRECT address (DIRECT://...) - PRIMARY ADDRESS
  ipnsName?: string;           // IPNS name for token sync
  nametag?: string;            // Registered nametag (@username)
}

// Access identity - use directAddress as primary
console.log(sphere.identity?.directAddress);    // DIRECT://0000be36... (PRIMARY)
console.log(sphere.identity?.nametag);          // alice (human-readable)
console.log(sphere.identity?.chainPubkey);      // 02abc123... (33-byte compressed)

Address Change Event

// Listen for address switches
sphere.on('identity:changed', (event) => {
  console.log('Switched to address index:', event.data.addressIndex);
  console.log('L3 address:', event.data.directAddress);
  console.log('Chain pubkey:', event.data.chainPubkey);
  console.log('Nametag:', event.data.nametag);
});

// Listen for nametag recovery (when importing wallet)
sphere.on('nametag:recovered', (event) => {
  console.log('Recovered nametag from Nostr:', event.data.nametag);
});

Payment Requests

Request payments from others with response tracking:

// Send payment request
const result = await sphere.payments.sendPaymentRequest('@bob', {
  amount: '1000000',
  coinId: 'UCT',
  message: 'Payment for order #1234',
});

// Wait for response (with 2 minute timeout)
if (result.success) {
  const response = await sphere.payments.waitForPaymentResponse(result.requestId!, 120000);
  if (response.responseType === 'paid') {
    console.log('Payment received! Transfer:', response.transferId);
  }
}

// Or subscribe to responses
sphere.payments.onPaymentRequestResponse((response) => {
  console.log(`Response: ${response.responseType}`);
});

// Handle incoming payment requests
sphere.payments.onPaymentRequest((request) => {
  console.log(`${request.senderNametag} requests ${request.amount} ${request.symbol}`);

  // Accept and pay
  await sphere.payments.payPaymentRequest(request.id);

  // Or reject
  await sphere.payments.rejectPaymentRequest(request.id);
});

Group Chat (NIP-29)

Relay-based group messaging using the NIP-29 protocol. The module embeds its own Nostr connection separate from the wallet transport.

Enabling Group Chat

// Enable with network defaults (wss://sphere-relay.unicity.network)
const { sphere } = await Sphere.init({
  ...providers,
  autoGenerate: true,
  groupChat: true,
});

// Enable with custom relay
const { sphere } = await Sphere.init({
  ...providers,
  autoGenerate: true,
  groupChat: { relays: ['wss://my-nip29-relay.com'] },
});

// Access the module
const gc = sphere.groupChat!;

Connection

// Connect to the NIP-29 relay
await gc.connect();
console.log('Connected:', gc.getConnectionStatus());

// Check if current user is a relay admin
const isRelayAdmin = await gc.isCurrentUserRelayAdmin();

Groups

import { GroupVisibility } from '@unicitylabs/sphere-sdk';

// Create a public group
const group = await gc.createGroup({
  name: 'General',
  description: 'Public discussion',
});

// Create a private group
const privateGroup = await gc.createGroup({
  name: 'Team',
  visibility: GroupVisibility.PRIVATE,
});

// Create a write-restricted group (only admins/writers can post)
const announcements = await gc.createGroup({
  name: 'Announcements',
  writeRestricted: true,
});

// Discover and join
const available = await gc.fetchAvailableGroups(); // public groups on relay
await gc.joinGroup(group.id);

// Join private group with invite
await gc.joinGroup(privateGroup.id, inviteCode);

// List joined groups
const groups = gc.getGroups();

// Leave or delete
await gc.leaveGroup(group.id);
await gc.deleteGroup(group.id); // admin only

Messaging

// Send a message
const msg = await gc.sendMessage(group.id, 'Hello!');

// Reply to a message
await gc.sendMessage(group.id, 'Agreed', { replyToId: msg.id });

// Fetch messages from relay
const messages = await gc.fetchMessages(group.id, { limit: 50 });

// Get locally cached messages
const cached = gc.getMessages(group.id);

// Listen for new messages in real-time
const unsubscribe = gc.onMessage((message) => {
  console.log(`[${message.groupId}] ${message.senderPubkey}: ${message.content}`);
});

Members & Moderation

// Get members
const members = gc.getMembers(group.id);

// Check roles
gc.isCurrentUserAdmin(group.id);     // boolean
gc.isCurrentUserModerator(group.id); // boolean
await gc.canModerateGroup(group.id); // includes relay admin check
gc.canWriteToGroup(group.id);       // false if write-restricted and not admin/moderator

// Moderate (requires admin/moderator role)
await gc.kickUser(group.id, userPubkey, 'reason');
await gc.deleteMessage(group.id, messageId);

Invites (Private Groups)

// Create invite code (admin only)
const invite = await gc.createInvite(group.id);

// Share invite code, recipient joins with:
await gc.joinGroup(group.id, invite);

Unread Counts

const total = gc.getTotalUnreadCount();
gc.markGroupAsRead(group.id);

Key Types

interface GroupData {
  id: string;
  relayUrl: string;
  name: string;
  description?: string;
  visibility: GroupVisibility;  // 'PUBLIC' | 'PRIVATE'
  writeRestricted?: boolean;   // Only admins and moderators can post
  memberCount?: number;
  unreadCount?: number;
  lastMessageTime?: number;
  lastMessageText?: string;
}

interface GroupMessageData {
  id?: string;
  groupId: string;
  content: string;
  timestamp: number;
  senderPubkey: string;
  senderNametag?: string;
  replyToId?: string;
}

interface GroupMemberData {
  pubkey: string;
  groupId: string;
  role: GroupRole;  // 'ADMIN' | 'MODERATOR' | 'MEMBER'
  nametag?: string;
  joinedAt: number;
}

Direct Messages (NIP-17)

End-to-end encrypted DMs via NIP-17 gift wrap, accessed through sphere.communications:

// Send a DM (by nametag or pubkey)
await sphere.communications.sendDM('@alice', 'Hello!');

// Listen for incoming DMs
sphere.communications.onDirectMessage((msg) => {
  console.log(`From ${msg.senderNametag ?? msg.senderPubkey}: ${msg.content}`);
});

DM History on Connect

By default, the SDK resumes from the last processed DM timestamp (persisted in storage). On first connect, it starts from "now" — no historical replay.

Use dmSince to control how far back to fetch DMs on first connect:

const { sphere } = await Sphere.init({
  ...providers,
  autoGenerate: true,
  dmSince: Math.floor(Date.now() / 1000) - 86400,  // last 24 hours
});

Once the SDK processes DMs, the timestamp is persisted and dmSince is ignored on subsequent connects.

Ephemeral Mode (No Caching)

For anonymous agents or LLM bots that don't need message history, disable DM caching:

const { sphere } = await Sphere.init({
  ...providers,
  communications: { cacheMessages: false },
});

// Stream-only: receive, process, forget
sphere.communications.onDirectMessage((msg) => {
  processAndReply(msg);
});

// sendDM still works — message is sent but not stored locally
await sphere.communications.sendDM('@alice', 'response');

When cacheMessages is false:

  • onDirectMessage() handlers and message:dm events fire normally
  • Messages are never stored in memory or persisted to storage
  • getConversation() / getConversations() return empty results
  • Deduplication is skipped (duplicate relay deliveries may trigger duplicate events)

Alternative: Manual Create/Load

View on GitHub

Recent activity

commits and pull requests

Releases and announcements

2 total
  1. **178 PRs promoted from `integration/all-fixes` to `main`.** - Release PR: [#345](https://github.com/unicity-sphere/sphere-sdk/pull/345) - Tag points at: `af41bea6fc36` - Period covered: ~4 months of accumulated profile-layer, UXF, recovery, and connectivity work since the previous release lineage (PRs #128–#130 → #303–#342). ## ⚠️ Breaking: UXF wire-shape default flip Default sends now emit UXF v1.0 bundles (`senderUxf=true`). Older SDK receivers **cannot decode** them. Pin a shared SDK version across senders/receivers during the transition, or pass explicit `features: { senderUxf: false }` to fall back to the legacy single-token TXF wire shape. See `docs/uxf/UXF-TRANSFER-CUTOVER-RUNBOOK.md` for the runbook. --- ### Changed (BREAKING — wire-shape default flip) - **UXF feature flags now default-ON** (T.8.D part 1 of 2 — production cutover, NO legacy code path removal). All four UXF feature flags moved from default `false` → default `true` in `PaymentsModuleConfig.features`: - `senderUxf` — `payments.send({transferMode:'instant'})` (the public default) now routes through the new UXF instant-sender; conservative-mode also routes through the UXF orchestrator. - `recipientUxf

  2. v0.2.4v0.2.4Feb 11, 2026

    ## Bug Fixes - **CLI process exit**: Fix CLI commands hanging ~10s after completion due to lingering Nostr WebSocket handles - **Instant split change token**: Fix sender losing change token when `process.exit()` killed background proof collection before completion ## Changes - `InstantSplitExecutor.submitBackgroundV5` now returns a trackable `Promise<void>` instead of fire-and-forget - `PaymentsModule` tracks background promises and exposes `waitForPendingOperations()` for callers to await completion - CLI `send` command awaits pending background operations before exiting - `InstantSplitResult` now includes optional `backgroundPromise` field ## Testing - 914 unit tests passing - 8/8 transfer e2e tests passing (balance + tombstone verified) - 8/8 CLI e2e tests passing (instant + conservative, UCT/BTC/ETH)

Commits per week

last 52 weeks
1310Week of 2025-08-09: 0 commitsWeek of 2025-08-16: 0 commitsWeek of 2025-08-23: 0 commitsWeek of 2025-08-30: 0 commitsWeek of 2025-09-06: 0 commitsWeek of 2025-09-13: 0 commitsWeek of 2025-09-20: 0 commitsWeek of 2025-09-27: 0 commitsWeek of 2025-10-04: 0 commitsWeek of 2025-10-11: 0 commitsWeek of 2025-10-18: 0 commitsWeek of 2025-10-25: 0 commitsWeek of 2025-11-01: 0 commitsWeek of 2025-11-09: 0 commitsWeek of 2025-11-16: 0 commitsWeek of 2025-11-23: 0 commitsWeek of 2025-11-30: 0 commitsWeek of 2025-12-07: 0 commitsWeek of 2025-12-14: 0 commitsWeek of 2025-12-21: 0 commitsWeek of 2025-12-28: 0 commitsWeek of 2026-01-04: 0 commitsWeek of 2026-01-11: 0 commitsWeek of 2026-01-18: 0 commitsWeek of 2026-01-25: 54 commitsWeek of 2026-02-01: 69 commitsWeek of 2026-02-08: 131 commitsWeek of 2026-02-15: 48 commitsWeek of 2026-02-22: 21 commitsWeek of 2026-03-01: 28 commitsWeek of 2026-03-08: 11 commitsWeek of 2026-03-15: 17 commitsWeek of 2026-03-22: 3 commitsWeek of 2026-03-29: 11 commitsWeek of 2026-04-05: 7 commitsWeek of 2026-04-12: 8 commitsWeek of 2026-04-19: 0 commitsWeek of 2026-04-26: 0 commitsWeek of 2026-05-03: 13 commitsWeek of 2026-05-10: 11 commitsWeek of 2026-05-17: 0 commitsWeek of 2026-05-24: 0 commitsWeek of 2026-05-31: 49 commitsWeek of 2026-06-07: 84 commitsWeek of 2026-06-14: 44 commitsWeek of 2026-06-21: 17 commitsWeek of 2026-06-28: 8 commitsWeek of 2026-07-05: 17 commitsWeek of 2026-07-12: 21 commitsWeek of 2026-07-19: 10 commitsWeek of 2026-07-26: 37 commitsWeek of 2026-08-02: 0 commitsAug 9, 2025Aug 2, 2026
719 commits in the last 52 weeks.

When work happens

weekday and hour
SunMonTueWedThuFriSat036912151821Sun 0:00 — 5 commitsSun 1:00 — 4 commitsSun 2:00 — 4 commitsSun 3:00 — 3 commitsSun 4:00 — 0 commitsSun 5:00 — 0 commitsSun 6:00 — 0 commitsSun 7:00 — 0 commitsSun 8:00 — 0 commitsSun 9:00 — 0 commitsSun 10:00 — 0 commitsSun 11:00 — 0 commitsSun 12:00 — 1 commitsSun 13:00 — 1 commitsSun 14:00 — 0 commitsSun 15:00 — 0 commitsSun 16:00 — 1 commitsSun 17:00 — 1 commitsSun 18:00 — 1 commitsSun 19:00 — 4 commitsSun 20:00 — 5 commitsSun 21:00 — 6 commitsSun 22:00 — 5 commitsSun 23:00 — 4 commitsMon 0:00 — 1 commitsMon 1:00 — 4 commitsMon 2:00 — 4 commitsMon 3:00 — 5 commitsMon 4:00 — 2 commitsMon 5:00 — 2 commitsMon 6:00 — 2 commitsMon 7:00 — 1 commitsMon 8:00 — 1 commitsMon 9:00 — 2 commitsMon 10:00 — 2 commitsMon 11:00 — 8 commitsMon 12:00 — 7 commitsMon 13:00 — 5 commitsMon 14:00 — 5 commitsMon 15:00 — 6 commitsMon 16:00 — 10 commitsMon 17:00 — 6 commitsMon 18:00 — 5 commitsMon 19:00 — 0 commitsMon 20:00 — 5 commitsMon 21:00 — 2 commitsMon 22:00 — 5 commitsMon 23:00 — 1 commitsTue 0:00 — 6 commitsTue 1:00 — 3 commitsTue 2:00 — 4 commitsTue 3:00 — 5 commitsTue 4:00 — 6 commitsTue 5:00 — 2 commitsTue 6:00 — 2 commitsTue 7:00 — 0 commitsTue 8:00 — 1 commitsTue 9:00 — 1 commitsTue 10:00 — 0 commitsTue 11:00 — 6 commitsTue 12:00 — 5 commitsTue 13:00 — 9 commitsTue 14:00 — 6 commitsTue 15:00 — 12 commitsTue 16:00 — 10 commitsTue 17:00 — 7 commitsTue 18:00 — 11 commitsTue 19:00 — 6 commitsTue 20:00 — 5 commitsTue 21:00 — 8 commitsTue 22:00 — 6 commitsTue 23:00 — 9 commitsWed 0:00 — 5 commitsWed 1:00 — 8 commitsWed 2:00 — 6 commitsWed 3:00 — 4 commitsWed 4:00 — 5 commitsWed 5:00 — 11 commitsWed 6:00 — 2 commitsWed 7:00 — 2 commitsWed 8:00 — 3 commitsWed 9:00 — 4 commitsWed 10:00 — 7 commitsWed 11:00 — 5 commitsWed 12:00 — 13 commitsWed 13:00 — 7 commitsWed 14:00 — 5 commitsWed 15:00 — 10 commitsWed 16:00 — 6 commitsWed 17:00 — 7 commitsWed 18:00 — 5 commitsWed 19:00 — 5 commitsWed 20:00 — 13 commitsWed 21:00 — 21 commitsWed 22:00 — 8 commitsWed 23:00 — 13 commitsThu 0:00 — 14 commitsThu 1:00 — 1 commitsThu 2:00 — 1 commitsThu 3:00 — 2 commitsThu 4:00 — 0 commitsThu 5:00 — 0 commitsThu 6:00 — 0 commitsThu 7:00 — 1 commitsThu 8:00 — 1 commitsThu 9:00 — 2 commitsThu 10:00 — 3 commitsThu 11:00 — 2 commitsThu 12:00 — 6 commitsThu 13:00 — 5 commitsThu 14:00 — 3 commitsThu 15:00 — 4 commitsThu 16:00 — 7 commitsThu 17:00 — 8 commitsThu 18:00 — 9 commitsThu 19:00 — 14 commitsThu 20:00 — 6 commitsThu 21:00 — 17 commitsThu 22:00 — 6 commitsThu 23:00 — 15 commitsFri 0:00 — 7 commitsFri 1:00 — 14 commitsFri 2:00 — 1 commitsFri 3:00 — 1 commitsFri 4:00 — 1 commitsFri 5:00 — 1 commitsFri 6:00 — 6 commitsFri 7:00 — 5 commitsFri 8:00 — 1 commitsFri 9:00 — 1 commitsFri 10:00 — 3 commitsFri 11:00 — 1 commitsFri 12:00 — 5 commitsFri 13:00 — 2 commitsFri 14:00 — 1 commitsFri 15:00 — 3 commitsFri 16:00 — 1 commitsFri 17:00 — 6 commitsFri 18:00 — 6 commitsFri 19:00 — 11 commitsFri 20:00 — 12 commitsFri 21:00 — 6 commitsFri 22:00 — 3 commitsFri 23:00 — 6 commitsSat 0:00 — 1 commitsSat 1:00 — 3 commitsSat 2:00 — 1 commitsSat 3:00 — 2 commitsSat 4:00 — 3 commitsSat 5:00 — 0 commitsSat 6:00 — 0 commitsSat 7:00 — 0 commitsSat 8:00 — 0 commitsSat 9:00 — 0 commitsSat 10:00 — 0 commitsSat 11:00 — 0 commitsSat 12:00 — 0 commitsSat 13:00 — 1 commitsSat 14:00 — 6 commitsSat 15:00 — 3 commitsSat 16:00 — 3 commitsSat 17:00 — 2 commitsSat 18:00 — 3 commitsSat 19:00 — 2 commitsSat 20:00 — 2 commitsSat 21:00 — 1 commitsSat 22:00 — 6 commitsSat 23:00 — 10 commits
Commit volume by weekday and hour (UTC). Larger dots mean more commits.
DateListRankStars gained
May 28, 2026daily#3+164
May 27, 2026daily#13+80
  • freeCodeCamp/freeCodeCamp

    freeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.

    453.6K stars · TypeScript

  • openclaw/openclaw

    Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

    385.5K stars · TypeScript

  • openclaw/openclaw

    Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

    384.4K stars · TypeScript

  • openclaw/openclaw

    Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

    384.4K stars · TypeScript

  • openclaw/openclaw

    Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

    384.4K stars · TypeScript

  • affaan-m/ECC

    The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.

    238.5K stars · JavaScript