# AutistMask AutistMask is a GPL-licensed JavaScript browser extension by [@sneak](https://sneak.berlin) that provides a minimal Ethereum wallet for Chrome and Firefox. It manages HD wallets derived from BIP-39 seed phrases and supports sending and receiving ETH and ERC-20 tokens, as well as web3 site connection and authentication via the EIP-1193 provider API. ## Getting Started ```bash git clone https://git.eeqj.de/sneak/autistmask.git cd autistmask make install make build ``` Load the extension: - **Chrome**: Navigate to `chrome://extensions/`, enable "Developer mode", click "Load unpacked", and select the `dist/chrome/` directory. - **Firefox**: Navigate to `about:debugging#/runtime/this-firefox`, click "Load Temporary Add-on", and select `dist/firefox/manifest.json`. ## Rationale MetaMask has become bloated with swap UIs, portfolio dashboards, analytics, tracking, and advertisements. It is no longer a simple wallet. Most alternatives (Rabby, Rainbow, etc.) only support Chromium browsers, leaving Firefox users without a usable option. AutistMask exists to provide the absolute minimum viable Ethereum wallet experience: manage seed phrases, derive HD addresses, send and receive ETH and ERC-20 tokens, and connect to web3 sites. Nothing else. No swaps (that's what the web is for), no analytics, no tracking, no ads, no portfolio views, no NFT galleries. Just a wallet. ## Design AutistMask is a browser extension targeting both Chrome (Manifest V3) and Firefox (Manifest V2/V3 as supported). The codebase is shared between both targets with platform-specific manifest files and a build step that produces separate output directories. ### Architecture ``` src/ background/ — service worker / background script index.js — extension lifecycle, message routing wallet.js — wallet management (create, import, derive via ethers.js) provider.js — EIP-1193 JSON-RPC provider implementation popup/ — popup UI (the main wallet interface) index.html index.js styles/ — CSS (Tailwind) content/ — content script injected into web pages index.js — injects the provider into page context inpage.js — the window.ethereum provider object shared/ — shared utilities vault.js — encrypted storage via libsodium constants.js — chain IDs, default RPC endpoints, ERC-20 ABI manifest/ chrome.json — Manifest V3 for Chrome firefox.json — Manifest V2/V3 for Firefox ``` ### UI Design Philosophy The UI follows a "Universal Paperclips" aesthetic — a deliberately spartan, almost brutalist approach. The guiding principle is that an unskilled, non-technical person should be able to figure out how to use it without any crypto knowledge. #### Visual Style - **Monochrome**: Black text on white background. No brand colors, no gradients, no color-coding. Color may be introduced later for specific semantic purposes (e.g. error states) but the baseline is monochrome. - **Text-first**: Every piece of information is presented as text. Balances are numbers. Addresses are hex strings. Status is a sentence. No progress spinners with animations — a text status line is sufficient. - **Monospace font**: All text is rendered in the system monospace font. Ethereum addresses, transaction hashes, and balances are inherently fixed-width data. Rather than mixing proportional and monospace fonts, we use monospace everywhere for visual consistency and alignment. - **No images**: Zero image assets in the entire extension. No logos, no illustrations, no token icons. Token identity is conveyed by symbol text (ETH, USDC, etc.). - **Tailwind CSS**: Utility-first CSS via Tailwind. No custom CSS classes for styling. Tailwind is configured with a minimal monochrome palette. This keeps the styling co-located with the markup and eliminates CSS file management. - **Vanilla JS**: No framework (React, Vue, Svelte, etc.). The popup UI is small enough that vanilla JS with simple view switching is sufficient. A framework would add bundle size, build complexity, and attack surface for no benefit at this scale. - **360x600 popup**: Standard browser extension popup dimensions. The UI is designed for this fixed viewport — no responsive breakpoints needed. #### Language & Labeling All user-facing text avoids crypto jargon wherever possible: - "Recovery phrase" instead of "seed phrase", "mnemonic", or "BIP-39 mnemonic" - "Address" instead of "account", "derived key", or "HD child" - "Password" instead of "encryption key" or "vault passphrase" - "Private key" instead of "secret key" or "signing key" - Buttons use plain verbs: "Send", "Receive", "Copy address", "Add", "Back", "Cancel", "Lock", "Unlock", "Allow", "Deny" - No bracket notation like `[locked]` or `[setup]` — just plain titles - Helpful inline descriptions where needed (e.g. "This password locks the wallet on this device. It is not the same as your recovery phrase.") - Error messages are full sentences ("Please enter your password." not "password required") #### Data Model The core hierarchy is **Wallets → Addresses**: - A **wallet** is either: - An **HD wallet** (recovery phrase): generates multiple addresses from a single 12/24 word recovery phrase using BIP-39/BIP-44 derivation. The user can add more addresses with a "+" button. - A **key wallet** (private key): a single address imported directly from a private key. No "+" button since there is only one address. - An **address** holds ETH and any user-added ERC-20 tokens. - The user can have multiple wallets, each with multiple addresses (HD) or a single address (key). #### Navigation The main view shows all addresses grouped by wallet, with ETH balances inline. The user taps an address to see its detail view (full address, balance, tokens, send/receive). Navigation is flat — every view has a "Back" or "Cancel" button that returns to the previous context. No deep nesting, no tabs, no hamburger menus. ### UI Views The popup has the following views, switched via simple show/hide: 1. **Lock**: Password input + Unlock button. Shown when the wallet is locked or on first open after browser restart. 2. **Welcome**: Shown on first use. Two options: "Add wallet" (recovery phrase based) and "Import private key". Password is set during the first wallet addition. 3. **Add wallet**: A unified view for both creating and importing recovery phrase wallets. The recovery phrase text area starts empty. A clickable die button `[die]` generates a random 12-word phrase and fills it in. If the user already has a phrase, they paste it directly. When the die is clicked, a warning box appears reminding the user to write the phrase down. Password fields are shown only on first use. 4. **Import private key**: Paste a private key. This creates a wallet with a single address. Password fields shown only on first use. 5. **Main**: All wallets listed, each showing its addresses with truncated address and ETH balance. "+" next to recovery phrase wallets to add another address. "+ Add wallet" and "+ Import private key" buttons at the bottom. Settings and Lock buttons in the header. Future: a sub-heading showing total portfolio value in USD (and eventually other currencies). 6. **Address detail**: Full address (click to copy), ETH balance, USD value (future), Send/Receive buttons, token list with "+ Add" button. 7. **Send**: Token selector, recipient address, amount. Cancel returns to address detail. 8. **Receive**: Full address displayed with "Copy address" button. 9. **Add token**: Enter contract address. The extension looks up the token name/symbol automatically. 10. **Settings**: Network (RPC endpoint URL) with explanatory text. 11. **Approval**: When a website requests wallet access or a signature, shows the site origin, request details, and Allow/Deny buttons. ### External Services AutistMask is not a fully self-contained offline tool. It necessarily communicates with external services to function as a wallet: - **Ethereum JSON-RPC endpoint**: The extension needs an Ethereum node to query balances (`eth_getBalance`), read ERC-20 token contracts (`eth_call`), estimate gas (`eth_estimateGas`), fetch nonces (`eth_getTransactionCount`), broadcast transactions (`eth_sendRawTransaction`), and check transaction receipts. The default endpoint is a public RPC (configurable by the user to any endpoint they prefer, including a local node). This is the only external service the extension talks to. What the extension does NOT do: - No analytics or telemetry services - No token list APIs (user adds tokens manually by contract address) - No price feed APIs (balances are shown in token units, not fiat) - No phishing/blocklist APIs - No Infura/Alchemy dependency (any JSON-RPC endpoint works) - No backend servers operated by the developer The user's RPC endpoint is the single point of external communication. Users who want maximum privacy can point it at their own Ethereum node. ### Dependencies AutistMask uses two runtime libraries. All cryptographic operations are delegated to these libraries — see the Crypto Policy section below. | Package | Version | License | Purpose | | ------------------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ethers` | 6.16.0 | MIT | All Ethereum operations: BIP-39 mnemonic generation/validation, BIP-32/BIP-44 HD key derivation (`m/44'/60'/0'/0/n`), secp256k1 signing, transaction construction, ERC-20 contract interaction, JSON-RPC communication, address derivation (keccak256). | | `libsodium-wrappers-sumo` | 0.8.2 | ISC | Password-based encryption of secrets at rest: Argon2id key derivation (`crypto_pwhash`), authenticated encryption (`crypto_secretbox` / XSalsa20-Poly1305). | Dev dependencies (not shipped in extension): | Package | Version | License | Purpose | | ------------------ | ------- | ------- | ------------------------- | | `esbuild` | 0.27.3 | MIT | JS bundler (inlines deps) | | `tailwindcss` | 4.2.1 | MIT | CSS compilation | | `@tailwindcss/cli` | 4.2.1 | MIT | Tailwind CLI | | `jest` | 30.2.0 | MIT | Test runner | | `prettier` | 3.8.1 | MIT | Code formatter | ### Crypto Policy **No raw crypto primitives in application code.** If the strings `aes`, `sha`, `pbkdf`, `hmac`, `encrypt`, `decrypt`, `hash`, `cipher`, `digest`, `sign` (case-insensitive) appear in our own source code (outside of `node_modules/`), it is almost certainly a bug. All cryptographic operations must go through `ethers` or `libsodium-wrappers-sumo`. This policy exists because: - Rolling your own crypto is the single most common source of security vulnerabilities in wallet software. - Both libraries are widely audited and battle-tested. - Keeping crypto out of application code makes security review tractable: reviewers only need to verify that we call the libraries correctly, not that we implemented crypto correctly. Exceptions require explicit authorization in a code comment referencing this policy. ### Key Decisions - **No framework**: The popup UI is vanilla JS and HTML. The extension is small enough that a framework adds unnecessary complexity and attack surface. - **Encrypted storage**: Recovery phrases and private keys are encrypted at rest in the extension's local storage using libsodium. The encryption scheme: - The user's password is run through Argon2id (`crypto_pwhash`) to derive a 256-bit encryption key. Argon2id is memory-hard, making GPU/ASIC brute force attacks expensive. - The derived key encrypts the secret material using XSalsa20-Poly1305 (`crypto_secretbox`), which provides authenticated encryption (the ciphertext cannot be tampered with without detection). - Stored blob: `{ salt, nonce, ciphertext }` (the auth tag is part of the `crypto_secretbox` output). - **The password is NOT used in address derivation.** It exists solely to protect the recovery phrase / private key on disk. Anyone with the recovery phrase can restore the wallet on any device without this password. This matches MetaMask's behavior. - **BIP-39 / BIP-44 via ethers.js**: Mnemonic generation, validation, and HD key derivation (`m/44'/60'/0'/0/n`) are handled entirely by ethers.js. The BIP-39 passphrase is always empty (matching MetaMask and most wallet software). The user's password is completely separate and has no effect on which addresses are generated. - **ethers.js for everything Ethereum**: Transaction construction, signing, gas estimation, RPC communication, ERC-20 contract calls, and address derivation are all handled by ethers.js. This means zero hand-rolled Ethereum logic. - **EIP-1193 provider**: The content script injects a `window.ethereum` object that implements the EIP-1193 provider interface, enabling web3 site connectivity. - **Minimal RPC**: The extension communicates with Ethereum nodes via JSON-RPC through ethers.js's `JsonRpcProvider`. The default endpoint is configurable. No Infura dependency — users can point it at any Ethereum JSON-RPC endpoint. ### Supported Functionality - Create new HD wallet (generates 12-word recovery phrase) - Import HD wallet from existing 12 or 24 word recovery phrase - Import single-address wallet from private key - Add multiple addresses within an HD wallet - Manage multiple wallets simultaneously - View ETH balance per address - View ERC-20 token balances (user adds token by contract address) - Send ETH to an address - Send ERC-20 tokens to an address - Receive ETH/tokens (display address, copy to clipboard) - Connect to web3 sites (EIP-1193 `eth_requestAccounts`) - Sign transactions requested by connected sites - Sign messages (`personal_sign`, `eth_sign`) - Lock/unlock with password - Configurable RPC endpoint - Future: USD value display (and other fiat currencies) ### Non-Goals - Token swaps (use a DEX in the browser) - NFT display or management - Multi-chain support (Ethereum mainnet only, for now) - Analytics, telemetry, or tracking of any kind - Advertisements or promotions - Phishing detection - Hardware wallet support (maybe later) - Token list auto-discovery (user adds tokens manually) - Fiat on/off ramps - Browser notifications - Transaction history (use Etherscan) ## TODO - [x] Project scaffolding (Makefile, Dockerfile, CI, manifests) - [x] Set up Tailwind CSS build pipeline - [x] Build popup UI views (all 13 views with stub state) - [ ] Implement BIP-39 mnemonic generation and validation - [ ] Implement BIP-32/BIP-44 HD key derivation for Ethereum - [ ] Implement private key import and address derivation - [ ] Implement encrypted storage for wallet data - [ ] Implement background wallet manager - [ ] Implement EIP-1193 provider and content script injection - [ ] Implement ETH balance lookup via RPC - [ ] Implement ETH send (transaction construction + signing) - [ ] Implement ERC-20 token management (add by contract, view balance, send) - [ ] Implement site connection approval flow - [ ] Implement transaction signing approval flow - [ ] Implement message signing (`personal_sign`, `eth_sign`) - [ ] Add USD value display (price feed TBD) - [ ] Add multi-currency fiat display support - [ ] Test on Chrome and Firefox - [ ] Write tests for crypto operations - [ ] Write tests for transaction construction - [ ] Security audit of key management ## License GPL-3.0. See [LICENSE](LICENSE). ## Author [@sneak](https://sneak.berlin)