| name | tokens |
| description | This skill should be used when working with BSV21 fungible tokens — sending tokens, checking token balances, listing token UTXOs, purchasing tokens from marketplace, deploying a fixed-supply token, deploying a mintable token, or minting additional supply. Triggers on 'send tokens', 'token balance', 'BSV21', 'BSV-20', 'fungible token', 'transfer tokens', 'deploy token', 'mint token', 'mintable token', 'token listing', 'buy tokens', or 'token UTXO'. Uses @1sat/actions from the 1sat-sdk. |
| disable-model-invocation | false |
Token Operations
Send, receive, list, deploy, and mint BSV21 fungible tokens using @1sat/actions.
Actions Overview
| 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 |
deployBsv21Mint | Deploy a token with fixed supply (deploy+mint) |
deployBsv21Auth | Deploy a mintable token via auth UTXOs (deploy+auth) |
mintBsv21 | Spend an auth UTXO to mint more supply or re-issue/transfer authority |
Burning tokens is not in this module. Burning is burnOrdinals in the ordinals module — see ../ordinals-marketplace/SKILL.md.
Calling Pattern
import { createContext, sendBsv21 } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const result = await sendBsv21.execute(ctx, input)
Most token actions submit to and validate against the overlay, so services is required. Two-phase signing is handled internally by executeTrackedAction / completeSignedAction — see ../action-patterns/SKILL.md for that flow.
Check Token Balances
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}`)
}
Balance Response (Bsv21Balance[])
interface Bsv21Balance {
p: string
id: string
sym?: string
icon?: string
dec: number
amt: string
all: { confirmed: bigint; pending: bigint }
listed: { confirmed: bigint; pending: bigint }
}
getBsv21Balances takes no input fields (GetBsv21BalancesInput = Record<string, never>); pass {}.
List Token UTXOs
import { listTokens, createContext } from '@1sat/actions'
const ctx = createContext(wallet)
const outputs = await listTokens.execute(ctx, { limit: 100 })
ListTokensInput is { limit?: number } (default 10000). Returns WalletOutput[] directly.
Send Tokens
import { sendBsv21, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const result = await sendBsv21.execute(ctx, {
tokenId: 'abc123...def456_0',
recipients: [
{ amount: '1000000', destination: { counterparty: '02abc...' } },
{ amount: '500000', destination: { address: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa' } },
],
})
if (result.txid) {
console.log('Sent! txid:', result.txid)
} else {
console.error('Error:', result.error)
}
Input (SendBsv21Input)
interface SendBsv21Input {
tokenId: string
recipients: SendBsv21Recipient[]
}
interface SendBsv21Recipient {
amount: bigint | string
destination: Destination
}
All token actions return TokenOperationResponse: { txid?: string; tx?: number[]; error?: string }. tx is AtomicBEEF (number[]), not hex.
How Token Sending Works
- Wallet lists all token UTXOs for the specified
tokenId
- UTXOs are batch-validated against the overlay (confirms unspent and valid)
- Sufficient UTXOs are selected to cover the requested total
- A BSV21 transfer inscription is created on each recipient's output
- If there's change, a token output returns the remainder to the sender
- An overlay processing fee is paid to the token's fund address (per token output)
- Transaction is signed, broadcast, and submitted to the overlay for indexing
Important: Token Amounts
Token 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 tokens
Purchase Tokens from Marketplace
import { purchaseBsv21, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const result = await purchaseBsv21.execute(ctx, {
tokenId: 'abc123...def456_0',
outpoint: 'txid_vout',
amount: '1000000',
marketplaceAddress: '1Market...',
marketplaceRate: 0.02,
})
PurchaseBsv21Request requires tokenId, outpoint, and amount; marketplaceAddress/marketplaceRate are optional. Requires services — the listing is an external UTXO, so the BEEF is fetched from the overlay.
Deploy a Fixed-Supply Token (deploy+mint)
deployBsv21Mint mints the entire supply in the deploy transaction. No further minting is possible — total supply is locked at deploy time.
import { deployBsv21Mint, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const result = await deployBsv21Mint.execute(ctx, {
symbol: 'MYTOKEN',
amount: '2100000000000000',
decimals: 8,
icon: 'iconTxid_0',
destination: { address: ownerAddress },
})
console.log('Deployed token:', result.tokenId)
Input (DeployBsv21MintInput) / Response (DeployBsv21Response)
interface DeployBsv21MintInput {
symbol: string
amount: bigint | string
decimals?: number
icon?: string
destination?: Destination
}
interface DeployBsv21Response {
txid?: string
tx?: number[]
tokenId?: string
error?: string
}
Deploy uses a funding intermediate then a synchronous broadcast via services.postBeef, so services is required.
Deploy a Mintable Token (deploy+auth)
deployBsv21Auth emits a single deploy+auth output that doubles as the genesis auth UTXO. Initial supply is zero — the auth holder mints supply later via mintBsv21.
import { deployBsv21Auth, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const result = await deployBsv21Auth.execute(ctx, {
symbol: 'MINTABLE',
decimals: 8,
icon: 'iconTxid_0',
destination: { address: authHolder },
})
console.log('Token ID:', result.tokenId)
console.log('Auth UTXO for future mints:', result.authOutpoint)
Input (DeployBsv21AuthInput) / Response (DeployBsv21AuthResponse)
interface DeployBsv21AuthInput {
symbol: string
decimals?: number
icon?: string
destination?: Destination
}
interface DeployBsv21AuthResponse extends DeployBsv21Response {
authOutpoint?: string
}
Mint, Re-issue, or End Minting (mintBsv21)
Spend an existing auth UTXO to mint new supply, transfer/continue authority, or permanently end minting. Requires services.
import { mintBsv21, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const result = await mintBsv21.execute(ctx, {
tokenId: 'deployTxid_0',
mint: { amount: '1000000', destination: { address: recipient } },
})
console.log('New auth UTXO:', result.authOutpoint)
await mintBsv21.execute(ctx, {
tokenId: 'deployTxid_0',
auth: { destination: { counterparty: '02other...' } },
})
await mintBsv21.execute(ctx, {
tokenId: 'deployTxid_0',
mint: { amount: '1000000', destination: { address: recipient } },
endMinting: true,
})
Input (MintBsv21Input) / Response (MintBsv21Response)
interface MintBsv21Input {
tokenId: string
mint?: { amount: bigint | string; destination: Destination }
auth?: { destination: Destination }
endMinting?: boolean
}
interface MintBsv21Response {
txid?: string
tx?: number[]
authOutpoint?: string
error?: string
}
Validation rules from source: at least one of mint/auth must be provided; endMinting without a mint returns cannot-end-minting-without-mint. mint.amount must be positive.
Token Selection (send)
When sending tokens, the SDK selects UTXOs automatically. The selection:
- Batch-validates candidates against the overlay (confirms unspent)
- Picks UTXOs until the total covers the requested amount
- Returns
insufficient-tokens if not enough validated UTXOs
Overlay Integration
BSV21 tokens require overlay validation. The services object handles this:
import { OneSatServices } from '@1sat/wallet'
const services = new OneSatServices('main')
const details = await services.bsv21.getTokenDetails(tokenId)
const valid = await services.bsv21.validateOutputs(tokenId, outpoints, { unspent: true })
Requirements
bun add @1sat/actions @1sat/wallet @1sat/templates @bsv/sdk