Sweep & Import
Import BSV, ordinals, and BSV21 tokens from external wallets into a BRC-100 wallet using WIF private keys.
Actions Overview
| Action | Description |
|---|---|
sweepBsv |
Sweep BSV satoshis from external inputs (via private keys) |
sweepOrdinals |
Sweep ordinal inscriptions from external inputs |
sweepBsv21 |
Sweep BSV21 fungible tokens from external inputs |
sweepDeposit |
Claim the wallet's own deposit-basket UTXOs into a funding output |
prepareSweepInputs |
Helper to build sweep inputs from indexed outputs |
sweepBsv/sweepOrdinals/sweepBsv21import external UTXOs you control viaPrivateKeys.sweepDepositis different — it claims plain BSV that already landed in this wallet's deposit basket (no external keys), see below.
Signing Keys
The external-sweep request types take keys: PrivateKey[] (from @bsv/sdk), parallel to inputs — not WIF strings. Convert a WIF first:
import { PrivateKey } from '@bsv/sdk'
const key = PrivateKey.fromWif('L1aW4aubDFB7yfDYK...')
// keys[i] signs inputs[i]
Sweep BSV
import { sweepBsv, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const result = await sweepBsv.execute(ctx, {
inputs: [
{
outpoint: 'txid_vout', // Outpoint format: txid_vout
satoshis: 50000,
lockingScript: '76a914...88ac', // Hex locking script
},
],
keys: [key], // PrivateKey[] parallel to inputs
// Optional: sweep specific amount (remainder returned to source)
amount: 25000,
})
if (result.txid) {
console.log('Swept:', result.txid)
}
SweepBsvRequest is { inputs: SweepInput[]; keys: PrivateKey[]; amount?: number }. Returns SweepBsvResponse: { txid?, beef?, error? } (beef is number[]).
Partial vs Full Sweep
- No
amount: Sweeps all input value (minus fees) into the wallet - With
amount: Sweeps that amount, returns the rest to the source address
Sweep Ordinals
import { sweepOrdinals, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const result = await sweepOrdinals.execute(ctx, {
inputs: [
{
outpoint: 'txid_vout',
satoshis: 1, // Ordinals are always 1 sat
lockingScript: '76a914...88ac',
},
],
keys: [key], // PrivateKey[] parallel to inputs
})
SweepOrdinalsRequest is { inputs: SweepInput[]; keys: PrivateKey[] }. SweepInput carries only outpoint/satoshis/lockingScript — content type, origin, and name are resolved from ORDFS internally (bulk metadata lookup), not passed on inputs.
What Happens During Ordinal Sweep
- Each ordinal gets a unique derived address via the P1SAT protocol (keyID = the input outpoint)
- Tags are set from resolved metadata:
type:{contentType},origin:{origin},name:{name} - Custom instructions are stored for future spending
- OpNS ordinals go to the
opnsbasket; others go to1sat - BSV-20 tokens are rejected (use
sweepBsv21instead) - Output order is preserved (
randomizeOutputs: false) to maintain ordinal positions
Sweep BSV21 Tokens
import { sweepBsv21, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const result = await sweepBsv21.execute(ctx, {
inputs: [
{
outpoint: 'txid_vout',
satoshis: 1,
lockingScript: '76a914...88ac',
tokenId: 'deployTxid_0', // Token ID
amount: '1000000', // Token amount as string
},
// All inputs MUST be the same tokenId
],
keys: [key], // PrivateKey[] parallel to inputs
})
SweepBsv21Request is { inputs: SweepBsv21Input[]; keys: PrivateKey[] }, where SweepBsv21Input extends SweepInput with tokenId: string and amount: string. Returns SweepBsv21Response.
Token Sweep Details
- All inputs must have the same
tokenId - Inputs are validated against the overlay (must be unspent and valid)
- Tokens are consolidated into a single output
- An overlay processing fee is paid automatically
- Transaction is submitted to the overlay for indexing
Sweep Deposit (own deposit basket)
sweepDeposit is not an external import. Inbound plain-BSV payments are parked in the wallet's DEPOSIT_BASKET (by internalizeBeef) until claimed. sweepDeposit spends every deposit-basket UTXO, signs them under the P1SAT protocol, and lets the wallet create a single BRC-29-derived change output in the funding basket. It needs no external keys or WIF.
import { sweepDeposit, createContext } from '@1sat/actions'
const ctx = createContext(wallet)
const result = await sweepDeposit.execute(ctx, {
limit: 50, // optional cap on UTXOs swept per tx, default 50
})
console.log(`Swept ${result.swept} deposit UTXOs, txid: ${result.txid}`)
SweepDepositInput is { limit?: number } (default 50). Returns SweepDepositResult: { txid?: string; swept: number; error? }. A failed sweep is harmless — the deposits stay in the basket and the next call retries. When the basket is empty it returns { swept: 0 } with no txid.
Building Sweep Inputs
If you have IndexedOutput objects (from the 1sat-stack API), use prepareSweepInputs:
import { prepareSweepInputs, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
// Fetch UTXOs from the API
const res = await fetch('https://api.1sat.app/1sat/owner/1Address.../txos')
const utxos = await res.json() // IndexedOutput[]
// Convert to sweep inputs (fetches locking scripts from BEEF)
const inputs = await prepareSweepInputs(ctx, utxos)
// Now use with sweepBsv, sweepOrdinals, or sweepBsv21
Complete Migration Example
import {
sweepBsv, sweepOrdinals, sweepBsv21, prepareSweepInputs, createContext
} from '@1sat/actions'
import { createNodeWallet } from '@1sat/wallet-node'
// 1. Create destination wallet
const { wallet, services } = await createNodeWallet({ mnemonic: newMnemonic })
const ctx = createContext(wallet, { services })
// 2. Fetch all UTXOs from old address
const oldAddress = '1OldAddress...'
const res = await fetch(`https://api.1sat.app/1sat/owner/${oldAddress}/txos`)
const allUtxos = await res.json()
// 3. Separate by type
const bsvUtxos = allUtxos.filter(u => u.satoshis > 1 && !u.data?.bsv21)
const ordUtxos = allUtxos.filter(u => u.satoshis === 1 && u.data?.inscription)
const tokUtxos = allUtxos.filter(u => u.data?.bsv21)
// 4. Build inputs
const bsvInputs = await prepareSweepInputs(ctx, bsvUtxos)
const ordInputs = await prepareSweepInputs(ctx, ordUtxos)
const tokInputs = await prepareSweepInputs(ctx, tokUtxos)
// 5. Sweep each type — keys must be parallel to inputs (one PrivateKey per input)
import { PrivateKey } from '@bsv/sdk'
const key = PrivateKey.fromWif('L1OldWallet...')
if (bsvInputs.length)
await sweepBsv.execute(ctx, { inputs: bsvInputs, keys: bsvInputs.map(() => key) })
if (ordInputs.length)
await sweepOrdinals.execute(ctx, { inputs: ordInputs, keys: ordInputs.map(() => key) })
// Group tokens by tokenId
const byToken = Map.groupBy(tokInputs, i => i.tokenId)
for (const [tokenId, inputs] of byToken) {
await sweepBsv21.execute(ctx, { inputs, keys: inputs.map(() => key) })
}
Requirements
bun add @1sat/actions @1sat/wallet @bsv/sdk
The sweep module requires services for BEEF fetching and overlay validation.