aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/ark.js
Commit message (Collapse)AuthorAgeFilesLines
* Renewing asks first, and a renewal links to its round's transactionAdam Soltys12 days1-8/+37
| | | | | | | | | | | | | | | | | | | | | - The coins page's Renew button opens a confirmation instead of renewing at once: how many coins, their amount, the fee, and what is left to spend meanwhile — with a red notice when that is nothing (the button selects every coin by default, which is how a user locked their whole balance for an hour on 2026-10-03). It says the coins can't be spent until the round's transaction confirms and that a renewal can't be cancelled; the wallet renews for free near expiry anyway. The inline notice from 328f2db moves into it. - A renewal keeps its round's transaction id as soon as the round has run (before it confirms): the in-progress card on the coins page links to it, and the finished renewal's history row carries it, so its detail shows the id with View/Copy like any on-chain movement. Renewals finished before this change have no id recorded. tools/ark/renew-confirm-ui-test.js, tools/ark/renew-txid-test.js Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01ULQZS9MQa6iAsXnrJUD9Du
* A Lightning payment that can't be covered says why, not 'Payment failed'Adam Soltys12 days1-6/+16
| | | | | | | | | | | | | | | | | | | | | | | | A user renewed all ten of their coins (55,611 sats) from the coins page, tried to pay a 55,300-sat invoice three minutes later, and was told only 'Payment failed'. Nothing had failed: the renewal's round ran at once, but its coins stay unspendable until the round's transaction confirms on-chain, and that block took 64 minutes. The quote found no spendable coin and gave up with the generic message; nothing reached the server. - The quote now tells apart (lnShortfall): money held by another payment (wait, as before — now by sum, not one coin covering it all), money a renewal holds ('Nothing was sent. N sats are being renewed…'), and a real shortfall ('needs X sats with its fee, Y available'). Coins leaving in an offboard or exit are never waited for. - The manager's 'insufficient ark balance' names a renewal too. - The coins page says before the tap that renewed coins can't be spent until the round confirms, and that renewing every coin leaves nothing to pay with meanwhile. tools/ark/ln-shortfall-test.js Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01ULQZS9MQa6iAsXnrJUD9Du
* Zaps find lightning addresses our relays have prunedAdam Soltys13 days1-1/+2
| | | | | | | | | | | | | | | Zapping a post looked the author's lightning address up on our four home relays only. Those prune old events, so anyone whose kind 0 hadn't changed in a while (1776, Brad Mills) came back as "no Lightning address or Ark address" — while their profile page, which also asks the index relays and the author's own relays, showed the address right there. The zap lookup now asks both ways at once and takes whichever carries a lightning address. And when someone does have one but Lightning can't be paid right now, the toast says that instead of claiming they have no address. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VKv2uEFRR42Gp6NyqcN3A9
* Balance card gear replaces Accounts; Spending's coins live behind itAdam Soltys13 days1-21/+35
| | | | | | | | | | | | | | | | | | | | - The "Accounts" pill on each balance face is now a gear. It opens that face's settings: Spending leads with its coins (renewal, the co-operative move to Savings, unilateral exit), then Security (lock, change password), the recovery phrase, auto-logout; Savings adds its public key. A wallet with no nostr identity also gets sign-in and log-out there, since it has no profile page to hold them. - The exit cost is no longer said under the balance; the Spending settings state it, always, not only when coin chains run deep. - The lone Manage link under the balance is gone (the gear is the way in); warning notices that are about the coins link to the same page. - The Accounts list, Add wallet and Delete all are no longer reached from the wallet (Delete all stays behind profile > Log out > forget). - Tests follow the gear: padlock-ask, lock-wallet and account-access pass again; cooperative-exit-ui clicks the Spending gear. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
* NWC: lookup_invoice, so BTCPay Server can connectAdam Soltys13 days1-1/+12
| | | | | | | | | | | | | | | | | | | BTCPay's NWC connector requires get_info, make_invoice, lookup_invoice and list_transactions; we never offered lookup_invoice, so it refused the connection string. - ArkManager.lnLookup answers lookup_invoice from wallet state (by hash or bolt11, either direction; pending/settled/expired/failed, preimage once claimed). A retry's several actions on one hash defer to the furthest. - The open app pushes a pending receive along before answering, so a merchant polling sees it settle. - The service worker now answers make_invoice and lookup_invoice too, and claims a paid invoice's HTLCs during the lookup: a BTCPay sale settles with the wallet closed. Its list_transactions reports receives as well. - get_info and the 13194 info event list the new method. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
* Zapping a post never falls back to an Ark gift DMAdam Soltys14 days1-6/+8
| | | | | | | | | A post's author with no Lightning or Ark address now gets a toast saying so (or a notice on the hold-to-zap card) instead of a locked gift sent by DM. Paying a person from Pay / the send form still falls back to one. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
* Sending on-chain from Spending states the fee and minimum up front.Adam Soltys14 days1-6/+36
| | | | | | | | | | | The form asks the ASP for a co-operative offboard quote (mining share + service fee, minimum = fee + dust) for the address on screen, disables Send for a balance below it and points to Lightning. A too-small amount reads in words instead of the manager's internal error, and the unilateral-exit note only appears when co-operation actually failed. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
* Add cooperative coin exits and clarify profile, preview and zap controlsAdam Soltys14 days1-9/+37
|
* Clear abandoned payments, reset profile Pay and restore native messaging BackAdam Soltys14 days1-1/+6
|
* Zapping someone without Ark just works: Lightning first, then a locked gift ↵Adam Soltys2026-09-281-53/+75
| | | | | | | | | | | | | | that looks like any zap. No warning and no gift wording: when a person has no Ark address and no Lightning address, the ordinary amount + Zap form sends a gift locked to their nostr key and DMs it; the receipt screen is the usual Zapped!, with the link and code only if the DM couldn't be delivered. One-tap post zaps take the same path instead of failing. The Lightning button appears only for someone who has a Lightning address, and an Ark advert for another network no longer stops the lookup short of Lightning. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Browser Back leaves the Manage and exit pages: they are history entries now, ↵Adam Soltys2026-09-271-2/+2
| | | | | | | and their own Back pops one. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* The Manage and exit pages keep the top nav; they are pages of the wallet, ↵Adam Soltys2026-09-271-1/+5
| | | | | | | not takeovers. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Expired dust gets a quiet line and a Forget button, not a red warning.Adam Soltys2026-09-271-3/+12
| | | | | | | | | | | A coin past its expiry and under the 330-sat floor can be renewed by nobody and exited by nobody; the home page said "the server can still renew it" in red over 21 sats. Now a muted line points at Manage, where the coin's row offers Forget: held as spent, quietly, so it stops counting and stops warning. Larger expired coins keep the warning. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Sending to a coinos.io name shows the person, not the ark address it became.Adam Soltys2026-09-271-2/+5
| | | | | | | | | The recipient box keeps the name, with their face beside it and "Sent instantly, no fee."; the review names them too. The ark1… the name resolved to is plumbing, and read as gibberish to anyone new to this. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Renewal: a submitted round the server has forgotten is repaired, not asked ↵Adam Soltys2026-09-261-1/+3
| | | | | | | | | | | | | | | | | | | | | | about forever. Adam's wallet carried a refresh action from August 31 in step 'submitted' whose unlock hash the server never ran: the round went through under a later submission (another hash), the inputs were spent by it and the 5,233-sat output reached the wallet through the mailbox — but the original action kept asking RoundParticipationStatus on every sync for four weeks, and since this week's coins page shows renewals in flight, it showed up as "in progress" with the raw gRPC error underneath. On "not found" the manager now checks the inputs: all spent means the round ran under another submission and the record is retired (failed, superseded — no history row, no card); none spent means the server dropped it before a round ran, so it is submitted again; a mix goes through the existing input repair. The coins page never prints a server error string; a failed check reads as "the wallet keeps trying". Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Coins page: a renewal in flight keeps the whole page; a renewal you asked ↵Adam Soltys2026-09-241-18/+55
| | | | | | | | | | | | | | | | for is in history. Tapping Renew marked the coins pending and the page, finding nothing spendable, collapsed to a single "Pending: N sats" line — no table, no exit explainer. Coins in a round now stay in the table (greyed, tagged renewing), with a card saying where the round is (queued, in a round waiting for a confirmation, finishing). The page no longer closes on Renew. History: a manual renewal shows as "Renewing · in progress" while the round runs, and as "Renewed" after, even when it was free (automatic free renewals stay out of history, as before). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* Send: the form is cleared the moment a payment goes through, not only when ↵Adam Soltys2026-09-241-0/+5
| | | | | | | | | its card is tapped. Leaving the sent card by a tab or Back kept the recipient and amount in the form, so the next send opened already filled in. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Always offer coin management for a Spending balanceAdam Soltys2026-09-231-7/+14
|
* Server comparison: drop the closed-app renewal row.Adam Soltys2026-09-221-3/+0
| | | | | Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Server comparison: the closed-app renewal row says "not verified there" for ↵Adam Soltys2026-09-221-1/+3
| | | | | | | Second, not "no". Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Payment detail: the unit is the tap-to-toggle tag; tests enter through the ↵Adam Soltys2026-09-221-2/+2
| | | | | | | wizard's import line. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Ark: receives waiting with another server are claimed; notifications belong ↵Adam Soltys2026-09-221-1/+38
| | | | | | | | | | | | | to the identity. Once per session the wallet visits every other Ark server it holds state with — its own manager, a receive rescan, a sync to claim — and says what arrived and where; the coins wait there until a switch back. The stored notification list remembers which identity fetched it, so another identity on the same wallet starts empty instead of reading the previous one's. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Send: a bitcoin: URI with ark= pays over Ark on the same server, on-chain ↵Adam Soltys2026-09-221-0/+18
| | | | | | | | | | | | with a note otherwise. Noah and Second's other wallets share BIP-21 URIs carrying an ark= address. Same server as ours: the send goes over Ark with the URI's amount (your own address routes to boarding). A different server: the on-chain address is used and a toast says why. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Ark server card: a side-by-side comparison of the servers, from their own ↵Adam Soltys2026-09-221-1/+54
| | | | | | | published info. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Ark server is a setting again; an address on another server falls back to ↵Adam Soltys2026-09-221-4/+74
| | | | | | | | | | | | | | | | | | | Lightning. Settings → Advanced → Ark server: coinos, Second (ark.second.tech) or a custom server, per network — the stored choice is honoured on mainnet again. Ark state is kept per server, so switching hides but never loses coins; the card says what the current server holds and asks before letting go. After a switch the reusable address, the nostr advert and the payment name's ark= instruction follow the new server. Sends between servers can't be arkoor, so an Ark address that names a different server (its first bytes hash the server key) is treated as no Ark address at all in the pay ladder — a coinos name or npub then goes over Lightning by itself — and a pasted one is refused with the reason. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Send: a Lightning payment while Savings is showing asks before it pays from ↵Adam Soltys2026-09-211-0/+41
| | | | | | | | | | | | | | | Spending. Every Lightning payment passes through the Ark flow, whichever balance is on screen, and the review's "from your Spending balance" line was easy to miss. With Savings selected the flow now stops at an ask first: switch to Spending and continue, move Savings into Spending first (the existing board panel — an on-chain step, so a fresh invoice afterwards), or cancel. Pasted invoices, Lightning addresses and LNURL all pass through; autopay (a trusted, budgeted code) never asks. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Spending card: the Manage link shows whenever there are coins, warning or not.Adam Soltys2026-09-211-4/+9
| | | | | Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Spending coins: Manage opens on the first tap.Adam Soltys2026-09-211-7/+13
| | | | | | | | | | | The notice reads the persisted Ark state; the page read the live manager, which is null for the first seconds after boot — so a tap before it had connected found no coins and closed itself. The page now reads the same state, dials the manager meanwhile, and keeps Renew and Exit now disabled until it is there. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Spending coins: the unilateral exit section sits below renewal.Adam Soltys2026-09-211-5/+5
| | | | | Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Spending coins: "Unilateral exit", with an Exit now button behind a ↵Adam Soltys2026-09-211-1/+29
| | | | | | | | | | | | confirmation. The confirmation shows the coin count, the amount, the estimated mining fees (paid from Savings as the exit unrolls) and what Savings holds for them, refuses when Savings can't carry the fees, and says the funds only land once the timelocks run out and that an exit can't be stopped. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Ark: the renewal notices and the Spending coins screen speak in sats, ↵Adam Soltys2026-09-211-8/+8
| | | | | | | whatever the display unit. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Point of sale: a paid sale opens its payment detail, which now says bill and ↵Adam Soltys2026-09-211-0/+2
| | | | | | | | | | | | tip. The row finds the ln-receive movement by the invoice it recorded and opens the same detail screen the history page shows; the detail asks features for more (arkMoveDetailExtra) and the till adds Bills, Tips (with the percentage) and the note. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Point of sale: a bill, an optional tip prompt, one invoice, and a ledger ↵Adam Soltys2026-09-211-0/+10
| | | | | | | | | | | | | | | | | | that keeps tips apart. A new deferred feature (src/features/pos.js): type the amount in the display unit, optionally a note, Charge. With the tip prompt on, the customer sees the bill and picks a percentage (configurable, default 10/15/20), their own figure, or no tip; one Lightning invoice is made for the total through the Ark feature's receive (new hooks arkLnInvoice/arkLnWatch/arkLnUnwatch/ arkLnCancel wrap createLnInvoice and pollArkLn). Paid, expired and cancelled sales are recorded with the bill and the tip as separate figures, and the fiat rate at the time; the amount screen shows today's bills, tips and total. Doors: Settings → Payments card, and a button under the payment address on Receive (a receiveExtras hook the names pane renders). Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Zaps: tap again while one flies, hold the bolt to change the amount.Adam Soltys2026-09-191-6/+13
| | | | | | | | | | | | | | | | A pending zap no longer refuses the next tap: the optimistic tally keeps one entry per zap and matches each report and receipt to the oldest it can describe, so two default-amount zaps stack, settle and hand over to their receipts one at a time. Instant post zaps run off the send screen's zap slot, so a second tap can't evict the one still resolving, and send action ids are unique within a millisecond. Holding ⚡ (500ms) opens the amount screen in an edit mode — Save alone, or Save and zap this note — from the action bar and the chat chips alike; the press's timer lives on the element so a background repaint mid-press can't turn a lifted finger into a hold. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Zaps: the sound, the sender, and one action bar for every post.Adam Soltys2026-09-181-3/+12
| | | | | | | | | | | | | | | | | | Tap feedback: a bolt strike, the amount popping off the button, the post trembling, and the zap.mp3 clip. The clip is 62KB of base64 and messages.js is boot code, so it is its own chunk, fetched once a zappable post is on screen and played inside the tap once loaded; the standalone build inlines it. Ark zap receipts are signed by the wallet key, which has no profile: they now carry the login identity as the sender (NIP-57's P tag), readers prefer it, and our own older receipts read as us. The action bar is the same six slots on every post: the zap button and the who-reacted chevron are always there (inert until they have something to do), so the first zap no longer reflows the row, and you can zap your own posts and messages. Community rows centre their unread dot off the edge. Also in here: reactions with custom emoji, reply-composer focus under the tapped post, profile fetch retry, and Lightning zap failures that say so. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Accounts from the header, snapshot-first Spending restore, name ownership ↵Adam Soltys2026-09-181-34/+52
| | | | | | | | | | | | | | | | grants. Header buttons always leave the current feature screen; an account with no remembered nostr identity opens Accounts instead of nothing. Logout confirms in place. A scan finishing after logout or an account switch no longer drops the new session into offline mode (stateGeneration guards in sync + ark). Spending restores from the snapshot first. A migration grant names the identity that owns an address; its manager may register it without becoming that identity (names/ownership.js). Browser tests for account access, profile retry, spending restore, thread reply focus, custom-emoji reactions and registry ownership. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Pay from a profile opens the Send form, not a lingering payment detail.Adam Soltys2026-09-161-1/+1
| | | | | | | | | | | A tapped history row claims the whole tab area whichever tab is picked, so jumping to ui.tab = 'send' from a profile's Pay button (or a zap's form fallback) with a renewal's detail still open showed that detail instead of the form. showSend() closes any open detail first; the three feature paths that jump to Send use it. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Coins page: renewal is automatic, and the copy says so first.Adam Soltys2026-09-151-4/+6
| | | | | | | | | | | | | | The renewal card opened with how to renew and mentioned only at the end that the wallet does it by itself, which read as an invitation to renew by hand. It now leads with the automatic, free renewal before expiry, then says renewing early is only for someone who would rather not count on the coinos server, with the round cadence taken from the server's own info (two minutes now, not the hour the old text claimed). The "past its expiry" footnote under the table is gone. Stale translations of the rewritten strings fall back to English. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Spending coins as a headed table instead of chip rows.Adam Soltys2026-09-151-30/+29
| | | | | | | | | | | Every row repeated "expiry", "exit cost", "renewal cost" as pills. The labels now sit once in a header row over right-aligned tabular figures; the table's wrapper scrolls sideways on a screen too narrow for it (the page never does), and it fits a 390px phone without scrolling. A tap anywhere on a row toggles it. A sub-satoshi renewal share reads "< 1 sat". Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* A tiny coin's renewal chip says "under 1 sat", not "1 sat".Adam Soltys2026-09-151-2/+10
| | | | | | | | | | | | | The server prices a renewal on all the coins together: ppm accumulates sub-satoshi across every input and rounds up once. The per-coin chip rounded each coin's share up on its own, so nine 1-sat coins each showed "renewal cost 1 sat" under a total of 4 sats — as if the page were skipping them. The chip now shows the coin's real share: "under 1 sat" for a sub-satoshi one, rounded otherwise, and its tooltip says how the fee is priced. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* A gift locked from Savings shows under Savings, not Spending.Adam Soltys2026-09-141-1/+1
| | | | | | | | | Every feature balance line landed on the Spending face, but a gift's locked reservation is on-chain money still sitting in Savings. Lines now say which side they belong to and draw on that card: gifts on Savings beside pending incoming, ark's Moving… on Spending as before. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* The balance says everything it has to say in one card.Adam Soltys2026-09-141-26/+33
| | | | | | | | | | | | Under the Spending balance, warnings stacked up as separate boxes — two red cards and a grey line, with three Manage links between them, all pointing at the same page. They are one card now: red if anything in it is a warning, plain otherwise, each notice on its own line, and a single Manage at the bottom right. A lone quiet advisory (the exit-cost line most wallets show) keeps its centred grey line. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01QYJfZ4A1TDCEY4Foq3CApR
* A zap answers as fast as the route allows.Adam Soltys2026-09-141-0/+12
| | | | | | | | | | | | | | | The wallet used to add two to four seconds of its own around a Lightning send: a random pause plus a relay look-back before paying, and a two-second poll for the result after. The notifier's claim now decides who pays in one round trip, with the relay look-back kept only for replays and for when the notifier gives no verdict; a phone answering from behind another app waits a second so a desktop tab wins the claim instead of paying at a throttled crawl. After the send, one held CheckLightningPayment call brings the preimage the moment CLN has it — for wallet-connect pays, the background worker, and the app's own Send. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01QYJfZ4A1TDCEY4Foq3CApR
* A zap answered by two tabs no longer reports as failed.Adam Soltys2026-09-141-0/+6
| | | | | | | | | | | | | | | Every open tab of the same wallet sees every wallet-connect request. Two hidden tabs on one laptop both tried to pay the same zap: the ASP refused the second with "payment already in progress", and that error reached Amethyst half a second before the first tab's preimage. A Web Lock per request now lets one tab (or the worker) answer while the rest stay quiet, and a device that does meet "already in progress" waits for the sibling payment's outcome — relaying its preimage only if the payer never speaks — instead of shouting a failure over sats that went through. The losing side also stops writing a failed row into history for it. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01QYJfZ4A1TDCEY4Foq3CApR
* The renewal happens whether or not anyone opens the app.Adam Soltys2026-09-131-1/+44
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Every coin expires, and until now the only thing standing between a balance and its expiry was maybeAutoRefresh: it renews when the wallet is opened inside the last 96 blocks, roughly sixteen hours. Bark's own developers say the same premise fails on mobile — people don't reliably open a wallet at the right hour, which is why every mobile Ark client they know of has moved to delegated refreshes and why they are building server-driven renewal. captaind already has it, and ark.coinos.io has been carrying the column unused: SubmitRoundParticipation takes a scheduled_height, and the server holds that participation out of every round until the height arrives, then runs it with nobody online — the round tree branch is signed by the server, which is the whole point of a delegated refresh. The fee is priced at the height it will run at, so a standing order lands in the free bracket rather than today's. So each spendable coin now gets one registered 36 blocks before its expiry, six hourly rounds of slack. It is deliberately not an action: the coin stays spendable, the wallet never reads as busy, and spending it simply kills the order — the server drops a participation whose input is gone, and _driveScheduled drops ours. The claim comes later, whenever the wallet next opens: the mailbox breadcrumb we already post wakes it, and the leaf cosign and forfeit are the ordinary refresh path from there. Nothing about the input's expiry blocks that claim, which is what makes this worth having. A wallet that goes quiet for a month comes back to a coin already renewed and anchored in a fresh tree, instead of one the ASP has swept and would have to honor out of goodwill. One cycle per visit is all it can buy — the replacement's id doesn't exist yet, so its own backstop waits for the next time the app is opened. tools/ark/scheduled-refresh-test.js covers the five behaviours on regtest: the server holds the order until its height, runs it while the wallet is closed, the wallet claims on return, a spend voids the order, and a claim still works after the input has EXPIRED (the last one mines a coin's whole lifetime, so it also re-refreshes the funding wallet first — an expired vtxo can be renewed but not sent). Riding along: the expired-coin notice that was already sitting in the working tree, and manager-test's tampered-vtxo check, which had been reading base64 vtxo bytes as hex since they moved to IndexedDB. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_019fMdLypdg5JaU7bdeUFF8v
* Say the currency once, and stop hedging about old prices.Adam Soltys2026-09-111-11/+11
| | | | | | | | | | | | | | | | | | | | | | | | | The balance read "US$74.67 USD" — the symbol and the unit tag saying the same thing twice. Amounts now format bare, like sats and BTC do: whatever labels the number says which money it is. The currency symbol stays where a number stands alone in prose, which is the "Worth then" line and nowhere else. The '~' marking a payment older than any price we sampled is gone too. It was bookkeeping the beta doesn't need: a payment we have a price for is shown at that price, one we don't is shown at today's, and nobody has to learn a squiggle. The currency picker has moved to sit beside the language picker at the foot of the page — display preferences together, where they're findable. It spent one day under Settings → Network, where it wasn't. And a bug this feature exposed rather than caused: nine notices end in the word "sats" in every language we ship, and were being handed the display unit. "Uncooperative exit costs about US$2.03 sats" — and long before fiat, "about 0.00435397 sats" for anyone in BTC. They're given sats now, which is what they claim to be showing. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
* Every payment remembers what it was worth.Adam Soltys2026-09-111-3/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | A payment is worth what it was worth WHEN IT HAPPENED, and that number is gone by the time you look at it unless someone wrote it down. So every price the app fetches is kept as a sample — one an hour, a few months back — and a payment's fiat value is read off the sample nearest its own timestamp. The history rows and both detail pages (on-chain and Spending) pass the time the money moved; balances and forms use the live price, as they should. A payment older than any sample we hold is priced at today's number and says so with a '~', because that is a different claim and shouldn't look like the same one. The detail page spells it out: "Worth then $X · $Y now". Fiat is a third position on the unit toggle rather than a second number beside the first: sats → BTC → your money → sats. One figure, and which figure is your choice. The ring skips fiat entirely until a price has been fetched — a third position showing blanks is worse than two that work. Typing an amount with the unit on USD means dollars, priced now. The rates are coinos' own endpoint, a map of every currency it knows, so there's no third party watching what the wallet asks about; the currency picker under Settings → Network lists all of them. Changing it starts the recorded history again rather than relabelling samples taken in another currency. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
* Include mining fees in the Spending deposit previewAdam Soltys2026-09-111-9/+25
|
* Report rejected Ark sends and exclude server-rejected expired inputsAdam Soltys2026-09-111-11/+14
|
* The wallet waits for its own state before painting, so nothing jumps.Adam Soltys2026-09-101-0/+25
| | | | | | | | | | | | | | | | | | | | | | | | | | | The Spending balance and its history are read from the ark store, which is IndexedDB: synchronous to read, asynchronous to OPEN. Boot didn't wait for it, so every refresh painted a Spending wallet with nothing in it — no balance, no history — and filled it in a beat later. On a phone that second paint arrived as a change rather than an arrival: the balance counted itself up from 0 in green, wearing the animation a received payment gets. Boot now waits for the store (capped at 800ms — a store that won't open must not hold the app hostage), and the shell stays up a moment longer instead of a wrong balance going up. If the store does land past the cap, initArk retries on it — so a Spending wallet whose state arrived late also connects, which it previously didn't until something else happened to kick it — and that catch-up paint forgets the last amounts first, so it reads as state arriving rather than money. Also: the zap tally chip loses its underline. In chat the chip is a reaction chip too, and .chat-react.on still rings it; on a post row the underline just read as an artifact. tools/boot-balance-test.js pins the ordering, with IndexedDB slowed so it can't pass by luck. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx