@kaspafy/wallet-core
v0.2.2
Published
High-level Kaspa SDK — KNS-aware sending, unified activity stream, real-time subscriptions, smart UTXO management, payload decoding, and multi-asset portfolio. The glue layer between raw Kaspa SDKs and production apps.
Maintainers
Readme
@kaspafy/wallet-core v0.2
The glue layer between raw Kaspa SDKs and production apps.
This package does NOT re-wrap @kaspa/wallet. If you need createWallet(), sendTransaction(), or getBalance(), use the Kaspa SDK directly. It's good at those things.
This package provides the stuff that doesn't exist anywhere else — the production-learned patterns, multi-API orchestration, and protocol implementations that took 2 years of running Kaspero on mainnet to get right.
What's Inside (and why it matters)
SmartSend — KNS-aware sending with auto-compound
The raw SDK makes you: resolve KNS → validate → check UTXOs → maybe compound → build TX → retry fees → verify. SmartSend does it in one call.
const { SmartSend } = require('@kaspafy/wallet-core');
const sender = new SmartSend({ wallet, rpc, mnemonic });
// Send to a .kas domain — auto-resolves, auto-compounds if needed
const result = await sender.send('alice.kas', 10.5, {
memo: 'Coffee payment',
verify: true, // poll until confirmed
autoCompound: true, // fix fragmented UTXOs before sending
});
// result.resolvedAddress → 'kaspa:qz...'
// result.resolvedDomain → 'alice.kas'
// result.confirmed → true
// result.compounded → true (if UTXOs were fragmented)ActivityStream — unified timeline
No single API gives you everything that happened on an address. You need Kaspa REST for KAS txs, Kasplex for KRC-20 ops, and payload decoding. This merges, deduplicates, and classifies them.
const { ActivityStream } = require('@kaspafy/wallet-core');
const stream = new ActivityStream();
const activity = await stream.fetch('kaspa:qz...', { limit: 100 });
// Returns chronologically sorted array:
// [
// { type: 'kas-receive', amountKas: 50.0, from: 'kaspa:qp...' },
// { type: 'krc20-transfer', token: 'BUNNY', tokenAmount: '1000' },
// { type: 'payload-document', payload: { type: 'kaspanotary-manifest', ... } },
// { type: 'kas-send', amountKas: -10.5, to: 'kaspa:qr...' },
// ]Decoder — classify any transaction
Given a txId or raw payload hex, detect whether it's a kaspanotary document, KRC-20 inscription, plain text memo, structured data, SHA-256 hash, or raw binary.
const { Decoder } = require('@kaspafy/wallet-core');
const decoder = new Decoder();
// From raw payload hex
const result = decoder.decode(payloadHex);
// → { type: 'kaspanotary-manifest', data: { fileHash, chunkTxIds, ... } }
// → { type: 'krc20-inscription', data: { p: 'krc-20', op: 'transfer', tick: 'bunny', ... } }
// → { type: 'text', data: { text: 'Invoice #1234' } }
// → { type: 'hash', data: { hash: 'a1b2c3...' } }
// Or fetch + decode in one call
const { tx, decoded } = await decoder.fetchAndDecode('abc123...txid');UtxoDoctor — prevent failed sends
Kaspa's UTXO model fragments wallets over time. When you hit 100+ UTXOs, sends fail with cryptic mass errors. The raw SDK gives zero warning. This catches it before your users see an error.
const { UtxoDoctor } = require('@kaspafy/wallet-core');
const doctor = new UtxoDoctor({ wallet, rpc, mnemonic });
// Diagnose
const health = await doctor.diagnose();
// → { health: 'fragmented', utxoCount: 147, needsCompound: true,
// recommendation: '147 UTXOs — moderately fragmented...',
// maxSendableKas: 245.5, compoundRounds: 2 }
// Fix it
const result = await doctor.compact('kaspa:qz...my-address', {
onRound: (n, txId) => console.log(`Round ${n}: ${txId}`),
});
// Runs multiple rounds until health is 'healthy'Portfolio — what do I own and what's it worth?
Aggregates KAS balance + all KRC-20 tokens (with correct per-token decimal conversion) + KNS domains + fiat valuation in one call across 3 APIs.
const { Portfolio } = require('@kaspafy/wallet-core');
const portfolio = new Portfolio({ fiatCurrency: 'eur' });
const holdings = await portfolio.get('kaspa:qz...');
// → {
// kas: { balance: 1250.5, fiatValue: 162.57 },
// tokens: [
// { tick: 'BUNNY', balanceHuman: 50000, decimals: 8 },
// { tick: 'NACHO', balanceHuman: 1200, decimals: 8 },
// ],
// domains: [
// { domain: 'alice.kas', isVerified: true },
// ],
// totals: { fiatValue: 162.57, assetCount: 4 },
// kasPrice: 0.13,
// }Subscriptions — real-time push events
The raw WASM SDK gives you RpcClient.subscribeUtxosChanged() and nested address objects. This gives you watch(address, callback). Handles reconnection, batch subscribe, debouncing rapid UTXO floods, and virtual-chain-changed for instant TX confirmation.
const { Subscriptions } = require('@kaspafy/wallet-core');
const subs = new Subscriptions({
borshUrl: 'ws://your-node:17110',
});
await subs.connect();
// Watch an address
await subs.watch('kaspa:qz...', (address, event) => {
console.log('Balance changed!', event.added.length, 'new UTXOs');
});
// Watch many at once
await subs.watchBatch([
{ address: 'kaspa:qz...', callback: handleUser1 },
{ address: 'kaspa:qp...', callback: handleUser2 },
]);
// Get notified when TXs are accepted into the virtual chain
subs.on('confirmations', (txIds) => {
console.log('Confirmed:', txIds.length, 'transactions');
});payloads — full file embedding protocol
The kaspanotary protocol for embedding entire files on the BlockDAG. 22KB chunks, binary headers, UTXO chaining for rapid sequential submission, manifest with SHA-256 verification, file reconstruction from a single TX ID. ~476 lines of battle-tested protocol code that doesn't exist in any other Kaspa package.
const { payloads } = require('@kaspafy/wallet-core');
// Embed a file
const result = await payloads.embedDocument(wallet, rpc, pdfBuffer, {
fileName: 'contract.pdf',
fileType: 'application/pdf',
title: 'Service Agreement',
creatorAddress: 'kaspa:qz...',
}, {
onChunkSubmitted: (i, total, txId) => console.log(`${i+1}/${total}`),
});
// → { manifestTxId: '...', chunkTxIds: [...], fileHash: '...' }
// Reconstruct from manifest (read-only, no wallet needed)
const doc = await payloads.reconstructDocument(result.manifestTxId);
// → { file: Buffer, verified: true, manifest: {...} }
// Cost estimate before committing
payloads.estimateCost(fileBuffer.length);
// → { chunkCount: 3, totalTxs: 4, feeKas: 0.00105 }providers — unified browser wallets
KasWare, Kastle, and Keystone each have different method names, connection flows, and response shapes. This normalizes them.
const { providers } = require('@kaspafy/wallet-core');
// Auto-detect and connect to whatever's installed
const conn = await providers.autoConnect();
// → { address: 'kaspa:qz...', provider: 'kasware' }
// Or check what's available
providers.detectAll();
// → ['kasware', 'kastle']Install
npm install @kaspafy/wallet-coreUse alongside (not instead of) the Kaspa SDK:
npm install @kaspa/wallet @kaspa/grpc-nodeWASM Setup (automatic)
The kaspa npm package is a broken shim — it requires a WASM binary that isn't included. If you've ever had to manually download the nightly zip, extract it, copy files, and rename kaspa.js → kaspa_wasm.js... that's what this fixes.
When you npm install @kaspafy/wallet-core, the postinstall script automatically:
- Downloads
kaspa-wasm32-sdk-latest.zipfrom Aspectron's nightly builds - Extracts the
nodejs/kaspa/files - Copies and renames them into
node_modules/kaspa/kaspa/ - Verifies the setup works
If kaspa isn't installed (you only need the REST-based modules), the script skips silently.
To re-run manually:
npx kaspafy-setup-wasm
# or
npm run setup-wasmArchitecture
@kaspafy/wallet-core
├── src/
│ ├── index.js # Exports only value-adds, not wrappers
│ ├── smartSend.js # KNS-aware, auto-compound, verify
│ ├── activity.js # Unified KAS + KRC-20 + payload timeline
│ ├── decoder.js # Classify any TX payload automatically
│ ├── utxoDoctor.js # Diagnose fragmentation, auto-compact
│ ├── portfolio.js # Multi-asset balance + fiat valuation
│ ├── subscriptions.js # Borsh wRPC push with clean callback API
│ ├── payloads.js # File embedding protocol (kaspanotary)
│ ├── kns.js # Minimal resolver (used by SmartSend)
│ └── providers/
│ └── index.js # Unified KasWare/Kastle/Keystone adapter
└── package.jsonSource Lineage
Extracted from production code running on Kaspa mainnet since 2024:
| Module | Derived From | What It Replaces | |---|---|---| | smartSend | services_kaspaNode.js + knsResolver.js | 3 manual SDK calls + 2 API calls | | activity | kaspaService.js + Kasplex API + decoder | Querying 3 APIs and merging yourself | | decoder | services_notaryChain.js payload parsing | Manual hex inspection | | utxoDoctor | compound logic from services_kaspaNode.js | Cryptic "mass exceeded" errors | | portfolio | currencyService.js + Kasplex + KNS APIs | 3+ API calls with unit math | | subscriptions | services_kaspaSubscriptions.js (617 lines) | Rolling your own reconnection/debounce | | payloads | services_notaryChain.js (736 lines) | Nothing — this protocol is unique |
API Reference
Full parameter-by-parameter reference with return types and edge cases: docs/API.md
Support
Questions, bugs, feature requests:
- Twitter/X: @KasperoLabs
- Email: [email protected]
License
MIT © Kaspero Labs
