Some checks failed
check / check (push) Has been cancelled
- Transaction values now use exactly 4 decimal places (was 6), matching balance display everywhere else - Transaction detail view shows "2026-02-25 15:04:23 (23 days ago)" instead of just the ISO date - Added Display Consistency policy to README
563 lines
24 KiB
Markdown
563 lines
24 KiB
Markdown
# 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.
|
|
|
|
#### No Layout Shift
|
|
|
|
Asynchronous state changes (clipboard confirmation, transaction status, error
|
|
messages, flash notifications) must never move existing UI elements. All dynamic
|
|
content areas reserve their space up front using `min-height` or always-present
|
|
wrapper elements. `visibility: hidden` is preferred over `display: none` when
|
|
the element's space must be preserved. This prevents jarring content jumps that
|
|
disorient users and avoids mis-clicks caused by shifting buttons.
|
|
|
|
#### Clickable Affordance
|
|
|
|
Every interactive element must visually indicate that it is clickable. Buttons
|
|
use a visible border, padding, and a hover state (invert to white-on-black).
|
|
Text that triggers an action (e.g. "Import private key") uses an underline. No
|
|
invisible hit targets, no bare text that happens to have a click handler. If it
|
|
does something when you click it, it must look like it does something when you
|
|
click it.
|
|
|
|
#### Display Consistency
|
|
|
|
The same data must be formatted identically everywhere it appears. Token and ETH
|
|
amounts are always displayed with exactly 4 decimal places (e.g. "1.0500 ETH",
|
|
"17.1900 USDT") — in balance lists, transaction lists, transaction details, send
|
|
confirmations, and any other context. Timestamps include both an ISO datetime
|
|
and a humanized relative age wherever shown. If a formatting rule applies in one
|
|
place, it applies in every place. Users should never see the same value rendered
|
|
differently on two screens.
|
|
|
|
#### 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")
|
|
|
|
#### Full Identifiers Policy
|
|
|
|
Addresses, transaction hashes, contract addresses, and all other cryptographic
|
|
identifiers are displayed in full whenever possible — never truncated. Address
|
|
poisoning attacks exploit truncated displays by generating fraud addresses that
|
|
share the same prefix and suffix as a legitimate address. If a user only sees
|
|
`0xAbCd...1234`, an attacker can create an address with the same visible
|
|
characters and trick the user into sending funds to it. Showing the complete
|
|
identifier defeats this class of attack. Truncation is only acceptable in
|
|
space-constrained contexts where the full identifier is accessible one tap away
|
|
(e.g. a tooltip or copy action).
|
|
|
|
#### 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.
|
|
|
|
### Screen Map
|
|
|
|
Navigation uses a stack model (like iOS): each action pushes a screen onto the
|
|
stack, and "Back" pops it. The root screen is either Welcome (no wallets) or
|
|
Home (has wallets). Screens are listed below with their elements and
|
|
transitions.
|
|
|
|
#### Welcome
|
|
|
|
- **When**: No wallets exist yet.
|
|
- **Elements**: "AutistMask" heading, brief intro text, "Add wallet" button.
|
|
- **Transitions**:
|
|
- "Add wallet" → pushes **AddWallet**
|
|
|
|
#### Home
|
|
|
|
- **When**: At least one wallet exists. This is the root screen.
|
|
- **Elements**:
|
|
- Header: "AutistMask", Settings button
|
|
- Total ETH balance across all addresses (large text)
|
|
- Total USD value (small text below ETH total, cached 5 min)
|
|
- List of wallets, each showing:
|
|
- Wallet name (editable — tap to rename)
|
|
- "+" button (HD wallets only) to derive next address
|
|
- List of addresses under that wallet:
|
|
- Address name (editable — tap to rename, default "Address N")
|
|
- Full address (untruncated)
|
|
- ETH balance + USD value (small text)
|
|
- "+ Add wallet" button at the bottom
|
|
- **Transitions**:
|
|
- Tap address → pushes **AddressDetail**
|
|
- "+" on wallet → derives address inline (no screen change)
|
|
- "+ Add wallet" → pushes **AddWallet**
|
|
- Settings → pushes **Settings**
|
|
|
|
#### AddWallet
|
|
|
|
- **When**: User wants to add a new wallet (from Home or Welcome).
|
|
- **Elements**:
|
|
- "Add Wallet" heading
|
|
- Instruction text
|
|
- Die button `[die]` (generates random recovery phrase, can be clicked
|
|
repeatedly)
|
|
- Recovery phrase textarea (empty by default, or filled by die)
|
|
- Backup warning box (shown after die is clicked)
|
|
- "Add" button, "Back" button
|
|
- "Have a private key instead?" link → pushes **ImportKey**
|
|
- **Transitions**:
|
|
- "Add" (valid phrase) → pops to **Home**
|
|
- "Back" → pops to previous (Home or Welcome)
|
|
- "Have a private key instead?" → pushes **ImportKey**
|
|
|
|
#### ImportKey
|
|
|
|
- **When**: User wants to import a single private key.
|
|
- **Elements**:
|
|
- "Import Private Key" heading
|
|
- Instruction text
|
|
- Private key input (password-masked)
|
|
- "Import" button, "Back" button
|
|
- **Transitions**:
|
|
- "Import" (valid key) → pops to **Home**
|
|
- "Back" → pops to previous
|
|
|
|
#### AddressDetail
|
|
|
|
- **When**: User tapped an address from Home.
|
|
- **Elements**:
|
|
- Header: wallet name, "Back" button
|
|
- Address name (editable — tap to rename)
|
|
- ENS name (if resolved, shown above address)
|
|
- Full address (tap to copy, "Copied!" feedback)
|
|
- ETH balance (large) + USD value (small)
|
|
- "Send" button, "Receive" button
|
|
- Token list with balances
|
|
- "+ Add" token button
|
|
- **Transitions**:
|
|
- "Send" → pushes **Send**
|
|
- "Receive" → pushes **Receive**
|
|
- "+ Add" → pushes **AddToken**
|
|
- "Back" → pops to **Home**
|
|
|
|
#### Send
|
|
|
|
- **When**: User wants to send ETH or a token from this address.
|
|
- **Elements**:
|
|
- "Send" heading
|
|
- Token selector (ETH + any added tokens)
|
|
- "To" input (accepts address or ENS name, resolves before sending)
|
|
- Amount input
|
|
- Fee estimate (shown after entering amount)
|
|
- "Send" button, "Cancel" button
|
|
- Status area (resolving ENS, confirming, errors)
|
|
- **Transitions**:
|
|
- "Send" (valid) → prompts for password (to decrypt private key), submits
|
|
transaction, shows result, stays on screen
|
|
- "Cancel" → pops to **AddressDetail**
|
|
|
|
#### Receive
|
|
|
|
- **When**: User wants to receive funds at this address.
|
|
- **Elements**:
|
|
- "Receive" heading
|
|
- Instruction text
|
|
- QR code encoding the address
|
|
- Full address (displayed, selectable)
|
|
- "Copy address" button
|
|
- "Back" button
|
|
- **Transitions**:
|
|
- "Back" → pops to **AddressDetail**
|
|
|
|
#### AddToken
|
|
|
|
- **When**: User wants to track an ERC-20 token on this address.
|
|
- **Elements**:
|
|
- "Add Token" heading
|
|
- Instruction text (find contract address on Etherscan)
|
|
- Contract address input
|
|
- Token info preview (name, symbol — fetched from contract)
|
|
- "Add" button, "Cancel" button
|
|
- **Transitions**:
|
|
- "Add" (valid contract) → pops to **AddressDetail**
|
|
- "Cancel" → pops to **AddressDetail**
|
|
|
|
#### Settings
|
|
|
|
- **When**: User tapped Settings from Home.
|
|
- **Elements**:
|
|
- "Settings" heading
|
|
- Network section: RPC endpoint URL input, "Save" button, explanatory text
|
|
- "Back" button
|
|
- **Transitions**:
|
|
- "Back" → pops to **Home**
|
|
|
|
#### Approval (future)
|
|
|
|
- **When**: A connected website requests wallet access or a transaction
|
|
signature. Opened by the background script, not by user navigation.
|
|
- **Elements**:
|
|
- "A website is requesting access" heading
|
|
- Site origin (bold)
|
|
- Request type and details (preformatted)
|
|
- "Allow" button, "Deny" button
|
|
- **Transitions**:
|
|
- "Allow" / "Deny" → closes popup (returns result to background script)
|
|
|
|
### 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.
|
|
|
|
- **CoinDesk CADLI price API**: Used to fetch ETH/USD and token/USD prices for
|
|
displaying fiat values. The price is cached for 5 minutes to avoid excessive
|
|
requests. No API key required. No user data is sent — only a list of token
|
|
symbols.
|
|
|
|
What the extension does NOT do:
|
|
|
|
- No analytics or telemetry services
|
|
- No token list APIs (user adds tokens manually by contract address)
|
|
- 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 and the CoinDesk price API are the only external
|
|
services. Users who want maximum privacy can point the RPC at their own node
|
|
(price fetching can be disabled in a future version).
|
|
|
|
### 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). |
|
|
| `qrcode` | 1.5.4 | MIT | QR code generation for the Receive screen (renders address as scannable QR on canvas). |
|
|
|
|
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.
|
|
|
|
### DEBUG Mode Policy
|
|
|
|
The `DEBUG` constant in the popup JS enables a red "DEBUG / INSECURE" banner and
|
|
a hardcoded test mnemonic. **DEBUG mode must behave as close to normal mode as
|
|
possible.** No `if (DEBUG)` branches that skip functionality, bypass security
|
|
flows, or alter program behavior beyond the banner and the hardcoded mnemonic.
|
|
Adding new DEBUG-conditional branches requires explicit approval from the
|
|
project owner.
|
|
|
|
### 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.
|
|
- **Split storage model**: Public data (xpubs, derived addresses, token lists,
|
|
balances) is stored unencrypted in extension local storage so the user can
|
|
view their wallets and balances at any time without entering a password.
|
|
Private data (recovery phrases, private keys) will be encrypted at rest using
|
|
libsodium — a password is only required when the user needs to sign a
|
|
transaction or message. The encryption scheme for private data:
|
|
- 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 — 0.1.0 MVP
|
|
|
|
Everything needed for a minimal working wallet that can send and receive ETH.
|
|
|
|
### Done
|
|
|
|
- [x] Project scaffolding (Makefile, Dockerfile, CI, manifests, esbuild)
|
|
- [x] Tailwind CSS build pipeline
|
|
- [x] Popup UI shell with screen stacking (Welcome, AddWallet, Home,
|
|
AddressDetail, Send, Receive, Settings)
|
|
- [x] BIP-39 mnemonic generation via ethers.js (die button)
|
|
- [x] BIP-39 mnemonic validation on import
|
|
- [x] BIP-32/BIP-44 HD key derivation (real addresses from xpub)
|
|
- [x] Private key import (real address via ethers.Wallet)
|
|
- [x] Xpub stored unencrypted for read-only address derivation
|
|
- [x] State persistence to extension storage (survives popup close)
|
|
- [x] Live ETH balance fetching via JSON-RPC (`eth_getBalance`)
|
|
- [x] ENS reverse lookup (address → name) and forward resolution (name → address
|
|
in Send field)
|
|
- [x] ETH/USD price fetching via CoinDesk API
|
|
- [x] USD value display next to ETH balances
|
|
- [x] Full address display everywhere (no truncation)
|
|
- [x] Token list module with ~150 ERC-20 tokens ordered by market cap
|
|
|
|
### Wallet Management
|
|
|
|
- [ ] Rename wallets (tap wallet name on Home to edit)
|
|
- [ ] Rename addresses (tap address name on AddressDetail to edit)
|
|
- [ ] Delete wallet (with confirmation)
|
|
- [ ] Delete address from HD wallet (with confirmation)
|
|
- [ ] Show wallet's recovery phrase (requires password, from Settings or wallet
|
|
context menu)
|
|
|
|
### Sending
|
|
|
|
- [x] Encrypt recovery phrase / private key with password via libsodium
|
|
(Argon2id + XSalsa20-Poly1305)
|
|
- [x] Password prompt on Send (decrypt private key to construct transaction)
|
|
- [x] Transaction construction via ethers.js (to, value, gasLimit, gasPrice)
|
|
- [ ] Gas estimation and fee display before confirming
|
|
- [x] Broadcast transaction via `eth_sendRawTransaction`
|
|
- [x] Transaction status feedback (pending → confirmed / failed)
|
|
|
|
### Receiving
|
|
|
|
- [x] QR code generation for address (qrcode library, renders to canvas)
|
|
|
|
### Display
|
|
|
|
- [ ] Home screen: total ETH balance summed across all addresses
|
|
- [ ] Home screen: total USD value (small text under total ETH)
|
|
- [ ] Cache ETH/USD price for 5 minutes (don't re-fetch on every popup open)
|
|
- [ ] Per-address USD value in small text under ETH balance everywhere
|
|
|
|
### Tokens (ERC-20)
|
|
|
|
- [ ] Add token by contract address (fetch name/symbol/decimals from contract)
|
|
- [ ] Display ERC-20 token balances per address
|
|
- [ ] Send ERC-20 tokens
|
|
|
|
### Testing
|
|
|
|
- [ ] Tests for mnemonic generation and address derivation
|
|
- [ ] Tests for xpub derivation and child address generation
|
|
- [ ] Tests for token list module (getTopTokenPrices, getTopTokenSymbols)
|
|
- [ ] Test on Chrome (Manifest V3)
|
|
- [ ] Test on Firefox (Manifest V2)
|
|
|
|
### Scam List
|
|
|
|
- [ ] Research and document each address in scamlist.js (what it is, why it's on
|
|
the list, source)
|
|
- [ ] Add more known fraud addresses from Etherscan labels (drainers, phishing,
|
|
address poisoning deployers)
|
|
|
|
### Post-MVP
|
|
|
|
- [ ] EIP-1193 provider injection (window.ethereum) for web3 site connectivity
|
|
- [ ] Site connection approval flow
|
|
- [ ] Transaction signing approval flow (requests from connected sites)
|
|
- [ ] Message signing (`personal_sign`, `eth_sign`)
|
|
- [ ] Multi-currency fiat display (EUR, GBP, etc.)
|
|
- [ ] Security audit of key management
|
|
|
|
## License
|
|
|
|
GPL-3.0. See [LICENSE](LICENSE).
|
|
|
|
## Author
|
|
|
|
[@sneak](https://sneak.berlin)
|