This skill should be used when building BSV transactions with the 1sat-sdk — sending BSV payments, batch payments, OP_RETURN data, custom scripts, signing workflows (createAction/signAction), or understanding the BRC-100 action system. Triggers on 'send BSV', 'build transaction', 'batch payment', 'OP_RETURN', 'createAction', 'signAction', 'sign transaction', 'payment requests', 'locking script', 'action registry', or 'BRC-100'. Uses @1sat/actions and @1sat/core packages.
From 1satnpx claudepluginhub b-open-io/claude-plugins --plugin 1satThis skill uses the workspace's default tool permissions.
Executes pre-written implementation plans: critically reviews, follows bite-sized steps exactly, runs verifications, tracks progress with checkpoints, uses git worktrees, stops on blockers.
Guides idea refinement into designs: explores context, asks questions one-by-one, proposes approaches, presents sections for approval, writes/review specs before coding.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Build and sign BSV transactions using the @1sat/actions system and @1sat/core utilities.
All 1sat-sdk operations use the BRC-100 action pattern:
createContext() → action.execute(ctx, input) → { txid, rawtx, error }
Actions work with any BRC-100 compatible wallet (OneSatWallet, browser extension, etc).
import { createContext } from '@1sat/actions'
// Basic context (wallet only)
const ctx = createContext(wallet)
// Full context (wallet + services for network operations)
const ctx = createContext(wallet, { services })
import { sendBsv, createContext } from '@1sat/actions'
const ctx = createContext(wallet)
// Simple payment
const result = await sendBsv.execute(ctx, {
requests: [
{ address: '1Recipient...', satoshis: 50000 },
],
})
// Multiple recipients (batch payment)
const result = await sendBsv.execute(ctx, {
requests: [
{ address: '1Alice...', satoshis: 10000 },
{ address: '1Bob...', satoshis: 20000 },
{ address: '1Charlie...', satoshis: 30000 },
],
})
// Custom locking script
const result = await sendBsv.execute(ctx, {
requests: [
{ script: '76a914...88ac', satoshis: 5000 }, // hex locking script
],
})
// OP_RETURN data
const result = await sendBsv.execute(ctx, {
requests: [
{ data: ['hello', 'world'], satoshis: 0 },
],
})
// Payment with inscription
const result = await sendBsv.execute(ctx, {
requests: [
{
address: '1Recipient...',
satoshis: 1,
inscription: {
base64Data: btoa('Hello on-chain'),
mimeType: 'text/plain',
},
},
],
})
import { sendAllBsv, createContext } from '@1sat/actions'
const ctx = createContext(wallet)
// Send entire wallet balance to one address
const result = await sendAllBsv.execute(ctx, {
destination: '1Recipient...',
})
import { signBsm, createContext } from '@1sat/actions'
const ctx = createContext(wallet)
const result = await signBsm.execute(ctx, {
message: 'Hello, I own this wallet',
encoding: 'utf8', // 'utf8' | 'hex' | 'base64'
})
// result: { address, pubKey, message, sig }
// sig is base64-encoded compact signature (BSM format)
// With derivation tag for domain-specific keys
const result = await signBsm.execute(ctx, {
message: 'Login to example.com',
tag: {
label: 'auth',
id: 'session123',
domain: 'example.com',
meta: {},
},
})
For operations involving custom scripts (ordinals, locks, tokens), the SDK uses a two-phase approach.
inputBEEF provides the SPV proof chain for the inputs being spent:
const createResult = await wallet.createAction({
description: 'Transfer ordinal',
inputBEEF: beefData, // Optional for wallet-owned inputs, required for external inputs
inputs: [{
outpoint: 'txid.vout',
inputDescription: 'Ordinal to transfer',
unlockingScriptLength: 108, // Expected script size
}],
outputs: [{
lockingScript: '76a914...88ac',
satoshis: 1,
outputDescription: 'Transferred ordinal',
basket: 'ordinals',
tags: ['type:image/png', 'origin:abc...'],
customInstructions: JSON.stringify({ protocolID, keyID }),
}],
options: {
signAndProcess: false, // Don't auto-sign yet
randomizeOutputs: false, // Preserve output order
},
})
Use the completeSignedAction helper for all two-phase actions. It handles BEEF merge (fixing stripped merkle proofs), script verification, signAction, and automatic abortAction on failure.
import { completeSignedAction } from '@1sat/actions'
const result = await completeSignedAction(
wallet,
createResult, // from createAction with signAndProcess: false
inputBEEF as number[], // BEEF from listOutputs (has full SPV proof chain)
async (tx) => {
// tx is a fully-wired Transaction ready for signing
// Return unlocking scripts keyed by input index
const spends: Record<number, { unlockingScript: string }> = {}
spends[0] = { unlockingScript: myUnlockingScript.toHex() }
return spends
},
)
// result.txid is the broadcast transaction ID
Why not raw signAction? The signable transaction BEEF from createAction has merkle proofs stripped (wallet-toolbox uses mergeRawTx internally). completeSignedAction merges the unsigned tx back into inputBEEF to reconstruct the full proof chain before signing. It also verifies unlocking scripts against locking scripts before submitting, and aborts the action automatically if signing fails.
All actions are registered in a global registry:
import { actionRegistry } from '@1sat/actions'
// List all registered actions
for (const action of actionRegistry.list()) {
console.log(`${action.meta.name} (${action.meta.category})`)
}
// Get a specific action
const send = actionRegistry.get('sendBsv')
// Convert to MCP tool definitions
const tools = actionRegistry.toMcpTools()
| Category | Actions |
|---|---|
payments | sendBsv, sendAllBsv |
ordinals | getOrdinals, transferOrdinals, listOrdinal, cancelListing, purchaseOrdinal, deriveCancelAddress |
tokens | listTokens, getBsv21Balances, sendBsv21, purchaseBsv21 |
inscriptions | inscribe |
locks | getLockData, lockBsv, unlockBsv |
signing | signBsm |
social | createSocialPost |
identity | publishIdentity, attest, updateProfile, getProfile |
sweep | sweepBsv, sweepOrdinals, sweepBsv21 |
opns | opnsRegister, opnsDeregister |
For operations outside the action system, use @1sat/core directly:
import {
createOrdinals, sendOrdinals, transferOrdTokens,
createOrdListings, purchaseOrdListing,
sendUtxos, deployBsv21Token, burnOrdinals,
TxBuilder, createTxBuilder,
} from '@1sat/core'
// TxBuilder for custom transaction construction
const builder = createTxBuilder({
utxos: paymentUtxos,
paymentPk: privateKey,
changeAddress: myAddress,
satsPerKb: 50,
})
import {
// Sigma protocol (on-chain signatures)
createSigma, signData,
// MAP protocol (metadata)
buildMapScript, createMap, isValidMap,
} from '@1sat/core'
// Script template classes (locking scripts, inscriptions, marketplace)
import {
Inscription, OrdP2PKH, OrdLock, Lock, BSV20, BSV21,
} from '@1sat/templates'
@1sat/templates provides the script template classes for all 1Sat protocols (Inscription, OrdP2PKH, OrdLock, Lock, BSV20, BSV21). These are also re-exported by @1sat/core for convenience.
The action system includes BAP identity operations that work with any BRC-100 wallet:
import { publishIdentity, updateProfile, getProfile, attest, resolveBapId, createContext } from '@1sat/actions'
const ctx = createContext({ wallet, chain: 'main' })
// Publish a pre-signed BAP ID transaction (root key signs, wallet funds)
const result = await publishIdentity.execute(ctx, { signedScript: signedScript.toHex() })
// Output lands in 'bap' basket with type:id tag
// Update on-chain profile (BAP ALIAS with schema.org Person data)
await updateProfile.execute(ctx, { profile: { name: 'Alice', description: 'Builder' } })
// Get current profile from wallet's bap basket
const { bapId, profile } = await getProfile.execute(ctx, {})
// Publish attestation hash on-chain
await attest.execute(ctx, { attestationHash: 'sha256-of-urn:bap:id:...', counter: '0' })
// Resolve BAP ID from wallet
const bapId = await resolveBapId(ctx)
Identity outputs are stored in the bap basket with tags like type:id and bapId:<hash>.
The wallet organizes outputs into baskets:
| Basket | Contents |
|---|---|
ordinals | Ordinal inscriptions (NFTs) |
bsv21 | BSV-21 fungible tokens |
bsocial | Social protocol outputs (posts, likes, follows) |
locks | Time-locked BSV |
opns | OpNS name ordinals |
Tags on outputs provide metadata for filtering:
const result = await wallet.listOutputs({
basket: 'ordinals',
includeTags: true,
includeCustomInstructions: true,
include: 'entire transactions', // include BEEF for spending
limit: 100,
})
bun add @1sat/actions @1sat/core @bsv/sdk