Cashu.me Wallet

repository·main·Indexed 42 days ago

https://github.com/cashubtc/cashu.me

A free and open-source progressive web app (PWA) wallet for the Cashu ecash protocol. It enables private sending and receiving of funds using Chaumian ecash, featuring support for Lightning, multi-mint payments, and Nostr integration. The project is built with Vue, Quasar, and Capacitor for mobile (Android/iOS) and Electron for desktop deployment.

Tokens
13.8K
Snippets
59
Records
68
Agent score
53%

What's inside Cashu.me

  1. Quick start with Docker

    main

    The fastest way to run a local instance of Cashu.me is using Docker Compose. This will set up the wallet environment locally.

    git clone https://github.com/cashubtc/cashu.me.git
    cd cashu.me
    docker compose up -d
  2. Configure self-hosting behind a reverse proxy

    main

    Because Cashu.me is a Single Page Application (SPA) using Vue Router in history mode, your web server must:

    1. Fallback to index.html for all non-file paths to allow page refreshes.
    2. Provide proper headers for the service worker.

    Example Caddyfile configuration:

    (cors) {
      @cors_preflight method OPTIONS
      @cors header Origin {args.0}
    
      handle @cors_preflight {
        header Access-Control-Allow-Origin "{args.0}"
        header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE"
        header Access-Control-Allow-Headers "Content-Type"
        header Access-Control-Max-Age "3600"
        respond "" 204
      }
    
      handle @cors {
        header Access-Control-Allow-Origin "{args.0}"
        header Access-Control-Expose-Headers "Link"
      }
    }
    
    host.com {
        import cors *
        encode gzip
    
        header /service-worker.js {
                Service-Worker-Allowed "/"
                Cache-Control "no-cache"
        }
    
        # SPA root
        root * /usr/share/caddy/cashu.me/
    
        # quasar vue router fallback history mode
        try_files {path} /index.html
    
        file_server
    }
  3. Build and sync for Mobile (Capacitor)

    main

    Cashu.me uses Capacitor to ship native Android and iOS apps. To build the web assets and sync them into the native projects, follow these steps:

    1. Build the PWA assets.
    2. Copy and sync the assets to the native platforms.
    3. Open the project in the respective native IDE (Android Studio or Xcode).

    To regenerate app icons and splash screens after modifying icon.png:

    npx capacitor-assets generate
    npm run build:pwa
    npx cap copy
    npx cap sync
    
    npx cap open android
    npx cap open ios
  4. Set up a local development environment

    main

    To develop on Cashu.me, ensure you have Node.js >= 22.4 and npm installed. Use the following commands to install dependencies and start the development server with hot reloading.

    npm install
    npm run dev
  5. Run unit and end-to-end tests

    main

    Unit Testing

    Unit tests use Vitest. To run a specific test file:

    npx vitest src/path/to/test.ts

    End-to-End (E2E) Testing

    E2E tests use Playwright against a local test stack (two CDK mints with a Lightning backend) started via Docker Compose.

    Full automated run:

    npm run test:e2e

    Manual stack management: If you want to manage the Docker stack yourself and run Playwright directly:

    npm run test:e2e:stack:up
    npm run test:e2e:playwright
    npm run test:e2e:stack:down
  6. Use MintClass to interact with a specific mint

    main

    The MintClass is a helper class that wraps a StoredMint object to provide high-level access to its API and related data. It is used to bridge the gap between stored state and live API interactions.

    Key Properties:

    • api: Returns a new instance of the @cashu/cashu-ts Mint class initialized with the mint's URL.
    • proofs: Returns all proofs in the proofsStore that belong to this mint's keysets.
    • allBalances: Returns a record of balances for all supported units in the mint.
    • keysets: Returns only the currently active keysets.
    • units: Returns a unique list of currency units supported by the mint's keysets.

    Methods:

    • unitBalance(unit: string): Returns the sum of all non-reserved proofs for a specific unit.
    • unitProofs(unit: string): Returns all non-reserved WalletProof objects for a specific unit.
    const mintClass = new MintClass(storedMint);
    const balance = mintClass.unitBalance('sat');
    const api = mintClass.api; // Access @cashu/cashu-ts Mint instance
  7. Manage Melt (Ecash to Bitcoin) Quotes

    main

    The walletMelt.ts module provides functions for managing the lifecycle of 'melt' operations, where ecash (Cashu proofs) is exchanged for Bitcoin. This includes normalizing quotes from different payment methods (Bolt11, Bolt12, Onchain), executing the melt, checking the status of outgoing invoices, and recovering change proofs.

    Key Concepts

    • Melt Quote: A request to exchange ecash for Bitcoin. It contains the amount, fee reserves, and payment details (like a Lightning invoice or Bitcoin address).
    • Normalization: Since different payment methods return different response shapes, normalizeMeltQuote is used to convert them into a consistent AppMeltQuote format used by the application.
    • Change Recovery: If a melt results in change (ecash returned to the user), the application stores OutputData to allow the user to reconstruct those proofs later.
  8. Deploy cashu.me using Docker Compose

    main

    You can deploy the cashu.me service using Docker Compose. The service builds from the local directory and maps the internal port 80 to port 3000 on your local loopback address (127.0.0.1).

    To run the service, use the following command in the directory containing the docker-compose.yaml file:

    docker-compose up
    services:
      cashu.me:
        image: cashu.me
        build: .
        container_name: cashu.me
        restart: always
        ports:
          - "127.0.0.1:3000:80"
  9. Configure Playwright E2E testing environment

    main

    The project uses Playwright for end-to-end (E2E) testing. The configuration is driven by several environment variables that allow switching between local development and CI environments.

    Key Environment Variables

    • E2E_APP_URL: The base URL of the application under test. Defaults to http://127.0.0.1:4173.
    • CI: If set, enables CI-specific behaviors such as forbidding .only tests, setting retries to 1, and changing the reporter to line and html.
    • E2E_VIDEO: Set to on to enable video recording for all tests; otherwise, it defaults to retain-on-failure.
    • E2E: Automatically set to true when running via the webServer command.

    Test Execution Settings

    • Test Directory: test/e2e/specs
    • Output Directory: test-results
    • Parallelism: Tests run sequentially (fullyParallel: false) with 1 worker.
    • Timeouts:
      • Global test timeout: 90,000ms
      • Expect assertion timeout: 20,000ms
      • Action timeout: 15,000ms
      • WebServer startup timeout: 120,000ms

    Browser Context Defaults

    • Device: Desktop Chrome
    • Locale: en-US
    • Permissions: clipboard-read, clipboard-write
    • Service Workers: Blocked
    • Trace: Enabled on-first-retry
    • Screenshot: Captured only-on-failure
    • Reduced Motion: reduce enabled
    // Example of setting the app URL via environment variable before running tests
    E2E_APP_URL=http://localhost:3000 npx playwright test
  10. Configure PWA (Progressive Web App) settings

    main

    The pwa object configures the application as a PWA. Key settings include:

    • workboxMode: Set to 'generateSW' or 'injectManifest'.
    • injectPwaMetaTags: Boolean to inject meta tags.
    • swFilename: The service worker filename (defaults to 'sw.js').
    • manifestFilename: The web manifest filename (defaults to 'manifest.json').
    • workboxOptions: Configuration for Workbox, such as skipWaiting: true and clientsClaim: true.
    pwa: {
      workboxMode: "generateSW",
      injectPwaMetaTags: true,
      swFilename: "sw.js",
      manifestFilename: "manifest.json",
      useCredentialsForManifestTag: false,
      workboxOptions: {
        skipWaiting: true,
        clientsClaim: true,
      },
    }
  11. Configure Capacitor for Cashu.me

    main

    The capacitor.config.ts file defines the configuration for the Capacitor native bridge. This configuration determines the application ID, the display name, and the directory containing the web assets that will be bundled into the native mobile application.

    Key configuration properties:

    • appId: The unique identifier for the application (e.g., me.cashu.wallet).
    • appName: The user-facing name of the application (Cashu.me).
    • webDir: The path to the compiled web assets. For this project, it points to dist/pwa/.
    import type { CapacitorConfig } from "@capacitor/cli";
    
    const config: CapacitorConfig = {
      appId: "me.cashu.wallet",
      appName: "Cashu.me",
      webDir: "dist/pwa/",
    };
    
    export default config;