Seedelf-Wallet

Flows

How money moves

flowchart LR
  Ext["Exchange or<br/>other wallet"] -- "pay" --> Dep["Public account<br/>(the Cardano account)"]
  Dep -- "make private" --> S["Private balance<br/>(Seedelf's wallet contract)"]
  Dep -- "send" --> Addr
  Dep -. "stake, vote" .-> Pool["A pool, a DRep"]
  S -- "send privately" --> Other["Any Seedelf"]
  Dep -- "send" --> Other
  S -- "make public" --> Addr["Any address"]
  S -- "out" --> OT["One-time account"]
  OT -- "CIP-30" --> D["dApp"]
  Dep -- "CIP-30 (chunk 15)" --> D
  D -. "proceeds" .-> OT
  OT -- "auto-return" --> S

Names: the wallet is one wallet on Cardano with two sides, and the screens say so (chunk 14): the private balance is the Seedelf balance, and the public account is the Cardano account. The flows are named to match: Make private is a move-in, Make public a withdrawal, and Send on each side a transfer (privately, to Seedelfs) or a send (publicly). The code and these docs’ internals keep the older names (move-in, withdraw, transfer).

Koios, here, is the server the wallet reads Cardano through and sends to. On mainnet that’s Seedelf Wallet’s own data layer first, and Koios for a part it can’t answer or with Settings’ Koios-only switch on (architecture.md); on preprod, Koios. Since 1.4.0 the screens say “the server” wherever Koios was named for an answer either may give.

The private balance (the Seedelf balance) means every UTxO at the wallet contract whose register this wallet owns. A Seedelf is a named token (5eed0e1f…) sitting in one of those UTxOs. Its register is what other people use to pay you.

Opening the wallet

The toolbar button opens the wallet in a full tab, or brings back the one already open (chunk 14). Settings’ Open Seedelf Wallet in switches it to the side panel, as Lace does: Chrome’s panel beside the page, which stays open as the user browses. Switching opens the wallet the new way at once and closes the page it was chosen on. The choice is the browser’s, not the wallet’s: removing the wallet keeps it. There’s no popup.

Back: each screen’s Back button, and since chunk 23 the browser’s too (Alt+←, a mouse’s back button): while a screen with a Back of its own shows, the page keeps one history entry of its own, at the same URL, and the browser’s Back presses the screen’s. Nothing goes in the URL, so a reload still starts at Home (ui/history.ts).

Every screen opens at its top (blind test §9.10): its title, its “Nothing is sent until …” and a banner are what’s seen first. The page is what scrolls and screens swap inside it, so each one used to keep the last one’s offset: “Review: stop staking” opened at its red button, and Home came back from Create Seedelf with its “sent” banner out of view. A Screen goes to the top when it opens and when its title changes (a form into its review, a list into an item, one step into the next), but not when the language does; Home does on coming back, on a change of tab and on the top bar’s mark; and the browser’s Back no longer restores an offset of its own. Back opens a screen where it was left, the app’s Back and the browser’s alike (pass two of the blind test’s fix round): Back to a long list (Settings, the governance actions, the pools) landed at its top, and the reader looked for their place again. Screen keeps each page’s offset as it scrolls and puts it back when Back leads to that page, following it for a moment while a list draws; going forward, and Home whichever way, still start at the top. Settings’ header stays in view as it scrolls, with Back and the title (blind test E05: on its 2,000 px page the way out had scrolled away). On a window 600 px tall or less, a review’s foot follows its rows instead of staying in view, so “Total leaving …” and “… after” come before its button (T04 at 360×480); the sign windows’ feet always do, and so does a site’s connect window’s, and every other screen’s stays in view.

Onboarding

