Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix error on unknown enabledTokenIds in exchange rate fetching #4700

Merged
merged 3 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- changed: Do not write tx.metadata in fiat sell transactions
- changed: Use new EdgeTransaction.savedAction to show extended transaction info in tx list and tx details
- changed: Use new EdgeTxAction data for Thorchain and Tron stake plugins
- changed: Make useAsyncEffect tags required
- fixed: USP vs legacy landing experiment distribution
- fixed: Paybis sell from Tron USDT
- fixed: Remove `minWidth` style from stake option card
Expand Down
1 change: 1 addition & 0 deletions src/actions/ExchangeRateActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ async function buildExchangeRates(state: RootState): Promise<GuiExchangeRates> {
exchangeRates.push({ currency_pair: `iso:USD_${walletIsoFiat}` })
}
for (const tokenId of wallet.enabledTokenIds) {
if (wallet.currencyConfig.allTokens[tokenId] == null) continue
const { currencyCode: tokenCode } = wallet.currencyConfig.allTokens[tokenId]
if (tokenCode !== currencyCode) {
exchangeRates.push({ currency_pair: `${tokenCode}_${walletIsoFiat}` })
Expand Down
12 changes: 8 additions & 4 deletions src/components/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,14 @@ export const Main = () => {
})

// Wait for the experiment config to initialize before rendering anything
useAsyncEffect(async () => {
if (isMaestro()) return
setLegacyLanding((await getExperimentConfigValue('legacyLanding')) === 'legacyLanding')
}, [])
useAsyncEffect(
async () => {
if (isMaestro()) return
setLegacyLanding((await getExperimentConfigValue('legacyLanding')) === 'legacyLanding')
},
[],
'setLegacyLanding'
)

return legacyLanding == null ? (
<LoadingSplashScreen />
Expand Down
134 changes: 69 additions & 65 deletions src/components/charts/SwipeChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,76 +171,80 @@ const SwipeChartComponent = (params: Props) => {
const maxPriceDataPoint = React.useMemo(() => chartData.find(point => point.y === maxPrice), [chartData, maxPrice])

// Fetch/cache chart data, set shared animation transition values
useAsyncEffect(async () => {
if (!isLoading) {
setIsLoading(true)
setChartData([])
sMinMaxOpacity.value = 0

// Use cached data, if available
const cachedChartData = cachedTimespanChartData.get(selectedTimespan)

const delayShowMinMaxLabels = () => {
// Delay the appearance of the min/max price labels while the chart
// price line finishes its entering animation
sMinMaxOpacity.value = withDelay(ANIMATION_DURATION.maxMinFadeInDelay, withTiming(1, { duration: ANIMATION_DURATION.maxMinFadeIn }))
}

try {
if (cachedChartData != null) {
// The chart price line animation is slow when transitioning directly
// between datasets.
// Add a delay so the component can get re-mounted with fresh data
// instead.
setTimeout(() => {
setChartData(cachedChartData)
setIsLoading(false)
delayShowMinMaxLabels()
}, 10)
} else {
const unixNow = Math.trunc(new Date().getTime() / 1000)
const fromParam = unixNow - queryFromTimeOffset
const fetchPath = sprintf(DATASET_URL_4S, assetId, defaultFiat, fromParam, unixNow)
let fetchUrl = `${COINGECKO_URL}${fetchPath}`
do {
// Construct the dataset query
const response = await fetch(fetchUrl)
const result = await response.json()
const marketChartRange = asCoinGeckoMarketApi(result)
if ('status' in marketChartRange) {
if (marketChartRange.status.error_code === 429) {
// Rate limit error
if (!fetchUrl.includes('x_cg_pro_api_key')) {
fetchUrl = `${COINGECKO_URL_PRO}${fetchPath}&x_cg_pro_api_key=${ENV.COINGECKO_API_KEY}`
continue
}
}
throw new Error(String(marketChartRange))
} else {
const rawChartData = marketChartRange.prices.map(rawDataPoint => {
return {
x: new Date(rawDataPoint[0]),
y: rawDataPoint[1]
}
})
const reducedChartData = reduceChartData(rawChartData, selectedTimespan)
useAsyncEffect(
async () => {
if (!isLoading) {
setIsLoading(true)
setChartData([])
sMinMaxOpacity.value = 0

// Use cached data, if available
const cachedChartData = cachedTimespanChartData.get(selectedTimespan)

const delayShowMinMaxLabels = () => {
// Delay the appearance of the min/max price labels while the chart
// price line finishes its entering animation
sMinMaxOpacity.value = withDelay(ANIMATION_DURATION.maxMinFadeInDelay, withTiming(1, { duration: ANIMATION_DURATION.maxMinFadeIn }))
}

setChartData(reducedChartData)
cachedTimespanChartData.set(selectedTimespan, reducedChartData)
setCachedChartData(cachedTimespanChartData)
try {
if (cachedChartData != null) {
// The chart price line animation is slow when transitioning directly
// between datasets.
// Add a delay so the component can get re-mounted with fresh data
// instead.
setTimeout(() => {
setChartData(cachedChartData)
setIsLoading(false)
delayShowMinMaxLabels()
break
}
} while (true)
}, 10)
} else {
const unixNow = Math.trunc(new Date().getTime() / 1000)
const fromParam = unixNow - queryFromTimeOffset
const fetchPath = sprintf(DATASET_URL_4S, assetId, defaultFiat, fromParam, unixNow)
let fetchUrl = `${COINGECKO_URL}${fetchPath}`
do {
// Construct the dataset query
const response = await fetch(fetchUrl)
const result = await response.json()
const marketChartRange = asCoinGeckoMarketApi(result)
if ('status' in marketChartRange) {
if (marketChartRange.status.error_code === 429) {
// Rate limit error
if (!fetchUrl.includes('x_cg_pro_api_key')) {
fetchUrl = `${COINGECKO_URL_PRO}${fetchPath}&x_cg_pro_api_key=${ENV.COINGECKO_API_KEY}`
continue
}
}
throw new Error(String(marketChartRange))
} else {
const rawChartData = marketChartRange.prices.map(rawDataPoint => {
return {
x: new Date(rawDataPoint[0]),
y: rawDataPoint[1]
}
})
const reducedChartData = reduceChartData(rawChartData, selectedTimespan)

setChartData(reducedChartData)
cachedTimespanChartData.set(selectedTimespan, reducedChartData)
setCachedChartData(cachedTimespanChartData)
setIsLoading(false)
delayShowMinMaxLabels()
break
}
} while (true)
}
} catch (e: any) {
showWarning(`Failed to retrieve market data for ${currencyCode}.`)
console.error(e)
}
} catch (e: any) {
showWarning(`Failed to retrieve market data for ${currencyCode}.`)
console.error(e)
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedTimespan])
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[selectedTimespan],
'swipeChart'
)

React.useEffect(() => {
if (chartData.length > 0) {
Expand Down
10 changes: 7 additions & 3 deletions src/components/modals/CategoryModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,13 @@ export function CategoryModal(props: Props) {
const categories = useSelector(state => state.ui.subcategories)

// Load the categories from disk:
useAsyncEffect(async () => {
await dispatch(getSubcategories())
}, [dispatch])
useAsyncEffect(
async () => {
await dispatch(getSubcategories())
},
[dispatch],
'categoryModal'
)

const sortedCategories = React.useMemo(() => {
// Transform the raw categories into row objects:
Expand Down
16 changes: 10 additions & 6 deletions src/components/modals/PermissionsSettingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ export function PermissionsSettingModal(props: {
? sprintf(lstrings.contacts_permission_modal_enable_settings_mandatory, name, permission)
: sprintf(lstrings.contacts_permission_modal_enable_settings, name, permission)

useAsyncEffect(async () => {
if (!isAppForeground || !mandatory) return
const status = await check(permissionNames[permission])
if (!checkIfDenied(status)) bridge.resolve(false)
return () => {}
}, [permission, isAppForeground])
useAsyncEffect(
async () => {
if (!isAppForeground || !mandatory) return
const status = await check(permissionNames[permission])
if (!checkIfDenied(status)) bridge.resolve(false)
return () => {}
},
[permission, isAppForeground],
'PermissionsSettingModal'
)

const handlePress = () => {
openSettings().catch(showError)
Expand Down
112 changes: 58 additions & 54 deletions src/components/modals/WalletListMenuModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,72 +140,76 @@ export function WalletListMenuModal(props: Props) {
dispatch(walletListMenuAction(navigation, walletId, option, tokenId)).catch(error => showError(error))
})

useAsyncEffect(async () => {
if (wallet == null) {
setOptions([
{ label: lstrings.string_get_raw_keys, value: 'getRawKeys' },
{ label: lstrings.string_archive_wallet, value: 'rawDelete' }
])
return
}
useAsyncEffect(
async () => {
if (wallet == null) {
setOptions([
{ label: lstrings.string_get_raw_keys, value: 'getRawKeys' },
{ label: lstrings.string_archive_wallet, value: 'rawDelete' }
])
return
}

if (tokenId != null) {
setOptions([
{
label: lstrings.string_resync,
value: 'resync'
},
{
label: lstrings.fragment_wallets_export_transactions,
value: 'exportWalletTransactions'
},
{
label: lstrings.fragment_wallets_delete_token,
value: 'delete'
}
])
return
}
if (tokenId != null) {
setOptions([
{
label: lstrings.string_resync,
value: 'resync'
},
{
label: lstrings.fragment_wallets_export_transactions,
value: 'exportWalletTransactions'
},
{
label: lstrings.fragment_wallets_delete_token,
value: 'delete'
}
])
return
}

const result: Option[] = []
const result: Option[] = []

const { pluginId } = wallet.currencyInfo
if (pausedWallets != null && !isKeysOnlyPlugin(pluginId)) {
result.push({
label: pausedWallets.has(walletId) ? lstrings.fragment_wallets_unpause_wallet : lstrings.fragment_wallets_pause_wallet,
value: 'togglePause'
})
}
const { pluginId } = wallet.currencyInfo
if (pausedWallets != null && !isKeysOnlyPlugin(pluginId)) {
result.push({
label: pausedWallets.has(walletId) ? lstrings.fragment_wallets_unpause_wallet : lstrings.fragment_wallets_pause_wallet,
value: 'togglePause'
})
}

for (const option of WALLET_LIST_MENU) {
const { pluginIds, label, value } = option
for (const option of WALLET_LIST_MENU) {
const { pluginIds, label, value } = option

if (Array.isArray(pluginIds) && !pluginIds.includes(pluginId)) continue
if (Array.isArray(pluginIds) && !pluginIds.includes(pluginId)) continue

// Special case for `manageTokens`. Only allow pluginsIds that have metatokens
if (value === 'manageTokens') {
if (Object.keys(account.currencyConfig[pluginId].builtinTokens).length === 0) continue
}
// Special case for `manageTokens`. Only allow pluginsIds that have metatokens
if (value === 'manageTokens') {
if (Object.keys(account.currencyConfig[pluginId].builtinTokens).length === 0) continue
}

// Special case for light accounts. Don't allow `getSeed` or `getRawKeys`
if (account.username == null && (value === 'getSeed' || value === 'getRawKeys')) continue
// Special case for light accounts. Don't allow `getSeed` or `getRawKeys`
if (account.username == null && (value === 'getSeed' || value === 'getRawKeys')) continue

result.push({ label, value })
}
result.push({ label, value })
}

const splittable = await account.listSplittableWalletTypes(wallet.id)
const splittable = await account.listSplittableWalletTypes(wallet.id)

const currencyInfos = getCurrencyInfos(account)
for (const splitWalletType of splittable) {
const info = currencyInfos.find(({ walletType }) => walletType === splitWalletType)
if (info == null || getSpecialCurrencyInfo(info.pluginId).isSplittingDisabled) continue
result.push({ label: sprintf(lstrings.string_split_wallet, info.displayName), value: `split${info.pluginId}` })
}
const currencyInfos = getCurrencyInfos(account)
for (const splitWalletType of splittable) {
const info = currencyInfos.find(({ walletType }) => walletType === splitWalletType)
if (info == null || getSpecialCurrencyInfo(info.pluginId).isSplittingDisabled) continue
result.push({ label: sprintf(lstrings.string_split_wallet, info.displayName), value: `split${info.pluginId}` })
}

setOptions(result)
setOptions(result)

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[],
'WalletListMenuModal'
)

return (
<ThemedModal bridge={bridge} onCancel={handleCancel}>
Expand Down
Loading
Loading