npm package: @kaspafy/wallet-core
Description: 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.
Author: Kaspero Labs
Latest version: 0.2.2

# @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.

```javascript
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.

```javascript
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.

```javascript
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.

```javascript
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.

```javascript
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.

```javascript
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.

```javascript
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.

```javascript
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

```bash
npm install @kaspafy/wallet-core
```

Use alongside (not instead of) the Kaspa SDK:
```bash
npm install @kaspa/wallet @kaspa/grpc-node
```

### WASM 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:
1. Downloads `kaspa-wasm32-sdk-latest.zip` from Aspectron's nightly builds
2. Extracts the `nodejs/kaspa/` files
3. Copies and renames them into `node_modules/kaspa/kaspa/`
4. Verifies the setup works

If `kaspa` isn't installed (you only need the REST-based modules), the script skips silently.

To re-run manually:
```bash
npx kaspafy-setup-wasm
# or
npm run setup-wasm
```

## Architecture

```
@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.json
```

## Source 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](./docs/API.md)

## Support

Questions, bugs, feature requests:
- Twitter/X: [@KasperoLabs](https://x.com/KasperoLabs)
- Email: kasperolabs@gmail.com

## License

MIT © Kaspero Labs