Skip to content

v0.4.1

Choose a tag to compare

@github-actions github-actions released this 03 Oct 23:12
· 26 commits to main since this release

Added

  • The waiting standard. waitingView(elapsedMs, kind, steps?) in @noirwire/shared/presentation says what to show as time passes: nothing before WAITING_DELAY_MS (300 ms), then a quiet signal, then a calm "still working" line after STILL_WORKING_AFTER_MS (4 s for content, a check or a review; 8 s for an action), and which step of multi-step work is current. A pure function of elapsed time: each platform owns its clock. importWaitingView and importFailedText do the same for an import, with IMPORT_LAST_STEP_AFTER_MS (6 s).

  • waitingCopy, and the import's progress words (onboardingCopy.import.progress, with the phone's lead and failure in mobileOnboardingCopy.import.progress).

  • Quiet retries for reads: withRetries, readWithRetries, READ_RETRY, isTransient in @noirwire/shared/application. Three tries, 400 ms and then 800 ms apart, on a request that never got through, ran out of time or came back 429 or 5xx. Wrapped: the balance refresh, live prices, price history, tracker multipliers, the lending rate and position, import lookups, checkRecipient, and preparing a review (a trade's price, a send's cost, whether a holding is open, the relayer's price and keys). Nothing that signs or submits is retried, and a refusal never is.

  • mobileSettingsCopy and mobileWalletCopy: the phone's Settings, Unlock and Reset words, as variants. unlockProblemText and isWrongPassword in @noirwire/shared/presentation.

  • walletCopy.reset.notRemoved, walletCopy.crossTab.notice and walletCopy.crossTab.refused, which the web app kept for itself.

  • decimalAmount in @noirwire/shared/domain: a period or a comma as the decimal separator, and null for text that could be read two ways.

  • signAsClient and unsignedTransaction in @noirwire/shared/testing, for a test's stand-in chain client to sign through the real signing guard. The testing layer may now import infrastructure/ for that.

  • The phone's layout tokens in @noirwire/shared/design: mobileRadius, layout, size, MIN_TARGET, opacity, overlayColor, fonts, motion, and space[8].

  • failureAccount in @noirwire/shared/presentation: a failure in its own account, which is what it is counted under.

  • lookFurtherForPortfolios(mnemonic, scheme, activity) in @noirwire/shared/infrastructure, and lookFurtherView with onboardingCopy.import.lookFurther ("Missing a portfolio? Look further"): a second scan a person can ask for from an import's result. It carries on from where the first stopped (SchemeActivity.scannedThrough) and gives up only after EXTENDED_DISCOVERY_GAP (100) unused addresses in a row.

  • canCreatePortfolio and unusedPortfoliosInARow in @noirwire/shared/application, and the constants DISCOVERY_GAP (20), EXTENDED_DISCOVERY_GAP and MAX_UNUSED_PORTFOLIOS_IN_A_ROW (10) in @noirwire/shared/domain.

  • createPacer in @noirwire/shared/application, a rate pacer with an injectable clock, and paceImportWith and IMPORT_REQUESTS_PER_SECOND (8) in @noirwire/shared/infrastructure. withRetries takes jitter and random.

  • MAX_ACTIVITY_ENTRIES (500) in @noirwire/shared/domain, olderNotKept on the Activity list view, and its words for both platforms.

  • focusRing and the focus token in @noirwire/shared/design; findStock and phraseWords in @noirwire/shared/infrastructure; canonicalSymbol on the catalog; tooPrecise and smallestAmount in @noirwire/shared/domain; portfolioNameTaken in @noirwire/shared/application; privacySectionHelp in @noirwire/shared/presentation; LOCK_SIGNAL_KEY in @noirwire/shared/wallet.

Changed

  • lock() locks the wallet everywhere it is open: it announces the lock through the vault, under noirwire.wallet.lock (a counter, nothing about the wallet), and every other tab or running copy locks on hearing it. A reset does the same when the record would not be removed. An idle lock stays with the tab that went idle.
  • changePassword refuses the password it already has: "That is already your password."
  • Every button variant, the icon button, the input and the chip carry a keyboard focus ring in a colour named outright (focusRing).
  • Every counted noun goes through plural: "1 asset", "1 result", "Order placed".
  • sendCopy.invalidAmount and mobileSendCopy.invalidAmount: "Enter a finite amount greater than zero." and "Enter an amount greater than zero." are now "Enter an amount, like 12.50."
  • An amount typed with more decimals than its asset has, which includes one below its smallest unit, is refused in a send, when moving money in, in Earn and in a trade: "That amount has too many decimals. The smallest amount is 0.000001 USDC."
  • parseRecoveryPhrase reads a phrase as people paste one (numbered lists, commas, line breaks, tabs, capitals) and says exactly what is wrong: how many words there were, which word is not a recovery phrase word and where it stands, or that the words do not form a phrase. It answers problem beside error.
  • stockBySymbol and the catalog's asset find a tracker whatever the capitals: nvdax is NVDAx.
  • createPortfolio refuses a name another portfolio already has, archived ones included, with duplicateName.
  • fundingAmountView takes touched and shows no error under an amount field nobody has typed in.
  • createPortfolio refuses with unusedPortfolios ("You have several portfolios that were never used. Use one of those first. An archived one can be restored.") once the wallet ends in ten portfolios in a row that never held or did anything, archived ones included.
  • An import asks about each candidate address in one request instead of two, paces all its requests to eight a second across both sets of addresses, and adds jitter to its pauses after a refusal.
  • The wallet's record keeps its 500 most recent activity entries. The oldest are dropped as new ones are written.

Failure and waiting messages say what happened, what it means for the money and what to do next, and no longer say how the app asked. This changes what the web says too.

String Was Is
onboardingCopy.import.networkFailed Could not reach the network to check balances. Try again. We couldn't finish importing your wallet. Nothing was saved in this browser. Try again.
onboardingCopy.import.checking Checking balances and recovering portfolios onchain... Finding your portfolios...
onboardingCopy.password.encryptFailed Could not encrypt the wallet. Try again. We couldn't encrypt your wallet, so nothing was saved. Try again.
mobileOnboardingCopy.import.networkFailed Could not reach the network to check this phrase. Try again. We couldn't finish importing your wallet. Nothing was saved on this phone. Try again.
mobileOnboardingCopy.import.offline You're offline. Importing needs the network to find what this phrase holds. You're offline. Nothing was saved on this phone. Go back online to import your wallet.
appCopy.networkGate.wrongNetwork(n) This app is built for n, but its network connection serves a different chain. Nothing can be sent until that is fixed. NoirWire is not connected to n as it should be. Your money has not moved, and nothing can be sent until this is fixed. Try again later.
appCopy.networkGate.unreachable The network could not be reached, so balances cannot be shown safely. We can't show your balances right now. Your money has not moved. Try again.
mobileAppCopy.network.checking Checking the network... Getting things ready...
errorsCopy.chain.noQuote Jupiter has no price for this order right now. There is no price for this order right now. Nothing was traded. Try again in a moment.
errorsCopy.chain.wrongNetwork The network connection serves a different chain than this app is built for. Nothing was signed or sent. NoirWire is not connected to Solana as it should be, so this was stopped. Nothing was signed or sent. Try again later.
errorsCopy.funding.failed Funding failed. We couldn't move this money. Nothing was moved. Try again.
errorsCopy.funding.privateNotStarted The private transfer could not be started. The private transfer could not be started. Nothing left your funding wallet. Try again.
errorsCopy.send.notCompleted Transfer could not be completed. Check the balance and recipient. This send can't be made. Nothing was sent. Check the amount and the recipient's address.
errorsCopy.send.failed Transfer failed. We couldn't complete this send. Nothing was sent. Try again.
errorsCopy.trade.noPrice Could not get a price for this trade. We couldn't get a price for this trade. Nothing was traded. Try again.
errorsCopy.earn.failed The transaction did not complete. This did not go through. Nothing was moved. Try again.
sendCopy.sending Sending onchain... Sending...
fundingCopy.unknown ...was sent, and the answer never came back. It may still arrive... ...was sent, but we could not confirm that it arrived. It may still arrive...
tradeCopy.costCheckFailed Could not check the network cost of this order. Get a new price. We couldn't work out the network cost of this order. Nothing was charged. Get a new price.
pieCopy.order.costCheckFailed Could not check the network cost of these orders. Try again. We couldn't work out the network cost of these orders. Nothing was charged. Try again.
mobilePortfolioCopy.home.refreshFailed, .detail.refreshFailed Could not refresh. Pull down to try again. We couldn't update your balances. What you see may be out of date. Pull down to try again.
mobileFundingCopy.page.readFailed Could not refresh. Pull down to try again. We couldn't update your balance. What you see may be out of date. Pull down to try again.
  • failureMessage no longer passes on a failure's own account when that only says how a request failed ("fetch failed", "429 Too Many Requests", "Jupiter returned 502."): the person is told the plain words for what the action was doing. Analytics still band the failure by its own account (failureAccount).
  • typedAmount, and with it the send, funding and Earn drafts, reads an amount through decimalAmount: "12,5" is 12.5 on both apps, and "1e3", "0x10" and grouped digits are no longer amounts.

Kept as they are, because they are honest and needed: "Network cost" and "Relay fee" as fee lines, "Solana" where the chain matters, "offline", and the privacy notes that name who can see what (NoirWire's server, the network provider, Jupiter, MagicBlock).

Fixed

  • An import's pace was taken before a request had one of its three slots, so requests that queued behind slow answers all started together when those came back. The turn is now taken inside the slot, right before the request is sent.

  • Locking the wallet in one tab left it unlocked and usable in the others.

  • Changing the password to the same password reported "Password changed".

  • A funded portfolio could be lost to a restore. An import stops looking after 20 unused addresses in a row, and creation allowed any number of empty portfolios: twenty empty ones and then a funded one put the funded one out of reach. Creation is now held to a limit safely under the scan's gap, a test holds the two constants to that, and a further scan can be asked for.

  • An import could fail on a provider that allows about ten requests a second: both sets of addresses started together and sent a burst, and pauses that lined up used up the retries. Requests now share one pace, and a failed attempt carries on from the last completed step when it is tried again instead of starting over.

  • The activity list grew without limit inside the encrypted record, so every write and unlock grew with it, and web storage could in time refuse a write.

  • An action that landed can no longer be reported as failed. send and fundDirectly answered failed when the balance read after a landed transaction threw, which invited paying again. Now, once the chain or the venue has confirmed, the answer is confirmed: the read after it is asked again quietly, and if it still cannot be had the answer carries settlement: "balancesEstimated" (balancesUnread(result) in @noirwire/shared/application), which a screen shows as done, "Balances will update shortly." The same holds for Earn deposits and withdrawals, a trade whose account was just opened, opening a pie's holdings, and a private transfer the service accepted. An unknown outcome stays unknown, and a reservation that cannot be ended never changes what an action answers. sendResultView, earnResultView and fundingOutcomeView take balancesUnread. With that guaranteed, a failure before anything was sent says so: "Nothing was sent", "Nothing was moved".

  • A password change left indeterminate no longer holds every later change until the app restarts: it is forgotten when the wallet is reset, and settled when the record read back is another one.