npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bubolabs/wallet-rn-sdk

v0.2.14

Published

React Native smoke SDK for bubo wallet UniFFI bridge

Downloads

2,620

Readme

@bubolabs/wallet-rn-sdk

React Native SDK for the Bubo wallet UniFFI bridge.

This package uses standard RN native-module layout and supports autolinking:

  • android/ React Native Android library module
  • ios/ Pod sources + vendored BuboWalletFFI.xcframework
  • react-native.config.js for Android package registration

Install

npm install @bubolabs/wallet-rn-sdk

iOS:

cd ios && pod install

API Overview

Public API is chain-first. Generic chainId-routed aggregators are not exported from package entry (deriveAddress*, deriveWallet*, buildAndSign*, signMessage*, signBytes*, signTypedData*).

Preferred (chain namespace API):

import { btc, eth, solana } from "@bubolabs/wallet-rn-sdk";

await btc.signMessage({
  mnemonic,
  account: 0,
  index: 0,
  message: "hello",
  mode: "bip322-simple",
});

await eth.signTypedData({
  mnemonic,
  account: 0,
  index: 0,
  typedData: JSON.stringify(typedData),
});

await solana.signTx({
  unsignedTx,
  unsignedTxEncoding: "base64",
  privateKey,
  keyEncoding: "base58",
});

Legacy flat function exports are still fully supported:

