@btcp-io/sdk
v0.2.0
Published
TypeScript SDK for the BSV MPC Wallet API — non-custodial 2-of-2 threshold ECDSA with Paillier encryption
Maintainers
Readme
@bsv-mpc/sdk
TypeScript SDK for bm — the non-custodial MPC Wallet API.
Client-side key generation, threshold ECDSA signing, and wallet recovery — all without the private key ever existing in one place.
Installation
npm install @bsv-mpc/sdkyarn add @bsv-mpc/sdkpnpm add @bsv-mpc/sdkRequirements: Node.js 18+ · Browser with Web Crypto API
Quick Start
import { MPCWalletClient } from '@bsv-mpc/sdk';
// 1. Initialize client
const client = new MPCWalletClient({
baseUrl: 'https://api.example.com',
});
// 2. Authenticate
await client.auth.requestOTP('[email protected]');
const tokens = await client.auth.verifyOTP('[email protected]', '123456');
// Tokens are stored automatically — subsequent requests are authenticated
// 3. Create an MPC wallet (DKG Round 1)
const wallet = await client.wallet.create({
chain: 'bsv',
network: 'testnet',
});
// wallet.walletId, wallet.serverPublicShareX, wallet.paillierN, ...
// 4. Complete key generation (DKG Round 2)
const keygen = await client.wallet.completeKeygen(wallet.walletId, {
clientPublicShareX: clientShare.publicShareX,
clientPublicShareY: clientShare.publicShareY,
});
console.log('Wallet address:', keygen.address);
console.log('Joint public key:', keygen.jointPublicKey);Sign a BSV Transaction
import { clientComputePartialSignature } from '@bsv-mpc/sdk';
// 1. Create an unsigned transaction
const tx = await client.transactions.create(walletId, {
inputs: [{
prevTxHash: 'abc123...',
prevTxIndex: 0,
prevOutputValue: 100000,
prevOutputScript: '76a914...',
}],
outputs: [{ address: '1BvBMSEYstWetq...', value: 50000 }],
});
// 2. Initiate MPC signing (Round 1 — server sends nonce)
const signInit = await client.signing.init(walletId, {
messageHash: tx.sighashes[0],
transactionHex: tx.unsignedTxHex,
});
// 3. Compute Paillier-encrypted partial signature (client-side)
const partialSig = clientComputePartialSignature(signInit, clientKeyShare);
// 4. Complete signing (Round 2 — server produces ECDSA signature)
const result = await client.signing.complete(walletId, {
sessionId: signInit.sessionId,
encryptedPartialSig: partialSig.ciphertext,
encryptedPartialSigExponent: partialSig.exponent,
combinedNonceX: partialSig.combinedNonceX,
combinedNonceY: partialSig.combinedNonceY,
});
// 5. Broadcast
const broadcast = await client.transactions.broadcast(walletId, {
signedTxHex: result.signedTxHex,
});
console.log('txid:', broadcast.txid);EVM Transactions (Polygon)
import { buildEVMTransaction, serializeSignedEVMTransaction } from '@bsv-mpc/sdk';
// Create wallet on Polygon Amoy testnet
const wallet = await client.wallet.create({ chain: 'polygon-amoy' });
// Build an EVM transaction
const evmTx = await client.transactions.createEVM(walletId, {
to: '0xRecipientAddress...',
value: '1000000000000000000', // 1 MATIC in wei
gasLimit: 21000,
});
// Sign via the same MPC two-round protocol
const signInit = await client.signing.init(walletId, {
messageHash: evmTx.messageHash,
});
// ... complete signing flowERC-20 Token Transfers
import { encodeERC20Transfer } from '@bsv-mpc/sdk';
const data = encodeERC20Transfer('0xRecipient...', 1000000n); // amount in token units
const tx = await client.transactions.createEVM(walletId, {
to: '0xTokenContractAddress...',
value: '0',
data,
gasLimit: 65000,
});WebAuthn / Passkey
// Register a passkey for high-value transaction signing
const options = await client.webauthn.registerBegin();
const credential = await navigator.credentials.create({ publicKey: options });
await client.webauthn.registerComplete(credential);
// Authenticate with passkey
const authOptions = await client.webauthn.authenticateBegin();
const assertion = await navigator.credentials.get({ publicKey: authOptions });
const { webauthnToken } = await client.webauthn.authenticateComplete(assertion);
// Use the token for policy-gated signing
await client.signing.init(walletId, {
messageHash: '...',
webauthnToken,
});Wallet Recovery
bm uses 2-of-3 Shamir secret sharing: server share + client share + recovery share. Any two shares can reconstruct the wallet.
// Export recovery share — store the package offline
const { recoveryPackage } = await client.recovery.export(walletId, {
password: 'user-chosen-strong-password',
});
// Verify the backup is intact
const { valid } = await client.recovery.verify(walletId, {
recoveryPackage,
password: 'user-chosen-strong-password',
});
// Recover wallet after device loss
await client.recovery.initiate(walletId, {
recoveryPackage,
password: 'user-chosen-strong-password',
recoveryType: 'device_lost',
});MFA (TOTP)
// Setup MFA
const { secret, qrCodeUrl } = await client.auth.setupMFA();
// Display QR code to user for Google Authenticator / Authy
// Verify and enable MFA
await client.auth.verifyMFA('123456');
// Subsequent logins require TOTP code
const tokens = await client.auth.verifyOTP('[email protected]', '123456', {
mfaCode: '789012',
});Token Management
The SDK handles token refresh automatically. Provide a callback to persist tokens:
const client = new MPCWalletClient({
baseUrl: 'https://api.example.com',
accessToken: savedTokens.accessToken, // restore from storage
refreshToken: savedTokens.refreshToken,
onTokenRefresh: (tokens) => {
// Persist new tokens when auto-refreshed
localStorage.setItem('tokens', JSON.stringify(tokens));
},
});Wagmi Integration
For EVM dApps using wagmi/viem:
import { MPCConnector } from '@bsv-mpc/sdk/wagmi';
const connector = new MPCConnector({
baseUrl: 'https://api.example.com',
});
// Use with wagmi's createConfigPeer dependencies (optional):
@wagmi/core>= 2.0.0viem>= 2.0.0react>= 18.0.0
Error Handling
import {
AuthenticationError,
PolicyViolationError,
PasskeyRequiredError,
NetworkError,
} from '@bsv-mpc/sdk';
try {
await client.signing.init(walletId, { messageHash: '...' });
} catch (error) {
if (error instanceof PasskeyRequiredError) {
// Policy requires WebAuthn — prompt user for passkey
} else if (error instanceof PolicyViolationError) {
// Spending policy violated (daily limit, velocity, etc.)
} else if (error instanceof AuthenticationError) {
// Token expired or invalid — redirect to login
} else if (error instanceof NetworkError) {
// HTTP error — check error.statusCode
}
}API Reference
MPCWalletClient
The main entry point. All sub-clients are accessed as properties:
| Property | Type | Description |
|----------|------|-------------|
| client.auth | AuthClient | OTP, JWT, MFA management |
| client.wallet | WalletClient | Wallet creation, keygen, balance |
| client.signing | SigningClient | MPC signing protocol |
| client.webauthn | WebAuthnClient | Passkey registration & auth |
| client.recovery | RecoveryClient | Backup export, verify, recover |
AuthClient
| Method | Description |
|--------|-------------|
| requestOTP(email) | Send OTP code to email |
| verifyOTP(email, code, options?) | Verify OTP, returns JWT tokens |
| refresh() | Refresh access token |
| setupMFA() | Get TOTP secret + QR URL |
| verifyMFA(code) | Enable MFA with TOTP code |
| disableMFA(code) | Disable MFA |
| me() | Get current user info |
WalletClient
| Method | Description |
|--------|-------------|
| create(params) | Create wallet (DKG Round 1) |
| completeKeygen(walletId, params) | Complete keygen (DKG Round 2) |
| get(walletId) | Get wallet details |
| getBalance(walletId) | Get on-chain balance |
| getAddress(walletId) | Get wallet address |
SigningClient
| Method | Description |
|--------|-------------|
| init(walletId, params) | Initiate MPC signing (Round 1) |
| complete(walletId, params) | Complete MPC signing (Round 2) |
WebAuthnClient
| Method | Description |
|--------|-------------|
| registerBegin() | Get passkey registration options |
| registerComplete(credential) | Complete passkey registration |
| authenticateBegin() | Get passkey auth challenge |
| authenticateComplete(assertion) | Authenticate, returns token |
RecoveryClient
| Method | Description |
|--------|-------------|
| export(walletId, params) | Export encrypted recovery share |
| verify(walletId, params) | Verify recovery backup integrity |
| initiate(walletId, params) | Initiate wallet recovery |
TypeScript Types
All request/response types are exported:
import type {
ClientConfig,
TokenPair,
AuthTokens,
WalletCreateParams,
WalletInfo,
KeygenCompleteParams,
KeygenResult,
BalanceInfo,
SignInitResponse,
ClientSignData,
SignOptions,
SignatureResult,
WebAuthnRegisterOptions,
WebAuthnAuthOptions,
ClientShareStorage,
RecoveryPackage,
RecoveryVerifyResult,
RecoveryInitiateParams,
EVMTransferParams,
EVMTransactionResult,
BSVSendParams,
BSVTransactionResult,
} from '@bsv-mpc/sdk';Crypto Utilities
Low-level cryptographic utilities are also exported for advanced use:
import {
aesGcmEncrypt,
aesGcmDecrypt,
deriveKeyFromPassword,
hkdfDerive,
scryptDerive,
hexToBytes,
bytesToHex,
base64urlEncode,
base64urlDecode,
} from '@bsv-mpc/sdk';License
MIT
