Transfer BTC
A complete Bitcoin send flow: check balance, select UTXOs and estimate fee, MPC sign each input, finalize, and broadcast.
Components used:
GET_BITCOIN_ACCOUNT_BALANCE— verify sufficient funds before buildingBUILD_BITCOIN_TRANSACTION— UTXO selection, fee estimation, and sighash generationSIGN_WITH_KEY_SHARE— MPC sign each input's sighashSIGN_BITCOIN_TRANSACTION— apply signatures and finalize the PSBTBROADCAST_BITCOIN_TRANSACTION— submit to the network
Code​
import { WorkspaceClient, ComponentModule } from 'caller-sdk';
const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });
const KEY_ID = 'your-key-id';
const DERIV = [2147532436, 2147483648, 2147483648, 0, 0]; // m/84'/0'/0'/0/0
const SENDER_PUBKEY = '02abc123...'; // compressed 33-byte public key
const SENDER_ADDR = 'bc1q...'; // sender's native SegWit address
const RECIPIENT_ADDR = 'bc1q...'; // recipient address
async function transferBtc(amountSatoshis: number) {
// 1. Check balance
const { balance } = await workspace
.call(ComponentModule.GET_BITCOIN_ACCOUNT_BALANCE, {
address: SENDER_ADDR,
})
.promise();
console.log(`Balance: ${balance} satoshis`);
if (balance < amountSatoshis) throw new Error('Insufficient BTC balance');
// 2. Build unsigned PSBT — selects UTXOs, estimates fee, produces one sighash per input
const { unsignedTransaction, messageHashes, estimatedFee } = await workspace
.call(ComponentModule.BUILD_BITCOIN_TRANSACTION, {
recipientAddress: RECIPIENT_ADDR,
amount: amountSatoshis,
publicKey: SENDER_PUBKEY,
locktime: null,
opReturnData: null,
changeAddress: null, // defaults to sender
})
.promise();
console.log(`Estimated fee: ${estimatedFee} satoshis`);
console.log(`Signing ${messageHashes.length} input(s)`);
// 3. MPC sign each input sighash
// Bitcoin transactions can have multiple inputs — sign each one
const signatures = await Promise.all(
messageHashes.map((messageHash) =>
workspace
.call(ComponentModule.SIGN_WITH_KEY_SHARE, {
keyId: KEY_ID,
derivationPath: DERIV,
messageHash,
})
.promise()
.then((r) => r.signature),
),
);
// 4. Apply signatures and finalize the PSBT
const { signedTransaction } = await workspace
.call(ComponentModule.SIGN_BITCOIN_TRANSACTION, {
unsignedTransaction,
signatures,
publicKey: SENDER_PUBKEY,
})
.promise();
// 5. Broadcast
const { txid } = await workspace
.call(ComponentModule.BROADCAST_BITCOIN_TRANSACTION, {
signedTransaction,
})
.promise();
console.log('Broadcast txid:', txid);
return txid;
}
// Send 50,000 satoshis (0.0005 BTC)
const txid = await transferBtc(50_000);
Workflow canvas layout​
GET_BITCOIN_ACCOUNT_BALANCE
│ balance (guard check)
â–¼
BUILD_BITCOIN_TRANSACTION
│ messageHashes[] │ unsignedTransaction
▼ │
SIGN_WITH_KEY_SHARE (×N inputs) │
│ signatures[] │
└──────────┬──────────────┘
â–¼
SIGN_BITCOIN_TRANSACTION
│ signedTransaction
â–¼
BROADCAST_BITCOIN_TRANSACTION
│ txid
â–¼
[Confirmed]
Fee deduction
Set the deductFee config option on BUILD_BITCOIN_TRANSACTION to true to send the exact amount minus fees — useful for draining a wallet to a new address.