import {
  btc,
  core,
  eth,
  solana,
  btcBuildAndSign,
  btcBuildAndSignFromPrivateKey,
  btcBuildPsbt,
  btcBuildTx,
  btcDeriveAddress,
  btcDeriveAddressFromPrivateKey,
  btcDeriveWallet,
  btcDeriveWalletFromPrivateKey,
  btcFrostDkgFinalize,
  btcFrostDkgRound1,
  btcFrostDkgRound2,
  btcFrostAggregateSignature,
  btcFrostBuildSigningPackage,
  btcFrostRound1Commit,
  btcFrostRound2SignShare,
  btcFrostTaprootComputeTweak,
  btcFrostNonceSessionScope,
  btcFrostResetNonceGuard,
  btcFrostTrustedDealerKeygen,
  createBtcFrostDkgSession,
  createBtcFrostSigningSession,
  runBtcFrostDkgSession,
  runBtcFrostSigningSession,
  btcInspectPsbt,
  btcPartialSignPsbt,
  btcSignBytes,
  btcSignBytesFromPrivateKey,
  btcSignMessage,
  btcSignMessageFromPrivateKey,
  btcSignPsbt,
  btcSignPsbtWithSigners,
  btcSignTx,
  btcVerifyPsbtFinalized,
  ethBuildAndSign,
  ethBuildAndSignFromPrivateKey,
  ethBuildTx,
  ethDeriveAddress,
  ethDeriveAddressFromPrivateKey,
  ethDeriveWallet,
  ethDeriveWalletFromPrivateKey,
  ethBuildEip1271IsValidSignatureCall,
  ethBuildEip1271RpcCall,
  ethDecodeSignedTx,
  generateMnemonic,
  validateMnemonic,
  init,
  listChainExtensions,
  listSupportedChains,
  ethEcrecover,
  ethRecoverTypedDataSigner,
  ethSignMessage,
  ethSignMessageFromPrivateKey,
  ethSignTx,
  ethSignTypedData,
  ethSignTypedDataFromPrivateKey,
  ethVerifyEip1271IsValidSignatureResult,
  generateMnemonic,
  init,
  listChainExtensions,
  listSupportedChains,
  solanaBuildAndSign,
  solanaBuildAndSignFromPrivateKey,
  solanaBuildTx,
  solanaDeriveAddress,
  solanaDeriveAddressFromPrivateKey,
  solanaDeriveWallet,
  solanaDeriveWalletFromPrivateKey,
  solanaInspectSignedTx,
  solanaSignBytes,
  solanaSignBytesFromPrivateKey,
  solanaSignMessage,
  solanaSignMessageFromPrivateKey,
  solanaSignTx,
  validateMnemonic,
} from "@bubolabs/wallet-rn-sdk";
  • Shared discovery + lifecycle:
    • namespaced: core.init, core.listSupportedChains, core.listChainExtensions
    • flat: init, listSupportedChains, listChainExtensions
  • BTC core API:
    • wallet management: btcDeriveAddress, btcDeriveWallet, btcDeriveAddressFromPrivateKey, btcDeriveWalletFromPrivateKey
    • transaction signing: btcBuildAndSign, btcBuildAndSignFromPrivateKey
    • transaction lifecycle: btcBuildTx, btcSignTx (PSBT-backed)
    • message/bytes signing: btcSignMessage, btcSignMessageFromPrivateKey, btcSignBytes, btcSignBytesFromPrivateKey
    • PSBT flow: btcBuildPsbt, btcSignPsbt, btcSignPsbtWithSigners, btcPartialSignPsbt, btcVerifyPsbtFinalized, btcInspectPsbt
    • FROST key setup: btcFrostTrustedDealerKeygen, btcFrostDkgRound1, btcFrostDkgRound2, btcFrostDkgFinalize
    • FROST share lifecycle helpers: btcFrostRefreshRound1, btcFrostRefreshRound2, btcFrostRefreshFinalize, btcFrostReshareRound1, btcFrostReshareRound2, btcFrostReshareFinalize
    • FROST public-state guards: btcFrostValidateShare, btcFrostValidatePublicState, btcFrostCompareGroupPublicKey
    • FROST threshold signing: btcFrostRound1Commit, btcFrostBuildSigningPackage, btcFrostRound2SignShare, btcFrostAggregateSignature, btcFrostTaprootComputeTweak, btcFrostVerifySignatureShare
    • FROST nonce lifecycle guard: btcFrostNonceSessionScope, btcFrostResetNonceGuard
    • FROST Taproot PSBT bridge: btcFrostBuildTaprootPsbtSigningInputs, btcFrostFinalizeTaprootPsbt
    • FROST offline orchestration helpers: createBtcFrostDkgSession, runBtcFrostDkgSession, createBtcFrostSigningSession, runBtcFrostSigningSession
  • ETH core API:
    • wallet management: ethDeriveAddress, ethDeriveWallet, ethDeriveAddressFromPrivateKey, ethDeriveWalletFromPrivateKey
    • transaction signing: ethBuildAndSign, ethBuildAndSignFromPrivateKey
    • transaction lifecycle: ethBuildTx, ethSignTx
    • message/typed-data signing: ethSignMessage, ethSignMessageFromPrivateKey, ethSignTypedData, ethSignTypedDataFromPrivateKey
    • recovery/verification: ethEcrecover, ethRecoverTypedDataSigner, ethDecodeSignedTx
    • contract-wallet helpers (EIP-1271): ethBuildEip1271IsValidSignatureCall, ethBuildEip1271RpcCall, ethVerifyEip1271IsValidSignatureResult, ethBuildEip1271RpcCallAndVerify
    • app-layer orchestration reference: docs/eth-eip1271-consumer-template.md
  • SOL core API:
    • wallet management: solanaDeriveAddress, solanaDeriveWallet, solanaDeriveAddressFromPrivateKey, solanaDeriveWalletFromPrivateKey
    • transaction signing: solanaBuildAndSign, solanaBuildAndSignFromPrivateKey
    • transaction lifecycle: solanaBuildTx, solanaSignTx, solanaInspectSignedTx (supports versioned_message payload path and multi-signer progress inspection)
    • message/bytes signing: solanaSignMessage, solanaSignMessageFromPrivateKey, solanaSignBytes, solanaSignBytesFromPrivateKey
    • app-layer multi-signer orchestration reference: docs/solana-multisigner-consumer-template.md
  • Low-level fallback:
    • invokeChainExtension is kept only as escape hatch for chain-specific experiments.

Migration Guide (0.1.14)

Starting from 0.1.14, app code should use chain-specific core APIs by default.

Recommended migration:

| Previous style | Chain-specific style | | --- | --- | | deriveAddress({ chainId: "btc", ... }) | btc.deriveAddress({ ... }) (or btcDeriveAddress) | | deriveWallet({ chainId: "btc", ... }) | btc.deriveWallet({ ... }) (or btcDeriveWallet) | | deriveAddressFromPrivateKey({ chainId: "eth", ... }) | eth.deriveAddressFromPrivateKey({ ... }) (or ethDeriveAddressFromPrivateKey) | | buildAndSign({ chainId: "solana", ... }) | solana.buildAndSign({ ... }) (or solanaBuildAndSign) | | signMessage({ chainId: "eth", ... }) | eth.signMessage({ ... }) (or ethSignMessage) | | signBytes({ chainId: "btc", ... }) | btc.signBytes({ ... }) (or btcSignBytes) | | signTypedData({ chainId: "eth", ... }) | eth.signTypedData({ ... }) (or ethSignTypedData) |

Before:

