@utexo/rgb-lib-wasm
v1.0.0-beta.3
Published
RGB wallet library WASM bindings for browsers
Keywords
Readme
rgb-lib-wasm
A WebAssembly port of rgb-lib for managing RGB protocol wallets in the browser.
Beta Software — This library is under active development and has not been audited. APIs may change without notice between versions. Use at your own risk.
What it does
rgb-lib-wasm lets you issue, send, and receive RGB assets from a web browser. It replaces the native Rust dependencies (filesystem, SQLite, TCP sockets) with browser-compatible alternatives:
- In-memory database with IndexedDB persistence
- Esplora indexer over HTTP
- Encrypted backup to a local file or a remote VSS server
Supported asset types:
- NIA (Non-Inflatable Asset)
- IFA (Inflatable Fungible Asset).
Prerequisites
- Rust 1.85.0+ with the
wasm32-unknown-unknowntarget - wasm-pack 0.14+
rustup target add wasm32-unknown-unknown
cargo install [email protected]Building
cd bindings/wasm
./build.shThis produces the pkg/ directory containing an npm-ready package (rgb_lib_wasm.js, .d.ts types, and the .wasm binary).
Trying the examples
cd bindings/wasm
python3 -m http.server 8000
# Open http://localhost:8000/examples/index.htmlThe examples/ folder contains interactive pages that cover the full API, split by topic:
| Page | Description |
|------|-------------|
| index.html | Quick start guide + automated "Run All" flow |
| wallet.html | Key management, wallet creation, go online, fund, create UTXOs, wallet state queries, fee estimation |
| bitcoin.html | Send BTC, drain wallet, PSBT sign/finalize utilities |
| rgb.html | Issue NIA/IFA, blind/witness receive, send RGB, inflate IFA, refresh/fail/delete transfers |
| backup.html | Local encrypted backup, VSS cloud backup, check proxy URL, error handling tests |
All pages share examples/lib/shared.js (WASM init, helpers, regtest utilities). Each page is self-contained with its own wallet setup section. Online operations (funding, sending, etc.) require the regtest Docker services — see DEV-ENVIRONMENT.md for setup.
Usage
import init, {
generateKeys, restoreKeys, checkProxyUrl,
validateConsignmentOffchain,
WasmWallet, WasmInvoice,
} from './pkg/rgb_lib_wasm.js';
await init();Key generation
const keys = generateKeys('Regtest');
// keys: { mnemonic, accountXpubVanilla, accountXpubColored, masterFingerprint }
const restored = restoreKeys('Regtest', keys.mnemonic);Creating a wallet
const walletData = {
dataDir: ':memory:',
bitcoinNetwork: 'Regtest',
databaseType: 'Sqlite',
maxAllocationsPerUtxo: 5,
accountXpubVanilla: keys.accountXpubVanilla,
accountXpubColored: keys.accountXpubColored,
mnemonic: keys.mnemonic,
masterFingerprint: keys.masterFingerprint,
vanillaKeychain: null,
supportedSchemas: ['Nia', 'Ifa'],
};
// create() restores state from IndexedDB if available
const wallet = await WasmWallet.create(JSON.stringify(walletData));
// Await durable IndexedDB commit before persisting dependent application state.
await wallet.flush();Persistence ordering
WasmWallet.create() and Rust Wallet::restore() finish IndexedDB restoration
before returning. They return an error instead of silently starting from fresh
state when a stored snapshot cannot be read or decoded.
Rust Wallet::flush() and JavaScript wallet.flush() resolve only after the
IndexedDB read-write transaction commits. Persist dependent application state,
acknowledge accepted RGB funding, or broadcast a transaction that depends on an
RGB mutation only after the flush succeeds. A failed flush leaves the current
in-memory wallet state intact and can be retried. Snapshots carry a monotonic
sequence, and IndexedDB rejects stale writes so an earlier automatic save cannot
overwrite a later explicit flush.
Lightning-specific consume_fascia, color_psbt_and_consume, and
accept_transfer operations await this durable commit before returning
success.
Going online
const online = await wallet.goOnline(true, 'http://localhost:8094/regtest/api');
await wallet.sync(online);Creating UTXOs
RGB allocations live on UTXOs. Before issuing or receiving assets you need colored UTXOs:
const unsignedPsbt = await wallet.createUtxosBegin(online, true, 5, 1000, 1n, false);
const signedPsbt = wallet.signPsbt(unsignedPsbt);
const count = await wallet.createUtxosEnd(online, signedPsbt, false);Issuing assets
// NIA (fixed supply)
const nia = wallet.issueAssetNia('TICK', 'My Token', 0, [1000]);
// IFA (inflatable supply)
const ifa = wallet.issueAssetIfa('IFAT', 'Inflatable', 0, [1000], [500], undefined);Sending RGB assets
All send operations follow a three-step PSBT workflow: begin (build unsigned PSBT) → sign → end (broadcast).
const recipientMap = {
[assetId]: [{
recipientId: invoiceString,
witnessData: { amountSat: 1000, blinding: null },
amount: 100,
transportEndpoints: ['rpc://localhost:3000/json-rpc'],
}]
};
const psbt = await wallet.sendBegin(online, recipientMap, false, 1n, 1);
const signed = wallet.signPsbt(psbt);
const result = await wallet.sendEnd(online, signed, false);
// result: { txid, batchTransferIdx }Sending BTC
const psbt = await wallet.sendBtcBegin(online, address, 50000n, 1n, false);
const signed = wallet.signPsbt(psbt);
const txid = await wallet.sendBtcEnd(online, signed, false);Receiving assets
// Blind receive (UTXO-based)
const blind = wallet.blindReceive(null, 'Any', null, ['rpc://localhost:3000/json-rpc'], 1);
// blind: { invoice, recipientId, expirationTimestamp, ... }
// Witness receive (address-based)
const witness = wallet.witnessReceive(null, { Fungible: 100 }, null, ['rpc://localhost:3000/json-rpc'], 1);Parsing invoices
const invoice = new WasmInvoice(invoiceString);
const data = invoice.invoiceData();
// data: { recipientId, assetSchema, assetId, assignment, network, expirationTimestamp, transportEndpoints }
const str = invoice.invoiceString(); // original stringQuerying wallet state
const btcBalance = wallet.getBtcBalance();
const assetBalance = wallet.getAssetBalance(assetId);
const metadata = wallet.getAssetMetadata(assetId);
// metadata: { assetSchema, name, ticker, precision, initialSupply, maxSupply, knownCirculatingSupply, timestamp, ... }
const assets = wallet.listAssets([]); // all schemas
const transfers = wallet.listTransfers(null); // all assets
const unspents = wallet.listUnspents(false);
const transactions = wallet.listTransactions();
const address = wallet.getAddress();Fee estimation
const feeRate = await wallet.getFeeEstimation(online, 6); // target 6 blocksDraining the wallet
const psbt = await wallet.drainToBegin(online, destinationAddress, true, 1n);
const signed = wallet.signPsbt(psbt);
const txid = await wallet.drainToEnd(online, signed);Inflating an IFA asset
const psbt = await wallet.inflateBegin(online, assetId, [200], 1n, 1);
const signed = wallet.signPsbt(psbt);
const result = await wallet.inflateEnd(online, signed);Local encrypted backup
Wallet state is encrypted with Scrypt + XChaCha20Poly1305 and returned as bytes:
// Backup
const bytes = wallet.backup('my-password');
// bytes is a Uint8Array — save it as a file download
// Check if backup is needed
const needed = wallet.backupInfo(); // true if wallet changed since last backup
// Restore (wallet must be created first with the same mnemonic)
wallet.restoreBackup(backupBytes, 'my-password');VSS cloud backup
VSS (Versioned Storage Service) provides cloud backup with optimistic locking:
// Configure (signing key is a 32-byte hex-encoded secp256k1 secret key)
wallet.configureVssBackup('http://vss-server:8082/vss', 'my-store-id', signingKeyHex);
// Upload
const version = await wallet.vssBackup();
// Check status
const info = await wallet.vssBackupInfo();
// info: { backupExists, serverVersion, backupRequired }
// Restore
await wallet.vssRestoreBackup();
// Disable
wallet.disableVssBackup();Transfer management
const refreshResult = await wallet.refresh(online, null, [], false);
const failed = await wallet.failTransfers(online, null, false, false);
const deleted = wallet.deleteTransfers(null, false);Utilities
// Validate an RGB proxy server
await checkProxyUrl('http://localhost:3000/json-rpc');
// Validate an RGB consignment offchain (before TX is broadcast)
// consignmentBytes is a Uint8Array of the strict-encoded consignment
const result = validateConsignmentOffchain(consignmentBytes, txid, 'Regtest');
// result: { valid: true, warnings: [...] }
// result: { valid: false, error: "invalid", details: "..." }
// PSBT signing and finalization (standalone)
const signed = wallet.signPsbt(unsignedPsbtBase64);
const finalized = wallet.finalizePsbt(signedPsbtBase64);API reference
Standalone functions
| Function | Description |
|----------|-------------|
| generateKeys(network) | Generate a new BIP39 mnemonic and derive xpubs |
| restoreKeys(network, mnemonic) | Restore xpubs from a mnemonic |
| checkProxyUrl(url) | Validate an RGB proxy server URL |
| validateConsignmentOffchain(bytes, txid, network) | Validate an RGB consignment using bundled witness data |
WasmInvoice
| Method | Description |
|--------|-------------|
| new(invoiceString) | Parse and validate an RGB invoice string |
| invoiceData() | Return parsed InvoiceData as a JS object |
| invoiceString() | Return the original invoice string |
WasmWallet methods
| Method | Async | Description |
|--------|-------|-------------|
| new(walletDataJson) | no | Create wallet (no IndexedDB restore) |
| create(walletDataJson) | yes | Create wallet with IndexedDB restore; fails if restore fails |
| flush() | yes | Durably commit current wallet state to IndexedDB |
| getWalletData() | no | Return WalletData as JS object |
| getAddress() | no | Get a new Bitcoin address |
| getBtcBalance() | no | Get BTC balance |
| getAssetBalance(assetId) | no | Get balance for an RGB asset |
| getAssetMetadata(assetId) | no | Get metadata for an RGB asset (name, ticker, precision, supply, etc.) |
| listAssets(schemas) | no | List known RGB assets |
| listTransfers(assetId?) | no | List RGB transfers |
| listUnspents(settledOnly) | no | List unspent outputs with RGB allocations |
| listTransactions() | no | List Bitcoin transactions |
| goOnline(skipCheck, indexerUrl) | yes | Connect to an Esplora indexer |
| sync(online) | yes | Sync wallet with indexer |
| createUtxosBegin(...) | yes | Prepare PSBT to create colored UTXOs |
| createUtxosEnd(online, psbt, skipSync) | yes | Broadcast UTXO creation PSBT |
| issueAssetNia(ticker, name, precision, amounts) | no | Issue a Non-Inflatable Asset |
| issueAssetIfa(...) | no | Issue an Inflatable Fungible Asset |
| blindReceive(...) | no | Create a blind receive invoice |
| witnessReceive(...) | no | Create a witness receive invoice |
| sendBegin(...) | yes | Prepare RGB send PSBT |
| sendEnd(online, psbt, skipSync) | yes | Broadcast RGB send PSBT |
| sendBtcBegin(...) | yes | Prepare BTC send PSBT |
| sendBtcEnd(online, psbt, skipSync) | yes | Broadcast BTC send PSBT |
| getFeeEstimation(online, blocks) | yes | Estimate fee rate for target blocks |
| drainToBegin(online, addr, destroyAssets, feeRate) | yes | Prepare drain PSBT |
| drainToEnd(online, psbt) | yes | Broadcast drain PSBT |
| inflateBegin(...) | yes | Prepare IFA inflation PSBT |
| inflateEnd(online, psbt) | yes | Broadcast IFA inflation PSBT |
| listUnspentsVanilla(online, minConf, skipSync) | yes | List non-colored UTXOs |
| refresh(online, assetId?, filter, skipSync) | yes | Refresh pending transfers |
| failTransfers(online, batchIdx?, noAssetOnly, skipSync) | yes | Fail pending transfers |
| deleteTransfers(batchIdx?, noAssetOnly) | no | Delete failed transfers |
| signPsbt(psbt) | no | Sign a PSBT |
| finalizePsbt(psbt) | no | Finalize a signed PSBT |
| backup(password) | no | Create encrypted backup (returns bytes) |
| restoreBackup(bytes, password) | no | Restore from encrypted backup |
| backupInfo() | no | Check if backup is needed |
| configureVssBackup(url, storeId, keyHex) | no | Configure VSS cloud backup |
| disableVssBackup() | no | Disable VSS cloud backup |
| vssBackup() | yes | Upload backup to VSS server |
| vssRestoreBackup() | yes | Restore from VSS server |
| vssBackupInfo() | yes | Query VSS backup status |
Cargo features
| Feature | Default | Description |
|---------|---------|-------------|
| esplora | yes | Online operations via Esplora HTTP indexer |
| camel_case | yes | camelCase JSON serialization for all types |
Running tests
# Native unit tests (94 tests)
cargo test --lib
# WASM unit tests (headless Firefox)
wasm-pack test --headless --firefox --release -- --test keys_wasm
wasm-pack test --headless --firefox --release -- --test wallet_wasm
# Integration tests (requires regtest Docker services)
cd tests && bash regtest.sh start
cd .. && wasm-pack test --headless --firefox --release -- --test integration_wasm
cd tests && bash regtest.sh stopSee DEV-ENVIRONMENT.md for the full regtest infrastructure setup, CI configuration, Docker Compose details, and troubleshooting.
License
MIT
