Global Rank · of 601 Skills
wallet-brc100-go AI Agent Skill
View Source: b-open-io/bsv-skills
MediumInstallation
npx skills add b-open-io/bsv-skills --skill wallet-brc100-go 20
Installs
BRC-100 Wallet Implementation Guide (Go)
Comprehensive guide for implementing BRC-100 conforming wallets using the go-wallet-toolbox package.
π― Quick Reference
Core Dependencies
import (
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/wallet"
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/storage"
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/services"
sdk "github.com/bsv-blockchain/go-sdk/wallet"
"github.com/bsv-blockchain/go-sdk/primitives/ec"
"github.com/bsv-blockchain/go-sdk/transaction"
)Installation
go get github.com/bsv-blockchain/go-wallet-toolbox
go get github.com/bsv-blockchain/go-sdkπ Table of Contents
- Wallet Initialization
- Transaction Operations
- Key Management
- Storage Configuration
- Certificate Operations
- Error Handling
- Production Patterns
1. Wallet Initialization
Pattern A: Simple Wallet Setup
package main
import (
"context"
"log"
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/wallet"
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/storage"
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/services"
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/defs"
sdk "github.com/bsv-blockchain/go-sdk/wallet"
"github.com/bsv-blockchain/go-sdk/primitives/ec"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func createWallet() (*wallet.Wallet, error) {
ctx := context.Background()
// 1. Generate root key (or derive from mnemonic)
rootKey, err := ec.NewPrivateKey()
if err != nil {
return nil, err
}
// 2. Create key deriver
keyDeriver := sdk.NewKeyDeriver(rootKey)
// 3. Setup SQLite storage
db, err := gorm.Open(sqlite.Open("wallet.db"), &gorm.Config{})
if err != nil {
return nil, err
}
storageOpts := &storage.Options{
Db: db,
StorageIdentityKey: rootKey.PubKey().Compressed(),
StorageName: "main-wallet",
}
walletStorage, err := storage.NewStorage(storageOpts)
if err != nil {
return nil, err
}
// 4. Setup services (mainnet)
servicesOpts := &services.Options{
Network: defs.Mainnet,
ArcURL: "https://arc.taal.com",
WocURL: "https://api.whatsonchain.com/v1/bsv/main",
}
walletServices, err := services.NewServices(ctx, servicesOpts)
if err != nil {
return nil, err
}
// 5. Create wallet
w, err := wallet.NewWallet(
ctx,
keyDeriver,
walletStorage,
wallet.WithServices(walletServices),
wallet.WithNetwork(defs.Mainnet),
)
if err != nil {
return nil, err
}
return w, nil
}Pattern B: Wallet with MySQL Storage
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func createMySQLWallet(ctx context.Context, rootKey *ec.PrivateKey) (*wallet.Wallet, error) {
// MySQL DSN
dsn := "user:password@tcp(127.0.0.1:3306)/wallet_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
storageOpts := &storage.Options{
Db: db,
StorageIdentityKey: rootKey.PubKey().Compressed(),
StorageName: "mysql-wallet",
}
walletStorage, err := storage.NewStorage(storageOpts)
if err != nil {
return nil, err
}
keyDeriver := sdk.NewKeyDeriver(rootKey)
walletServices, err := services.NewServices(ctx, &services.Options{
Network: defs.Mainnet,
ArcURL: "https://arc.taal.com",
})
if err != nil {
return nil, err
}
return wallet.NewWallet(
ctx,
keyDeriver,
walletStorage,
wallet.WithServices(walletServices),
wallet.WithNetwork(defs.Mainnet),
)
}Pattern C: Testnet Wallet
func createTestnetWallet(ctx context.Context) (*wallet.Wallet, error) {
rootKey, _ := ec.NewPrivateKey()
keyDeriver := sdk.NewKeyDeriver(rootKey)
db, _ := gorm.Open(sqlite.Open("testnet.db"), &gorm.Config{})
storageOpts := &storage.Options{
Db: db,
StorageIdentityKey: rootKey.PubKey().Compressed(),
StorageName: "testnet-wallet",
}
walletStorage, _ := storage.NewStorage(storageOpts)
// Testnet services
walletServices, err := services.NewServices(ctx, &services.Options{
Network: defs.Testnet,
ArcURL: "https://arc-test.taal.com",
WocURL: "https://api.whatsonchain.com/v1/bsv/test",
})
if err != nil {
return nil, err
}
return wallet.NewWallet(
ctx,
keyDeriver,
walletStorage,
wallet.WithServices(walletServices),
wallet.WithNetwork(defs.Testnet),
)
}2. Transaction Operations
Create a Transaction
import (
"github.com/bsv-blockchain/go-sdk/script"
sdk "github.com/bsv-blockchain/go-sdk/wallet"
)
func sendBSV(
ctx context.Context,
w *wallet.Wallet,
recipientAddress string,
satoshis uint64,
) (string, error) {
// Build locking script from address
lockingScript, err := script.NewP2PKHFromAddress(recipientAddress)
if err != nil {
return "", err
}
// Create action args
args := &sdk.CreateActionArgs{
Description: "Send BSV payment",
Outputs: []sdk.CreateActionOutput{
{
LockingScript: lockingScript.String(),
Satoshis: satoshis,
OutputDescription: fmt.Sprintf("Payment to %s", recipientAddress),
Basket: "default",
Tags: []string{"payment"},
},
},
Options: &sdk.CreateActionOptions{
AcceptDelayedBroadcast: false, // Broadcast immediately
RandomizeOutputs: true, // Privacy
},
}
// Create action
result, err := w.CreateAction(ctx, args)
if err != nil {
return "", err
}
if result.TxID != nil {
return *result.TxID, nil
}
// Handle signing if needed
if result.SignableTransaction != nil {
return signAndFinalize(ctx, w, result.SignableTransaction)
}
return "", fmt.Errorf("unexpected result format")
}Sign a Transaction
func signTransaction(
ctx context.Context,
w *wallet.Wallet,
reference string,
unlockingScripts map[uint32]sdk.UnlockingScriptSend,
) (*sdk.SignActionResult, error) {
args := &sdk.SignActionArgs{
Reference: reference,
Spends: unlockingScripts,
}
result, err := w.SignAction(ctx, args)
if err != nil {
return nil, err
}
return result, nil
}Check Wallet Balance
func getBalance(ctx context.Context, w *wallet.Wallet) (uint64, error) {
// Use special operation for quick balance
args := &sdk.ListOutputsArgs{
Basket: "00000000000000000000000000000000", // Special basket for balance
}
result, err := w.ListOutputs(ctx, args)
if err != nil {
return 0, err
}
return uint64(result.TotalOutputs), nil
}
// Get detailed balance with UTXOs
func getDetailedBalance(ctx context.Context, w *wallet.Wallet) (*sdk.ListOutputsResult, error) {
args := &sdk.ListOutputsArgs{
Basket: "default",
Spendable: to.Ptr(true),
Limit: to.Ptr(uint32(100)),
Offset: to.Ptr(uint32(0)),
}
result, err := w.ListOutputs(ctx, args)
if err != nil {
return nil, err
}
log.Printf("Found %d spendable outputs", len(result.Outputs))
for _, output := range result.Outputs {
log.Printf(" %s: %d satoshis", output.Outpoint, output.Satoshis)
}
return result, nil
}List Transaction History
func listTransactions(ctx context.Context, w *wallet.Wallet) (*sdk.ListActionsResult, error) {
args := &sdk.ListActionsArgs{
Labels: []string{},
LabelQueryMode: to.Ptr(sdk.LabelQueryModeAny),
Limit: to.Ptr(uint32(50)),
Offset: to.Ptr(uint32(0)),
}
result, err := w.ListActions(ctx, args)
if err != nil {
return nil, err
}
log.Printf("Found %d actions", result.TotalActions)
for _, action := range result.Actions {
log.Printf(" %s: %s - %s", *action.TxID, action.Status, action.Description)
}
return result, nil
}3. Key Management
Get Public Key
// Get identity key
func getIdentityKey(ctx context.Context, w *wallet.Wallet) (string, error) {
args := &sdk.GetPublicKeyArgs{
IdentityKey: to.Ptr(true),
}
result, err := w.GetPublicKey(ctx, args)
if err != nil {
return "", err
}
return result.PublicKey, nil
}
// Get derived key for protocol
func getDerivedKey(
ctx context.Context,
w *wallet.Wallet,
protocolID sdk.ProtocolID,
keyID string,
counterparty string,
) (string, error) {
args := &sdk.GetPublicKeyArgs{
ProtocolID: protocolID,
KeyID: keyID,
Counterparty: to.Ptr(counterparty),
}
result, err := w.GetPublicKey(ctx, args)
if err != nil {
return "", err
}
return result.PublicKey, nil
}Encrypt/Decrypt Data
import "encoding/base64"
func encryptMessage(
ctx context.Context,
w *wallet.Wallet,
plaintext string,
recipientPubKey string,
) (string, error) {
args := &sdk.WalletEncryptArgs{
Plaintext: []byte(plaintext),
ProtocolID: sdk.ProtocolID{2, "secure-messaging"},
KeyID: "msg-key",
Counterparty: to.Ptr(recipientPubKey),
}
result, err := w.Encrypt(ctx, args)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(result.Ciphertext), nil
}
func decryptMessage(
ctx context.Context,
w *wallet.Wallet,
ciphertext string,
senderPubKey string,
) (string, error) {
ciphertextBytes, err := base64.StdEncoding.DecodeString(ciphertext)
if err != nil {
return "", err
}
args := &sdk.WalletDecryptArgs{
Ciphertext: ciphertextBytes,
ProtocolID: sdk.ProtocolID{2, "secure-messaging"},
KeyID: "msg-key",
Counterparty: to.Ptr(senderPubKey),
}
result, err := w.Decrypt(ctx, args)
if err != nil {
return "", err
}
return string(result.Plaintext), nil
}Create Signature
func signData(ctx context.Context, w *wallet.Wallet, data string) (string, error) {
args := &sdk.CreateSignatureArgs{
Data: []byte(data),
ProtocolID: sdk.ProtocolID{2, "document-signing"},
KeyID: "sig-key",
Counterparty: to.Ptr("self"),
}
result, err := w.CreateSignature(ctx, args)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(result.Signature), nil
}4. Storage Configuration
SQLite Storage
import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func setupSQLiteStorage(dbPath string, identityKey []byte) (*storage.Storage, error) {
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
if err != nil {
return nil, err
}
opts := &storage.Options{
Db: db,
StorageIdentityKey: identityKey,
StorageName: "sqlite-wallet",
}
return storage.NewStorage(opts)
}MySQL Storage
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func setupMySQLStorage(dsn string, identityKey []byte) (*storage.Storage, error) {
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
opts := &storage.Options{
Db: db,
StorageIdentityKey: identityKey,
StorageName: "mysql-wallet",
}
return storage.NewStorage(opts)
}PostgreSQL Storage
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func setupPostgresStorage(dsn string, identityKey []byte) (*storage.Storage, error) {
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
opts := &storage.Options{
Db: db,
StorageIdentityKey: identityKey,
StorageName: "postgres-wallet",
}
return storage.NewStorage(opts)
}5. Certificate Operations
Acquire Certificate (Direct Protocol)
func acquireDirectCertificate(
ctx context.Context,
w *wallet.Wallet,
certType string,
certifier string,
) (*sdk.AcquireCertificateResult, error) {
identityKey, _ := getIdentityKey(ctx, w)
args := &sdk.AcquireCertificateArgs{
AcquisitionProtocol: sdk.AcquisitionProtocolDirect,
Type: certType,
Certifier: certifier,
SerialNumber: []byte("cert-serial-123"),
Subject: identityKey,
RevocationOutpoint: "txid.vout",
Fields: map[string][]byte{
"name": []byte("Alice Smith"),
"email": []byte("alice@example.com"),
},
KeyringForSubject: map[string]string{
// Master keyring data
},
Signature: []byte("signature-bytes"),
}
return w.AcquireCertificate(ctx, args)
}Acquire Certificate (Issuance Protocol)
func requestCertificateIssuance(
ctx context.Context,
w *wallet.Wallet,
) (*sdk.AcquireCertificateResult, error) {
args := &sdk.AcquireCertificateArgs{
AcquisitionProtocol: sdk.AcquisitionProtocolIssuance,
Type: "https://example.com/kyc-certificate",
Certifier: "certifier-identity-key",
CertifierURL: to.Ptr("https://certifier.example.com"),
Fields: map[string]interface{}{
"name": "Alice Smith",
"birthdate": "1990-01-01",
"country": "US",
},
}
return w.AcquireCertificate(ctx, args)
}List Certificates
func listCertificates(ctx context.Context, w *wallet.Wallet) (*sdk.ListCertificatesResult, error) {
args := &sdk.ListCertificatesArgs{
Certifiers: []string{"certifier-identity-key"},
Types: []string{"https://example.com/user-certificate"},
Limit: to.Ptr(uint32(50)),
Offset: to.Ptr(uint32(0)),
}
result, err := w.ListCertificates(ctx, args)
if err != nil {
return nil, err
}
log.Printf("Found %d certificates", result.TotalCertificates)
for _, cert := range result.Certificates {
log.Printf(" Type: %s, Certifier: %s", cert.Type, cert.Certifier)
}
return result, nil
}Prove Certificate
func proveCertificate(
ctx context.Context,
w *wallet.Wallet,
certificateID string,
) (*sdk.ProveCertificateResult, error) {
args := &sdk.ProveCertificateArgs{
CertificateID: certificateID,
FieldsToReveal: []string{"name", "email"},
Verifier: "verifier-identity-key",
Privileged: to.Ptr(false),
}
return w.ProveCertificate(ctx, args)
}6. Error Handling
Standard Error Handling
import (
"errors"
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/defs"
)
func handleWalletError(err error) {
if err == nil {
return
}
// Check for specific error types
var invalidParamErr *defs.ErrInvalidParameter
if errors.As(err, &invalidParamErr) {
log.Printf("Invalid parameter: %s - %s", invalidParamErr.Parameter, invalidParamErr.Message)
return
}
var internalErr *defs.ErrInternal
if errors.As(err, &internalErr) {
log.Printf("Internal error: %s", internalErr.Message)
return
}
// Generic error
log.Printf("Wallet error: %v", err)
}Transaction Error Handling
func sendWithErrorHandling(
ctx context.Context,
w *wallet.Wallet,
recipient string,
satoshis uint64,
) (string, error) {
txid, err := sendBSV(ctx, w, recipient, satoshis)
if err != nil {
handleWalletError(err)
// Check if it's a review actions error
if strings.Contains(err.Error(), "review") {
log.Printf("Transaction requires review")
// Handle review flow
}
return "", err
}
log.Printf("Transaction sent: %s", txid)
return txid, nil
}7. Production Patterns
Wallet Manager Pattern
type WalletManager struct {
wallet *wallet.Wallet
storage *storage.Storage
mu sync.RWMutex
}
func NewWalletManager(ctx context.Context, rootKey *ec.PrivateKey) (*WalletManager, error) {
db, err := gorm.Open(sqlite.Open("wallet.db"), &gorm.Config{})
if err != nil {
return nil, err
}
storageOpts := &storage.Options{
Db: db,
StorageIdentityKey: rootKey.PubKey().Compressed(),
StorageName: "managed-wallet",
}
walletStorage, err := storage.NewStorage(storageOpts)
if err != nil {
return nil, err
}
keyDeriver := sdk.NewKeyDeriver(rootKey)
walletServices, err := services.NewServices(ctx, &services.Options{
Network: defs.Mainnet,
ArcURL: "https://arc.taal.com",
})
if err != nil {
return nil, err
}
w, err := wallet.NewWallet(
ctx,
keyDeriver,
walletStorage,
wallet.WithServices(walletServices),
wallet.WithNetwork(defs.Mainnet),
)
if err != nil {
return nil, err
}
return &WalletManager{
wallet: w,
storage: walletStorage,
}, nil
}
func (wm *WalletManager) GetWallet() *wallet.Wallet {
wm.mu.RLock()
defer wm.mu.RUnlock()
return wm.wallet
}
func (wm *WalletManager) Close() error {
wm.mu.Lock()
defer wm.mu.Unlock()
// Cleanup resources
return nil
}Retry Pattern
func sendWithRetry(
ctx context.Context,
w *wallet.Wallet,
recipient string,
satoshis uint64,
maxRetries int,
) (string, error) {
var lastErr error
for attempt := 1; attempt <= maxRetries; attempt++ {
txid, err := sendBSV(ctx, w, recipient, satoshis)
if err == nil {
return txid, nil
}
lastErr = err
log.Printf("Attempt %d failed: %v", attempt, err)
if attempt < maxRetries {
time.Sleep(time.Second * time.Duration(attempt))
}
}
return "", fmt.Errorf("all %d retries failed: %w", maxRetries, lastErr)
}Background Monitor Pattern
import "github.com/bsv-blockchain/go-wallet-toolbox/pkg/monitor"
func setupMonitor(
ctx context.Context,
walletStorage *storage.Storage,
walletServices *services.WalletServices,
) (*monitor.Monitor, error) {
opts := &monitor.Options{
Storage: walletStorage,
Services: walletServices,
Network: defs.Mainnet,
}
m, err := monitor.NewMonitor(ctx, opts)
if err != nil {
return nil, err
}
// Start monitoring in background
go func() {
if err := m.Start(ctx); err != nil {
log.Printf("Monitor error: %v", err)
}
}()
return m, nil
}π Additional Resources
- BRC-100 Specification: https://bsv.brc.dev/wallet/0100
- Go Wallet Toolbox: https://github.com/bsv-blockchain/go-wallet-toolbox
- Go SDK: https://github.com/bsv-blockchain/go-sdk
- Examples: https://github.com/bsv-blockchain/go-wallet-toolbox/tree/master/examples
- Storage Server: https://github.com/bsv-blockchain/go-wallet-toolbox/blob/master/docs/storage_server.md
π Common Patterns Summary
| Task | Function | Key Args |
|---|---|---|
| Send BSV | CreateAction() |
Outputs, Options |
| Check balance | ListOutputs() |
Special basket |
| List UTXOs | ListOutputs() |
Basket, Spendable |
| Get history | ListActions() |
Labels, Limit |
| Get pubkey | GetPublicKey() |
ProtocolID, KeyID |
| Encrypt data | Encrypt() |
Plaintext, Counterparty |
| Get certificate | AcquireCertificate() |
Type, Certifier |
Remember: Always use context for cancellation, handle errors explicitly, and follow Go best practices for concurrent wallet access!
Installs
Security Audit
View Source
b-open-io/bsv-skills
More from this source
Power your AI Agents with
the best open-source models.
Drop-in OpenAI-compatible API. No data leaves Europe.
Explore Inference APIGLM
GLM 5
$1.00 / $3.20
per M tokens
Kimi
Kimi K2.5
$0.60 / $2.80
per M tokens
MiniMax
MiniMax M2.5
$0.30 / $1.20
per M tokens
Qwen
Qwen3.5 122B
$0.40 / $3.00
per M tokens
How to use this skill
Install wallet-brc100-go by running npx skills add b-open-io/bsv-skills --skill wallet-brc100-go in your project directory. Run the install command above in your project directory. The skill file will be downloaded from GitHub and placed in your project.
No configuration needed. Your AI agent (Claude Code, Cursor, Windsurf, etc.) automatically detects installed skills and uses them as context when generating code.
The skill enhances your agent's understanding of wallet-brc100-go, helping it follow established patterns, avoid common mistakes, and produce production-ready output.
What you get
Skills are plain-text instruction files β not executable code. They encode expert knowledge about frameworks, languages, or tools that your AI agent reads to improve its output. This means zero runtime overhead, no dependency conflicts, and full transparency: you can read and review every instruction before installing.
Compatibility
This skill works with any AI coding agent that supports the skills.sh format, including Claude Code (Anthropic), Cursor, Windsurf, Cline, Aider, and other tools that read project-level context files. Skills are framework-agnostic at the transport level β the content inside determines which language or framework it applies to.
Chat with 100+ AI Models in one App.
Use Claude, ChatGPT, Gemini alongside with EU-Hosted Models like Deepseek, GLM-5, Kimi K2.5 and many more.