Seedelf-Wallet

Chunk 12 plan: the style and flow iteration

v1 is built (chunks 1–11c). The user found 11a’s pass “kind of looks like Lace but also totally does not”, so this chunk works through the user’s own list: mostly small front-end changes. Branch web-wallet/style-flow, one PR into seedelf-web-wallet.

How it runs: the user tests the built extension and sends findings. Each finding goes in the list below with what was decided. Batches land with popup and tab screenshots for the user to check before they rebuild.

Start here

  1. git fetch origin && git checkout web-wallet/style-flow
  2. Read this plan and the newest roadmap handoff note.
  3. Build and test (from seedelf-platform/seedelf-web-wallet/extension): npm run build && npm test && npm run e2e.
  4. Ask the user for new findings; add them to the list.

Rules that still hold

The list

# Finding Decision Status
1 Home shows “— ₳” and empty lists while it loads. Lace covers loading with an animated logo overlay. A splash only while there’s nothing to show: the first reading after an unlock, restore or create. A cached reading shows Home at once. The emblem on navy, breathing, with a teal arc orbiting it. When the balances arrive it fades out into the wallet (the emblem grows slightly). It gives up after about 8 s so a slow Koios can’t hide Refresh or an error. Static under reduced motion. ✅ built; the user checks
2 The Seedelf identity card (the public value) isn’t needed: users have no use for it anywhere. Drop it from Home. The worker keeps seedelfPublicValue in account, because a unit test checks key derivation end to end through it. ✅ built; the user checks
3 A flat token list breaks with hundreds of tokens. Match how Lace handles them. Home shows the first 5, then “View all N”. A Tokens screen has Tokens and NFTs tabs, a search (name, ticker, policy ID, fingerprint) and a sort (name or amount). Tapping a token opens its details (policy ID, asset name, fingerprint, each with Copy). NFTs are told apart without new queries: CIP-68 label 222 is an NFT, 333 and 444 are fungible, and otherwise a single unit with no decimals is an NFT. Avatars are the logo, or the name’s first two letters. ✅ built; the user checks
4 Fungible token data (ticker, name, decimals, logo) hardly changes, so ship it in the wallet instead of querying for it. It’s refreshed at each release. A curated registry per network, bundled. A hand-kept list of token units per network. npm run tokens pulls each one’s registry metadata through Koios asset_info at build time, shrinks logos to 96 px WebP, and writes the bundle. It’s seeded with preprod (tUSDM, the one registered token testers are likely to hold) and mainnet (25 well-known FTs, each the only registry entry for its ticker). The release checklist runs it. Tokens off the list show their own name and a letter avatar, so a lookalike ticker never gets a real logo. ✅ built; the user checks
5 Modals must be centred; a token’s details were cut off when it sat low in the list. One Modal, centred, never taller than the window, with its body scrolling. The popup is sized by its page, and a <dialog> doesn’t count towards that size, so the old bottom sheet could spill past it. The details’ ids read as plain rows, so they fit without scrolling. ✅ built; the user checks
6 Move in any amount of a token, not only all of it. An amount box per token, with Max for all of it (TokenAmounts, as in Send and Withdraw, which gain Max too). The Rust build::move_in takes (policy, name, quantity); every UTxO holding the token is still spent, and the rest goes back with the change. It refuses more than is held, or none. ✅ built; the user checks
7 “Cardano account” is long next to “Seedelf” in Home’s tabs. The tab says “Cardano”. The balance under it still says “Cardano account”. ✅ built; the user checks
8 Lace has settings; we have none. A Settings screen, from a gear in the top bar: show the recovery phrase (password first), change the password, remove the wallet, About. No Koios requests. A wrong password here counts towards the unlock back-off. ✅ built; the user checks
9 Lace has an activity list; we show only the transaction just sent. Activity. Cardano account: account_txs (newest first, then only after the last-seen block) and one batched tx_info per page of 20, only while Activity is open. Seedelf: no requests at all, and never a question about a specific transaction: received UTxOs from the scan, and what this wallet sent. The history is kept encrypted on the device (a key derived from the phrase), unreadable while locked. Checked with a recorded page of the public 12-word account’s real preprod transactions. ✅ built; the user checks
10 Lace has an address book. Seedelf names are 64 hex characters. Contacts: named Seedelfs and addresses, encrypted on the device with the history’s key, offered in Send and Withdraw. No Koios requests. privacy.md’s “never on disk” becomes “never on disk unencrypted”. Saved from Send and Withdraw once found, picked with Contacts, managed in Settings. ✅ built; the user checks
11 Forms list every token (the follow-up below). A token picker like Lace’s “Add assets”: search, pick, then an amount box for each picked token only, with Max and ×. Move in, Send and Withdraw. No Koios requests. ✅ built; the user checks
12 The balance reading pulls the whole contract, 1 request per 1,000 UTxOs, and the contract only grows. An incremental scan. A full scan on unlock and every 30 minutes; in between, only UTxOs newer than the last-seen block (credential_utxos?block_height=gt.N, checked live: 5 of 35 rows in one request). The wallet drops its own spends as it makes them; the full scan catches another device’s spends and rollbacks. The owned set stays in session memory. Checked live: the second reading filtered, and agreed. ✅ built
13 Sending part of a token seemed impossible: the user expected an amount on Add tokens, and missed the box the form gives each picked token. Kept as built (pick, then an amount box per token, with Max). The user found it once shown. Offered, not asked for: focus the first new box after Add, and a line in the picker saying amounts come next. kept
14 The Cardano side can only Receive and Move in. How much should it do? Send from the Cardano account, for now. An address, $handle or contact; an amount or Max, and tokens; signed at review with the account’s keys, like a move-in; no giveme.my. One output; change to 0/0; 5 ₳ UTxOs stay put. Built by build::account_send, which shares the move-in’s code (Payee). Paid in the open, and the form says so. Koios: 3 requests at Review (plus a handle’s lookup), submittx at Send. Staking and governance are left open (below). ✅ built; the user checks
15 Tokens need ADA with them, and the user has to guess how much. The minimum is worked out in Move in, Send to a Seedelf, Withdraw and Send. With tokens, the amount can stay empty (“Minimum”): only the least ADA the network accepts goes. Less than that, with or without tokens, is raised to it. The review says so, and from what. WebAssembly raises it with core’s minimum functions; core’s builders still refuse less, for the CLI. Lace shows the minimum as an error instead. No new requests. ✅ built; the user checks
16 A Seedelf’s name, what people pay, sits at the bottom of Home; the Cardano side has Receive for its address. Receive on the Seedelf tab (Receive, Send, Withdraw, Create; the round buttons narrow to 64 px so four fit the popup). It lists each Seedelf’s whole name with Copy, from the last reading, with no requests; with none, it says to create one. ✅ built; the user checks
17 Then Your Seedelfs on Home isn’t needed; show a name whole at full size, and cut it to fit as the window narrows. Home drops the list; Receive has it all: tag, locked ADA, Copy and Remove, and the name on its own line. MiddleEllipsis keeps it whole when it fits and cuts the middle when it doesn’t, keeping the last six characters (CSS only: the head gives way, the tail never does). The e2e test checks both: whole in the tab, cut in the popup. ✅ built; the user checks
18 In a token’s amount box the commas acted like part of the number when deleting; and it took more than the wallet holds. One amount rule for every box (sanitizeAmount, AmountField): the commas regroup on every edit (ADA’s box too), Backspace or Delete on a comma takes the digit beside it, and the caret stays among the same digits. A token’s box refuses more than the wallet holds, keeping the previous value with a note, as ADA’s refuses more than 45 billion; its decimals are capped as ADA’s 6 are. Not the token’s supply: only what’s held. ✅ built; the user checks
19 Welcome back’s password box has no way to show the password. Show/Hide beside the label, as the new-password form has (PasswordField), on Welcome back and on Settings’ two password boxes (Show recovery phrase, Change password’s current one). It starts hidden, and hides again when the screen is left. ✅ built; the user checks
20 Are account UTxOs found by address or by payment key? Everything under the account’s payment keys is the user’s money, whatever the staking part. By payment key now. Discovery still walks the gap limit over account_addresses; then every payment key in that range goes to credential_utxos (75 a request, under Koios’s 5,120-byte body limit), instead of account_utxos by stake key. Enterprise addresses and our key with someone else’s stake key are counted and spent; WebAssembly checks the address’s payment key against the path before signing, and refuses a script. Change still goes to 0/0, which gathers them back. Same request count. ✅ built; the user checks
21 A way to set a collateral on the Cardano side, like Lace’s: in Settings, or automatically when there’s no pure 5 ₳ UTxO. Settings → Collateral, after Lace’s sheet. The collateral is one pure-ADA 5 ₳ UTxO under the account, kept out of every payment and put up by the account-paid mint (build::account_mint takes it, and never spends it). Automatic: with none chosen, the wallet takes the oldest pure 5 ₳ UTxO the account holds, with no transaction. That replaces “5 ₳ UTxOs stay put” (the builders spent none): now only the collateral does, and other 5 ₳ UTxOs are spent like any. Set collateral: from a 5 ₳ UTxO the account holds, nothing is sent; with none, it pays 5 ₳ to the account’s own 0/0 (reviewed like a send; its output 0 is the collateral from Send on, “waiting” until it’s read, for up to 10 minutes). Reclaim collateral returns it to the balance, and the wallet then takes none by itself until one is set again. Nothing pays 5 ₳ without asking: v1 needs no collateral of the user’s (the mint picks one of its UTxOs without one), so an automatic payment would only lock money away. Seedelf spends never use it (giveme.my lends theirs), and the page says so. Koios: none to see, take or reclaim it; the payment is Send’s 3 at Review and submittx. ✅ built; the user checks
22 A way to view UTxOs on both sides. UTxOs on each Home tab, under Activity (the two now share a card). Kept ones first (locked, the collateral, a Seedelf’s), then largest first: the ADA, how many tokens, a tag, and the outpoint. A UTxO opens its details: its tokens, the transaction with Copy, the output, the block, and on the Cardano side the address. The Seedelf side’s privacy note: only this wallet can tell they’re yours, and looking one up on an explorer tells that site. From the last reading: no requests. ✅ built; the user checks
23 A way to lock UTxOs on both sides, so they can’t be included when spending. Lock and Unlock in a UTxO’s details. A locked UTxO is left out of every payment on its side, Max included: the builders only ever see the rest (core’s account builders now spend what they’re given; the web wallet leaves out locked UTxOs and the collateral). Home still counts it in the balance and says so (“2 UTxOs · 25 ₳ locked”); the forms offer only what’s unlocked (“3 ₳ available · 25 ₳ locked”), and say when everything is locked. A Seedelf’s UTxO can’t be locked (only Remove spends it), and the collateral is reclaimed in Settings, not unlocked. The choices are sealed on the device (coins.<network>, with Contacts’ key): which Seedelf UTxOs are yours is exactly what the contract hides. No requests. ✅ built; the user checks
24 Locking from the list: a lock icon at the right of each UTxO row, while the row still opens its details, with their Lock. A lock toggle at the end of each row (aria-pressed, teal when locked): it locks or unlocks at once. The row opens the details as before. The list’s order is set when it’s read, so a row stays put while it’s toggled; locked ones come first the next time. A Seedelf’s UTxO and the collateral have no toggle, only their tag. ✅ built; the user checks
25 UTxOs and Activity need a refresh like Home’s, instead of going back to Home to refresh. Home’s refresh row on both (“Updated just now” and the button; one RefreshRow now). UTxOs and the Seedelf side’s Activity: Refresh is a balance reading, the same 3 requests as Home’s (arrivals are noted by it), and Home picks up the new reading. The Cardano side’s Activity: Refresh asks for what’s newer (account_txs after the newest block, then tx_info only for new ones). Opening either screen still asks nothing new. ✅ built; the user checks
26 A Seedelf created without a tag was shown as “Unnamed”, which could be a real tag. No stand-in anywhere. An untagged Seedelf shows only its token name: Create’s preview (“With no tag, it’s listed by its token name alone”) and review, Receive’s list, Send’s “Found” line and review, and Remove’s title and review. ✅ built; the user checks
27 A Seedelf’s UTxO details overflowed the card: its full token name wasn’t cut. The worker sends the tag and the name apart (seedelf: { name, label }). The details say “It holds your Seedelf tag” (or “one of your Seedelfs”), and show the name cut to fit, with Copy for all of it. The e2e test checks nothing spills out. ✅ built; the user checks
28 A UTxO holding many tokens makes its details too long. The first five tokens by name, then “Show all N tokens”, which lists every one in a box of its own (at most 260 px, scrolling), with a search from 10 tokens up. The details keep their size, and Lock stays in view at the foot. ✅ built; the user checks
29 The transaction banner at the top: its text and Dismiss should be centred, and Dismiss blended into the teal. One TxBanner, centred: the status with its spinner or tick, the Cardanoscan link under it, then Dismiss as a neutral pill (white text on 10 % white, a light border), which stands out on a confirmed banner’s teal. The Collateral page’s “waiting” uses it too. ✅ built; the user checks

Compared with Lace (2.4.0) and left out on purpose: hardware wallets, swaps, Buy, other chains, mobile, analytics, the light theme (decided earlier); NFT images and fiat prices (new hosts: IPFS, CoinGecko); Identity Center, Passport, RealFi, Earn Rewards, the dApp Explorer and Lace’s notification centre (Lace’s own); languages, web+cardano: links and a QR scanner (not yet worth it). The dApp connector and Authorized dApps come with the contract round trip, after v1; the collateral setting came early, as item 21.

Follow-ups noticed on the way