Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-20 12:53:26 +04:00
parent 6a5a8300cf
commit 3269095c97
15 changed files with 148 additions and 96 deletions

View file

@ -12,6 +12,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.StatusSource
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.utils.StringsSigns.DASH_SIGN
@ -40,7 +41,7 @@ class TokenItemStateConverter(
createSubtitleState(it, appCurrency)
},
private val subtitle2StateProvider: (CryptoCurrencyStatus) -> TokenItemState.Subtitle2State? = {
createSubtitle2State(currencyStatus = it)
createSubtitle2State(status = it)
},
private val fiatAmountStateProvider: (CryptoCurrencyStatus) -> TokenItemState.FiatAmountState? = {
createFiatAmountState(status = it, appCurrency = appCurrency)
@ -147,7 +148,7 @@ class TokenItemStateConverter(
return totalAmount.format { crypto(currency) }
}
fun CryptoCurrencyStatus.getStakedBalance() =
private fun CryptoCurrencyStatus.getStakedBalance() =
(value.yieldBalance as? YieldBalance.Data)?.getTotalWithRewardsStakingBalance().orZero()
private fun createTitleState(currencyStatus: CryptoCurrencyStatus): TokenItemState.TitleState {
@ -190,15 +191,16 @@ class TokenItemStateConverter(
}
}
private fun createSubtitle2State(currencyStatus: CryptoCurrencyStatus): TokenItemState.Subtitle2State? {
return when (currencyStatus.value) {
private fun createSubtitle2State(status: CryptoCurrencyStatus): TokenItemState.Subtitle2State? {
return when (status.value) {
is CryptoCurrencyStatus.Loaded,
is CryptoCurrencyStatus.Custom,
is CryptoCurrencyStatus.NoQuote,
is CryptoCurrencyStatus.NoAccount,
-> {
TokenItemState.Subtitle2State.TextContent(
text = currencyStatus.getFormattedCryptoAmount(includeStaking = true),
text = status.getFormattedCryptoAmount(includeStaking = true),
isFlickering = status.value.isFlickering(),
)
}
is CryptoCurrencyStatus.Loading,
@ -221,6 +223,7 @@ class TokenItemStateConverter(
-> {
TokenItemState.FiatAmountState.Content(
text = status.getFormattedFiatAmount(appCurrency = appCurrency, includeStaking = true),
isFlickering = status.value.isFlickering(),
icons = buildList {
if (!status.getStakedBalance().isZero()) {
TokenItemState.FiatAmountState.Content.IconUM(
@ -228,6 +231,12 @@ class TokenItemStateConverter(
useAccentColor = true,
).let(::add)
}
if (status.value.getStatusSource() == StatusSource.ONLY_CACHE) {
TokenItemState.FiatAmountState.Content.IconUM(
iconRes = R.drawable.ic_error_sync_24,
useAccentColor = false,
).let(::add)
}
}.toImmutableList(),
)
}
@ -248,6 +257,7 @@ class TokenItemStateConverter(
price = fiatRate.getFormattedCryptoPrice(appCurrency),
priceChangePercent = priceChange.format { percent() },
type = priceChange.getPriceChangeType(),
isFlickering = value.isFlickering(),
)
} else {
TokenItemState.SubtitleState.Unknown
@ -263,5 +273,15 @@ class TokenItemStateConverter(
private fun BigDecimal.getPriceChangeType(): PriceChangeType {
return PriceChangeConverter.fromBigDecimal(value = this)
}
private fun CryptoCurrencyStatus.Value.isFlickering(): Boolean = getStatusSource() == StatusSource.CACHE
private fun CryptoCurrencyStatus.Value.getStatusSource(): StatusSource? {
return when (this) {
is CryptoCurrencyStatus.Loaded -> source
is CryptoCurrencyStatus.NoAccount -> source
else -> null
}
}
}
}

View file

@ -10,10 +10,9 @@ import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkStatus
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.extensions.addOrReplace
import com.tangem.utils.extensions.replaceBy
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.launch
private typealias NetworkStatusesByWalletId = Map<String, Set<NetworkStatusDM>>
@ -22,8 +21,6 @@ internal class DefaultNetworksStatusesStore(
private val persistenceDataStore: DataStore<NetworkStatusesByWalletId>,
) : NetworksStatusesStore {
private val mutex = Mutex()
override fun get(key: UserWalletId): Flow<Set<NetworkStatus>> {
return runtimeDataStore.get(provideStringKey(key))
}
@ -42,11 +39,20 @@ internal class DefaultNetworksStatusesStore(
if (cachedStatuses.isNotEmpty()) {
send(cachedStatuses)
/**
* Required for storing cache data.
* This will help to recognize networks that are still uploaded.
*
* @see mergeStatuses
*/
storeAll(key = key, values = cachedStatuses)
}
runtimeDataStore.get(provideStringKey(key))
.onEach { runtimeStatuses ->
val mergedStatuses = mergeStatuses(
networks = networks,
cachedStatuses = cachedStatuses,
runtimeStatuses = runtimeStatuses,
)
@ -57,36 +63,20 @@ internal class DefaultNetworksStatusesStore(
}
override suspend fun getSyncOrNull(key: UserWalletId): Set<NetworkStatus>? {
return runtimeDataStore.getSyncOrNull(provideStringKey(key))
return runtimeDataStore.getSyncOrNull(key = provideStringKey(key))
}
override suspend fun store(key: UserWalletId, value: NetworkStatus) {
mutex.withLock {
val newValues = getSyncOrNull(key)
?.addOrReplace(value) { it.network == value.network }
?: setOf(value)
runtimeDataStore.store(provideStringKey(key), newValues)
storeNetworkStatusInPersistence(key, value)
}
storeAll(key = key, values = setOf(value))
}
override suspend fun storeAll(key: UserWalletId, values: Collection<NetworkStatus>) {
mutex.withLock {
val currentValues = getSyncOrNull(key) ?: emptySet()
val updatedValues = currentValues.toMutableSet()
override suspend fun storeAll(key: UserWalletId, values: Set<NetworkStatus>) {
coroutineScope {
val updatedValues = getSyncOrNull(key).orEmpty()
.addOrReplace(items = values) { prev, new -> prev.network == new.network }
values.forEach { newValue ->
val isReplaced = updatedValues.replaceBy(newValue) {
it.network == newValue.network
}
if (!isReplaced) {
updatedValues.add(newValue)
}
}
runtimeDataStore.store(provideStringKey(key), updatedValues)
launch { runtimeDataStore.store(key = provideStringKey(key), value = updatedValues) }
launch { storeNetworkStatusInPersistence(userWalletId = key, statuses = updatedValues) }
}
}
@ -96,19 +86,24 @@ internal class DefaultNetworksStatusesStore(
* If a status with the same network is in both sets, the status from [runtimeStatuses] is used.
*/
private fun mergeStatuses(
networks: Set<Network>,
cachedStatuses: Set<NetworkStatus>,
runtimeStatuses: Set<NetworkStatus>,
): Set<NetworkStatus> {
return runtimeStatuses
.map { runtime ->
runtime.takeIf { it.value !is NetworkStatus.Unreachable }
?: getCachedStatusIfPossible(cachedStatuses = cachedStatuses, runtime = runtime)
return networks.mapNotNullTo(hashSetOf()) { network ->
val runtimeStatus = runtimeStatuses.firstOrNull { it.network == network }
if (runtimeStatus == null || runtimeStatus.value is NetworkStatus.Unreachable) {
getCachedStatusIfPossible(cachedStatuses = cachedStatuses, network = network)
?: runtimeStatus
} else {
runtimeStatus
}
.toSet()
}
}
private fun getCachedStatusIfPossible(cachedStatuses: Set<NetworkStatus>, runtime: NetworkStatus): NetworkStatus {
val cached = cachedStatuses.firstOrNull { it.network == runtime.network } ?: return runtime
private fun getCachedStatusIfPossible(cachedStatuses: Set<NetworkStatus>, network: Network): NetworkStatus? {
val cached = cachedStatuses.firstOrNull { it.network == network } ?: return null
val updatedCachedStatus = when (val status = cached.value) {
is NetworkStatus.NoAccount -> status.copy(source = StatusSource.ONLY_CACHE)
@ -122,22 +117,17 @@ internal class DefaultNetworksStatusesStore(
return if (updatedCachedStatus != null) {
cached.copy(value = updatedCachedStatus)
} else {
runtime
null
}
}
private suspend fun storeNetworkStatusInPersistence(userWalletId: UserWalletId, networkStatus: NetworkStatus) {
val network = networkStatus.network
private suspend fun storeNetworkStatusInPersistence(userWalletId: UserWalletId, statuses: Set<NetworkStatus>) {
// Converter will return null if the network status is not supported
val newStatus = NetworkStatusDataModelConverter.convert(value = networkStatus) ?: return
val newStatuses = NetworkStatusDataModelConverter.convertSet(input = statuses).filterNotNull().toSet()
persistenceDataStore.updateData { storedStatuses ->
val userWalletStatuses = storedStatuses[userWalletId.stringValue].orEmpty()
val updatedStatuses = userWalletStatuses.addOrReplace(item = newStatus) { it.networkId == network.id }
storedStatuses.toMutableMap().apply {
this[userWalletId.stringValue] = updatedStatuses
this[userWalletId.stringValue] = newStatuses
}
}
}

View file

@ -15,5 +15,5 @@ interface NetworksStatusesStore {
suspend fun store(key: UserWalletId, value: NetworkStatus)
suspend fun storeAll(key: UserWalletId, values: Collection<NetworkStatus>)
suspend fun storeAll(key: UserWalletId, values: Set<NetworkStatus>)
}

View file

@ -36,4 +36,16 @@ inline fun <T> Set<T>.addOrReplace(item: T, predicate: (T) -> Boolean): Set<T> {
}
return mutableList
}
inline fun <T> Set<T>.addOrReplace(items: Set<T>, predicate: (T, T) -> Boolean): Set<T> {
val updatedValues = this.toMutableSet()
items.forEach { newValue ->
val isReplaced = updatedValues.replaceBy(item = newValue, predicate = { predicate(it, newValue) })
if (!isReplaced) updatedValues.add(newValue)
}
return updatedValues
}

View file

@ -129,7 +129,7 @@ internal class DefaultNetworksRepository(
}
if (statusesToRefresh != null) {
networksStatusesStore.storeAll(key = userWalletId, values = statusesToRefresh)
networksStatusesStore.storeAll(key = userWalletId, values = statusesToRefresh.toSet())
}
}

View file

@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import timber.log.Timber
internal class DefaultQuotesRepository(
private val tangemTechApi: TangemTechApi,
@ -115,25 +116,26 @@ internal class DefaultQuotesRepository(
val replacementIdsResult = quotesUnsupportedCurrenciesAdapter.replaceUnsupportedCurrencies(
rawCurrenciesIds.map { it.value }.toSet(),
)
val response = safeApiCallWithTimeout(
safeApiCallWithTimeout(
call = {
val coinIds = replacementIdsResult.idsForRequest.joinToString(separator = ",")
tangemTechApi.getQuotes(appCurrencyId, coinIds).bind()
val response = tangemTechApi.getQuotes(appCurrencyId, coinIds).bind()
val updatedResponse = quotesUnsupportedCurrenciesAdapter.getResponseWithUnsupportedCurrencies(
response = response,
filteredIds = replacementIdsResult.idsFiltered,
)
quotesStore.store(updatedResponse)
},
onError = { error ->
Timber.e(error)
cacheRegistry.invalidate(rawCurrenciesIds.map { getQuoteCacheKey(it) })
quotesStore.storeEmptyQuotes(currenciesIds = rawCurrenciesIds)
throw error
},
)
val updatedResponse = quotesUnsupportedCurrenciesAdapter.getResponseWithUnsupportedCurrencies(
response,
replacementIdsResult.idsFiltered,
)
quotesStore.store(updatedResponse)
}
private suspend fun filterExpiredCurrenciesIds(

View file

@ -1,5 +1,6 @@
package com.tangem.domain.tokens.model
import com.tangem.domain.models.StatusSource
import java.math.BigDecimal
/**
@ -27,5 +28,6 @@ sealed class TotalFiatBalance {
data class Loaded(
val amount: BigDecimal,
val isAllAmountsSummarized: Boolean,
val source: StatusSource,
) : TotalFiatBalance()
}

View file

@ -1,5 +1,6 @@
package com.tangem.domain.tokens.model
import com.tangem.domain.models.StatusSource
import java.math.BigDecimal
/**
@ -47,6 +48,7 @@ sealed class TokenList {
override val totalFiatBalance: TotalFiatBalance = TotalFiatBalance.Loaded(
amount = BigDecimal.ZERO,
isAllAmountsSummarized = true,
source = StatusSource.ACTUAL,
)
}

View file

@ -1,6 +1,7 @@
package com.tangem.domain.tokens.operations
import arrow.core.NonEmptyList
import com.tangem.domain.models.StatusSource
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.TotalFiatBalance
@ -34,14 +35,14 @@ internal class TokenListFiatBalanceOperations(
is CryptoCurrencyStatus.NoAmount,
-> {
if (BlockchainUtils.isIncludeToBalanceOnError(token.currency.network.id.value)) {
fiatBalance = recalculateNoAccountBalance(fiatBalance)
fiatBalance = recalculateNoAccountBalance(status, fiatBalance)
} else {
fiatBalance = TotalFiatBalance.Failed
break
}
}
is CryptoCurrencyStatus.NoAccount -> {
fiatBalance = recalculateNoAccountBalance(fiatBalance)
fiatBalance = recalculateNoAccountBalance(status, fiatBalance)
}
is CryptoCurrencyStatus.Loaded -> {
fiatBalance = recalculateBalance(status, fiatBalance)
@ -55,11 +56,15 @@ internal class TokenListFiatBalanceOperations(
return fiatBalance
}
private fun recalculateNoAccountBalance(currentBalance: TotalFiatBalance): TotalFiatBalance {
private fun recalculateNoAccountBalance(
status: CryptoCurrencyStatus.Value,
currentBalance: TotalFiatBalance,
): TotalFiatBalance {
return (currentBalance as? TotalFiatBalance.Loaded)?.copy(isAllAmountsSummarized = false)
?: TotalFiatBalance.Loaded(
amount = BigDecimal.ZERO,
isAllAmountsSummarized = false,
source = (status as? CryptoCurrencyStatus.NoAccount)?.source ?: StatusSource.ACTUAL,
)
}
@ -77,6 +82,7 @@ internal class TokenListFiatBalanceOperations(
) ?: TotalFiatBalance.Loaded(
amount = status.fiatAmount + fiatStakingBalance,
isAllAmountsSummarized = true,
source = status.source,
)
}
}
@ -95,6 +101,7 @@ internal class TokenListFiatBalanceOperations(
) ?: TotalFiatBalance.Loaded(
amount = status.fiatAmount.orZero() + fiatYieldBalance,
isAllAmountsSummarized = isTokenAmountCanBeSummarized,
source = StatusSource.ACTUAL,
)
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.domain.tokens.mock
import arrow.core.NonEmptyList
import arrow.core.toNonEmptyListOrNull
import com.tangem.domain.models.StatusSource
import com.tangem.domain.tokens.mock.MockNetworksGroups.failedNetworksGroups
import com.tangem.domain.tokens.mock.MockNetworksGroups.loadedNetworksGroups
import com.tangem.domain.tokens.mock.MockNetworksGroups.sortedNetworksGroups
@ -79,6 +80,7 @@ internal object MockTokenLists {
totalFiatBalance = TotalFiatBalance.Loaded(
amount = tokens.sumOf { it.value.fiatAmount ?: BigDecimal.ZERO },
isAllAmountsSummarized = true,
source = StatusSource.ACTUAL,
),
)
}
@ -95,6 +97,7 @@ internal object MockTokenLists {
.flatMap { it.currencies as NonEmptyList<CryptoCurrencyStatus> }
.sumOf { it.value.fiatAmount ?: BigDecimal.ZERO },
isAllAmountsSummarized = true,
source = StatusSource.ACTUAL,
),
)
}

View file

@ -12,6 +12,7 @@ import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.*
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.StatusSource
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
import com.tangem.domain.staking.model.stakekit.RewardBlockType
@ -121,7 +122,7 @@ internal class TokenDetailsLoadedBalanceConverter(
onBalanceSelect = clickIntents::onBalanceSelect,
selectedBalanceType = currentState.selectedBalanceType,
isBalanceSelectorEnabled = isBalanceSelectorEnabled,
isBalanceFlickering = false, // TODO: Implement in [REDACTED_JIRA]
isBalanceFlickering = status.value.isFlickering(),
)
is CryptoCurrencyStatus.Loading -> TokenDetailsBalanceBlockState.Loading(
actionButtons = currentState.actionButtons,
@ -344,4 +345,14 @@ internal class TokenDetailsLoadedBalanceConverter(
RewardBlockType.RewardUnavailable -> TextReference.EMPTY
}
}
private fun CryptoCurrencyStatus.Value.isFlickering(): Boolean = getStatusSource() == StatusSource.CACHE
private fun CryptoCurrencyStatus.Value.getStatusSource(): StatusSource? {
return when (this) {
is CryptoCurrencyStatus.Loaded -> source
is CryptoCurrencyStatus.NoAccount -> source
else -> null
}
}
}

View file

@ -4,8 +4,8 @@ import arrow.core.Either
import arrow.core.getOrElse
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.domain.common.util.getCardsCount
import com.tangem.domain.visa.model.VisaCurrency
import com.tangem.domain.wallets.models.UserWallet
@ -78,17 +78,20 @@ internal class SetBalancesAndLimitsTransformer(
},
cardCount = userWallet.getCardsCount(),
isZeroBalance = visaCurrency.balances.available.isZero(),
isBalanceFlickering = false, // TODO: Implement in [REDACTED_JIRA]
isBalanceFlickering = false,
)
}
}
private fun createAdditionalInfo(visaCurrency: VisaCurrency): WalletAdditionalInfo {
val fiatAmount = BigDecimalFormatter.formatFiatAmount(
fiatAmount = visaCurrency.fiatRate?.let { visaCurrency.balances.available.multiply(it) },
fiatCurrencyCode = visaCurrency.fiatCurrency.code,
fiatCurrencySymbol = visaCurrency.fiatCurrency.symbol,
)
val fiatAmount = visaCurrency.fiatRate?.let { visaCurrency.balances.available.multiply(it) }
.format {
fiat(
fiatCurrencyCode = visaCurrency.fiatCurrency.code,
fiatCurrencySymbol = visaCurrency.fiatCurrency.symbol,
)
}
val infoContent = stringReference(
value = buildString {
append(fiatAmount)

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.util.getCardsCount
import com.tangem.domain.tokens.error.TokenListError
@ -62,14 +63,12 @@ internal class SetTokenListErrorTransformer(
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
balance = BigDecimalFormatter.formatFiatAmount(
fiatAmount = BigDecimal.ZERO,
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
),
balance = BigDecimal.ZERO.format {
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
},
cardCount = selectedWallet.getCardsCount(),
isZeroBalance = true,
isBalanceFlickering = false, // TODO: Implement in [REDACTED_JIRA]
isBalanceFlickering = false,
)
}
}

View file

@ -1,8 +1,10 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.util.getCardsCount
import com.tangem.domain.models.StatusSource
import com.tangem.domain.tokens.model.TotalFiatBalance
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
@ -54,14 +56,12 @@ internal class MultiWalletCardStateConverter(
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
balance = BigDecimalFormatter.formatFiatAmount(
fiatAmount = fiatBalance.amount,
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
),
balance = fiatBalance.amount.format {
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
},
isZeroBalance = fiatBalance.amount.isZero(),
cardCount = selectedWallet.getCardsCount(),
isBalanceFlickering = false, // TODO: Implement in [REDACTED_JIRA]
isBalanceFlickering = fiatBalance.source == StatusSource.CACHE,
)
}
}

View file

@ -1,12 +1,15 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.util.getCardsCount
import com.tangem.domain.models.StatusSource
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.utils.StringsSigns.DASH_SIGN
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.isZero
@ -65,17 +68,15 @@ internal class SingleWalletCardStateConverter(
balance = formatFiatAmount(status = status, appCurrency = appCurrency),
cardCount = selectedWallet.getCardsCount(),
isZeroBalance = status.fiatAmount?.isZero(),
isBalanceFlickering = false, // TODO: Implement in [REDACTED_JIRA]
isBalanceFlickering = (status as? CryptoCurrencyStatus.Loaded)?.source == StatusSource.CACHE,
)
}
private fun formatFiatAmount(status: CryptoCurrencyStatus.Value, appCurrency: AppCurrency): String {
val fiatAmount = status.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
val fiatAmount = status.fiatAmount ?: return DASH_SIGN
return BigDecimalFormatter.formatFiatAmount(
fiatAmount = fiatAmount,
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
return fiatAmount.format {
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
}
}
}