Skip to content

Commit 00faa89

Browse files
committed
Gate balance effects and FIO refresh on engine readiness
With the core's wallet cache (wallet cache v2 phase 1), wallet objects exist before their engines load, and waitForAllWallets resolves in that window. Three login-path surfaces consumed engine state immediately: - The action queue's address-balance effect read balanceMap right after awaiting the wallet, which could evaluate a balance effect against cached, possibly stale balances. It now reports not-yet-effective until the engine has fully synced, matching the conservatism the loan flow already applies. - The FIO address refresh called otherMethods on pre-engine wallets, which is {} in that window. Services now waits for each FIO wallet's engine-backed otherMethods (bounded by a generous safety-valve timeout) before refreshing. - FioService's periodic expired-domain check called otherMethods.getFioAddresses the same way (caught live on the sim). It now skips pre-engine wallets and lets the next 30s cycle retry, which also avoids wedging its one-shot expiredChecking latch.
1 parent d76796f commit 00faa89

6 files changed

Lines changed: 80 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased (develop)
44

5+
- changed: Gate action-queue balance-effect checks and the login FIO address refresh on engine readiness, so wallets emitted from the core's new wallet cache (before their engines load) cannot mis-evaluate balance effects or crash the FIO refresh.
6+
57
## 4.50.0 (staging)
68

79
- added: Changelly swap provider

eslint.config.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,6 @@ export default [
330330
'src/components/services/DeepLinkingManager.tsx',
331331
'src/components/services/EdgeContextCallbackManager.tsx',
332332

333-
'src/components/services/FioService.ts',
334333
'src/components/services/LoanManagerService.ts',
335334
'src/components/services/NetworkActivity.ts',
336335
'src/components/services/PasswordReminderService.ts',

src/components/services/FioService.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ interface Props {
2828

2929
type NameDates = Record<string, Date>
3030

31-
export const FioService = (props: Props) => {
31+
export const FioService = (props: Props): null => {
3232
const { account, navigation } = props
3333
const dispatch = useDispatch()
3434

@@ -65,10 +65,19 @@ export const FioService = (props: Props) => {
6565
}
6666

6767
if (expiredChecking.current) return
68+
69+
// Wallet objects can exist before their engines do (wallet cache),
70+
// and refreshFioNames calls engine-backed otherMethods.
71+
// Skip pre-engine wallets and let the next cycle retry them:
72+
const readyWallets = fioWallets.current.filter(
73+
fioWallet => fioWallet.otherMethods.getFioAddresses != null
74+
)
75+
if (readyWallets.length === 0) return
76+
6877
expiredChecking.current = true
6978

7079
const walletsToCheck: EdgeCurrencyWallet[] = []
71-
for (const fioWallet of fioWallets.current) {
80+
for (const fioWallet of readyWallets) {
7281
if (!walletsCheckedForExpired.current[fioWallet.id]) {
7382
walletsToCheck.push(fioWallet)
7483
}
@@ -130,7 +139,10 @@ export const FioService = (props: Props) => {
130139
return null
131140
}
132141

133-
function arraysEqual(arr1: EdgeCurrencyWallet[], arr2: EdgeCurrencyWallet[]) {
142+
function arraysEqual(
143+
arr1: EdgeCurrencyWallet[],
144+
arr2: EdgeCurrencyWallet[]
145+
): boolean {
134146
if (arr1.length !== arr2.length) return false
135147

136148
arr1.sort((a, b) => a.id.localeCompare(b.id))

src/components/services/Services.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
width
2828
} from '../../util/scaling'
2929
import { snooze } from '../../util/utils'
30+
import { waitForWalletOtherMethods } from '../../util/waitForWalletOtherMethods'
3031
import { AlertDropdown } from '../navigation/AlertDropdown'
3132
import { AccountCallbackManager } from './AccountCallbackManager'
3233
import { ActionQueueService } from './ActionQueueService'
@@ -88,6 +89,20 @@ export const Services: React.FC<Props> = props => {
8889
console.warn('registerNotificationsV2 error:', error)
8990
})
9091

92+
// Wallet objects can exist before their engines do (wallet cache),
93+
// and the FIO refreshes below call engine-backed otherMethods,
94+
// so wait for each FIO wallet's engine first:
95+
const fioWallets = Object.values(account.currencyWallets).filter(
96+
wallet => wallet.currencyInfo.pluginId === 'fio'
97+
)
98+
await Promise.all(
99+
fioWallets.map(async wallet => {
100+
await waitForWalletOtherMethods(wallet)
101+
})
102+
).catch((error: unknown) => {
103+
console.warn('waitForWalletOtherMethods error:', error)
104+
})
105+
91106
await dispatch(refreshConnectedWallets).catch((error: unknown) => {
92107
console.warn(error)
93108
})

src/controllers/action-queue/runtime/checkActionEffect.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,17 @@ export async function checkActionEffect(
120120
// TODO: Use effect.address when we can check address balances
121121
const { aboveAmount, belowAmount, tokenId, walletId } = effect
122122
const wallet = await account.waitForCurrencyWallet(walletId)
123+
124+
// The wallet object can exist before its engine loads (wallet cache),
125+
// so don't evaluate the effect against cached, possibly stale
126+
// balances. Report "not yet effective" until the engine has synced:
127+
if (wallet.syncStatus.totalRatio < 1) {
128+
return {
129+
delay: 15000,
130+
isEffective: false
131+
}
132+
}
133+
123134
const walletBalance = wallet.balanceMap.get(tokenId) ?? '0'
124135

125136
return {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { EdgeCurrencyWallet } from 'edge-core-js'
2+
3+
// Engine creation for a large account can take minutes on login,
4+
// matching how long waitForAllWallets used to take before the wallet
5+
// cache existed, so this is a safety valve rather than a deadline:
6+
const OTHER_METHODS_TIMEOUT_MS = 10 * 60 * 1000
7+
8+
/**
9+
* Waits for a wallet's engine-backed `otherMethods` to arrive.
10+
*
11+
* The core's wallet cache emits wallet objects before their engines
12+
* exist, and `wallet.otherMethods` is guaranteed to be `{}` until the
13+
* engine loads. Rejects after a timeout so a wallet whose engine never
14+
* loads cannot hang callers forever.
15+
*/
16+
export async function waitForWalletOtherMethods(
17+
wallet: EdgeCurrencyWallet,
18+
timeoutMs: number = OTHER_METHODS_TIMEOUT_MS
19+
): Promise<void> {
20+
if (Object.keys(wallet.otherMethods).length > 0) return
21+
22+
await new Promise<void>((resolve, reject) => {
23+
const timeout = setTimeout(() => {
24+
unsubscribe()
25+
reject(
26+
new Error(`Timed out waiting for wallet ${wallet.id} engine methods`)
27+
)
28+
}, timeoutMs)
29+
const unsubscribe = wallet.watch('otherMethods', otherMethods => {
30+
if (Object.keys(otherMethods).length > 0) {
31+
clearTimeout(timeout)
32+
unsubscribe()
33+
resolve()
34+
}
35+
})
36+
})
37+
}

0 commit comments

Comments
 (0)