await signMessage({
  chainId: "btc",
  mnemonic,
  account: 0,
  index: 0,
  message: "hello",
});

After:

await btc.signMessage({
  mnemonic,
  account: 0,
  index: 0,
  message: "hello",
});

invokeChainExtension is still supported, but treat it as low-level fallback, not the primary wallet integration path.

Chain Capability Matrix

The matrix is generated from chain adapter source and validated in CI. Sync command:

node ./scripts/check-capability-matrix.mjs --write

| chainId | deriveAddress | deriveAddressFromPrivateKey | deriveWallet | deriveWalletFromPrivateKey | buildAndSign | buildAndSignFromPrivateKey | signBytes | signBytesFromPrivateKey | signMessage | signMessageFromPrivateKey | signTypedData | signTypedDataFromPrivateKey | buildTx | signTx | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | btc | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No | No | Yes | Yes | | eth | Yes | Yes | Yes | Yes | Yes | Yes | No | No | Yes | Yes | Yes | Yes | Yes | Yes | | solana | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No | No | Yes | Yes |

Signing Semantics By Chain

signMessage, signBytes, and signTypedData are chain-specific. Do not assume one chain's signing semantics on another chain.

| chainId | signMessage | signBytes | signTypedData | | --- | --- | --- | --- | | eth | EIP-191 style message hash signing (hash_message) | Not supported | EIP-712 typed data signing. typedData must be valid EIP-712 JSON | | btc | Supports mode: "ecdsa" \| "bip322-simple" (ecdsa default) | Signs provided raw bytes with BTC secp256k1 flow | Not supported | | solana | Signs UTF-8 text message with ed25519 (messageEncoding fixed to utf8) | Signs provided raw bytes with ed25519 | Not supported |

For btc and solana, signatures depend on exact input bytes. Keep serialization deterministic across client/server. For solana, use solanaSignBytes* for binary payloads (hex/base64); solanaSignMessage* is reserved for human-readable UTF-8 message text.

Chain-Specific Core API

The SDK is now organized by chain core capabilities instead of forcing everything into homogeneous APIs.

  • BTC:
    • account/address: btcDeriveAddress, btcDeriveWallet, btcDeriveAddressFromPrivateKey, btcDeriveWalletFromPrivateKey
    • signing: btcBuildAndSign, btcBuildAndSignFromPrivateKey, btcBuildTx, btcSignTx, btcSignMessage*, btcSignBytes*
    • PSBT: btcBuildPsbt, btcSignPsbt, btcSignPsbtWithSigners, btcPartialSignPsbt, btcVerifyPsbtFinalized, btcInspectPsbt
    • FROST key setup: btcFrostTrustedDealerKeygen, btcFrostDkgRound1, btcFrostDkgRound2, btcFrostDkgFinalize
    • FROST share lifecycle helpers: btcFrostRefreshRound1, btcFrostRefreshRound2, btcFrostRefreshFinalize, btcFrostReshareRound1, btcFrostReshareRound2, btcFrostReshareFinalize
    • FROST public-state guards: btcFrostValidateShare, btcFrostValidatePublicState, btcFrostCompareGroupPublicKey
    • FROST threshold signing: btcFrostRound1Commit, btcFrostBuildSigningPackage, btcFrostRound2SignShare, btcFrostAggregateSignature, btcFrostTaprootComputeTweak, btcFrostVerifySignatureShare
    • FROST nonce lifecycle guard: btcFrostNonceSessionScope, btcFrostResetNonceGuard
    • FROST Taproot PSBT bridge: btcFrostBuildTaprootPsbtSigningInputs, btcFrostFinalizeTaprootPsbt
    • FROST offline orchestration: createBtcFrostDkgSession, runBtcFrostDkgSession, createBtcFrostSigningSession, runBtcFrostSigningSession, createBtcFrostRefreshSession, runBtcFrostRefreshSession, createBtcFrostReshareSession, runBtcFrostReshareSession
  • ETH:
    • account/address: ethDeriveAddress, ethDeriveWallet, ethDeriveAddressFromPrivateKey, ethDeriveWalletFromPrivateKey
    • signing: ethBuildAndSign, ethBuildAndSignFromPrivateKey, ethBuildTx, ethSignTx, ethSignMessage*, ethSignTypedData*
    • verification/recovery: ethEcrecover, ethRecoverTypedDataSigner, ethDecodeSignedTx
    • contract-wallet helpers (EIP-1271): ethBuildEip1271IsValidSignatureCall, ethBuildEip1271RpcCall, ethVerifyEip1271IsValidSignatureResult, ethBuildEip1271RpcCallAndVerify
    • app-layer orchestration reference: docs/eth-eip1271-consumer-template.md
  • SOL:
    • account/address: solanaDeriveAddress, solanaDeriveWallet, solanaDeriveAddressFromPrivateKey, solanaDeriveWalletFromPrivateKey
    • signing: solanaBuildAndSign, solanaBuildAndSignFromPrivateKey, solanaBuildTx, solanaSignTx, solanaInspectSignedTx (incl. versioned_message path + multi-signer state), solanaSignMessage*, solanaSignBytes*
    • app-layer multi-signer orchestration reference: docs/solana-multisigner-consumer-template.md

invokeChainExtension remains available as a low-level escape hatch, but app code should prefer chain-specific core APIs.

BTC FROST DKG Flow

The SDK supports two key-setup modes for BTC FROST Taproot:

  • Trusted dealer setup:
    • btcFrostTrustedDealerKeygen
  • Decentralized DKG setup:
    • btcFrostDkgRound1 -> btcFrostDkgRound2 -> btcFrostDkgFinalize

Recommended coordinator process for DKG:

  1. each participant runs btcFrostDkgRound1; coordinator collects coefficientCommitmentsHex.
  2. each participant runs btcFrostDkgRound2; coordinator routes shares[].toIdentifier.
  3. each participant runs btcFrostDkgFinalize with full round1 commitments + routed shares.
  4. coordinator verifies all finalize outputs have identical groupPublicKeyXOnlyHex.

Production offline orchestration helper:

  • createBtcFrostDkgSession(config) gives a stateful session object with:
    • round request builders (buildRound1Request, buildRound2Request, buildFinalizeRequest)
    • per-round replay protection (acceptRound1Result, acceptRound2Result, acceptFinalizeResult)
    • persistence hooks (toState, BtcFrostDkgSession.fromState)
  • runBtcFrostDkgSession(config) runs round1/round2/finalize end-to-end using SDK executor defaults.

Coordinator helper example:

const dkg = await runBtcFrostDkgSession({
  threshold: 2,
  participants: 3,
});
// dkg.groupPublicKeyXOnlyHex
// dkg.participants[i].secretShareHex (store per participant securely)

Refresh/Reshare boundary helpers (0.2.8+ API surface):

  • btcFrostRefreshRound1/2/Finalize
  • btcFrostReshareRound1/2/Finalize
  • btcFrostValidateShare
  • btcFrostValidatePublicState
  • btcFrostCompareGroupPublicKey
  • session helpers:
    • createBtcFrostRefreshSession, runBtcFrostRefreshSession
    • createBtcFrostReshareSession, runBtcFrostReshareSession

Signing orchestration helpers (0.2.14+ API surface):

  • createBtcFrostSigningSession:
    • stateful signing session with replay protection
    • supports toState / BtcFrostSigningSession.fromState
    • explicit steps: round1 commit -> build signing package -> round2 shares -> aggregate
  • runBtcFrostSigningSession:
    • one-call offline runner for the full signing path
    • consumes participant identifiers + per-participant secret shares

These APIs are offline primitives for consumer-side orchestration. Coordinator policy (refresh cadence, roster governance, approval flow, transport) stays in app/backend layer.

After setup (trusted dealer or DKG), run threshold signing with:

  1. btcFrostRound1Commit
  2. btcFrostBuildSigningPackage
  3. btcFrostRound2SignShare
  4. btcFrostAggregateSignature

Nonce lifecycle guard:

  • btcFrostRound2SignShare now enforces one-time use for each nonceSecretHex within process lifecycle; nonce reuse will be rejected.

For Taproot output-key context, apply:

  • btcFrostTaprootComputeTweak

FROST offline verification + PSBT bridge:

  • btcFrostVerifySignatureShare (single-share verification / blame attribution)
  • btcFrostBuildTaprootPsbtSigningInputs (extract per-input Taproot key-spend message hashes)
  • btcFrostFinalizeTaprootPsbt (inject aggregated Schnorr signatures into PSBT inputs)

Types

type SupportedChainInfo = {
  chainId: string;
  capabilities: string[];
};

type ChainExtensionInfo = {
  chainId: string;
  methods: string[];
};

type DeriveAddressRequest = {
  chainId: string;
  mnemonic: string;
  account: number; // uint32
  index: number; // uint32
  passphrase?: string | null;
};

type DeriveAddressFromPrivateKeyRequest = {
  chainId: string;
  privateKey: string;
  keyEncoding?: "utf8" | "hex" | "base64" | "wif"; // default: hex
};

type DerivedWallet = {
  address: string;
  privateKey: string;
  privateKeyEncoding: "hex" | "base58" | "wif";
  publicKey: string;
  publicKeyEncoding: "hex" | "base58" | "wif";
};

type BuildAndSignRequest = {
  chainId: string;
  mnemonic: string;
  txPayload: string;
  txEncoding?: "utf8" | "hex" | "base64"; // default: utf8
};

type BuildAndSignResult = {
  chainId: string;
  signedTxHex: string;
};

type BuildAndSignFromPrivateKeyRequest = {
  chainId: string;
  privateKey: string;
  keyEncoding?: "utf8" | "hex" | "base64" | "wif"; // default: hex
  txPayload: string;
  txEncoding?: "utf8" | "hex" | "base64"; // default: utf8
};

type BuildAndSignFromPrivateKeyResult = BuildAndSignResult;

type BtcSignMessageMode = "ecdsa" | "bip322-simple";

type SignMessageRequest = {
  chainId: string;
  mnemonic: string;
  account: number;
  index: number;
  passphrase?: string | null;
  message: string;
  messageEncoding?: "utf8" | "hex" | "base64"; // default: utf8
  mode?: BtcSignMessageMode; // btc only, default: ecdsa
};

type SignMessageFromPrivateKeyRequest = {
  chainId: string;
  privateKey: string;
  keyEncoding?: "utf8" | "hex" | "base64" | "wif"; // default: hex
  message: string;
  messageEncoding?: "utf8" | "hex" | "base64"; // default: utf8
  mode?: BtcSignMessageMode; // btc only, default: ecdsa
};

type SignBytesRequest = {
  chainId: string;
  mnemonic: string;
  account: number;
  index: number;
  passphrase?: string | null;
  bytes: string;
  bytesEncoding?: "utf8" | "hex" | "base64"; // default: utf8
};

type SignBytesFromPrivateKeyRequest = {
  chainId: string;
  privateKey: string;
  keyEncoding?: "utf8" | "hex" | "base64" | "wif"; // default: hex
  bytes: string;
  bytesEncoding?: "utf8" | "hex" | "base64"; // default: utf8
};

type SignTypedDataRequest = {
  chainId: string;
  mnemonic: string;
  account: number;
  index: number;
  passphrase?: string | null;
  typedData: string;
  typedDataEncoding?: "utf8" | "hex" | "base64"; // default: utf8
};

type SignTypedDataFromPrivateKeyRequest = {
  chainId: string;
  privateKey: string;
  keyEncoding?: "utf8" | "hex" | "base64" | "wif"; // default: hex
  typedData: string;
  typedDataEncoding?: "utf8" | "hex" | "base64"; // default: utf8
};

type SignResult = {
  chainId: string;
  signatureHex: string;
};

type InvokeChainExtensionRequest = {
  chainId: string;
  method: string;
  payload: string;
  payloadEncoding?: "utf8" | "hex" | "base64"; // default: utf8
};

type EthEcrecoverRequest = {
  message: string;
  messageEncoding?: "utf8" | "hex" | "base64"; // default: utf8
  signature: string;
  signatureEncoding?: "hex" | "base64"; // default: hex
};

type BtcNetwork = "mainnet" | "testnet" | "signet" | "regtest";
type BtcAddressType = "p2pkh" | "p2sh-p2wpkh" | "p2wpkh" | "p2tr";

type BtcDeriveAddressRequest = {
  mnemonic: string;
  account?: number; // default: 0
  change?: number; // default: 0
  index?: number; // default: 0
  passphrase?: string | null;
  network?: BtcNetwork; // default: mainnet
  addressType?: BtcAddressType; // default: p2pkh
};

type BtcDeriveAddressFromPrivateKeyRequest = {
  privateKey: string;
  keyEncoding?: "utf8" | "hex" | "wif"; // default: wif
  network?: BtcNetwork; // default: mainnet
  addressType?: BtcAddressType; // default: p2pkh
};

type BtcDerivedWallet = DerivedWallet & {
  network: BtcNetwork;
  addressType: BtcAddressType;
};

BTC API Notes

  • btcDeriveAddress / btcDeriveWallet support:
    • network: mainnet, testnet, signet, regtest (default mainnet)
    • addressType: p2pkh, p2sh-p2wpkh, p2wpkh, p2tr (default p2pkh)
  • btcDeriveWallet returns:
    • privateKey in WIF (privateKeyEncoding: "wif")
    • compressed publicKey in hex (publicKeyEncoding: "hex")
  • btcDeriveWalletFromPrivateKey accepts both:
    • WIF private key (keyEncoding: "wif")
    • hex private key (keyEncoding: "hex")
  • btcSignPsbt is strict finalize path:
    • uses one private key
    • requires all inputs to be finalizable in one call
  • btcSignPsbtWithSigners supports production multi-signer orchestration:
    • accepts multiple signer keys in one call
    • optional targetInputIndexes per signer
    • supports finalize: false for round-based signing and finalize: true for strict completion
  • btcBuildPsbt / btcBuildTx support both:
    • single-input fallback (prevTxid, prevVout, prevScriptPubkeyHex, prevValueSat)
    • multi-UTXO mode (inputs[] with per-input txid, vout, scriptPubkeyHex, valueSat, optional derivation path selectors)
  • btcPartialSignPsbt is non-finalizing friendly:
    • signs only inputs controlled by the provided key
    • returns finalized + missingFinalizedInputs so multi-party rounds can continue
  • DKG orchestration helpers (0.2.7+):
    • createBtcFrostDkgSession: stateful coordinator with replay protection and toState/fromState
    • runBtcFrostDkgSession: round1/round2/finalize convenience runner for offline coordinator workflows
  • signing orchestration helpers (0.2.14+):
    • createBtcFrostSigningSession: stateful signing coordinator with replay protection and toState/fromState
    • runBtcFrostSigningSession: round1/build/round2/aggregate convenience runner for offline coordinator workflows
  • SDK public API is chain-first:
    • prefer btc.* namespace (or btcXxx flat chain-specific exports)
    • do not route wallet operations through generic chainId-based aggregator methods

Example

import {
  btcBuildPsbt,
  btcBuildTx,
  btcDeriveAddress,
  btcDeriveWallet,
  btcDeriveAddressFromPrivateKey,
  btcDeriveWalletFromPrivateKey,
  btcPartialSignPsbt,
  btcSignPsbt,
  btcSignPsbtWithSigners,
  btcSignTx,
  btcVerifyPsbtFinalized,
  ethBuildAndSignFromPrivateKey,
  ethDeriveAddressFromPrivateKey,
  ethSignMessage,
  generateMnemonic,
  validateMnemonic,
  init,
  listSupportedChains,
  ethEcrecover,
} from "@bubolabs/wallet-rn-sdk";

const mnemonic = await generateMnemonic();
const isValid = await validateMnemonic(mnemonic);
if (!isValid) throw new Error("generated mnemonic must be valid");

await init();
const chains = await listSupportedChains();
const address = await btcDeriveAddress({
  mnemonic,
  account: 0,
  change: 0,
  index: 0,
  passphrase: null,
});

const wallet = await btcDeriveWallet({
  mnemonic,
  account: 0,
  change: 0,
  index: 0,
});
// btc privateKey is WIF
const restoredBtc = await btcDeriveWalletFromPrivateKey({
  privateKey: wallet.privateKey,
  keyEncoding: "wif",
});

const btcTestnetTaproot = await btcDeriveWallet({
  mnemonic,
  account: 0,
  change: 0,
  index: 0,
  network: "testnet",
  addressType: "p2tr",
});

const btcNestedSegwitAddr = await btcDeriveAddressFromPrivateKey({
  privateKey: wallet.privateKey,
  keyEncoding: "wif",
  network: "mainnet",
  addressType: "p2sh-p2wpkh",
});

const signed = await btcBuildAndSign({
  mnemonic,
  txPayload: "<tx payload>",
  txEncoding: "utf8",
});

const signedFromPk = await ethBuildAndSignFromPrivateKey({
  privateKey: "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
  keyEncoding: "hex",
  txPayload: "<tx payload>",
  txEncoding: "utf8",
});

const messageSig = await ethSignMessage({
  mnemonic,
  account: 0,
  index: 0,
  passphrase: null,
  message: "hello from bubo",
  messageEncoding: "utf8",
});

const restored = await ethDeriveAddressFromPrivateKey({
  privateKey: "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
  keyEncoding: "hex",
});

const recovered = await ethEcrecover({
  message: "hello from bubo",
  messageEncoding: "utf8",
  signature: messageSig.signatureHex,
  signatureEncoding: "hex",
});

