SkillAgentSearch skills...

Sphere SDK

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

Install / Use

npx skills add unicity-sphere/sphere-sdk

Installs into whichever agent you are using.

About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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 over the wallet-api vertical (durable server-side intents, mailbox delivery, crash-safe resume under the same transferId); server custody — the backend holds inventory, keys stay local
  • Payment Requests - Request payments over the wallet-api rail with encrypted memos and durable settling
  • 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 payment rail
  • Multi-Address - HD address derivation (BIP32/BIP44)
  • 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

Setup is two provider layers, not one. createBrowserProviders / createNodeProviders build only the base (storage + transport + oracle). You must then attach the wallet-api transport config with createWalletApiProviders — money moves only through the wallet-api vertical. Skipping it fails loudly: Sphere.init throws INVALID_CONFIG.

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. Attach the wallet-api transport config. Returns { ...base, walletApi } —
//    a plain config object ({ network, baseUrl, deviceId?, ... }) that Sphere.init consumes.
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 land automatically while the wallet runs (mailbox drain +
//    wake socket). To drain explicitly (e.g. a CLI/batch app), call receive():
const { transfers } = await sphere.payments.receive();
sphere.on('transfer:incoming', (t) => console.log('received from', t.senderNametag));

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

What just happened (the provider model)

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

| Layer | Built by | What it supplies | |-------|----------|-------------------| | Base | createBrowserProviders / createNodeProviders | storage (keys/identity/journals), transport (Nostr — messaging/nametags only), oracle (gateway/trust base) | | wallet-api transport | createWalletApiProviders(base, …) | walletApi — the transport CONFIG ({ network, baseUrl, deviceId?, fetchFn?, webSocketFactory?, paymentsV2Transport? }) the payments vertical is composed from |

  • The rail is wallet-api, not Nostr. Transfers are certified on-chain by the token engine and the finished token is deposited into the recipient's wallet-api mailbox. Nostr carries messaging/nametags — it does not move payments.
  • Custody is server-side. The wallet-api backend holds your token inventory; your keys never leave the client. (Own-storage custody was rescinded — there is no local token store.)
  • The money ports are contract-enforced. StoragePort/DeliveryPort (modules/payments-v2/ports.ts) have wallet-api implementations; the paymentsV2Transport seam in the walletApi config lets tests/custom hosts inject a whole replacement bundle.
  • network placement. Required on createBrowserProviders/createNodeProviders AND in the walletApi config (init throws INVALID_CONFIG without either); 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 automatic. The open intent is replayed under the same transferId (recovers the proof + delivery, or records the spend if a rival tx won; never a second spend): partially-committed outcomes converge in-process, and every remaining open intent is resumed when the vertical starts (Sphere.init / Sphere.load / an address switch). There is no public resume API to call.
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.
  } else {
    // genuine failure — safe to surface to the user / retry
  }
}

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, mint) fail loudly (AGGREGATOR_ERROR) on those networks until

Related Skills

View on GitHub
GitHub Stars5.4k
CategoryDevelopment
Updated2h ago
Forks105

Languages

TypeScript

Security Score

100/100

Audited on Aug 8, 2026

No findings