Transactions

Build approved BRC-100 payments to a recipient-supplied address, validate network and satoshis, handle AtomicBEEF and recover uncertain submissions.

Read as Markdown

Describe the action you want the user's BRC-100 wallet to perform. The wallet implementation selects its funding inputs, constructs change, calculates fees and enforces approval. WalletClient is the app-to-wallet bridge; it does not itself hold the user's funds or private keys.

Checked October 26, 2026 with @bsv/sdk 4.0.0. Use a maintained, patched Node.js 22 or 24 release. Install npm install @bsv/sdk@4.0.0, and connect Client on desktop or open the app inside Explorer on mobile. Keep a separate wallet/profile with a small, deliberate test amount for spending examples.

Connect before spending

Start with the connection-only guide. Check the responding wallet's network and version before constructing an action. An identity key is not a type-1 address; use the funding and exchange guide when the destination comes from an exchange.

Send to a recipient-supplied address

The following function validates a complete traditional P2PKH address, checks its network, and builds a payment to that destination. It contains no prefilled recipient or locking script. Obtain the address from the intended recipient or your own exchange deposit screen and review it with the amount before calling the function. Amounts are integer satoshis, not BSV or USD. The wallet also needs funds for its network fee.

1import { P2PKH, Utils, WalletClient } from '@bsv/sdk'
2
3const wallet = new WalletClient('auto')
4
5async function payAddress(recipientAddress: string, satoshis: number) {
6  if (!Number.isSafeInteger(satoshis) || satoshis <= 0) {
7    throw new Error('Enter a positive, safe integer amount in satoshis')
8  }
9  await wallet.waitForAuthentication()
10  const { network } = await wallet.getNetwork({})
11  const decoded = Utils.fromBase58Check(recipientAddress)
12  const expectedPrefix = network === 'mainnet' ? 0x00 : 0x6f
13  if (decoded.prefix.length !== 1 || decoded.prefix[0] !== expectedPrefix) {
14    throw new Error('Recipient address does not match the wallet network')
15  }
16  const lockingScript = new P2PKH().lock(recipientAddress).toHex()
17  const action = await wallet.createAction({
18    description: 'Pay the reviewed recipient',
19    outputs: [{ lockingScript, satoshis, outputDescription: 'Recipient payment' }],
20    options: { acceptDelayedBroadcast: false }
21  })
22  return action
23}
24
25// Deliberately not called here: calling payAddress requests a real spend.
Copied!

Address checksum/format/network validation does not establish ownership, exchange support, deposit minimums or BRC-234 lookup coverage. Those are separate checks. A wallet permission prompt is the user's decision, and a rejection is not a reason to try another transport or silently resubmit.

Interpret the result correctly

payAddress returns the full action result so the app can preserve the necessary recovery context. Keep it securely rather than logging it publicly. createAction returns a result object, not a Transaction instance with arbitrary helper methods or a guaranteed fee-breakdown field. txid and tx are optional. When transaction bytes are present, tx is AtomicBEEF, not a raw transaction hex string: parse with Transaction.fromAtomicBEEF(tx) if you need to inspect it. Retain the appropriate result and transaction identifiers according to your app's recovery design without logging wallet-sensitive data publicly.

A returned TXID, a successful submission, confirmation and a recipient/exchange credit are different milestones. With immediate-broadcast mode, a service error can leave the original action queued for retry. On a timeout or error, check the original wallet action and history before creating another payment. Do not assume an exception proves that nothing was signed or broadcast. See payment recovery.

Track application outputs in baskets

An action output can include a basket, tags and customInstructions when the app needs to track it. Query the intended basket with listOutputs, paginate, and request only the details needed. Do not assume your output is index 0: wallet change and output randomization can affect its position. Identify the actual output by its outpoint and expected script/value, or use the wallet's returned/tracked output records. See baskets.

Spend a custom input

To spend an app-owned contract output, retain the correct source proof/AtomicBEEF, exact outpoint, value, locking script and derivation/contract metadata. Supply the source evidence through inputBEEF and the explicit input through inputs. The wallet signs inputs it controls; only inputs requiring your custom unlocking script need the additional signing flow.

When createAction returns signableTransaction, parse its AtomicBEEF, locate the requested input in that actual template, construct its unlocking script, then call signAction with the returned reference and a spends entry at the correct input index. Do not supply a custom script for wallet-added funding inputs. The signing guide provides a checked finalization helper and explains interrupted preparations.

Read the current wallet interfaces and SDK source for all options. Preserve wallet approval, signing metadata and recovery state before extending this into a production payment flow.

Was this page helpful?