Cashu playground

Run cashu-ts flows and see what actually changes.

Choose a flow, run it against a test mint, and compare the request, response, wallet state, and cashu-ts code. Test shortcuts are labelled where they appear.

Primary mint: https://nofee.testnut.cashu.spaceFallback: https://testnut.cashu.spaceLive calls and test-only steps are labelled in the timeline.Open starter kit

How it works

Start here.

1. Choose a flowUse the recipe cards below. Create wallet is the clean first run.
2. Press runThe timeline shows each request and response.
3. Copy the codeThe code panel shows the cashu-ts calls and the state your app owns.

Choose a flow

These cards are clickable.

Inputs

Receive token

Mint: https://nofee.testnut.cashu.space

Mint a fresh token in the background, receive it live, then store receiver proofs.

Ready. Run this to mint a fresh token, receive it live, and inspect receiver-side proofs.

Code

cashu-ts code

receive-token.ts

Kept close to the cashu-ts docs. Add your own storage functions where shown.

import { Wallet } from '@cashu/cashu-ts'

const mintUrl = "https://nofee.testnut.cashu.space"
const token = "cashueyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vbm9mZWUudGVzdG51dC5jYXNodS5zcGFjZSIsInByb29mcyI6W3siYW1vdW50Ijo4LCJpZCI6IjAwOWExZjI5MzI1M2U0MWUiLCJzZWNyZXQiOiJkZW1vLXNlbmQtMSIsIkMiOiIwMnNlbmQxIn0seyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImRlbW8tc2VuZC0yIiwiQyI6IjAyc2VuZDIifSx7ImFtb3VudCI6MSwiaWQiOiIwMDlhMWYyOTMyNTNlNDFlIiwic2VjcmV0IjoiZGVtby1zZW5kLTMiLCJDIjoiMDJzZW5kMyJ9XX1dfQ"

const wallet = new Wallet(mintUrl)
await wallet.loadMint()

const proofs = await wallet.ops.receive(token).run()
await saveProofs(proofs) // your app persistence function

Workings

Request / response timeline

4 steps

Step 1

Accept pasted token

EXAMPLE

LOCAL receiveToken(token)

When run with the default token, the API mints a fresh live token first and uses that instead of this static example.

Request

{
  "token": "cashueyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vbm9mZWUudGVzdG51dC5jYXNodS5zcGFjZSIsInByb29mcyI6W3siYW1vdW50Ijo4LCJpZCI6IjAwOWExZjI5MzI1M2U0MWUiLCJzZWNyZXQiOiJkZW1vLXNlbmQtMSIsIkMiOiIwMnNlbmQxIn0seyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImRlbW8tc2VuZC0yIiwiQyI6IjAyc2VuZDIifSx7ImFtb3VudCI6MSwiaWQiOiIwMDlhMWYyOTMyNTNlNDFlIiwic2VjcmV0IjoiZGVtby1zZW5kLTMiLCJDIjoiMDJzZW5kMyJ9XX1dfQ"
}

Response

{
  "accepted": true,
  "prefix": "cashu",
  "length": 391
}

Step 2

Decode token payload

EXAMPLE

LOCAL JSON.parse(base64url(token))

The token carries the mint URL and proofs. This step exposes both before import.

Request

{
  "token": "cashueyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vbm9mZWUudGVzdG51dC5jYXNodS5zcGFjZSIsInByb29mcyI6W3siYW1vdW50Ijo4LCJpZCI6IjAwOWExZjI5MzI1M2U0MWUiLCJzZWNyZXQiOiJkZW1vLXNlbmQtMSIsIkMiOiIwMnNlbmQxIn0seyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImRlbW8tc2VuZC0yIiwiQyI6IjAyc2VuZDIifSx7ImFtb3VudCI6MSwiaWQiOiIwMDlhMWYyOTMyNTNlNDFlIiwic2VjcmV0IjoiZGVtby1zZW5kLTMiLCJDIjoiMDJzZW5kMyJ9XX1dfQ"
}

