Skip to content

Wallets refresh strategy

Overtorment edited this page Aug 21, 2020 · 2 revisions

current wallets refresh strategy

Upon cold boot,

all wallets fetch their balances & transactions (technically happens on screen/wallets/list.js). It happens consequtively, wallet after wallet, not in parralel. Each wallet saves timetamps _lastBalanceFetch and _lastTxFetch in its internals.

Upon wallet card snapped to,

wallet checks internal method timeToRefreshBalance() which currently checks is last balance fetch was more than 5 minutes ago, and if yes, calls for the wallet fetchBalance(). Then, if fetched balance is different from previous saved balance (or there is unconfirmed balance) wallet calls fetchTransactions().

If balance check didnt indicate that we need to fetch transactions, wallet has secondary mechanism to decide to refetch transactions: wallet calls its method timeToRefreshTransaction(), which for onchain wallets checks whethere there are transactions with < 7 confirmations and for lightning wallets checks if last tx fetch was more than 5 minutes ago. And only then fetches transactions.

Technically this happens on screen/wallets/list.js

Events based

Screens screen/wallets/list.js & screen/wallets/transactions.js subscribe to event REMOTE_TRANSACTIONS_COUNT_CHANGED and once this event is emitted (for example upon transaction broadcast on some screen) - screen fetches balance and transactions. For screen/wallets/list.js it fetches for currently snapped-to wallet card. For screen/wallets/transactions.js it fetches for currently opened wallet. This event REMOTE_TRANSACTIONS_COUNT_CHANGED subscription is exclusive, meaning only the last screen that subscribed to this event will get this event and will trigger fetch. This works well for scenario, for example, wne user opens a wallet card (subscrion happens), then creates & broadcasts transaction (event emitted), and navigation returns user to opened wallet. After artificial delay of 4 seconds fetch will be triggered. This delay is needed to give a change to transaction to propagate through the network, otherwise if you broadcast transaction and immediately fetch transactions for a wallet - electrum server wont have this transaction yet.

Upon push open

BW processes push payload (which might have address, txid, or ln invoice hash), and tries to find a wallet which owns this address/txid/hash. If wallet is found - we navigate user to this specific wallet and trigger REMOTE_TRANSACTIONS_COUNT_CHANGED so the screen will refetch balance & transactions (no delay this time as we assume everything is propagated on bitcoin network). This happens in App.js: App._processPushNotifications()

Not yet happening, but probably should

  • If push received but not opened - nothing happens, payload is just ignored, while we probably should wait a little bit (to make sure user did not tap notification bubble), and re-fetch relevant wallet. TBH I dont know how to trigger redrawing correct screen (for a case when user is staring at some other wallet atm)
  • When user is on receive screen, he just showed (or sent) QR to someone, and awaiting payment. If payment happens, user wont know. I think we should just periodically check this receive address by timer and change screen if we see some movement there. Braind dead simple and no subscription/notification complexity involved.

Why refresh is no simple matter

Because we don't have a kind of backend where we could set a goal of every response be, say, 200ms, and always know that hitting backend is a cheap operation and always refresh everything every chance we have. Instead we have electrum servers which might be slow to respond. Especially when user has many addresses in hierarchy - when we fetch balance we check every single one of them in case some old address was reused. If user has a phat wallet with thousands of txs balance fetch could easy take like 10sec. Imagine if user has several such wallets, those times stack. Even worse if user uses EPS or Electrs which do not support batching yet. That means BW goes address after address, which in my tests could take minutes (just to check all addresses and make sure we don't need to fetch transactions).

And if we do need to fetch transactions - sometimes single transaction can take like 500Kb (batched exchange withdrawal) which is a lot for javascript to digest at once.

Thats why current refresh mechanism is very cautious to not hang whole app.