2993df29cba396f1d8e440e3de75f8a331582919
Koins
Multi-chain desktop wallet for Ethereum, BSC, Polygon, and Monero. Built with Bun + Electrobun + Svelte.
Features
- Multi-coin mode: ETH, BSC, Polygon wallet with seed phrase vault stored in system keychain
- Monero mode: Self-contained Monero wallet — auto-downloads
monero-wallet-rpcbinary on first launch, no manual binary install - Touch ID unlock: Biometric auth via Swift helper, no node-gyp or FFI dylib
- Password fallback: PBKDF2-salted password stored in keychain, used when biometric is unavailable
- QR codes: Address QR with chain logo overlay
- Transaction history: Cached in local SQLite via Drizzle ORM
- Settings: Reset app clears all DB tables and keychain entries
Prerequisites
- Bun 1.2+
Getting Started
bun install
cd apps/desktop
bun run dev
Architecture
apps/desktop/
├── src/
│ ├── bun/ # Backend (Electrobun main process)
│ │ ├── index.ts # RPC handlers
│ │ ├── lib/
│ │ │ ├── monero/ # Monero RPC management
│ │ │ │ ├── binary.ts # Download/extract monero-wallet-rpc
│ │ │ │ └── wallet.ts # Child process + RPC client
│ │ │ ├── alchemy/ # Alchemy API calls
│ │ │ ├── viem/ # viem chain calls
│ │ │ ├── tokens/ # Token metadata (DB-first cache)
│ │ │ ├── transactions/ # Tx history + details (DB-first cache)
│ │ │ ├── db/ # Drizzle ORM + SQLite schema
│ │ │ └── biometric.ts # Biometric auth (Swift subprocess)
│ │ └── native/
│ │ └── biometric-helper.swift # Touch ID prompt binary
│ ├── lib/
│ │ ├── states/
│ │ │ └── wallet.svelte.ts # Wallet state (singleton)
│ │ ├── components/
│ │ │ └── EvmDashboard.svelte # Shared EVM dashboard
│ │ └── electrobun.ts # RPC type definitions
│ └── routes/
│ ├── index.svelte # Login/welcome page
│ ├── multicoin/ # ETH / BSC / Polygon routes
│ ├── monero/ # Monero dashboard
│ ├── settings.svelte # App settings
│ └── tx/ # Transaction details
└── package.json
Key Decisions
- Monero via RPC, not WASM:
monero-ts's WASM wallet (MoneroWalletFull) doesn't work in Bun because the C++ HTTP client fails at runtime. Instead,monero-wallet-rpcruns as a child process. - SQLite cache: All chain data (tx history, receipts, token metadata) cached in SQLite via Drizzle, not localStorage.
- Biometric auth: Uses a Swift binary spawned via
Bun.spawnSync()(notdlopen— Bun crashes on ObjC runtime; notnode-mac-auth— fragile node-gyp chain). - Separate account types: EVM and Monero are separate account types with their own seed formats and auth flows.
Dependencies
- Viem — EVM chain RPC calls + wallet operations
- monero-ts — Monero RPC client
- Drizzle ORM — SQLite schema + queries
- Svelte 5 — UI framework
- Electrobun — Desktop app shell
- sv-router — File-based routing
- Lucide — Icons
- QRCode — QR code generation
Scripts
bun run dev # Start development server
bun run db:push # Push Drizzle schema to SQLite