# Transactions

Canonical: https://docs.projectbabbage.com/docs/guides/wallet/transactions

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

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](https://docs.projectbabbage.com/docs/metanet-client/developers). 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](https://docs.projectbabbage.com/docs/metanet-client/funding) 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.

```ts
import { P2PKH, Utils, WalletClient } from '@bsv/sdk'

const wallet = new WalletClient('auto')

async function payAddress(recipientAddress: string, satoshis: number) {
  if (!Number.isSafeInteger(satoshis) || satoshis <= 0) {
    throw new Error('Enter a positive, safe integer amount in satoshis')
  }
  await wallet.waitForAuthentication()
  const { network } = await wallet.getNetwork({})
  const decoded = Utils.fromBase58Check(recipientAddress)
  const expectedPrefix = network === 'mainnet' ? 0x00 : 0x6f
  if (decoded.prefix.length !== 1 || decoded.prefix[0] !== expectedPrefix) {
    throw new Error('Recipient address does not match the wallet network')
  }
  const lockingScript = new P2PKH().lock(recipientAddress).toHex()
  const action = await wallet.createAction({
    description: 'Pay the reviewed recipient',
    outputs: [{ lockingScript, satoshis, outputDescription: 'Recipient payment' }],
    options: { acceptDelayedBroadcast: false }
  })
  return action
}

// Deliberately not called here: calling payAddress requests a real spend.
```

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](https://docs.projectbabbage.com/docs/metanet-client/payments).

## 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](https://docs.projectbabbage.com/docs/concepts/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](https://docs.projectbabbage.com/docs/guides/wallet/signing) provides a checked finalization helper and explains interrupted preparations.

Read the [current wallet interfaces](https://bsv-blockchain.github.io/ts-sdk/reference/wallet/) and [SDK source](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/sdk) for all options. Preserve wallet approval, signing metadata and recovery state before extending this into a production payment flow.
