1 / install
Install the library
npm install @cashu/cashu-ts
Starter kit
Start with one test mint, one local wallet state model, the five cashu-ts flows most apps need, and a 60-second no-signup paid-download demo using a fresh test token.
Hosted example
No signup, no account setup: mint a fresh 21 sat test token, paste it into the hosted demo, and see the server record a short-lived grant before returning the sample file link.
1 / install
npm install @cashu/cashu-ts
2 / wallet
Make the mint and unit explicit. Production apps should treat mint choice as a product trust decision.
import { Wallet } from '@cashu/cashu-ts'
const mintUrl = 'https://nofee.testnut.cashu.space'
const wallet = new Wallet(mintUrl, { unit: 'sat' })
await wallet.loadMint()
const mintInfo = wallet.getMintInfo()
const activeKeyset = wallet.keyChain.cache.keysets.find(
(keyset) => keyset.unit === 'sat' && keyset.active,
)3 / state
Proofs are bearer money. Track spendable, pending, and spent states so refreshes and retries do not lose value.
type StoredProof = {
amount: number
id: string
secret: string
C: string
state: 'spendable' | 'pending' | 'spent'
}
type WalletRecord = {
mintUrl: string
unit: 'sat'
keysetId?: string
proofs: StoredProof[]
}
const sumBalance = (proofs: StoredProof[]) =>
proofs.filter((proof) => proof.state === 'spendable').reduce((sum, proof) => sum + proof.amount, 0)4 / app pattern
A small useful pattern: buyer pays sats, the app records the payment, then returns a short-lived download URL.
type DigitalAsset = {
id: string
title: string
priceSats: number
fileKey: string
}
type UnlockGrant = {
id: string
assetId: string
amountSats: number
mintUrl: string
paymentRef: string
expiresAt: string
}
async function createUnlockGrant(asset: DigitalAsset, paymentRef: string): Promise<UnlockGrant> {
return {
id: crypto.randomUUID(),
assetId: asset.id,
amountSats: asset.priceSats,
mintUrl: 'https://nofee.testnut.cashu.space',
paymentRef,
expiresAt: new Date(Date.now() + 10 * 60 * 1000).toISOString(),
}
}Build order