Onboarding runs in a full tab. From the side panel, Create and Restore open one, and a line under them says so before the panel closes (chunk 23). A tab opened that way (#create, #restore) drops the hash once the wallet exists, so a later Remove wallet lands on the welcome screen, not on Create’s first step with a new phrase (chunk 23’s second review).

Lock and unlock

Settings (the gear in the top bar, while unlocked): in a build with both networks (every build but a preprod-only one for tests), Network (Mainnet or Preprod, which asks first and says what the other network is, the network it’s on staying pressed until the move is confirmed, chunk 23’s second review; while preprod is on, a strip under the top bar of every screen says its ADA has no value; on mainnet, Read Cardano through Koios only, with a line under it saying which server the wallet is using: Seedelf’s data layer, Koios for some parts until a time (the latest of their holds, 1.5.0’s release review, C13) after it didn’t answer, or Koios alone); Wallet: Public accounts (chunk 18: the accounts the phrase has used, to name, find and switch to, Look for the next account, and an account number to Check it or Add it; see keys-and-accounts.md), Contacts, Collateral; Preferences: Language (English, Spanish or Japanese, chunk 19, with a link to report a translation error), Open Seedelf Wallet in (a full tab or the side panel; see Opening the wallet) and Show ADA’s value in (eight currencies, or nothing, which asks for no prices; mainnet only); Sites and mixing (chunk 23: each a page of its own, since inline they made Settings one 3,400 px page; its row says what it does now, “On: sites can ask to connect” or “On: money from swaps and sites returns mixed, 2 waves deep”, where a bare “On” read as the feature itself, chunk 23’s second review): Sites, the dApp connector (off, and disabled with the reason, on a Chrome that can’t keep sites out of the wallet’s storage), The account sites use (with several accounts, chunk 18), Ask for your password to sign for a site (on by default) and Connected sites; Lovejoin: whether private sessions come back through it (on by default; off, they come back directly, and it says what that ties and saves), how deep each box mixes and how long it waits, what a mix costs on this network, that it hides boxes from people reading the chain, not from Koios or giveme.my, and that it has had no third-party audit; Staking: whether payments spend the rewards, and that turning it off keeps them apart until you withdraw them (chunk 23’s second review); Security: Lock after, Show recovery phrase (the password again first), Check recovery phrase, Change password, Remove wallet (typed confirmation; it first lists what’s still open on each network, and with anything listed needs Remove it anyway, leaving that behind too: see below); and About (the version, the network, the source code and the privacy policy, which says whom the wallet talks to and what each sees; a paragraph repeating it here went, at the owner’s call, 2026-10-06, as giveme.my’s owner is still said where it’s used, Collateral and the reviews, and each private review’s giveme.my note now says Koios sees the transaction from the IP address that reads the public account, which the paragraph alone had said). Settings asks Koios nothing, except to set a collateral by payment (and, while it’s on its way, to ask about it every 15 s, tx_status, as Home does), to look for or check a public account (one account_addresses request each), and to disconnect a site on a private session, which reads the session’s account first.

Remove wallet deletes the wallet from this browser, and the welcome screen after it says “Wallet removed from this browser.” (chunk 23). It keeps where the wallet opens, the network, the language and Read Cardano through Koios only, so the restore after it asks only the server that switch chose (1.5.0’s release review, C08). Its note says the phrase brings back the private balance, the Lovejoin boxes and the public accounts up to the first unused one (a restore looks only in order, so an account added by number past an unused one doesn’t come back by itself; release review C09), and what private sessions’ accounts hold, which the restore scan finds (chunk 27; until then it said that money didn’t show after a restore yet, the promise independent review M5 made), though not what a site’s session left open at the site, like a listing or an order the wallet can’t cancel, which it says to close there first (C17). Its warning to have the phrase first links Show recovery phrase, whose Back comes back to Remove wallet. Before anything is typed it lists what’s still open, per network (independent review M2, M5): a payment Koios didn’t answer, which may still go through (an encrypted record of it stays in this browser: restoring this same phrase here watches it again, but making or restoring another wallet here first deletes it, and while nothing watches it a payment could pay twice); private sessions still open, pointing to Bring everything back or a running swap’s Stop (a restore’s scan finds what their accounts hold, but not what each was for: a swap’s approval, a site’s connection, nor what a site’s session left open at the site), one the scan found named “found after a restore”; something left behind at a session’s account; a Lovejoin chain still being sent, which removing the wallet stops partway; and a mix from the public account stopped at a transaction that may have gone through (an encrypted record of it stays too: the same phrase restored here looks for it again before another mix from the account is built, and another wallet made or restored here first deletes it; final review F1). With anything listed, or when it couldn’t check, Remove wallet waits for Remove it anyway, leaving that behind as well as the typed confirmation. After a restore, the wallet asks Koios how each Lovejoin box it has no record of went into the pool: a box a mix made comes back by itself as before; a box a deposit made waits, not mixed yet, for Mix again or Bring one back anyway; a box Koios hasn’t said the making of waits until it has, and Mix my boxes again can’t be used meanwhile (the owner’s call on independent review M14).

Check recovery phrase (chunk 14, after Lace’s recovery phrase verification) is for a written copy: type it (12, 15 or 24 boxes with autocomplete, or paste it), and the wallet says only whether it’s this wallet’s phrase, never which words differ, so it tells someone at an unlocked browser nothing a whole phrase they already have wouldn’t. No password is needed, and nothing is kept: a match empties the boxes. It opens on as many boxes as this wallet’s phrase has words, which the worker reads off the unlocked wallet, asking nobody (phrase-words, chunk 23’s second review): the length is no secret, every length being at least 128 bits. A phrase that isn’t one is said as restore says it: the first word off the list, by its number, or “These words don’t make a valid phrase”, never the core’s “checksum” (chunk 23’s second review).

Activity (the row at the bottom of each Home tab), newest first and grouped by day, after Lace’s Activity tab. Each side has its own list, and each list starts with a row for the other’s (“Public activity: Payments to and from your public account”, and the other way round): asked when ADA last came in, E01’s tester answered from Private activity alone (blind test §4 entry 8; the owner’s call). The other list is read only when that row is pressed, as from its own tab, and Back comes back to the list it was opened from, then to Home. Each entry opens its details, with the transaction on Cardanoscan. The link opens in the browser’s own profile, only when clicked; on a private entry it says that opening it tells Cardanoscan, and the browser’s history, that the transaction is yours (privacy review §3.4).

Contacts name the Seedelfs and addresses (or $handles) the user pays, after Lace’s address book. They’re managed in Settings, picked with Contacts above Send’s Seedelf name or Withdraw’s destination, and saved from either form with Save to contacts once it’s found. They’re encrypted on the device (see privacy.md).

UTxOs (the row under Activity on each Home tab, chunk 12) lists that balance’s UTxOs from the last reading, asking Koios nothing (Refresh there is Home’s, a new balance reading; the public side’s only exception looks, at most every two minutes, for a mix from the public account stopped at a transaction that may have gone through: tx_status, utxo_info; independent review L5): the kept ones first (locked, the collateral, a Seedelf’s), then the largest. Each shows its ADA, how many tokens, and its outpoint, and opens its details: the tokens, the transaction (with Copy), the output and block, and on the Cardano side the address.

Collateral (Settings, chunk 12, after Lace’s) is 5 ₳ of the Cardano account set aside for transactions that run a script: creating a Seedelf from the account, a Lovejoin mix from the public account (chunk 16), and a connected site’s transaction (getCollateral, chunk 15). Each public account has its own (chunk 18): the one shown is the account the wallet is on. It’s only taken if the script fails, which the wallet checks before sending what it builds itself (Ogmios), and it’s kept out of every payment.

See keys-and-accounts.md for details.

Several public accounts

One phrase holds many Cardano accounts, and the wallet works on one at a time (chunk 18). Each is a separate public account, with its own addresses, staking, collateral and locked UTxOs; the private balance is one for the whole phrase. The keys and the reasons are in keys-and-accounts.md.

Receive publicly (the public account)

Receive, on the Public tab or the first Get started step, shows:

Anything that can pay a Cardano address can fund the wallet. The screen also says that anyone can see what this address receives: to be paid privately, share your Seedelf’s name instead, with Show my Seedelfs’ names. That note is a warning, amber, since chunk 23’s second review (RX-4): in the teal of the privacy notes it read as reassurance. For a restored wallet, the funds already in the account show up here too.

Receive privately (the private balance)

Receive on the Private tab (chunk 12) is where your Seedelfs live. It asks Koios nothing: the list comes from the last balance reading. Chunk 23 made each a card whose whole name is the thing to share: the bold tag read as what to give out (the review’s SE-1).

Make private (public account → private balance)

This is the equivalent of the CLI’s external sweep, built by the same core code (seedelf-core::build). Built in chunk 7.

Send publicly (public account → any addresses or Seedelfs)

An ordinary Cardano payment from the account, so a user needn’t open another wallet to pay from it. Built by seedelf-core::build::account_send_many, which shares the move-in’s UTxO choice, change and signing. Added in chunk 12; paying a Seedelf (the CLI’s fund, from the account) and several recipients at once came in chunk 14.

  1. Send on the Public tab, between Receive and Make private. It’s disabled while the account is empty or a transaction is still confirming.
  2. To: an address or an ADA Handle, read and checked exactly as for a withdrawal, with Contacts. Your own account’s address gets a note: the payment comes back, less the fee.
    • Another of your public accounts (chunk 18) is allowed, and said: the note names it, by its number and, once renamed, its name too (“This is your own Account 2 · Savings”), as the connector’s windows do, and says anyone can see the two accounts paying each other and tell they’re one wallet’s. With several accounts, Your accounts beside Contacts lists every account but the one you’re on, derived on the device, so opening it asks nobody anything; picking one only fills the field. See keys-and-accounts.md.
    • Or someone’s Seedelf, by its whole name, found as Transfer finds it: “Found: tag · 5eed0e1f…”. Koios is never asked about its token. Contacts offers Seedelfs and addresses both.
    • It’s paid like a move-in, but under a fresh re-randomization of the Seedelf’s register instead of your own: WebAssembly checks the UTxO holding it and refuses an unsafe register, as for a transfer. Only its owner can spend the payment.
    • Your own Seedelf is refused, pointing to Move in, which does the same thing.
  3. What’s sent: an ADA amount or Max, and tokens from the picker. The minimum ADA is worked out as for a move-in. Max is the move-in’s: everything but the fee and what the tokens you keep need, and the collateral and locked UTxOs stay put. Each address gets one output (a Seedelf, one per 20 tokens), in order; the change goes back to 0/0.
    • Several recipients (see below): up to 20, each an address, a handle or a Seedelf, with its own amount and tokens.
    • A note (optional, chunk 14, as Lace’s “Add a note”): one line of at most 64 characters, written on the transaction as CIP-20’s message (label 674), which wallets and explorers show with it. The box counts the characters, and says anyone can read it, for good (a longer one pasted is cut to 64, and says so, chunk 23’s second review); paying a Seedelf, that it could say whose Seedelf this pays. Core refuses tabs and line breaks, and splits a note over 64 bytes into CIP-20’s lines. The fee pays for its bytes and the hash of them the body carries: 0.00242 ₳ more for “Invoice 42”, 0.00484 ₳ for all 64 characters. Make private, the private side’s flows and staking offer none.
    • An ADA Handle going to a Seedelf is warned about, as on Make private: in a private balance, anyone paying the handle from another wallet pays the contract with nothing to say whose it is.
  4. Review: where it goes, the whole address or Seedelf name, wrapped, never shortened (a shortened one is what an address poisoner’s lookalike matches), the amount and tokens, the note, the fee, any staking rewards it draws on (already in the public account’s balance: nothing extra leaves), the Total leaving your public account (with how many tokens leave too, chunk 23’s second review) and Public account after (chunk 23: no change row, which read as the balance left); with several recipients, what they get in all, the fee and those two totals come first, then each under “Recipient N” (see below). The account’s keys sign here, inside WebAssembly.
  5. Send submits exactly the reviewed transaction. No script runs, so no collateral and no giveme.my. A banner follows it to “Payment confirmed”. It’s listed in the Cardano account’s Activity from Koios, not in the Seedelf history.

Privacy: it’s paid in the open, from the account. The form says so, and that paying from Seedelf instead avoids the link. For a Seedelf, it adds that anyone can see the money went into Seedelf, though not whose Seedelf it is; for several recipients, that they can be seen to be paid together. A note is as public as the payment.

Several recipients

Send on either side and Make public each pay up to 20 recipients in one transaction, as Eternl’s multi-send does (chunk 14). Make private, Create and Remove pay one.

Read the transaction (every review, and the connector’s window)

Transaction details, on every review and on the connector’s sign window: the transaction itself, decoded from the CBOR that is about to be signed or sent — its inputs, outputs, datums, scripts, certificates, votes, redeemers and metadata — and a tab with the raw bytes. Added in chunk 17 (archive/plans/chunk-17-transaction-view.md), from the owner’s ask after the 2026-09-28 mainnet test. Eternl is the model.

Staking and voting (public account)

Stake the Cardano account with a pool, spend or withdraw its rewards, and delegate its vote, as in Lace, so a user needs no second wallet. Built in chunk 13 by seedelf-core::build::account_staking and the staking module, which patch certificates and a withdrawal into an account transaction (see architecture.md). Only the Cardano account stakes: Seedelf money has no staking part.

Privacy: staking and voting are public and name the account. Every screen says so, and the Staking and governance page says Seedelf money can’t be staked.

Be your own DRep, and vote (public account)

Register the account as a DRep, vote on the live governance actions, keep a profile, retire. Built in chunk 21 (archive/plans/chunk-21-governance.md): the same patch as staking’s, with DRep certificates and the body’s voting_procedures, signed with the DRep key (CIP-105’s 3/0, as Lace derives it) inside WebAssembly. Lace itself has no such screens: it hands GovTool the key over CIP-95, which the connector here does too (21b; see Connect a site).

Privacy: a DRep is paid for from the account, so the chain ties it and every vote to the account. Every screen and review says so; so does the note that private money carries no voting power.

Create a Seedelf

A mint links the Seedelf to whatever pays for it (see privacy.md), so the order matters: mint first, then move in.

The steps:

  1. Create a Seedelf on the Private tab. It’s disabled while a transaction is still confirming, or when there’s nothing to pay with. Until the wallet has a Seedelf, the Public tab says to create one before moving in. Reached with nothing to pay with, Review waits, says “Fund your public account first: it pays for your Seedelf”, and offers Show my public address, whose Back returns to the form (chunk 23’s second review, GS-2: it built, then said to make ADA private first).
  2. An optional tag (“Tag (optional, anyone can read it)”): at most 15 characters of printable ASCII, which the form calls plain characters, with no accents (chunk 23: “ASCII” was the only word for it); a character it refuses gets an error naming it.
    • It’s previewed as the wallet will list it, along with how its name starts.
    • Anyone can read it on chain, as its label says.
    • Without one, the wallet shows the Seedelf by its token name alone, never a stand-in such as “Unnamed”, which could be someone’s tag.
  3. Pay with: Cardano account (the default) or Seedelf balance, as two cards rather than the pill switch Home’s tabs use, since the choice decides what the Seedelf is linked to (chunk 23), each with a note on what it links. The account is chosen unless only the private balance can pay (with both empty it was the private balance, chunk 23’s second review, GS-2), and the private one’s note opens in plain words, not “A stealth mint” (PY-11).
  4. Review. Nothing leaves the wallet but chain reads, and for the account-paid mint one Ogmios evaluation.
    • WebAssembly picks the UTxOs that pay (pure ADA first, as few as it can). For the account, it drafts the transaction, Ogmios (through Koios) measures the policy, and WebAssembly finishes it. For a stealth mint it measures the policy and the spends itself (since the crypto review, 2026-09-25): a draft’s proofs would tell Koios which private UTxOs are yours, even for a review never sent.
    • Account: the account’s collateral is put up (see Collateral above). Without one, one of its UTxOs is, as in the CLI: ADA-only if there is one, a 5 ₳ one first. A token UTxO also works, since the collateral return gives its tokens back. The account’s keys sign here, as for a move-in. The fee is about 0.21 ₳, because only the policy runs.
    • Stealth: the inputs are proven under a new one-time key, and giveme.my will lend the collateral. The fee is about 0.26 ₳.
    • The review shows the tag, what pays, the ADA locked with the Seedelf (about 1.75 ₳, the minimum for its UTxO), the Seedelf’s whole name (after those, not first, since chunk 23’s second review, PY-11), the fee, and the total leaving the side that pays, with its balance after (chunk 23: not the change).
  5. Create Seedelf (the review’s button names the action, chunk 23).
    • Account: submits the transaction signed at review.
    • Stealth: only now does giveme.my see it. WebAssembly checks giveme.my’s signature and adds it with the one-time key’s.
    • Either way, exactly the reviewed transaction is submitted. A banner follows it from “Seedelf creation sent” to “Seedelf created”, on the tab of the side that paid, and it’s listed in the Private tab’s Receive.

Details:

Send privately (private balance → any Seedelf)

This is the equivalent of the CLI’s transfer, built by the same core code (seedelf-core::build::transfer). Built in chunk 9.

  1. Send on the Private tab. It’s disabled while the private balance is empty or a transaction is still confirming.
  2. The recipient: paste the Seedelf’s full name, 64 hex characters starting 5eed0e1f. Tags aren’t unique (anyone can mint “alice”), so the name is what counts. Spaces and capitals are tidied away. The label’s ⓘ says what a Seedelf’s name is (the name someone using Seedelf Wallet gives out to be paid privately) and what to do with an ordinary address; the line under the field says an address works too (blind test §9.7: “Seedelf name” stopped two testers, T04b and T06, and nothing on their way defined it).
    • An ordinary address or a $handle pasted here is said to be one, and both ways to pay it are offered, as two buttons, each saying in a line what it shows (blind test §9.7, T04b: offered Make public alone, a tester would have paid from the private balance and never learned the public account has an ordinary Send). Pay from your private balance opens Make public, and the payment “shows as coming from the Seedelf contract”; Pay from your public account opens the public account’s Send, an ordinary payment that shows as coming from that account. Each takes the address and the amount typed. When the public account can’t pay now (nothing in it, a transaction on its way), its button is off and says why, beside what it would show. With several recipients there are no buttons, since the others would be lost: the line says to pay it on its own (chunk 23: the rule for a Seedelf’s name was all it said, and the feature wanted was a screen away).
    • The wallet looks the name up in the whole wallet contract, the query a balance reading already makes, and shows “Found: tag · 5eed0e1f…”, or “No Seedelf with that name on preprod.”
    • Koios is never asked about the recipient’s token (see privacy.md).
    • Your own Seedelf is allowed, with a warning: the payment comes back to your Seedelf balance, less the fee.
  3. What’s sent: an ADA amount (the move-in rules: 6 decimals, the supply cap, “more than you have”, and the minimum worked out), and optionally part of any token in the Seedelf balance, each with its own amount.
    • Or Max (blind test §9.6: T05’s tester found the most by about 15 guesses, and Send publicly, Make private and Make public all had one): to a single Seedelf, all the ADA in up to 20 private UTxOs, the largest first (those holding a token sent first), less the fee and the least ADA the tokens you keep need, as Send publicly’s Max leaves them (build::transfer_most). Tokens added go with it; the others stay in the private balance with exactly their minimum, and nothing else stays, so with none kept the balance can be emptied. The form says so, and that anyone can see UTxOs spent together are one owner’s; the review gives the amount, says what stays with the tokens kept and why, which histories it merges, and what it left (past 20, a token that wouldn’t fit, a reference script, a return through Lovejoin being sent). Its figure is exactly what its review shows.
    • Up to 20 recipients at once (see Several recipients): build::transfer pays them all.
    • The form nudges towards round amounts, and says that sending right after moving in is easy to match by timing.
  4. Review. Nothing leaves the wallet but chain reads.
    • WebAssembly checks the recipient’s UTxO: it’s in the wallet contract, holds that Seedelf, and has a register as its datum. It refuses a register that a payment would be lost under: points that don’t decode, points outside the prime-order subgroup, or the identity (anyone could spend a payment to that).
    • The payment goes under a fresh re-randomization of the recipient’s register, never the register as found. The change goes under fresh copies of your own.
    • It picks the Seedelf UTxOs that pay: first the ones holding the tokens being sent (the biggest holdings first), then, keeping money with different histories apart where it can (privacy review §2.3): one UTxO that pays alone, the smallest that does; then UTxOs of one history; only then several, money you made private first and boxes back from Lovejoin last, one at a time. When that would take more than one transaction can spend (too many small UTxOs, or 16 KiB), it tries other orders, spending as few boxes as pay, and at the last the CLI’s own, so it never refuses what the CLI would pay (independent review M6). When it has to merge histories, the review says what’s spent together and what that ties, in a plain note, never that nothing else could pay: nothing to press, and Send goes as it is (independent review L39). While none of the UTxOs has a history on the device (after a restore, say), pure ADA, largest first, as few as it can.
    • The outputs are shuffled, so the change isn’t always last (§3.7).
    • The inputs are proven under a new one-time key, and WebAssembly measures the spends itself (uplc, as a Lovejoin chain is): no draft with its proofs goes to Koios’s Ogmios. Only the wallet script runs. The fee is about 0.23 ₳ for one input, and 0.27 ₳ for two.
    • Too much, and what has to stay (blind test §4.4, T05). Picking checks each choice with guessed budgets, about 1.3× what the scripts use; when none pays by them, the largest UTxOs together are measured before the amount is refused, so it’s refused only when the review’s own fee can’t pay it, and anything up to Max’s figure goes. Refused, it says about how much can go, from a build as Max would make it on the UTxOs already read (no request), and what has to stay: “Not enough ADA: with the fee, your private balance can pay up to about 26.08 ₳ here, since 1.64642 ₳ has to stay with the tokens you keep. Send them too to free it.” “About”: each build’s fee moves a few hundred lovelace with its one-time key. When the amount fits but what would stay is less than the least ADA the network accepts, it says that, with the least. It said “Not enough ADA in your private balance for this and its fee.” for amounts well within the fee on the screen. A refusal goes once the form changes.
    • The review shows the recipient (its tag, and its whole name, wrapped), the amount and tokens, the fee, the Total leaving your private balance (a payment to one of your own Seedelfs isn’t leaving it) and Private balance after (chunk 23: no change row, and no count of the UTxOs that pay; the transaction’s details have both).
  5. Send. As for a stealth mint: only now does giveme.my see the transaction. The review says so in a line of its own: giveme.my, run by Logical Mechanism, who make Seedelf Wallet, lends the collateral, free, 5 ₳ of its own, and sees the transaction with the IP address; an ⓘ says what collateral is and that nothing of the user’s is put up (blind test §9.5: “Send asks giveme.my to lend the collateral” said none of it). That’s true of every spend from the private balance: giveme.my’s fixed UTxO is the collateral input, its return goes back to giveme.my, nothing in the transaction pays it, and the network takes collateral only if a script fails, which the wallet measured at review. Before giveme.my is asked, Send checks the device’s record of what it has spent (spent.ts): one of the review’s inputs spent since, by another of the wallet’s transactions, refuses it as a stale review without asking giveme.my. When it refuses, or the review is too old to send, nothing is sent and Send gives way to Refresh and review again, which builds it afresh from the chain; the refusal’s own words, giveme.my’s among them, are under Details (chunk 23: the message said “refresh” on a screen with no Refresh, and Send stayed live). When the form behind the review can’t be built as it now stands (Home has since read less than it asks for, another page having spent the same UTxOs, say), the button goes back to the form, which says why; before, it did nothing (the release review). Make public and a session’s Top up do the same. The headline says who refused where the wallet can tell (blind test §9.5, T09: every refusal opened with a guess at the user’s own money): giveme.my’s refusal names giveme.my, says none of the money moved, and to wait a few minutes and try again if it’s refused again; an outage or a limit (5xx, 429) says to wait first; only a review the device knows is stale, or too old, keeps “something it spends may have been spent or changed since you reviewed it”. The refusal stays until the new review is in, its button saying Building a new review…, and the new review says “Review updated just now. Check it again.” over Send (chunk 23’s second review: the rebuild showed “Sending…”, and came back looking unchanged). The same goes for Make public, Create a Seedelf and Remove. One that went out already is never called stale: Send again, from a page that missed the answer (a restarted worker, a second page), says it was sent already and that Home shows it, and nothing is built or sent (chunk 23’s second review, fix round: “Nothing was sent” under Refresh and review again built a second payment). WebAssembly checks its signature and adds it with the one-time key’s, and exactly the reviewed transaction is submitted. A banner follows it to “Private payment confirmed”. Once it lands, only the private side is read again, not the public account (privacy review §2.9).

Details:

Make public (private balance → any address)

Built in chunk 10, by the same core code as the CLI’s sweep and remove (seedelf-core::build). Both are Seedelf script spends: proofs under a new one-time key, giveme.my’s collateral at Send, and the scripts measured in the wallet at review (Ogmios until the crypto review).

Send to an address

  1. Make public on the Private tab, whose first line says what it’s for: it pays any Cardano address or $handle from the private balance (chunk 23), and the payment shows at their end as coming from the Seedelf contract (blind test §9.7). The title’s ⓘ says what the name means: how money leaves the private balance, to anyone, your own public account included. It’s disabled while the private balance is empty or a transaction is still confirming. Private Send’s Pay from your private balance, for a pasted address, opens it with the address and amount filled in.
  2. To: a Cardano address, or an ADA Handle like $name; or several, up to 20 (see Several recipients, build::sweep_many). What’s typed is read after a short pause, and shown: “Sends to addr_test1…”, or “$name is addr_test1…”.
    • A handle is looked up through Koios (asset_nft_address), the plain name first, then the CIP-68 one. Koios sees which handle is asked about.
    • Only a normal address on this network is accepted: not a script (its output would carry no datum), not a stake address, not the other network. The same goes for the address a handle resolves to. The message names which (“That’s a mainnet address; this wallet is on Preprod.”, a stake address, a script’s), in the user’s language, where WebAssembly’s one English sentence covered all of them (chunk 23’s second review). Send publicly reads its To the same way.
    • Your own Cardano account gets a warning: withdrawing there links the money back to it, and to whoever paid it into Seedelf. The wallet recognizes any address carrying the account’s staking key, as every address a normal wallet shows for the account does, and any whose payment key is the account’s (the first 20 of each chain, and those the last reading found), an enterprise address among them (privacy review §2.17). Since chunk 18 that’s every public account the wallet knows, not only the one it’s on, and the warning names the account, by its number and, once renamed, its name too (“Account 2 · Savings”); Your accounts beside Contacts offers them, as on Send publicly, with a note saying anyone can link the account picked to this money and to whoever made it private.
  3. What’s sent:
    • An amount (the move-in rules: 6 decimals, the supply cap, “more than you have”, and the minimum worked out), plus optional token amounts, as for a transfer. The change goes back into the Seedelf balance under fresh copies of your register.
    • Or Max: everything, up to 20 UTxOs at once (a transaction fits about that many script spends), with every token, less the fee. The largest go first, whatever their histories, and the review says how many are left for another withdrawal, and which histories it merges. The form says it sends all that fits in one payment, up to 20 UTxOs with every token, less the fee, and that anyone can see UTxOs spent together are one owner’s (a privacy note: “Spending them together ties them to each other” read as ambiguous, the blind test’s fix round).
    • The form nudges towards round amounts, and says that paying the money back to where it came from, such as the account that made it private, links its way out to its way in: said as an if, since “Making money public where it came from links it back” read as a warning against any payment (blind test T04b, T06). Paying one of your own accounts gets its own warning.
    • Too much says about how much can go, keeping the tokens not sent, and what has to stay, as private Send does (a build of the most a payment of those tokens can be, build::sweep_most; Max itself sends every token).
  4. Review: where it goes (the handle, and the whole address, wrapped), the amount, or “Amount (all of it)” for Max (chunk 23’s second review: “Everything” alone wasn’t a label), the tokens under “Tokens”, the fee, the total leaving the private balance and the balance after (chunk 23: not the change, nor how many UTxOs pay). The fee is about 0.27 ₳ for two inputs.
  5. Send, as for a transfer. A banner follows it to “Made public”.

Remove a Seedelf

  1. Remove, behind Manage on a Seedelf’s card in the Private tab’s Receive (chunk 23’s second review, RX-2; a link apart from Copy since chunk 23).
  2. Send what’s freed to: the side that paid for the Seedelf is chosen (privacy review §3.2), except where another of your public accounts paid: then nothing is, since the public account here would tie the two (the owner’s call, 2026-10-06). The wallet keeps who paid at each mint’s Send (sealed, per network); for a Seedelf minted before that or elsewhere, it works it out from what it holds, never asking Koios: another of its contract UTxOs from the mint’s transaction means a stealth mint, the account’s UTxOs or Activity an account-paid one. Otherwise neither is chosen, and Review waits for the user’s choice: either side can make a link they didn’t choose, so the wallet won’t make it for them. The note says the private balance is the safer guess, since it can’t tie the public account to private money already held, and each card says in a line what it links for this Seedelf, the note under them in full (chunk 23’s second review, RX-3, which first chose the private balance; its own review kept the choice the user’s).
    • Cardano account, its receive address 0/0. A Seedelf the account paid for (the default since chunk 8b) is linked to it anyway, so this links nothing new. For a stealth-minted one it ties the account to the Seedelf’s name and the private UTxOs that paid for it, and says so. When another of the wallet’s public accounts paid for it, the card says what the warning under the cards says (“Ties Account 3 to Account 1, which paid for this Seedelf”), no longer “Links nothing new”; the choice made for the user stays the same (the owner’s call, C08).
    • Seedelf balance, under a fresh copy of your register. This is for a Seedelf you minted from the Seedelf balance. For one the account paid for, it ties the Seedelf’s name to that new UTxO, and to whatever it’s later spent with, and says so.
  3. Review: the Seedelf, by its tag and whole name, what comes back (the ADA locked with it, about 1.75 ₳, less a fee of about 0.24 ₳), and the fee. Both scripts run: the wallet’s spend and the policy’s burn.
  4. Remove Seedelf (the review’s button names the action, chunk 23). The token is burned (the screens say the Seedelf is removed), and the banner follows it to “Seedelf removed”.

Details:

Connect a site (public account)

CIP-30 for the public account, as Lace offers it (chunk 15). The design is in architecture.md; the plan, with the private steps after it, in archive/plans/chunk-15-dapp-connector.md.

  1. Turn it on: Settings → Sites → Let sites connect to Seedelf Wallet, or Let sites connect on the dApps page. Each site then gets the public account or a private session, chosen in the connect window (see Any site below). Chrome asks to let the wallet onto https sites; only then are its two scripts added to pages, those loaded after and those open already, so a site open beside the wallet finds it without a reload (blind test §9.2, T15: it was found only after a reload, which nothing said). The switch says so, and to reload a site that still doesn’t list the wallet: it looked for wallets only as it loaded. Off (the default), sites can’t see the wallet. With several accounts (chunk 18), the public account a site gets is the one Settings → Sites → The account sites use names (account 0 by default), whichever the wallet is working on, so switching never shows a site a second account.
  2. Connect: a site’s Connect wallet lists Seedelf Wallet (when the site lists every CIP-30 wallet). Its enable() opens the connector’s window: the site’s address as Chrome reports it, and two choices under “Choose what the site sees”, Your public account and A private session, as two cards with what each costs inside it (chunk 23: a pill switch with its explanations in a list apart read as a tab, and nothing said a choice was needed), with neither chosen (privacy review §3.3). Each says what it costs: the public account shows the site its addresses, balance and UTxOs, which it keeps, and it can recognize this browser later; a private session gives the site only a new one-time account (what the wallet gives it: “sees only” read as a promise the note on what anyone can follow then qualified, blind test §7) for a network fee each way, about 0.25 ₳ each, and about a minute, in one line no harder to read than the public account’s (chunk 23’s second review, CW-7; blind test §9.8); the 5 ₳ kept aside, which comes back, is said under the amount once the session is chosen (the copy-trim pass). Once the session is chosen, it says in plain words how the money comes back: when Settings brings sessions back through Lovejoin, ADA to spare goes through Lovejoin, a mixer, first, for about 4.1 ₳ a 10 ₳ box at Settings’ depth (its mixes and bringing the box back) and one more network fee, for its deposit, and hours later; otherwise straight back, for one fee (T16: “as Settings has it” and “Lovejoin” meant nothing to the tester). With several accounts it names the account the site would get, by number and name (“Your public account, Account 3 · Savings:”), and says above the two cards, before either is chosen, that sites always use the one Settings → Sites chooses, whichever is on screen (chunk 23; blind test T15: said only once chosen, below the fold, it surprised a tester with Account 2 on screen). Connect waits until the public account is chosen, and comes after what choosing it gives the site: the window’s buttons follow its body, as a signature’s do, and the chosen option’s privacy note comes first under the two cards (the pass-two visual review: kept in view at 400×605, Connect could be pressed with that note wholly under it). Decline (it was Cancel, which didn’t say it refuses the site; blind test T17) has a line over it: “Decline, or closing this window, turns dapp.example away: it can ask again in 10 s” (or the longer wait a decline in a row starts). Declined, the window says so, “You declined dapp.example. It can ask again in 10 s, or sooner if you let it on the dApps page.”, for 4 s before it closes, with Close to close it now, where it said only “Nothing’s waiting.” (T17, T17r). The title’s ⓘ says what each choice means and where a site is disconnected (T16: it said only the last); Decline’s own line says what Decline does. With more than one request waiting, the top of the window says “1 of N waiting”, with Decline all, which declines each as closing the window does (chunk 23’s second review, CW-7), and then says “You declined every request.”, adding, when a site’s connect was among them, that it can ask again after a short wait and where to see how long.
  3. Use it: reads need no window. A transaction or a message to sign opens the window, which names what the site is connected to under its address: “Connected to private session N”, or with several accounts “Connected to Account 3 · Savings” (chunk 23), since a signature comes from the dApp account whichever account is on screen:
    • A transaction: what it does to the public account, all told (“Total leaving your public account”, as the wallet’s own reviews say it, or the account by number and name with several, “Total leaving Account 3 · Savings”; chunk 23’s second review, CW-5, CW-7, for “sends, net”; each token that moves), the fee and any staking money collected, each “counted in the total” (chunk 23: “(included)” said included in nothing), the fee, who it pays (a contract, Seedelf Wallet’s contract with or without a register, an address), the collateral at risk, any staking change, minting, a note, whether it runs contracts, and which keys sign. Sign or Decline. Unless Settings → Sites says otherwise, Sign needs the password, typed in the window’s foot over the two buttons (chunk 23: in the body, it started below the fold of the 400×640 window, and Sign looked broken). The foot follows the request rather than staying in view, so Sign is reached only past who it pays and every warning: kept in view at 400×605, it covered both (chunk 23’s second review, CW-1). The line under the title says where Sign is (“Nothing happens until you press Sign, at the end”), and on preprod the test network’s line sits in the window’s top bar rather than a row of its own: at 400×605 Sign sat 266 px under the fold with nothing saying so, and the privacy note was cut mid-sentence (blind test T18). A payment to another of the wallet’s accounts is named (“Your private session N”; “Your public account” for any of your public accounts, whichever is on screen, by number and name where there are several: “Your public account, Account 3 · Savings”), with a warning that signing ties the two together on chain, “It moves money between Account 1 and Account 3 · Savings”; an output is named by its payment key alone: someone else’s address, or a contract’s, that carries one of the wallet’s stake keys reads “An address” (or “A contract”), with a warning of its own, “Someone else’s address in it carries the stake key of Account 3 · Savings: that money isn’t yours. Signing still ties Account 1 · Main to Account 3 · Savings on chain, where anyone can see it.” (1.5.0’s release review, C01); another account’s own address past its first 20 keys, under its own stake key, is named as that account’s, never someone else’s (1.5.0’s cross-area review, X06; with no stake key or another’s, it isn’t: 1.5.0’s last review, L11); a certificate or a withdrawal that names another of the wallet’s stake keys names it (“A certificate for the stake key of Account 3 · Savings”), where it said “a stake key that isn’t yours”, with a warning, “A certificate or withdrawal in it names the stake key of Account 3 · Savings: signing ties Account 1 · Main to Account 3 · Savings on chain, where anyone can see it.” (X05), and a stake pool’s registration that makes one the pool’s reward account keeps its own line (“Registers stake pool …”) with that warning (L1), each once the wallet could check: when it couldn’t, the line still says “isn’t yours” (L3); the prompt says the other side isn’t in it only when it checked and found nothing (independent review M12; the release review). A site on the public account has its own account left out, and since chunk 29 the others named (before, a payment to another read “An address”).
    • A message (CIP-8): the address, what it signs with (“Your address”, “Your stake address” or “Your DRep”, chunk 23, for “Your payment key”; with several accounts, which one’s, “The address of Account 3 · Savings”, chunk 23’s second review, CW-5), and the message as text, or hex when it isn’t text. Signing moves no money.
    • A transaction that needs someone else’s signature, when the site didn’t ask for a partial one, or one that would hand the collateral to someone else, is refused before the window opens.
    • Governance (CIP-95, chunk 21b): a site that calls enable({ extensions: [{ cip: 95 }] }) (GovTool does) has the connect window say what governance gives under Your public account, and under A private session that a session has no DRep. Under Your public account it’s a switch, Give it governance too (CIP-95), off by default, the most private choice: left off, the site connects without it and isn’t asked again until it connects anew. A site connected already that asks for it later gets a short question of its own, Allow or Decline (it was Cancel; blind test T17); Decline, or closing the window, leaves it connected, without governance and not asked again. Granted, getExtensions() answers [{ cip: 95 }], the API has cip95 (getPubDRepKey, getRegisteredPubStakeKeys, getUnregisteredPubStakeKeys, signData), and the DRep key 3/0 signs the account’s own DRep certificates and votes, which the prompt states as sentences (“Registers your DRep (a 500 ₳ deposit).”, “It casts 2 votes as your DRep…”), and messages for the DRep by its ID or its key’s enterprise address. Settings’ Connected sites marks a site that has it.
  4. Locked: a connected site’s isEnabled() still says it’s connected, so polling it doesn’t show when the wallet is in use, but its reads are refused at once, in the words a stranger hears, with no window (privacy review §2.11). enable(), a signature or a submit opens the window, which names the sites waiting and asks for the password first. It has Decline, which closes it, and no Forgot password: a window a site opened never leads to typing the recovery phrase (chunk 23’s second review, CW-4). Sign still asks for the password after the unlock, as Ask for your password to sign for a site says. Closing it answers “The user declined.”, and for a while (below), while the wallet stays locked, that site’s signatures and sends are refused without asking, in the words a site that isn’t connected hears, never “Seedelf Wallet is locked.” (independent review L36); a site that isn’t connected can’t reopen it with enable() then either.
    • One connect question at a time per site (independent review L35): however many of its pages call enable(), they share one, and while locked they take one place in the window together, so a site open in many tabs isn’t refused for its own tabs. A connect question declined, or closed on, isn’t asked again for 10 s, then a minute, then five minutes for each decline in a row (back to 10 s after ten quiet minutes, or once the site connects); its enable() hears “The user declined this site just now. It can ask again in N s.” in the same words locked or not, which a dApp can show. A flat minute left a user who cancelled by mistake clicking Connect on a site that did nothing (chunk 23’s second review, CW-3). The wallet says it too (blind test §9.2, T17r: only the site did): the dApps page’s Sites and Settings’ Connected sites list each site still waiting, declined on the network the wallet is on and not connected, with the time left counting down, “and it asked again” once it has, and Let it ask now, which ends the wait. Only the user can press it, so a site still can’t bring the window back by asking; a decline after it counts as one in a row. The waits outlast the worker Chrome stops when idle, and a lock (the cross-area review: kept in its memory, a minute’s wait lasted half of one). One site has at most 5 requests waiting, of the window’s 20.
  5. Disconnect: Settings → Connected sites, whose row says how many there are, lists them, each with Disconnect, which asks first; the dApps page’s Sites lists the same, the site’s address first, then its page’s title and the account it gets with several (blind test E03, T15). The list is sealed on the device. Disconnected, a site’s next call is refused (CIP-30’s Refused, its only word for a wallet that’s gone), but CIP-30 has no event to tell an open page sooner: the wallet says “dapp.example is disconnected until it asks again and you agree. Its open pages may look connected until reloaded.” (E03: the open page still showed “Connected” and the balance). A site on a private session (Any site below) ends its session too, so its Disconnect waits, and says why, while the session’s funding, its return or the return’s chain through Lovejoin is on its way (a chain a lock, a closed browser or a refused transaction stopped isn’t: what it left waits at the account, so Disconnect says to bring everything back first; 1.5.0’s cross-area review, X10), or while the account held something when last read; the worker checks again, and its refusal shows. Right after a funding, a top-up or the site’s own transaction lands, it may say Koios hasn’t caught up with the session yet: try again in a minute (independent review M4). A funding or top-up that neither Koios nor any read has seen yet may still land, and Disconnect says so, and waits for it up to two hours after it was sent (final review F13). Disconnecting declines what the site was still waiting for; turning the connector off, or removing the wallet, declines every site’s (independent review L33).

The account’s own outputs of a transaction it signed are kept, so a site can build its next transaction on them before they’re on chain. Among the wallet’s recent sends, a site finds only the outputs that pay its own account; anything else it asks about goes through its lookup budget and Koios, as a stranger’s does, and a signature is checked only against its own account’s Lovejoin chain (privacy review §2.1, §2.2). A site’s submitTx goes through Koios, as the wallet’s own sends do, and its own outputs are kept for that site alone. What its submits spend is kept apart from what the wallet spent, so a site can’t push that out (independent review L7).

Contract round trip

Money leaves Seedelf to use a contract, then comes back: a private session on a one-time account (account 24301', payment key 0/i and its own stake key 2/i; see keys-and-accounts.md). The plan is archive/plans/chunk-15-dapp-connector.md.

There are two ways to pay a contract privately, the owner’s designs (2026-09-25), and the dApp decides which:

  1. A round trip through a one-time account (built). Private UTxOs fund a never-used key account with the amount and 5 ₳ of collateral of its own. The dApp uses that account as an ordinary wallet, and what the contract pays back lands there. The wallet then brings everything at it back into the private balance, the collateral too, signed by the account’s key, with no giveme.my.
  2. Straight from Seedelf (designed, not built). Seedelf inputs, proven under a one-time key with giveme.my’s collateral, pay the contract, and the proceeds go to a fresh account, or straight into Seedelf where the contract can pay one (A2, below).

giveme.my checks every input against the chain before it signs, so it can’t back a chained transaction. A dApp whose flow chains needs the round trip through a one-time account with its own 5 ₳ collateral. Paying a contract straight from Seedelf fits only a dApp that takes a payment with a datum and runs nothing at submission, because the wallet must build that transaction itself: a dApp’s own transaction can’t be re-signed with Seedelf inputs swapped in, since that changes its body, breaks any signature the dApp added, and shows its scripts a Seedelf input where they expect the user’s.

The first built use is a swap through Minswap’s aggregator (A1, below). Its build-tx takes only a sender: it picks the sender’s UTxOs from its own view of the chain and pays the proceeds and any refund back to the sender (minswap.ts). So a swap needs a key address, funded and confirmed first, which a one-time account is. Private CIP-30 (Any site) offers the same session to any site.

A2, a Minswap V2 order paid straight to a Seedelf (designed, not built). A V2 order’s success and refund receivers may be a script address with EODInlineDatum { hash }, which the batcher must pay with exactly that datum (minswap-dex-v2’s order_validation.ak, validate_order_receiver). So an order built from Seedelf inputs under giveme.my’s collateral could pay its proceeds into the contract under a fresh register, with no return step. Minswap’s SDK takes a 2 ₳ deposit for an order; the order’s canceller authorizes a cancel (a key’s signature, OAMSignature), or, with an expiry set, anyone may cancel it after that for a tip of at most the order’s max_tip, the rest going to the refund receiver. It covers Minswap V2’s own pools, not the aggregator’s routes, and waits on one preprod swap showing a batcher fills an order whose receiver is a contract.

A private swap (built in chunk 15, runs itself since 15b)

Home’s Private tab → dApps, a grid of the dApps the wallet uses privately → Minswap: its swaps, newest first, each with where it’s at, and New swap. While a swap runs, Home’s Private tab shows it under Swaps in progress, a row that opens it, and Minswap’s tile says how many run.

  1. New swap, in Minswap’s shape: a You pay card over a You receive card, each a big amount beside its token, with a round Switch between them that swaps the two sides.
    • You pay: ADA or a token in the private balance, with what’s held under it, and Half and Max. ADA’s Max leaves the swap’s costs, the collateral and 1 ₳ for the fee, and says so under the cards once it’s used (“Max is 16 ₳. The other 12 ₳ pays the swap’s costs…”, chunk 23’s second review). Both round down to a whole ₳ or token unit, so the amount looks typed (what’s left stays in the private balance), and the form says they still tell Minswap roughly how much the private balance holds (privacy review §2.13).
    • You receive: any token Minswap lists, picked from what’s held or from the wallet’s own token list, searched on the device (privacy review §3.11). Minswap’s list is searched only when neither matches, or on Search Minswap, and Minswap then knows what was searched for.
    • The quote: it comes in as you type, asked once typing pauses for 0.6 s, since Minswap limits how often it’s asked. What you receive fills in, with the rate under the cards; tap the rate to turn it round. Its details show the minimum received, the price impact (amber from 3%, red from 5%, with a warning), the slippage, the route (one DEX for each part of the swap, never on through a second: DEXes that take orders, or Danogo’s pools on their own), the DEX’s fee, and the order’s deposit (back with the proceeds). The refresh button asks again. A quote for another amount or pair than the one typed is refused, in words, rather than shown (chunk 29; the usability review’s DX-8): approved, it would set the minimum for an amount the user isn’t paying.
    • Slippage: a chip at the top of the form with its value (“Slippage: 1%”), amber from 5% (chunk 23’s second review: it was an unlabelled sliders icon). It offers 0.5, 1 or 3%, or your own from 0.1% to 20% (a field with no placeholder, so nothing looks set that isn’t). From 5% a warning says the order can be filled for up to that much less than the quote, in the dialog, on the form and on the review.
    • The button says what’s missing (Select a token, Enter an amount, Not enough ADA), then Review swap. A quote over a minute old is asked for again before the funding is built on it.
  2. Review the swap: what you pay and receive about, at least, the slippage, the price impact and the route, and, by them, a warning line that the wallet relies on Minswap for that minimum: it asks Minswap for it, but can’t read it back from the order Minswap builds (chunk 23’s second review; reading it would mean decoding each DEX’s order datum). Then the funding: The swap and its costs (it was a bold “For the swap”, which read as the total; blind test T10), broken into the swap, the DEX’s fee, Minswap’s, the order’s deposit (none against Danogo’s pools, so no row) and the room left for network fees, set in under it so they read as its parts, and add up to it; the 5 ₳ kept aside, the fee, and, as every other review says them, Total leaving your private balance and Private balance after, not the change. Then What the swap costs, and what comes back: Costs in all, about first, then, set in under it, the DEX’s fee, Minswap’s, the network fees of its transactions (this payment’s exact, and about 0.25 ₳ each for the order and the return, which come out of the room: nothing prices them before they’re built, so the figure is a measured estimate, ui/swap.ts; a swap against Danogo’s pools runs their scripts, so about 0.75 ₳ for it instead of the order’s, chunk 24), Lovejoin’s when the return takes a box (its mixes, the boxes’ way back, and its deposit as a fourth fee), and what comes back, what’s received and the ADA that isn’t used up (blind test §9.8: one Start swap approves three transactions, and T10’s tester added up Transaction details to find what left). Then the steps it takes by itself, and Start swap (“Nothing is sent until you press Start swap”).
    • On the way back, through Lovejoin (on as Settings has it; a switch here, Bring it back through Lovejoin, turns it off for this swap, and the runner keeps to it): the spare ADA, and a token→ADA swap’s proceeds, go through Lovejoin first. Review reads Lovejoin’s pool once (kept five minutes, never as you type), and prices only the boxes it has room for; with none, it says the ADA would come back directly, tied to the session (privacy review §2.7), and the switch’s own note says it applies only if the pool has room by the time it comes back (chunk 23’s second review). It shows the boxes, their mixes and fees, about 0.3 ₳ a box to bring them back, and the wait, with the no-audit note. It says this swap keeps that depth and wait: Settings’ Lovejoin changes only swaps started after it, turning Lovejoin off there doesn’t change this one, and Stop can bring it back directly (independent review L21).
    • If it’s stopped or refunded: an ADA→token swap’s funding would come back through Lovejoin, and the review prices that too, with the no-audit note (privacy review §2.8).
  3. The funding (Start swap, the one approval): a Seedelf spend with giveme.my’s collateral, paying the session’s account twice: the swap with its costs and 2 ₳ of room, and 5 ₳ as the account’s own collateral. The change goes back into the private balance. The session is recorded before it’s sent, so this browser never uses its account twice, even if the send fails, and Send first asks the chain once more that the account is unused, in case the phrase used it elsewhere (independent review M13). Home’s banner watches this payment; the user lands on the swap’s own page. Refused (giveme.my turns it away, the review is too old, the account was used meanwhile), Start swap gives way to Build it again, which builds a fresh review on the next unused account, giveme.my’s or the node’s own words under Details; giveme.my’s refusal is named up front, with the money unmoved and what to try if it happens again (blind test §9.5); the refused try’s session stays, as failed, with why (“giveme.my, the service that lends its collateral, turned it down”, where it said something it spends had changed, or, for its outage, that it “couldn’t take it just then”). One that may have gone out all the same (Koios didn’t answer, or it went and the page missed the answer) isn’t built again, since both would run, whatever the refusal: its record is read first (chunk 23’s second review, fix round). See where it is opens its page (chunk 23’s second review: Send stayed, and pressed again said “That session was started already”). Leaving the review takes its alert with it.
  4. From here it runs itself, on a timeline of four steps: Funded, Order placed, Filled, Back in your private balance. Each shows waiting, done (with its transaction on Cardanoscan) or paused, and a line under them says what’s happening in plain words. The page checks every 20 s (Refresh checks now); with the page closed, the worker checks once a minute while the wallet is unlocked.
    • The order, once the funding is on chain: a fresh quote and Minswap’s swap for the account (Minswap picks the account’s UTxOs itself, so it waits for the funding). WebAssembly reads it against the session’s key alone; the key signs it, the signature goes in without changing a byte of Minswap’s transaction, and Koios submits it. It’s placed only within what was approved: the session’s UTxOs, its key alone, no more paid out than was funded, and paid only to the session, to an order made out to it, and to Minswap’s quoted fee; and only once the wallet has built each order’s own cancel and run it through the DEX’s script, so an order no cancel could spend pauses (“it places an order the wallet couldn’t cancel”), and a Koios that can’t give that script holds the swap as a retry (1.3.0’s release review, C05). Minswap builds the order with its own fresh minimum, and won’t build one under the approved minimum; after a dip within the slippage, the wallet asks with only the slippage that keeps Minswap’s at the approved one. The order’s minimum isn’t read back. The timeline’s “Asked for at least” is what the placed order asked for, once one is (independent review L24). Anything else pauses and says why, with Try again and Review it myself (the order’s review, as a button used to give: the user’s own, held to the same checks except the funding limit, and to its own fresh quote’s minimum and Minswap fee, which it shows; on a route through Danogo, the swap’s review, Review the swap: what the session puts into the pools, its own net, never the pools’ reserves, Minswap’s fee, the network fee and what’s back, with no order, fill, Stop or collateral row, and an aside saying the collateral’s owner signs it too; C10). On mainnet that includes Minswap’s fresh route going through a DEX the wallet doesn’t check (independent review M17), and, on either network, a route Minswap still makes of more than one leg, or with Danogo beside another DEX (C34). After Stop, a cancel the wallet can’t build or won’t sign pauses with “The wallet couldn’t cancel the order: … Try again builds the cancel afresh.”, and the list says “Couldn’t cancel its order” (C15).
    • A route through Danogo (chunk 24) has no order: Minswap’s transaction swaps the account’s funds with Danogo’s pools at once, and carries Minswap’s own collateral, signed already. The wallet signs it only when it spends nothing of anyone’s but the pools’ and that collateral, and puts up none of the account’s own as collateral; pays only the account, the pools it spends (each where it was) and Minswap’s fee; pays its fees out of the funded costs; and gives the account at least the approved minimum. Review it myself is held to the same, but to its own fresh quote’s minimum and Minswap fee, which it shows, not the approved ones. If Minswap routes a swap approved against the pools through an order instead, the runner pauses it; Review it myself, the user’s own review, may still send that order (1.3.0’s release review, C08, C09, C21, C22). The quote’s plan and the timeline, from the approval on, say Swapped against the DEX’s pools and filled In the swap itself (C13). While it’s on its way, the line under the steps says “The swap is on its way: waiting for the network to confirm it.” and the list “Swapping”; once it lands, “It all comes back to your private balance next, by itself.” and “Filled”. Once it has gone out there’s no Stop: nothing waits to be cancelled. If Stop was pressed as it went out, nothing says “Cancelled”: a warning says it can’t be cancelled, it’s swapped if it lands, and everything comes back either way, and a Stop confirmation opened as it went out closes (C24). If someone else’s swap took the pool first, the network refuses it, and the runner builds it again, or, when Koios took it, once it has gone unseen for 15 minutes (C03). If that swap built again pauses, Review it myself builds it again too, and a Stop pressed then warns that the swap went against the pools, never that an order went out (X04).
    • Filled: a DEX’s batchers pay the proceeds to the account, usually within a few blocks. It waits as long as it takes: the wallet never cancels an order by itself. An order the DEX refunds shows as Refunded (“Refunded: the order wasn’t filled, so what you swapped is back in your private balance.”), and a route split between DEXes with a leg refunded as Partly filled, never as done (independent review M18).
    • Back: what’s at the account into the private balance, under fresh registers, signed by the session’s key. A token UTxO the session’s own transactions didn’t make comes back only when its own ADA pays its way; one that doesn’t stays at the account, and the swap’s page lists it as left behind (independent review H1, H2). With Lovejoin on for this swap, the spare ADA (a token→ADA swap’s proceeds too) goes in as boxes of 10 ₳ first, each coming back later on its own; the rest comes back now, merged into the funding’s change. Once it’s on chain, the account is empty, Koios shows the funding’s outputs spent and no order of the swap is still open, the swap is done, and the whole card turns the success colour (independent review M4, L15), with a Received row: what its return brought back at once, as that return’s review said (a chain’s boxes come later; chunk 23’s second review). The account is never used again.
    • Each step’s transaction is on Cardanoscan, with one note under the steps saying what opening it tells.
  5. Stop, there until the order is filled or refunded (after that everything is coming back anyway, so it goes; chunk 23’s second review), is always the user’s: one confirmation, then an order that waits is cancelled (by the wallet’s own cancel, built from the order on chain with the session’s own keys and collateral, its fee from the orders’ ADA and the account’s change, the 5 ₳ kept aside spent only when nothing else can pay it (1.3.0’s release review, C01), never through Minswap, whose cancel route is gone: chunk 24, Step 3; read and signed as the swap was, a SundaeSwap V3 order’s by the session’s stake key too, as its owner; the refund comes back to the account) and everything comes back. Before any order, everything just comes back. A cancel the wallet can’t build or won’t sign pauses with “The wallet couldn’t cancel the order: … Try again builds the cancel afresh.” (C15), and the timeline’s step reads “Couldn’t cancel its order: An order is still open at a DEX” until a cancel goes out, never “Cancelled” (1.4.1). The confirmation reads the swap as it opens, and closes when that reading leaves nothing to stop (a swap against the pools gone out, the order filled or refunded, or one stopping already), the page showing that reading (C24); with no order placed, it says “If no order has gone out yet…”, since the runner may be placing one (for a swap approved against Danogo’s pools, “If the swap hasn’t gone out yet…”, and that one going out can’t be cancelled, 1.4.1), and when one had gone out before Stop took effect, the page says so, and that it’s cancelled unless a batcher fills it first (independent review L22), or, for a swap against Danogo’s pools, that it can’t be cancelled and everything comes back either way (above). After a cancel, the return waits until every order of the swap is spent and Koios lists the cancel’s refund (C12), and cancels one still open (a split route’s second, say) the same way (independent review L16). Should Koios not find one of them yet, the page says so plainly, with a neutral Order open tag, and it’s cancelled once Koios does. When it would come back through Lovejoin, the confirmation opens with the approval’s switch, Bring it back through Lovejoin, on as approved, and under it what that takes, as the worker works it out then; turned off, it says the return’s one fee and that anyone can tie it on chain to the session and its funding, and the button reads Stop and bring it back directly (privacy review §2.8, §4.1). That way was a link under the costs before, and the owner, stopping a real swap after a price drop, never saw it.

Pause and resume: locked, the swap waits, and unlocking carries on after a fresh wait, from 2 minutes to at most 20 after the unlock (less with a short auto-lock), rather than at once, whichever check finds the step first (privacy review §3.1; independent review L10, L12; what the user asks for still goes at once). A Lovejoin chain is sent only while the wallet is unlocked: locking partway stops it, even while a transaction waits to be tried again, and a lock and an unlock during that wait stop it too (independent review L14). What’s left comes back directly. A closed browser, a restarted worker, or a wallet opened hours later all carry on from what’s stored and on chain. A failure (Koios down, Minswap’s rate limit) turns the step amber and says what’s wrong and when it tries again (30 s, doubling to five minutes), with Try now. The auto-lock setting doesn’t change for a swap.

If a session stalls, everything is recoverable from the phrase: money left in the account comes back; an order that never fills is stopped, then brought back. A session whose funding never reached the chain shows so, with why it was turned away when the wallet knows (something it spends had changed, the collateral service out of reach, the network busy or refusing it; chunk 23’s second review), and can be removed from the list (its index isn’t reused), unless Koios knows one of its funding’s outputs unspent: then it landed after all, so it isn’t forgotten, and it goes on (a swap or a mix runs again; independent review M4). Sessions from before swaps ran themselves keep their buttons (Place the order, Cancel the order, Bring it back). After a restore, or with the phrase in another browser, the restore scan finds what’s left (O5, chunk 27; keys-and-accounts.md, The restore scan): a restore runs it on the network it’s made on, and Find leftovers (the dApps page’s After a restore) runs it on any. While no scan has finished on a network since a restore, the restore’s own or Find leftovers’, and none runs there, After a restore there says “The restore didn’t look for private sessions’ money on Preprod, or didn’t finish looking: press Find leftovers.”, naming the network (1.5.0’s release review, C19). Each one-time account the device has no record of that holds something, or has an order open, is listed there as Private session N, Found after a restore, and opens the page sessions from before swaps ran themselves have, without Place the order: Cancel the order while one is open, then Bring it back, or Bring everything back. Its page warns that a swap or a site using the phrase in another browser may still use the account, so bring it back from one browser only. So does Bring everything back’s review when it takes one. Neither a found session’s return review nor its page once it’s over says the account is never used again (C18), and once it’s over its page never says everything came back: one that ended by itself brought nothing back from here (C02). It says “It’s over in this browser: nothing more at its account comes back from here.” (1.5.0’s cross-area review, X09). While its return’s chain through Lovejoin is being sent, its page says so (Where it’s at: Coming back; Through Lovejoin: Sending 4 of 10 transactions) and offers no Bring it back, and its row reads Running · Coming back. If the chain stops (a lock, say), the page says where it stopped, and Bring it back sends the rest directly (C09). If a lock or a closed browser stopped it before any of it went, the page says it stopped after 0 of its transactions, and Bring it back builds the return again, through Lovejoin (X03). A found session this device has sent nothing for ends by itself once nothing at its account would come back and no order of it is open, whether another browser emptied it or it holds only what no return takes. With nothing left it leaves the list, and Find leftovers finds the account again if money reaches it; with something left behind it’s closed, and Remove wallet names it as left behind (C02). One with a swap the restore found (an order its key placed, open or not) ends only at a refresh 15 minutes or more after the first refresh that found it so, and stays listed meanwhile: an order’s fill or refund pays the account in the very transaction that spends the order, which a reading can miss for a moment, so a payout read late is still there to bring back (X01). Meanwhile its page says so: “Nothing at its account was left to bring back when it was last read, and its order is over. Once that has held for 15 minutes, the next refresh ends it, unless something comes in first, which Bring it back then takes.” Bring it back stays, as the secondary button, since it reads the account again and takes a payout a refresh missed, and Home’s restore note doesn’t count it (1.5.0’s last review, L4). A found session whose order waits and whose account holds no ADA alone of 5 ₳ or more (a return from before chunk 24 took everything) can’t put up the cancel’s collateral, which is the account’s own, so its page says so and offers Put in 5 ₳ to cancel instead of Cancel the order (chunk 32): 5 ₳ from the private balance, reviewed as a site’s top-up is, with what it links. While it’s on its way, the page says so and Cancel the order waits; once a read finds it, the cancel goes as any does, and the 5 ₳ comes back with the return. The cancel is run first on a stand-in 5 ₳: if the order’s script would refuse it, the page says “The wallet couldn’t cancel the order: …” and no 5 ₳ goes (C30). The 5 ₳ takes the session’s own private money first, where some is left (C36). Once the cancel is sent, the page says it’s on its way, offers no 5 ₳, and Cancel the order waits until it lands (C04). A found session whose return would go through Lovejoin, but whose account holds no UTxO of exactly 5 ₳ of ADA alone for the mixes, gets Put in 5 ₳ for Lovejoin beside Bring it back while no order waits, reviewed as the cancel’s 5 ₳ is, with what it links; Bring it back alone comes back directly, as its review says (C53). While the 5 ₳ is on its way, the page says to wait for it, and Bring it back stays, enabled, as the secondary button: a return built then comes back directly, its review saying the 5 ₳ wasn’t at the account yet rather than gone (X20), in the past tense since that reason is kept with the session and its page shows it after the 5 ₳ has landed (L9).

Any site (private CIP-30, chunk 15c)

The same session, offered to a site over CIP-30 instead of the public account. The plan is archive/plans/chunk-15c-private-cip30.md.

  1. Connect: the site’s enable() opens the connector’s window, with neither choice made. A private session asks what to put in it (ADA, and tokens): choosing it brings the amount into view and into focus, and until there’s an amount its button says Enter an amount (chunk 23’s second review, CW-2). The funding’s review shows it, each token with its amount (independent review L37), with 5 ₳ kept aside for contracts (the session’s collateral, said for what it’s for, chunk 23’s second review, CW-8), its fee, and the total leaving the private balance and the balance after, as every review says them (blind test T16: the change, of a UTxO the review didn’t show, matched nothing the tester could work out; the privacy note still names it, since anyone, the site included, can follow it). Under them, Later, to bring it back: the return’s network fee, about 0.25 ₳, the fees both ways, and, when Settings brings sessions back through Lovejoin, two fees (Lovejoin’s deposit and the return, which a chain through it ends with) and about what a 10 ₳ box of it costs, with what Lovejoin is in plain words (blind test §9.8). Send needs the password when Ask for your password to sign for a site is on. A funding that’s refused (giveme.my, a review too old) can’t be sent again, since its session was recorded first: as a swap’s and a mix’s funding, Send gives way to building it again on the next unused account, with the refusal’s words under Details and giveme.my named up front when it refused (blind test §9.5, T16), or, when it may have gone out all the same, to where to look for it; Decline still declines the site (chunk 23’s second review, DX-1), with the line over it that says how long that turns the site away, as on the choice (it was Cancel, which said neither; the blind test’s cross-area review). The review’s line on giveme.my says it lends 5 ₳ of its own, not the session’s 5 ₳ kept aside, and charges nothing, and its privacy note says the payment ties the private UTxOs it spends, and any change, to the account, for anyone to see on chain (it said “as Make public does”, which leaned on a screen T16’s tester had never seen).
  2. Out: a Seedelf spend to a fresh one-time account, with giveme.my’s collateral. The window waits until Koios sees the money (about a minute), then enable() answers, so the site’s first reading already shows it. Closing the window doesn’t undo the payment.
  3. Use: the site sees an ordinary wallet: that account, its reward address and its 5 ₳ collateral (none while the session’s own chain through Lovejoin being sent needs it; independent review L32), and nothing else. Each transaction and message gets the window’s prompt, signed with the session’s keys.
  4. Top up and Bring it back from the dApps page’s Sites. Top up’s review lists each token with its amount (independent review L37), and adds a new 5 ₳ collateral when the account has none left: a return takes it, or a site’s transaction spent it (independent review M9); it gives the total leaving the private balance and the balance after, not the change (blind test §9.8); refused, its Send gives way to rebuilding it, as private Send’s does. Bring it back’s review opens with a switch, Bring it back through Lovejoin, on when Lovejoin takes the return; off, it rebuilds the return to come back directly and says what that ties, and stays there to turn back on. Through Lovejoin its rows give the boxes, their mixes, when each comes back, and about 0.3 ₳ a box to bring them back, paid from each as it comes (BOX_BACK_ESTIMATE, the worker’s figure; pass two of the blind test’s fix round: the chain’s fees left it out, so a 10 ₳ box came back as about 9.7 ₳ with nothing to say so). It was a link, “Bring it back directly instead”, under the costs: a cheaper, less private way is a switch seen first, as Stop’s has been since the owner missed its link in a real swap (blind test §9.8). A swap’s return, brought back by hand, has the same switch and rows. On the session’s page each action that can’t be pressed says why under the buttons (Top up waits for the funding, or is off while the site doesn’t use the session; Bring it back with nothing to bring; Disconnect until everything is back), and a session whose site is connected to nothing, after a cancelled connect, says so rather than that another request connected it (chunk 23’s second review). Bringing it back leaves the site connected, to an empty account, because something still open at the site (a listing, an order) may pay it later. When its chain through Lovejoin stopped partway, what’s left stays at the account until Bring it back sends it, directly (independent review L23); while that isn’t on chain yet, a return built meanwhile comes back directly and says why (independent review L17).
  5. Disconnect (the site’s page, or Settings → Connected sites) asks first, then ends the session, once nothing is on its way and the account is empty. An empty read alone isn’t enough, since a Koios backend behind the funding reads it empty: Koios is also asked about what the session’s fundings, top-ups and the site’s own signed transactions paid the account, and while it shows one unspent, or doesn’t know one it should, Disconnect says Koios hasn’t caught up: try again in a minute (independent review M4). While a funding or top-up hasn’t been seen at all, it says it may still land, and to try again two hours after it was sent (final review F13). It is never used again, and the site’s next connect asks again. The site keeps what it already saw. The session’s record goes, and with it the site’s origin, unless something left behind is still at its account (privacy review §3.12; independent review L19).

Bring everything back (the dApps page, when sessions hold money): every site’s session, every older swap brought back by hand, and every session a restore’s scan found, with nothing on its way, back into the private balance in one go. Each comes back in its own transaction, sent one after another. A session a restore’s scan found is named there by its number, with Found after a restore beside it, as on the dApps page (1.5.0’s release review, C37), and the review warns that a swap or a site using the phrase in another browser may still use its account, so bring it back from one browser only (C18). One whose return’s chain through Lovejoin is still being sent, a site’s too, isn’t offered until the chain is all sent or stops (C09). The review lists them, with the total and the fees (and, when boxes go through Lovejoin, about 0.3 ₳ a box to bring them back, as a single return’s review says), and a tap leaves one out: a site still in use, say. When some go through Lovejoin, it says so, with the no-audit note, and opens with a switch, Bring them back through Lovejoin: off, it builds them all again to come back directly, says what that ties, and stays to turn back on (privacy review §4.1; blind test §9.8: it was a link, Bring them back directly instead, under the costs). Sent together, their deposits land in the same block or two and their mixes share blocks, and a shallow pool serves the first sessions while the rest come back directly: bringing each back from its own page, hours apart, avoids both.

Mix through Lovejoin (the dApps page)

Lovejoin is a mixer for ADA in 10 ₳ boxes, run inside the wallet (chunk 16). A private session’s spare ADA goes through it on the way back (Settings → Lovejoin, on by default; see A private swap). Its page is where the wallet’s boxes are, and where ADA can be mixed on its own: Home’s Private tab → dApps → Lovejoin, on a network where it’s deployed (preprod and mainnet, networks.ts). The design is in architecture.md.