Sphere SDK - Browser Quick Start¶
Get up and running with Sphere SDK in web applications in under 5 minutes.
Installation¶
| Package | Required | Description |
|---|---|---|
@unicitylabs/sphere-sdk |
Yes | The SDK |
That's it! No additional dependencies for basic usage. Browser uses native WebSocket.
Note: No API key is bundled with the SDK. The
testnetgateway (testnet2, see below) requires one — inject it viaoracle: { apiKey: '...' }. The testnet2 key is not a secret (see.env.example):sk_ddc3cfcc001e4a28ac3fad7407f99590. A mainnet key, by contrast, IS a secret — keep it in your deploy environment only.Networks: since the v1→v2 cutover,
network: 'testnet'points at the testnet2 v2 gateway (https://gateway.testnet2.unicity.network; the network id comes from the trust base).'testnet2'is an alias of the same configuration.mainnet/devstill point at v1-era aggregators and cannot serve the v2 engine yet — wallet operations there fail withAGGREGATOR_ERROR.
Framework Setup¶
Vanilla JavaScript / TypeScript¶
import { Sphere } from '@unicitylabs/sphere-sdk';
import { createBrowserProviders } from '@unicitylabs/sphere-sdk/impl/browser';
import { createWalletApiProviders } from '@unicitylabs/sphere-sdk/impl/shared/wallet-api';
async function initWallet() {
// Step 1: Create base providers (storage + transport + oracle)
const base = createBrowserProviders({
network: 'testnet',
oracle: { apiKey: 'sk_ddc3cfcc001e4a28ac3fad7407f99590' }, // public testnet2 key
});
// Step 2: Wrap with wallet-api for v2 mailbox delivery and token storage
const providers = createWalletApiProviders(base, {
baseUrl: 'https://wallet-api.unicity.network',
network: 'testnet2',
deviceId: 'my-stable-device-id', // Must be stable across sessions
});
// Step 3: Initialize wallet
const { sphere, created, generatedMnemonic } = await Sphere.init({
...providers,
autoGenerate: true,
});
if (created && generatedMnemonic) {
// IMPORTANT: Show to user and ask them to save it!
alert('Save your recovery phrase: ' + generatedMnemonic);
}
return sphere;
}
React¶
import { useState, useEffect } from 'react';
import { Sphere } from '@unicitylabs/sphere-sdk';
import { createBrowserProviders } from '@unicitylabs/sphere-sdk/impl/browser';
import { createWalletApiProviders } from '@unicitylabs/sphere-sdk/impl/shared/wallet-api';
function useWallet() {
const [sphere, setSphere] = useState<Sphere | null>(null);
const [loading, setLoading] = useState(true);
const [mnemonic, setMnemonic] = useState<string | null>(null);
useEffect(() => {
const init = async () => {
// Step 1: Create base providers
const base = createBrowserProviders({
network: 'testnet',
oracle: { apiKey: 'sk_ddc3cfcc001e4a28ac3fad7407f99590' }, // public testnet2 key
});
// Step 2: Wrap with wallet-api for v2 delivery
const providers = createWalletApiProviders(base, {
baseUrl: 'https://wallet-api.unicity.network',
network: 'testnet2',
deviceId: 'my-stable-device-id',
});
// Step 3: Initialize wallet
const { sphere, created, generatedMnemonic } = await Sphere.init({
...providers,
autoGenerate: true,
});
if (created && generatedMnemonic) {
setMnemonic(generatedMnemonic);
}
setSphere(sphere);
setLoading(false);
};
init();
return () => {
sphere?.destroy();
};
}, []);
return { sphere, loading, mnemonic };
}
function App() {
const { sphere, loading, mnemonic } = useWallet();
if (loading) return <div>Loading wallet...</div>;
if (mnemonic) {
return (
<div>
<h2>Save your recovery phrase!</h2>
<code>{mnemonic}</code>
<button onClick={() => /* clear mnemonic after user confirms */}>
I've saved it
</button>
</div>
);
}
return (
<div>
<p>Address: {sphere?.identity?.directAddress}</p>
<p>Nametag: {sphere?.identity?.nametag || 'Not registered'}</p>
</div>
);
}
Vue 3¶
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { Sphere } from '@unicitylabs/sphere-sdk';
import { createBrowserProviders } from '@unicitylabs/sphere-sdk/impl/browser';
import { createWalletApiProviders } from '@unicitylabs/sphere-sdk/impl/shared/wallet-api';
const sphere = ref<Sphere | null>(null);
const loading = ref(true);
const mnemonic = ref<string | null>(null);
onMounted(async () => {
// Step 1: Create base providers
const base = createBrowserProviders({
network: 'testnet',
oracle: { apiKey: 'sk_ddc3cfcc001e4a28ac3fad7407f99590' }, // public testnet2 key
});
// Step 2: Wrap with wallet-api for v2 delivery
const providers = createWalletApiProviders(base, {
baseUrl: 'https://wallet-api.unicity.network',
network: 'testnet2',
deviceId: 'my-stable-device-id',
});
// Step 3: Initialize wallet
const result = await Sphere.init({
...providers,
autoGenerate: true,
});
if (result.created && result.generatedMnemonic) {
mnemonic.value = result.generatedMnemonic;
}
sphere.value = result.sphere;
loading.value = false;
});
onUnmounted(() => {
sphere.value?.destroy();
});
</script>
<template>
<div v-if="loading">Loading wallet...</div>
<div v-else-if="mnemonic">
<h2>Save your recovery phrase!</h2>
<code>{{ mnemonic }}</code>
</div>
<div v-else>
<p>Address: {{ sphere?.identity?.directAddress }}</p>
</div>
</template>
Next.js (App Router)¶
'use client';
import { useState, useEffect } from 'react';
import { Sphere } from '@unicitylabs/sphere-sdk';
// Dynamic import to avoid SSR issues
async function initWallet() {
const { createBrowserProviders } = await import(
'@unicitylabs/sphere-sdk/impl/browser'
);
const { createWalletApiProviders } = await import(
'@unicitylabs/sphere-sdk/impl/shared/wallet-api'
);
// Step 1: Create base providers
const base = createBrowserProviders({
network: 'testnet',
oracle: { apiKey: 'sk_ddc3cfcc001e4a28ac3fad7407f99590' }, // public testnet2 key
});
// Step 2: Wrap with wallet-api for v2 delivery
const providers = createWalletApiProviders(base, {
baseUrl: 'https://wallet-api.unicity.network',
network: 'testnet2',
deviceId: 'my-stable-device-id',
});
// Step 3: Initialize wallet
return Sphere.init({
...providers,
autoGenerate: true,
});
}
export default function WalletPage() {
const [sphere, setSphere] = useState<Sphere | null>(null);
useEffect(() => {
initWallet().then(({ sphere }) => setSphere(sphere));
return () => { sphere?.destroy(); };
}, []);
if (!sphere) return <div>Loading...</div>;
return <div>Address: {sphere.identity?.directAddress}</div>;
}
Storage & Delivery¶
The v2 SDK combines three storage mechanisms:
| Data | Storage | Persistence | Role |
|---|---|---|---|
| Wallet (mnemonic, nametag) | localStorage |
Per-domain, survives refresh | Local secret key |
| Tokens (confirmed transfers) | IndexedDB |
Per-domain, larger capacity | Token ledger |
| Mailbox (recipient delivery) | Wallet API | Server-backed, cross-device | v2 certified transfer inbox |
SSR Note: If localStorage is unavailable (SSR), an in-memory fallback is used.
Wallet-API Delivery: The WalletApiMailboxProvider polls the wallet-api server for incoming certified transfers (on behalf of recipient nametags). This is the reference implementation of v2 token delivery — Nostr and IPFS are messaging/backup only, not the payment rail.
Configuration Options¶
import { createBrowserProviders } from '@unicitylabs/sphere-sdk/impl/browser';
import { createWalletApiProviders } from '@unicitylabs/sphere-sdk/impl/shared/wallet-api';
// Step 1: Base providers (required for network, oracle, transport)
const base = createBrowserProviders({
// Network: 'mainnet' | 'testnet' | 'testnet2' | 'dev'
// ('testnet' IS testnet2 — the v2 gateway; mainnet/dev are still v1-era and
// cannot serve the v2 engine yet)
network: 'testnet',
// Transport options
transport: {
relays: ['wss://custom-relay.com'], // Replace defaults
additionalRelays: ['wss://extra-relay.com'], // Add to defaults
timeout: 5000,
autoReconnect: true,
debug: false,
},
// Oracle (v2 gateway) options — REQUIRED AT RUNTIME for send/mint
oracle: {
url: 'https://gateway.testnet2.unicity.network', // Replace default gateway URL
apiKey: 'sk_ddc3cfcc001e4a28ac3fad7407f99590', // Gateway API key (public testnet2 key)
},
// For a custom trust base URL, build the oracle directly with
// createUnicityAggregatorProvider({ url, apiKey, trustBaseUrl, network })
// from '@unicitylabs/sphere-sdk/impl/browser'.
// Price provider (optional — enables fiat value display)
price: {
platform: 'coingecko', // Currently supported: 'coingecko'
apiKey: 'CG-xxx', // Optional (free tier works without key)
cacheTtlMs: 60000, // Cache TTL in ms (default: 60s)
},
// Token sync (optional IPFS backup)
tokenSync: {
ipfs: {
enabled: true,
additionalGateways: ['https://my-ipfs-gateway.com'],
},
},
});
// Step 2: Wrap with wallet-api for v2 mailbox delivery (REQUIRED for v2)
const providers = createWalletApiProviders(base, {
baseUrl: 'https://wallet-api.unicity.network', // Canonical testnet2 host
network: 'testnet2',
deviceId: 'my-stable-device-id', // Must be stable across sessions
});
// Step 3: Initialize wallet with the composed providers
const { sphere } = await Sphere.init({
...providers,
autoGenerate: true,
});
Common Operations¶
Display Wallet Info¶
const identity = sphere.identity;
console.log('L3 Address:', identity?.directAddress); // DIRECT://...
console.log('Public Key:', identity?.chainPubkey); // 02abc...
console.log('Nametag:', identity?.nametag); // @username
Check Balance & Assets¶
// Get assets with price data (price fields are null without PriceProvider)
const assets = await sphere.payments.getAssets();
for (const asset of assets) {
console.log(`${asset.symbol}: ${asset.totalAmount} (${asset.tokenCount} tokens)`);
if (asset.fiatValueUsd != null) {
console.log(` Value: $${asset.fiatValueUsd.toFixed(2)}`);
}
}
// Per-coin balances (synchronous, no price data)
const balances = sphere.payments.getBalance();
// Total portfolio value in USD (null if PriceProvider not configured)
const totalUsd = await sphere.payments.getFiatBalance();
document.getElementById('balance').textContent =
totalUsd != null ? `$${totalUsd.toFixed(2)}` : 'N/A';
Top Up (Testnet Self-Mint)¶
There is no faucet — on testnet you top up by self-minting tokens via the v2 token engine:
import { TokenRegistry } from '@unicitylabs/sphere-sdk';
// mintFungibleToken takes the hex coin id, not the symbol
const coinId = TokenRegistry.getInstance().getCoinIdBySymbol('UCT');
const res = await sphere.payments.mintFungibleToken(coinId!, 100_000_000n);
if (res.success) {
console.log('Minted token:', res.tokenId);
} else {
console.error('Mint failed:', res.error);
}
Look Up Asset Metadata¶
The TokenRegistry provides metadata (symbol, name, decimals, icons) for all registered assets on the network:
import { TokenRegistry } from '@unicitylabs/sphere-sdk';
const registry = TokenRegistry.getInstance();
// List all registered assets
const allAssets = registry.getAllDefinitions();
const coins = registry.getFungibleTokens();
const nfts = registry.getNonFungibleTokens();
// Look up a specific asset
const uct = registry.getDefinitionBySymbol('UCT');
console.log(uct?.name, uct?.decimals); // 'Unicity Token', 8
// Reverse lookup: symbol → coin ID
const coinId = registry.getCoinIdBySymbol('UCT');
Note: The registry is configured automatically by
createBrowserProviders()andSphere.init(). Data is fetched from the network and cached inlocalStorage.
Send Tokens¶
import { isSphereError } from '@unicitylabs/sphere-sdk';
async function sendTokens(recipient: string, amount: string) {
try {
const result = await sphere.payments.send({
coinId: 'UCT',
amount: '1000000',
recipient: '@alice',
});
// Transfer is certified on-chain, now awaiting recipient mailbox delivery
console.log('Status:', result.status); // 'pending', 'submitted', 'confirmed', 'delivered', 'completed', etc.
// deliveryPending=true means certified but recipient delivery deferred (NORMAL, not a failure)
if (result.deliveryPending) {
console.log('Transfer certified; recipient mailbox delivery in progress...');
}
showToast('Sent!');
} catch (error) {
if (isSphereError(error)) {
switch (error.code) {
case 'INSUFFICIENT_BALANCE': showToast('Not enough funds'); break;
case 'INVALID_RECIPIENT': showToast('Recipient not found'); break;
case 'TRANSPORT_ERROR': showToast('Network issue, try again'); break;
default: showToast(error.message);
}
} else {
showToast('Something went wrong');
}
}
}
Fetch Pending Transfers¶
For explicit receive (useful in batch operations or when you need to poll):
const { transfers } = await sphere.payments.receive();
console.log(`Received ${transfers.length} new transfers`);
The legacy
ReceiveOptions(finalize,timeout,pollInterval) are deprecated no-ops: v2 transfers arrive as finished tokens and are stored confirmed immediately — there is no finalization phase.
Register Nametag¶
Note:
registerNametag()registers the name by publishing a Nostr identity binding (name ↔ chain pubkey, first-seen-wins). A self-issued v2 Unicity ID token is additionally minted and stored best-effort — registration never fails because of it. Runtime name resolution uses only the Nostr binding.
async function registerNametag(username: string) {
// Publishes the Nostr binding; throws if the name is already taken
await sphere.registerNametag(username);
console.log('Registered:', sphere.identity?.nametag);
}
// Alternative: register during init
const { sphere } = await Sphere.init({
...providers,
autoGenerate: true,
nametag: 'alice',
});
Listen for Events¶
// Incoming transfers — handlers receive the event payload directly
sphere.on('transfer:incoming', (transfer) => {
const from = transfer.senderNametag ?? transfer.senderPubkey;
showNotification(`Received ${transfer.tokens.length} token(s) from ${from}`);
});
// Direct messages
sphere.communications.onDirectMessage((msg) => {
showNotification(`Message from ${msg.senderNametag ?? msg.senderPubkey}: ${msg.content}`);
});
// Connection status
sphere.on('connection:changed', (status) => {
updateConnectionStatus(status.connected);
});
Send Direct Message¶
Invoicing¶
⚠️ Experimental — not production-ready. Invoicing/accounting is implemented and unit-tested, but it has no live/e2e verification, is used by no shipped app, and is not enabled in the Sphere wallet (nor supported over Connect). Treat the API as unstable.
Enable the accounting module when initializing the wallet:
const { sphere } = await Sphere.init({
...providers,
autoGenerate: true,
accounting: true, // Enable with defaults
});
Create an invoice (mints an invoice data token via the v2 token engine):
const result = await sphere.accounting!.createInvoice({
targets: [
{
address: sphere.identity!.directAddress!, // Pay to self (your DIRECT:// address)
assets: [
{ coin: ['UCT', '5000000'] }, // Request 5 UCT (in smallest units)
],
},
],
memo: 'Order #1234',
dueDate: Date.now() + 7 * 24 * 60 * 60 * 1000, // Due in 7 days
});
console.log('Invoice ID:', result.invoiceId);
Check invoice status:
const status = await sphere.accounting!.getInvoiceStatus(result.invoiceId!);
// status.state: 'OPEN' | 'PARTIAL' | 'COVERED' | 'CLOSED' | 'CANCELLED' | 'EXPIRED'
console.log('State:', status.state);
Pay an invoice (as the payer, after importing the invoice token):
const transferResult = await sphere.accounting!.payInvoice(invoiceId, {
targetIndex: 0, // Which target to pay (index into invoice.targets)
// amount: '5000000', // Omit to pay the full remaining amount
});
Close an invoice (only target parties may close):
List invoices:
const invoices = await sphere.accounting!.getInvoices({ createdByMe: true });
for (const ref of invoices) {
console.log(ref.invoiceId, ref.terms.memo, ref.closed ? 'CLOSED' : 'OPEN');
}
Listen for invoice events:
sphere.on('invoice:payment', (data) => {
console.log(`Payment received for invoice ${data.invoiceId}`);
});
sphere.on('invoice:covered', (data) => {
console.log(`Invoice ${data.invoiceId} fully covered!`);
});
Note:
createInvoice()requires the Oracle provider (v2 gateway config), which is included automatically bycreateBrowserProviders().
Token Swaps¶
The swap module enables trustless two-party token exchanges via an escrow service. Enable it when initializing:
const { sphere } = await Sphere.init({
...providers,
autoGenerate: true,
accounting: true, // Required (swap uses invoices internally)
swap: true, // Enable swap module
});
Propose a swap:
const result = await sphere.swap!.proposeSwap({
partyA: sphere.identity!.directAddress!,
partyB: '@bob',
partyACurrency: 'UCT',
partyAAmount: '1000000',
partyBCurrency: 'USDU',
partyBAmount: '500000',
timeout: 3600,
escrowAddress: '@escrow-testnet',
});
console.log('Swap ID:', result.swapId);
// result.swap.progress === 'proposed'
Listen for incoming proposals and accept:
sphere.on('swap:proposal_received', async (data) => {
console.log('Swap proposal from:', data.senderNametag ?? data.senderPubkey);
console.log('Deal:', data.deal);
// Accept and deposit
await sphere.swap!.acceptSwap(data.swapId);
await sphere.swap!.deposit(data.swapId);
});
Deposit into a swap (proposer side, after counterparty accepts):
sphere.on('swap:announced', async (data) => {
// Escrow is ready, deposit now
const transferResult = await sphere.swap!.deposit(data.swapId);
console.log('Deposit sent:', transferResult.id);
});
Monitor progress:
sphere.on('swap:completed', (data) => {
console.log('Swap completed!', data.swapId, 'Payout verified:', data.payoutVerified);
});
sphere.on('swap:cancelled', (data) => {
console.log('Swap cancelled:', data.swapId, 'Reason:', data.reason);
});
sphere.on('swap:failed', (data) => {
console.log('Swap failed:', data.swapId, data.error);
});
List and query swaps:
// All active swaps
const swaps = sphere.swap!.getSwaps({ excludeTerminal: true });
// Detailed status (optionally query the escrow for its view)
const status = await sphere.swap!.getSwapStatus(swapId, { queryEscrow: true });
console.log('Progress:', status.progress, 'Escrow state:', status.escrowState);
Import Existing Wallet¶
// From mnemonic (recovery, plaintext storage — default)
const { sphere } = await Sphere.init({
...providers,
mnemonic: 'word1 word2 word3 ... word12',
});
// From mnemonic with password encryption
const { sphere } = await Sphere.init({
...providers,
mnemonic: 'word1 word2 word3 ... word12',
password: 'my-secret-password',
});
// Load existing wallet with password
const { sphere } = await Sphere.init({
...providers,
password: 'my-secret-password',
});
// Nametag will be auto-recovered from Nostr if it was registered
sphere.on('nametag:recovered', (data) => {
console.log('Recovered nametag:', data.nametag);
});
Complete React Example¶
import { useState, useEffect, useCallback } from 'react';
import { Sphere } from '@unicitylabs/sphere-sdk';
import { createBrowserProviders } from '@unicitylabs/sphere-sdk/impl/browser';
import { createWalletApiProviders } from '@unicitylabs/sphere-sdk/impl/shared/wallet-api';
function WalletApp() {
const [sphere, setSphere] = useState<Sphere | null>(null);
const [balance, setBalance] = useState<string>('0');
const [recipient, setRecipient] = useState('');
const [amount, setAmount] = useState('');
const [status, setStatus] = useState('Loading...');
// Initialize wallet
useEffect(() => {
const init = async () => {
// Step 1: Create base providers
const base = createBrowserProviders({
network: 'testnet',
oracle: { apiKey: 'sk_ddc3cfcc001e4a28ac3fad7407f99590' },
});
// Step 2: Wrap with wallet-api for v2 delivery
const providers = createWalletApiProviders(base, {
baseUrl: 'https://wallet-api.unicity.network',
network: 'testnet2',
deviceId: 'my-stable-device-id',
});
// Step 3: Initialize wallet
const { sphere, created, generatedMnemonic } = await Sphere.init({
...providers,
autoGenerate: true,
});
if (created && generatedMnemonic) {
// In production, show modal to save mnemonic
console.log('NEW WALLET - Save mnemonic:', generatedMnemonic);
}
setSphere(sphere);
setStatus('Connected');
// Load balance (total USD value, null if no PriceProvider)
const bal = await sphere.payments.getFiatBalance();
setBalance(bal != null ? `$${bal.toFixed(2)}` : 'N/A');
// Listen for incoming
sphere.on('transfer:incoming', async () => {
const newBal = await sphere.payments.getFiatBalance();
setBalance(newBal != null ? `$${newBal.toFixed(2)}` : 'N/A');
});
};
init().catch((err) => setStatus('Error: ' + err.message));
return () => { sphere?.destroy(); };
}, []);
// Send tokens
const handleSend = useCallback(async () => {
if (!sphere || !recipient || !amount) return;
setStatus('Sending...');
try {
const result = await sphere.payments.send({
recipient,
amount,
coinId: 'UCT',
});
setStatus(result.deliveryPending ? 'Sent (delivery in progress)' : 'Sent!');
setRecipient('');
setAmount('');
// Refresh balance
const bal = await sphere.payments.getFiatBalance();
setBalance(bal != null ? `$${bal.toFixed(2)}` : 'N/A');
} catch (err: any) {
setStatus('Error: ' + err.message);
}
}, [sphere, recipient, amount]);
return (
<div style={{ padding: 20 }}>
<h1>Sphere Wallet</h1>
<p>Status: {status}</p>
{sphere && (
<>
<div style={{ marginBottom: 20 }}>
<strong>Address:</strong> {sphere.identity?.directAddress}
<br />
<strong>Nametag:</strong> {sphere.identity?.nametag || 'Not registered'}
<br />
<strong>Balance:</strong> {balance}
</div>
<div>
<h3>Send Tokens</h3>
<input
placeholder="@recipient or address"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
/>
<input
placeholder="Amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
<button onClick={handleSend}>Send</button>
</div>
</>
)}
</div>
);
}
export default WalletApp;
Bundler Configuration¶
Vite¶
Works out of the box. No special config needed.
Webpack 5¶
Add node polyfills:
// webpack.config.js
const { ProvidePlugin } = require('webpack');
module.exports = {
resolve: {
fallback: {
buffer: require.resolve('buffer/'),
},
},
plugins: [
new ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}),
],
};
Create React App¶
Use react-app-rewired or eject:
// config-overrides.js
const webpack = require('webpack');
module.exports = function override(config) {
config.resolve.fallback = {
buffer: require.resolve('buffer/'),
};
config.plugins.push(
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
})
);
return config;
};
Security Best Practices¶
Never Store Mnemonic in Code¶
// BAD - Don't do this!
const mnemonic = 'word1 word2 word3...';
// GOOD - Let user input it
const mnemonic = document.getElementById('mnemonicInput').value;
Prompt User to Save Mnemonic¶
if (created && generatedMnemonic) {
// Show modal, not just console.log
showMnemonicModal(generatedMnemonic);
}
Clear Sensitive Data¶
// When user logs out
await sphere.destroy();
// Optionally clear all SDK-owned wallet data (keys + tokens)
await Sphere.clear({
storage: providers.storage,
tokenStorage: providers.tokenStorage,
});
Use HTTPS¶
Always serve your app over HTTPS in production.
Troubleshooting¶
"localStorage is not defined" (SSR)¶
Use dynamic import:
const { createBrowserProviders } = await import(
'@unicitylabs/sphere-sdk/impl/browser'
);
const { createWalletApiProviders } = await import(
'@unicitylabs/sphere-sdk/impl/shared/wallet-api'
);
"Buffer is not defined"¶
Install and configure polyfill:
Add to your entry point:
CORS Errors¶
If aggregator/relay requests fail with CORS: - Check if URLs are correct for your network - Use a proxy in development - Contact relay/aggregator operators
IndexedDB Errors¶
// Check if IndexedDB is available
if (!window.indexedDB) {
console.warn('IndexedDB not supported, tokens won\'t persist');
}
WebSocket Connection Failed¶
const base = createBrowserProviders({
network: 'testnet',
oracle: { apiKey: 'sk_ddc3cfcc001e4a28ac3fad7407f99590' },
transport: {
debug: true, // Enable logging
timeout: 10000, // Increase timeout
autoReconnect: true, // Auto-retry
},
});
Debug Logging¶
Enable SDK debug logging in the browser console:
import { logger } from '@unicitylabs/sphere-sdk';
// Enable all debug logging
logger.configure({ debug: true });
// Enable only transport logs
logger.setTagDebug('Nostr', true);
Browser Support¶
| Browser | Version | Notes |
|---|---|---|
| Chrome | 89+ | Full support |
| Firefox | 89+ | Full support |
| Safari | 15+ | Full support |
| Edge | 89+ | Full support |
| Mobile Chrome | 89+ | Full support |
| Mobile Safari | 15+ | Full support |
Required APIs: localStorage, IndexedDB, WebSocket, fetch, crypto.subtle
Next Steps¶
- API Reference - Full API documentation
- Integration Guide - Advanced integration patterns
- Node.js Quick Start - For server-side usage