This skill should be used when working with BSV21 fungible tokens — sending tokens, checking token balances, listing token UTXOs, purchasing tokens from marketplace, deploying new tokens, or burning tokens. Triggers on 'send tokens', 'token balance', 'BSV21', 'BSV-20', 'fungible token', 'transfer tokens', 'deploy token', 'burn tokens', 'token listing', 'buy tokens', or 'token UTXO'. Uses @1sat/actions and @1sat/core packages from the 1sat-sdk.
From 1satnpx claudepluginhub b-open-io/claude-plugins --plugin 1satThis skill uses the workspace's default tool permissions.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
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.
Send, receive, list, deploy, and manage BSV21 fungible tokens using @1sat/actions.
| Action | Description |
|---|---|
listTokens | List all BSV21 token UTXOs in the wallet |
getBsv21Balances | Aggregated balances grouped by token ID |
sendBsv21 | Send tokens to a counterparty, address, or paymail |
purchaseBsv21 | Purchase tokens from a marketplace listing |
import { getBsv21Balances, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const balances = await getBsv21Balances.execute(ctx, {})
for (const token of balances) {
const displayAmt = Number(BigInt(token.amt)) / (10 ** token.dec)
console.log(`${token.sym ?? token.id}: ${displayAmt}`)
}
interface Bsv21Balance {
p: string // Protocol: 'bsv-20'
id: string // Token ID (txid_vout)
sym?: string // Symbol (e.g., 'MYTOK')
icon?: string // Icon URL/outpoint
dec: number // Decimal places
amt: string // Total amount (raw, as string)
all: { confirmed: bigint; pending: bigint }
listed: { confirmed: bigint; pending: bigint }
}
import { listTokens, createContext } from '@1sat/actions'
const ctx = createContext(wallet)
const outputs = await listTokens.execute(ctx, { limit: 100 })
// Returns WalletOutput[] with tags like 'id:{tokenId}', 'amt:{amount}', 'dec:{decimals}'
import { sendBsv21, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
// Send by counterparty public key (preferred - wallet can derive keys)
const result = await sendBsv21.execute(ctx, {
tokenId: 'abc123...def456_0', // Token ID (deploy txid_vout)
amount: '1000000', // Raw amount as string (respects decimals)
counterparty: '02abc...', // Recipient's identity public key
})
// Send by address (external - not tracked in recipient's wallet)
const result = await sendBsv21.execute(ctx, {
tokenId: 'abc123...def456_0',
amount: '500000',
address: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa',
})
if (result.txid) {
console.log('Sent! txid:', result.txid)
} else {
console.error('Error:', result.error)
}
tokenIdamountToken amounts are in raw units (like satoshis for BSV). If a token has 8 decimals:
'100000000' = 1.0 tokens'50000000' = 0.5 tokens'1' = 0.00000001 tokensimport { purchaseBsv21, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const result = await purchaseBsv21.execute(ctx, {
tokenId: 'abc123...def456_0',
outpoint: 'txid_vout', // Listed token outpoint
amount: '1000000', // Tokens in the listing
marketplaceAddress: '1Market...', // Optional: marketplace fee address
marketplaceRate: 0.02, // Optional: 2% marketplace fee
})
No action exists for token deployment yet. Use
@1sat/coredirectly with raw private keys.
import { deployBsv21Token } from '@1sat/core'
const result = await deployBsv21Token({
symbol: 'MYTOKEN',
decimals: 8,
icon: 'iconTxid_0', // Outpoint of icon inscription
utxos: paymentUtxos,
initialDistribution: {
address: ownerAddress,
tokens: 21_000_000, // Display amount
},
destinationAddress: ownerAddress,
paymentPk: paymentPrivateKey,
changeAddress: changeAddress,
})
console.log('Deployed token:', result.tx.id('hex'))
No action exists for token burning yet. Use
@1sat/coredirectly with raw private keys.
import { transferOrdTokens, TokenType } from '@1sat/core'
const result = await transferOrdTokens({
protocol: TokenType.BSV21,
tokenID: 'abc123...def456_0',
decimals: 8,
utxos: paymentUtxos,
inputTokens: tokenUtxos,
distributions: [], // Empty = no recipients
burn: true, // Burns all input tokens
paymentPk: paymentPrivateKey,
ordPk: ordPrivateKey,
changeAddress: changeAddress,
})
When sending tokens, the SDK selects UTXOs automatically. The selection:
insufficient-tokens error if not enough validated UTXOsBSV21 tokens require overlay validation. The services object handles this:
import { OneSatServices } from '@1sat/wallet'
const services = new OneSatServices('main')
// Token details (symbol, decimals, icon, fee info)
const details = await services.bsv21.getTokenDetails(tokenId)
// Validate outputs exist and are unspent
const valid = await services.bsv21.validateOutputs(tokenId, outpoints, { unspent: true })
// After sending, transactions are automatically submitted to the overlay
bun add @1sat/actions @1sat/core @1sat/wallet @bsv/sdk