const builtPsbt = await btcBuildPsbt({
  mnemonic,
  account: 0,
  addressIndex: 0,
  changeIndex: 0,
  network: "testnet",
  addressType: "p2wpkh",
  prevTxid: "6000000000000000000000000000000000000000000000000000000000000006",
  prevVout: 0,
  prevScriptPubkeyHex: "0014d85c2b71d0060b09c9886aeb815e50991dda124d",
  prevValueSat: 100000,
  toAddress: "tb1q4ryc5g5q2sz66mh58f9x7w6j5xw5lf7k0k4a2s",
  amountSat: 10000,
  feeSat: 1000,
});

const builtPsbtMultiInput = await btcBuildPsbt({
  mnemonic,
  account: 0,
  addressIndex: 0,
  changeIndex: 0,
  network: "testnet",
  addressType: "p2wpkh",
  inputs: [
    {
      txid: "7000000000000000000000000000000000000000000000000000000000000007",
      vout: 0,
      scriptPubkeyHex: "0014d85c2b71d0060b09c9886aeb815e50991dda124d",
      valueSat: 60000,
    },
    {
      txid: "8000000000000000000000000000000000000000000000000000000000000008",
      vout: 1,
      scriptPubkeyHex: "0014d85c2b71d0060b09c9886aeb815e50991dda124d",
      valueSat: 50000,
    },
  ],
  toAddress: "tb1q4ryc5g5q2sz66mh58f9x7w6j5xw5lf7k0k4a2s",
  amountSat: 100000,
  feeSat: 1000,
});

const signedPsbt = await btcSignPsbt({
  psbt: builtPsbt.psbtHex,
  privateKey: wallet.privateKey, // WIF
  keyEncoding: "wif",
  network: "testnet",
});

const signedByMultiSigners = await btcSignPsbtWithSigners({
  psbt: builtPsbtMultiInput.psbtHex,
  network: "testnet",
  finalize: false,
  signers: [
    {
      privateKey: wallet.privateKey, // WIF
      keyEncoding: "wif",
      targetInputIndexes: [0],
    },
  ],
});

const partiallySigned = await btcPartialSignPsbt({
  psbt: builtPsbt.psbtHex,
  privateKey: wallet.privateKey, // WIF
  keyEncoding: "wif",
  network: "testnet",
});

const builtTx = await btcBuildTx({
  mnemonic,
  account: 0,
  addressIndex: 0,
  changeIndex: 0,
  network: "testnet",
  addressType: "p2wpkh",
  prevTxid: "6000000000000000000000000000000000000000000000000000000000000006",
  prevVout: 0,
  prevScriptPubkeyHex: "0014d85c2b71d0060b09c9886aeb815e50991dda124d",
  prevValueSat: 100000,
  toAddress: "tb1q4ryc5g5q2sz66mh58f9x7w6j5xw5lf7k0k4a2s",
  amountSat: 10000,
  feeSat: 1000,
});

const signedTx = await btcSignTx({
  psbt: builtTx.psbtHex,
  privateKey: wallet.privateKey, // WIF
  keyEncoding: "wif",
  network: "testnet",
});

const verifiedPsbt = await btcVerifyPsbtFinalized({
  psbt: signedTx.psbtHex,
});

txPayload JSON Examples

txEncoding uses "utf8" by default, so pass JSON text directly. For full canonical schemas (required/optional/default fields), see repo doc: docs/chain-payload-schemas.md.

BTC:

{
  "account": 0,
  "address_index": 0,
  "change_index": 0,
  "network": "mainnet",
  "address_type": "p2pkh",
  "prev_txid": "0000000000000000000000000000000000000000000000000000000000000001",
  "prev_vout": 0,
  "prev_script_pubkey_hex": "76a914d986ed01b7a22225a70edbf2ba7cfb63a15cb3aa88ac",
  "prev_value_sat": 100000,
  "to_address": "1BoatSLRHtKNngkdXEeobR76b53LETtpyT",
  "amount_sat": 10000,
  "fee_sat": 1000,
  "lock_time": 0,
  "sequence": 4294967295
}

address_type supports p2pkh, p2sh-p2wpkh, p2wpkh, p2tr (default p2pkh).

ETH (legacy transfer):

{
  "account": 0,
  "index": 0,
  "chain_id": 1,
  "nonce": 0,
  "gas_price_wei": "1000000000",
  "gas_limit": 21000,
  "to_address": "0x1111111111111111111111111111111111111111",
  "value_wei": "1000000000000000"
}

ETH (EIP-1559 transfer):

