Updated on 2026-08-14

This commit is contained in:
Tangem 2023-02-20 21:04:44 +03:00
parent 2b5fcdf0f9
commit db93629493
9 changed files with 79 additions and 54 deletions

View file

@ -6,16 +6,18 @@ import java.math.BigDecimal
* Represents fiat balance of [WalletStoreModel] list
* @property amount Amount of the total balance
* */
sealed class TotalFiatBalance {
open val amount: BigDecimal = BigDecimal.ZERO
sealed interface TotalFiatBalance {
val amount: BigDecimal?
object Loading : TotalFiatBalance()
object Loading : TotalFiatBalance {
override val amount: BigDecimal? = null
}
data class Error(
override val amount: BigDecimal,
) : TotalFiatBalance()
override val amount: BigDecimal?,
) : TotalFiatBalance
data class Loaded(
override val amount: BigDecimal,
) : TotalFiatBalance()
) : TotalFiatBalance
}

View file

@ -16,6 +16,7 @@ import java.math.BigDecimal
* founds will be destroyed. Null if currency don't have existential deposit
* @param fiatRate Wallet's fiat rate, used to calculate fiat balance. Null if not provided
* @param isCardSingleToken shows that [Currency] is a card token
* @param isCustom shows that currency is a custom
* */
data class WalletDataModel(
val currency: Currency,
@ -24,6 +25,7 @@ data class WalletDataModel(
val existentialDeposit: BigDecimal?,
val fiatRate: BigDecimal?,
val isCardSingleToken: Boolean,
val isCustom: Boolean,
val historyTransactions: List<TransactionData>?,
) {
@ -59,9 +61,7 @@ data class WalletDataModel(
data class NoAccount(
val amountToCreateAccount: BigDecimal?,
) : Status() {
override val isErrorStatus: Boolean = true
}
) : Status()
data class Unreachable(
override val errorMessage: String?,

View file

@ -2,12 +2,14 @@ package com.tangem.tap.domain.model.builders
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.domain.common.util.UserWalletId
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.tap.common.extensions.getBlockchainTxHistory
import com.tangem.tap.common.extensions.getTokenTxHistory
import com.tangem.tap.domain.model.UserWallet
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
@ -26,23 +28,23 @@ interface WalletStoreBuilder {
companion object {
operator fun invoke(
userWalletId: UserWalletId,
userWallet: UserWallet,
blockchainNetwork: BlockchainNetwork,
): BlockchainNetworkWalletStoreBuilder {
return BlockchainNetworkWalletStoreBuilderImpl(userWalletId, blockchainNetwork)
return BlockchainNetworkWalletStoreBuilderImpl(userWallet, blockchainNetwork)
}
operator fun invoke(
userWalletId: UserWalletId,
userWallet: UserWallet,
walletManager: WalletManager,
): WalletMangerWalletStoreBuilder {
return WalletMangerWalletStoreBuilderImpl(userWalletId, walletManager)
return WalletMangerWalletStoreBuilderImpl(userWallet, walletManager)
}
}
}
private class BlockchainNetworkWalletStoreBuilderImpl(
private val userWalletId: UserWalletId,
private val userWallet: UserWallet,
private val blockchainNetwork: BlockchainNetwork,
) : WalletStoreBuilder.BlockchainNetworkWalletStoreBuilder {
private var walletManager: WalletManager? = null
@ -52,11 +54,12 @@ private class BlockchainNetworkWalletStoreBuilderImpl(
}
override fun build(): WalletStoreModel {
val blockchainWalletData = blockchainNetwork.getBlockchainWalletData(walletManager)
val tokensWalletsData = blockchainNetwork.getTokensWalletsData(walletManager)
val cardDerivationStyle = userWallet.scanResponse.card.derivationStyle
val blockchainWalletData = blockchainNetwork.getBlockchainWalletData(walletManager, cardDerivationStyle)
val tokensWalletsData = blockchainNetwork.getTokensWalletsData(walletManager, cardDerivationStyle)
return WalletStoreModel(
userWalletId = userWalletId,
userWalletId = userWallet.walletId,
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath?.let { DerivationPath(it) },
walletsData = listOf(blockchainWalletData) + tokensWalletsData,
@ -68,7 +71,7 @@ private class BlockchainNetworkWalletStoreBuilderImpl(
}
private class WalletMangerWalletStoreBuilderImpl(
private val userWalletId: UserWalletId,
private val userWallet: UserWallet,
private val walletManager: WalletManager,
) : WalletStoreBuilder.WalletMangerWalletStoreBuilder {
@ -78,7 +81,7 @@ private class WalletMangerWalletStoreBuilderImpl(
val tokenWalletsData = wallet.getTokens().firstOrNull()?.toTokenWalletData(walletManager)
return WalletStoreModel(
userWalletId = userWalletId,
userWalletId = userWallet.walletId,
blockchain = wallet.blockchain,
derivationPath = wallet.publicKey.derivationPath,
walletsData = listOf(blockchainWalletData) + listOfNotNull(tokenWalletsData),
@ -89,35 +92,45 @@ private class WalletMangerWalletStoreBuilderImpl(
}
}
private fun BlockchainNetwork.getBlockchainWalletData(walletManager: WalletManager?): WalletDataModel {
private fun BlockchainNetwork.getBlockchainWalletData(
walletManager: WalletManager?,
cardDerivationStyle: DerivationStyle?,
): WalletDataModel {
val currency = Currency.Blockchain(
blockchain = blockchain,
derivationPath = derivationPath,
)
return WalletDataModel(
currency = Currency.Blockchain(
blockchain = blockchain,
derivationPath = derivationPath,
),
currency = currency,
status = WalletDataModel.Loading,
walletAddresses = walletManager?.wallet?.createAddressesData().orEmpty(),
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
isCardSingleToken = false,
isCustom = currency.isCustomCurrency(cardDerivationStyle),
historyTransactions = walletManager?.getBlockchainTxHistory(),
)
}
private fun BlockchainNetwork.getTokensWalletsData(walletManager: WalletManager?): List<WalletDataModel> {
private fun BlockchainNetwork.getTokensWalletsData(
walletManager: WalletManager?,
cardDerivationStyle: DerivationStyle?,
): List<WalletDataModel> {
return this.tokens
.map { token ->
val currency = Currency.Token(
token = token,
blockchain = blockchain,
derivationPath = derivationPath,
)
WalletDataModel(
currency = Currency.Token(
token = token,
blockchain = blockchain,
derivationPath = derivationPath,
),
currency = currency,
status = WalletDataModel.Loading,
walletAddresses = walletManager?.wallet?.createAddressesData().orEmpty(),
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
isCardSingleToken = walletManager?.cardTokens?.contains(token) ?: false,
isCustom = currency.isCustomCurrency(cardDerivationStyle),
historyTransactions = walletManager?.getTokenTxHistory(token),
)
}
@ -135,6 +148,7 @@ private fun Blockchain.toBlockchainWalletData(walletManager: WalletManager): Wal
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
isCardSingleToken = false,
isCustom = false,
historyTransactions = walletManager.getBlockchainTxHistory(),
)
}
@ -152,6 +166,7 @@ private fun Token.toTokenWalletData(walletManager: WalletManager): WalletDataMod
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
isCardSingleToken = walletManager.cardTokens.contains(this),
isCustom = false,
historyTransactions = walletManager.getTokenTxHistory(this),
)
}

View file

@ -27,7 +27,9 @@ internal class DefaultTotalFiatBalanceCalculator : TotalFiatBalanceCalculator {
when (walletsData.findStatus()) {
TotalFiatBalanceStatus.Loading -> TotalFiatBalance.Loading
TotalFiatBalanceStatus.Error -> TotalFiatBalance.Error(calculateAmount())
TotalFiatBalanceStatus.Loaded -> TotalFiatBalance.Loaded(calculateAmount())
TotalFiatBalanceStatus.Loaded -> TotalFiatBalance.Loaded(
amount = calculateAmount() ?: BigDecimal.ZERO,
)
}
}
}
@ -47,7 +49,8 @@ internal class DefaultTotalFiatBalanceCalculator : TotalFiatBalanceCalculator {
is WalletDataModel.VerifiedOnline,
is WalletDataModel.SameCurrencyTransactionInProgress,
is WalletDataModel.TransactionInProgress,
is WalletDataModel.NoAccount -> if (walletData.fiatRate == null) {
is WalletDataModel.NoAccount,
-> if (walletData.isCustom || walletData.fiatRate == null) {
TotalFiatBalanceStatus.Error
} else {
TotalFiatBalanceStatus.Loaded
@ -60,14 +63,17 @@ internal class DefaultTotalFiatBalanceCalculator : TotalFiatBalanceCalculator {
}
}
private fun Sequence<WalletDataModel>.calculateTotalFiatAmount(): BigDecimal {
private fun Sequence<WalletDataModel>.calculateTotalFiatAmount(): BigDecimal? {
return this
.filterNot { it.isCustom }
.map { walletData ->
walletData.fiatRate
?.takeUnless { walletData.status.isErrorStatus }
?.let { walletData.status.amount.toFiatValue(it) }
?: BigDecimal.ZERO
}
.reduce(BigDecimal::plus)
.reduce { acc, value ->
value?.let { acc?.plus(it) }
}
}
private fun getCurrentStatus(
@ -88,8 +94,6 @@ internal class DefaultTotalFiatBalanceCalculator : TotalFiatBalanceCalculator {
}
private enum class TotalFiatBalanceStatus {
Loading,
Error,
Loaded,
Loading, Error, Loaded,
}
}

View file

@ -175,7 +175,7 @@ internal class DefaultWalletCurrenciesManager(
.flatMap { walletManager ->
walletStoresRepository.storeOrUpdate(
userWalletId = userWalletId,
walletStore = WalletStoreBuilder(userWalletId, blockchainNetwork)
walletStore = WalletStoreBuilder(userWallet, blockchainNetwork)
.walletManager(walletManager)
.build(),
)

View file

@ -137,7 +137,7 @@ internal class DefaultWalletStoresManager(
{ walletManager ->
walletStoresRepository.storeOrUpdate(
userWalletId = userWalletId,
walletStore = WalletStoreBuilder(userWalletId, blockchainNetwork)
walletStore = WalletStoreBuilder(userWallet, blockchainNetwork)
.walletManager(walletManager)
.build(),
)
@ -171,7 +171,7 @@ internal class DefaultWalletStoresManager(
val userWalletId = userWallet.walletId
walletStoresRepository.storeOrUpdate(
userWalletId = userWalletId,
walletStore = WalletStoreBuilder(userWalletId, walletManager)
walletStore = WalletStoreBuilder(userWallet, walletManager)
.build(),
)
}

View file

@ -6,6 +6,6 @@ import java.math.BigDecimal
data class TotalBalance(
val state: ProgressState,
val fiatAmount: BigDecimal,
val fiatAmount: BigDecimal?,
val fiatCurrency: FiatCurrency,
)

View file

@ -37,6 +37,7 @@ import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.formatWithSpaces
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.wallet.R
import com.valentinilk.shimmer.shimmer
import java.math.BigDecimal
@ -91,7 +92,7 @@ internal class TotalBalanceCard @JvmOverloads constructor(
ProgressState.Refreshing,
ProgressState.Done,
-> TotalBalanceCardState.Success(
amount = status.fiatAmount,
amount = status.fiatAmount ?: BigDecimal.ZERO,
fiatCurrency = status.fiatCurrency,
onChangeFiatCurrencyClick = onChangeCurrencyClick,
)
@ -232,9 +233,11 @@ private fun LoadedAmount(
@Composable
private fun buildAmountString(
amount: BigDecimal,
amount: BigDecimal?,
fiatCurrencySymbol: String,
): AnnotatedString {
if (amount == null) return AnnotatedString(text = UNKNOWN_AMOUNT_SIGN)
val format = DecimalFormat.getInstance(Locale.getDefault()) as DecimalFormat
val scaledAmount = amount
.setScale(2, RoundingMode.HALF_UP)
@ -255,12 +258,12 @@ private fun buildAmountString(
}
private sealed interface TotalBalanceCardState {
val amount: BigDecimal
val amount: BigDecimal?
val onChangeFiatCurrencyClick: () -> Unit
val fiatCurrency: FiatCurrency
object Empty : TotalBalanceCardState {
override val amount: BigDecimal = BigDecimal.ZERO
override val amount: BigDecimal? = null
override val fiatCurrency: FiatCurrency = FiatCurrency.Default
override val onChangeFiatCurrencyClick: () -> Unit = { /* no-op */ }
}
@ -273,7 +276,7 @@ private sealed interface TotalBalanceCardState {
}
data class Failure(
override val amount: BigDecimal,
override val amount: BigDecimal?,
override val fiatCurrency: FiatCurrency,
override val onChangeFiatCurrencyClick: () -> Unit,
) : TotalBalanceCardState

View file

@ -3,23 +3,24 @@ package com.tangem.tap.features.walletSelector.ui
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.walletSelector.redux.UserWalletModel
import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem
import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem
import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
import java.math.BigDecimal
internal fun List<UserWalletModel>.toUiModels(
appCurrency: FiatCurrency,
): Sequence<UserWalletItem> {
return this.asSequence().map { userWalletModel ->
with(userWalletModel) {
val formatAmount = { amount: BigDecimal ->
amount.toFormattedFiatValue(appCurrency.symbol)
}
val balance = when (fiatBalance) {
is TotalFiatBalance.Error -> UserWalletItem.Balance.Error(formatAmount(fiatBalance.amount))
is TotalFiatBalance.Loaded -> UserWalletItem.Balance.Loaded(formatAmount(fiatBalance.amount))
is TotalFiatBalance.Error -> UserWalletItem.Balance.Error(
amount = fiatBalance.amount?.toFormattedFiatValue(appCurrency.symbol) ?: UNKNOWN_AMOUNT_SIGN,
)
is TotalFiatBalance.Loaded -> UserWalletItem.Balance.Loaded(
amount = fiatBalance.amount.toFormattedFiatValue(appCurrency.symbol),
)
is TotalFiatBalance.Loading -> UserWalletItem.Balance.Loading
}
when (type) {