Decoded mint

{
  "mint": "https://nofee.testnut.cashu.space"
}

Decoded proofs

[
  {
    "amount": 8,
    "id": "009a1f293253e41e",
    "secret": "demo-send-1",
    "C": "02send1"
  },
  {
    "amount": 2,
    "id": "009a1f293253e41e",
    "secret": "demo-send-2",
    "C": "02send2"
  },
  {
    "amount": 1,
    "id": "009a1f293253e41e",
    "secret": "demo-send-3",
    "C": "02send3"
  }
]

Step 3

Validate token shape

EXAMPLE

LOCAL validateProofs()

Before import, check the decoded mint and proof fields against what the receiver expected.

Request

{
  "expectedMint": "https://nofee.testnut.cashu.space",
  "decodedMint": "https://nofee.testnut.cashu.space"
}

Mint check

{
  "expectedMint": "https://nofee.testnut.cashu.space",
  "decodedMint": "https://nofee.testnut.cashu.space",
  "mintMatchesExpected": true
}

Proof summary

{
  "proofCount": 3,
  "totalAmount": 11
}

Step 4

Receive live token

READY

LOCAL wallet.receive()

When run, cashu-ts spends the token proofs at the mint and returns fresh receiver-owned proofs.

Request

{
  "proofs": [
    {
      "amount": 8,
      "id": "009a1f293253e41e",
      "secret": "demo-send-1",
      "C": "02send1"
    },
    {
      "amount": 2,
      "id": "009a1f293253e41e",
      "secret": "demo-send-2",
      "C": "02send2"
    },
    {
      "amount": 1,
      "id": "009a1f293253e41e",
      "secret": "demo-send-3",
      "C": "02send3"
    }
  ]
}

Response

{
  "balance": 11,
  "proofs": [
    {
      "amount": 8,
      "id": "009a1f293253e41e",
      "secret": "demo-send-1",
      "C": "02send1"
    },
    {
      "amount": 2,
      "id": "009a1f293253e41e",
      "secret": "demo-send-2",
      "C": "02send2"
    },
    {
      "amount": 1,
      "id": "009a1f293253e41e",
      "secret": "demo-send-3",
      "C": "02send3"
    }
  ]
}

State

Before / after

Token output

Decoded mint

https://nofee.testnut.cashu.spaceExpected https://nofee.testnut.cashu.space.

Mint check

MatchesShape validation happens before proofs are imported.

Imported proofs

8 sat + 2 sat + 1 sat3 proofs moved into the receiver wallet.

Receiver balance

11 satWallet goes from 0 sat to 11 sat.

Before summary

{
  "balance": 0,
  "expectedMint": "https://nofee.testnut.cashu.space"
}

After summary

{
  "balance": 11,
  "expectedMint": "https://nofee.testnut.cashu.space",
  "decodedMint": "https://nofee.testnut.cashu.space",
  "importedAmount": 11,
  "importedProofs": [
    8,
    2,
    1
  ]
}

Cashu token

cashueyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vbm9mZWUudGVzdG51dC5jYXNodS5zcGFjZSIsInByb29mcyI6W3siYW1vdW50Ijo4LCJpZCI6IjAwOWExZjI5MzI1M2U0MWUiLCJzZWNyZXQiOiJkZW1vLXNlbmQtMSIsIkMiOiIwMnNlbmQxIn0seyJhbW91bnQiOjIsImlkIjoiMDA5YTFmMjkzMjUzZTQxZSIsInNlY3JldCI6ImRlbW8tc2VuZC0yIiwiQyI6IjAyc2VuZDIifSx7ImFtb3VudCI6MSwiaWQiOiIwMDlhMWYyOTMyNTNlNDFlIiwic2VjcmV0IjoiZGVtby1zZW5kLTMiLCJDIjoiMDJzZW5kMyJ9XX1dfQ