noise -> signal

000

press any key to skip

Browser-based non-custodial HD wallet generator for Solana and Ethereum

Problem

Creating crypto wallets usually requires trusting wallet software or a server; Marsyn lets users derive Ethereum and Solana keypairs entirely client-side from a BIP-39 recovery phrase.

What it does

A single-page React app that generates or imports a BIP-39 mnemonic, then derives Solana wallets (ed25519-hd-key + tweetnacl, path m/44'/501'/i'/0') and Ethereum wallets (ethers HDNodeWallet, path m/44'/60'/i'/0') in the browser, showing public/private keypairs in copyable cards and persisting the mnemonic, chain choice, and wallet list in localStorage.

Architecture

[ui] Marsyn React SPA (App.tsx + wallet cards) (client)
[crypto] Client-side crypto libs (bip39, ed25519-hd-key, tweetnacl, ethers, bs58) (model)
[storage] localStorage (mnemonic, wallets, chain) (store)

ui ──▶ crypto · generate/derive keypairs from mnemonic
ui ──▶ storage · persist and reload wallets on mount
crypto ──▶ ui · public/private keys rendered in wallet cards

Simplified — box-and-arrow sketch of the repo structure, not a system diagram.

Decisions

  1. 01

    All key derivation in-browser

    Non-custodial claim: no backend exists — keys are derived from the mnemonic via bip39/ethers/nacl directly in the client bundle

    Evidence: src/App.tsx

  2. 02

    Standard HD derivation paths per chain

    Uses BIP-44 paths m/44'/501'/i'/0' for Solana and m/44'/60'/i'/0' for Ethereum so wallets are compatible with other wallet software

    Evidence: src/App.tsx (lines 108, 150)

  3. 03

    localStorage as the only persistence layer

    Stores mnemonic, selected chain, and the full wallet list (including private keys) as JSON in localStorage to survive reloads without a server

    Evidence: src/App.tsx (lines 42-71, 92)

  4. 04

    Sequential index counter for wallet derivation

    A single currentIndex state increments per added wallet and resets to 1 when the list is cleared, driving deterministic child-key derivation

    Evidence: src/App.tsx (lines 22, 120, 130-136)

  5. 05

    vite-plugin-node-polyfills

    Buffer/stream shims needed for bip39/ethers crypto code to run in the browser bundle

    Evidence: vite.config.ts

The hard parts

  • HD key derivation across two curve families (ed25519 for Solana vs secp256k1 via ethers for Ethereum) handled in one codebase (inferred from mixed libs in package.json)
  • Browser bundling of Node-oriented crypto libraries requiring polyfills

Known limitations

  • Mnemonic and all private keys are persisted in plaintext localStorage (src/App.tsx lines 42-44, 92) — clearing browser data or XSS leaks funds
  • Only one chain can be active at a time: the blockChain flag locks the UI to Sol or Eth once chosen (src/App.tsx lines 101-105, 143-147)
  • Deleting a single wallet does not decrement the index, so a re-added wallet skips an index (src/App.tsx removeWallet, lines 127-137)
  • Whole mnemonic is stored on every wallet object, duplicating the seed in the wallets array (src/types/index.ts)
  • No export/backup beyond copy-to-clipboard; no encryption, no PIN
  • No tests, no CI, no license

Command palette

Jump to a section, project or link