{
  "account": 0,
  "index": 0,
  "chain_id": 1,
  "nonce": 0,
  "max_fee_per_gas_wei": "1000000000",
  "max_priority_fee_per_gas_wei": "100000000",
  "gas_limit": 21000,
  "to_address": "0x1111111111111111111111111111111111111111",
  "value_wei": "1000000000000000"
}

For ETH tx payload, use either legacy (gas_price_wei) or EIP-1559 (max_fee_per_gas_wei + max_priority_fee_per_gas_wei), not both.

ETH build-path APIs (ethBuildTx, ethBuildAndSign*, and generic buildAndSign* with chainId: "eth") run adapter preflight checks for txEncoding: "utf8" JSON payloads:

  • required fields: nonce, gas_limit, to_address
  • fee-model exclusivity: legacy vs EIP-1559 cannot be mixed
  • EIP-1559 constraint: max_priority_fee_per_gas_wei <= max_fee_per_gas_wei
  • supports camelCase aliases and normalizes to snake_case before native call: chainId, gasLimit, toAddress, gasPriceWei, maxFeePerGasWei, maxPriorityFeePerGasWei, valueWei, dataHex

Solana (system transfer):

{
  "tx_kind": "system_transfer",
  "account": 0,
  "index": 0,
  "to_address": "11111111111111111111111111111111",
  "lamports": 1000,
  "message_version": "legacy",
  "recent_blockhash": "11111111111111111111111111111111"
}

Solana (SPL token transfer, transferChecked):

{
  "tx_kind": "spl_token_transfer",
  "account": 0,
  "index": 0,
  "source_token_account": "11111111111111111111111111111111",
  "destination_token_account": "11111111111111111111111111111111",
  "mint_address": "11111111111111111111111111111111",
  "amount": 1000,
  "decimals": 6,
  "message_version": "legacy",
  "recent_blockhash": "11111111111111111111111111111111"
}

Solana (high-level v0 compile with ALT lookup tables):

{
  "tx_kind": "system_transfer",
  "account": 0,
  "index": 0,
  "to_address": "11111111111111111111111111111111",
  "lamports": 1000,
  "message_version": "v0",
  "address_lookup_tables": [
    {
      "table_address": "11111111111111111111111111111111",
      "addresses": ["11111111111111111111111111111111"]
    }
  ],
  "recent_blockhash": "11111111111111111111111111111111"
}

Solana (precompiled versioned message, including ALT-compiled message bytes):

{
  "tx_kind": "versioned_message",
  "versioned_message": "<base64-serialized-VersionedMessage>",
  "versioned_message_encoding": "base64"
}

For Solana tx payload:

  • tx_kind defaults to system_transfer when omitted.
  • spl_token_transfer uses SPL Token program transferChecked (amount in raw token units).
  • optional token_program_id can override the default SPL Token program id.
  • message_version supports legacy (default) and v0.
  • address_lookup_tables can be provided when message_version=v0; each table has table_address + addresses[].
  • versioned_message lets app/backend provide precompiled message bytes (ALT flows included).
  • for versioned_message, recent_blockhash is not required in payload (already embedded in message bytes).
  • for solanaBuildTx + versioned_message, signer fields are optional; for other Solana tx kinds signer is required.

Chain Feature Notes

  • Published package content depends on Rust feature set at build time.
  • When BUBO_RUST_FEATURES is not set, packaging auto-enables all chain-* features from crates/ffi/Cargo.toml.
  • signMessage* is implemented for eth, btc, and solana.
  • signBytes* is implemented for btc and solana.
  • signTypedData* is implemented only for eth.
  • You can still override for focused builds, for example:
BUBO_RUST_FEATURES=chain-eth,chain-btc ./scripts/build-rn-sdk-native-artifacts.sh

Compatibility Smoke API

Legacy smoke APIs remain available for regression checks:

  • runSmoke()
  • runBtcSmoke()
type WalletSmokeResult = {
  supportedChains: Array<{ chainId: string; capabilities: string[] }>;
  deriveChainId: string;
  derivedAddress: string;
  signChainId: string | null;
  signedTxHex: string | null;
};

Distribution Scripts

cd ../..
./scripts/package-rn-sdk.sh

Builds a publishable tarball and verifies runtime assets are present/synced.

./scripts/verify-rn-sdk-distribution.sh

Installs the tarball into example/ and verifies iOS/Android native builds.

./scripts/publish-rn-sdk.sh --dry-run
./scripts/publish-rn-sdk.sh --publish --tag latest

Publish wrapper:

  • default is dry-run
  • real publish requires --publish