Updated on 2026-08-14

This commit is contained in:
Tangem 2022-05-30 15:46:21 +03:00
commit 57177fbfd1
9 changed files with 106 additions and 114 deletions

View file

@ -1,11 +0,0 @@
package com.tangem.tap.domain.extensions
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.extensions.isAboveZero
fun Map<AmountType, Amount>.toSendableAmounts(): List<Amount> {
return this.toList().unzip().second
.filter { it.type != AmountType.Reserve }
.filter { it.isAboveZero() }
}

View file

@ -7,8 +7,7 @@ import com.tangem.domain.common.isTangemTwin
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.extensions.isWalletDataSupported
import com.tangem.tap.domain.extensions.signedHashesCount
import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.features.wallet.models.hasPendingTransactions
import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransactions
import org.rekotlin.Action
import java.util.*
@ -62,9 +61,7 @@ private fun handleEraseWallet(
(card?.isWalletDataSupported == true &&
(!state.scanResponse.isTangemNote() && !state.scanResponse.supportsBackup()))
val notEmpty = state.wallets.any {
it.hasPendingTransactions() || it.amounts.toSendableAmounts().isNotEmpty()
}
val notEmpty = state.wallets.any { it.hasSendableAmountsOrPendingTransactions() }
val eraseWalletState = when {
notAllowedByCard -> EraseWalletState.NotAllowedByCard
notEmpty -> EraseWalletState.NotEmpty

View file

@ -1,20 +1,26 @@
package com.tangem.tap.features.wallet.models
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.TransactionStatus
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.*
import com.tangem.blockchain.extensions.isAboveZero
import com.tangem.tap.common.extensions.toFormattedString
import com.tangem.tap.domain.extensions.toSendableAmounts
import java.math.BigDecimal
data class PendingTransaction(
val address: String?,
val amount: BigDecimal?,
val amountUi: String?,
val currency: String,
val type: PendingTransactionType
)
val transactionData: TransactionData,
val type: PendingTransactionType,
) {
val address: String? = when (type) {
PendingTransactionType.Incoming -> transactionData.destinationAddress
PendingTransactionType.Outgoing -> transactionData.sourceAddress
PendingTransactionType.Unknown -> null
}
val amountValue: BigDecimal? = transactionData.amount.value
val amountValueUi: String? = amountValue?.toFormattedString(transactionData.amount.decimals)
val currency: String = transactionData.amount.currencySymbol
}
enum class PendingTransactionType { Incoming, Outgoing, Unknown }
@ -22,29 +28,11 @@ fun TransactionData.toPendingTransaction(walletAddress: String): PendingTransact
if (this.status == TransactionStatus.Confirmed) return null
val type: PendingTransactionType = when {
this.sourceAddress == walletAddress -> {
PendingTransactionType.Outgoing
}
this.destinationAddress == walletAddress -> {
PendingTransactionType.Incoming
}
else -> {
PendingTransactionType.Unknown
}
this.sourceAddress == walletAddress -> PendingTransactionType.Outgoing
this.destinationAddress == walletAddress -> PendingTransactionType.Incoming
else -> PendingTransactionType.Unknown
}
val address = if (this.sourceAddress == walletAddress) {
this.destinationAddress
} else {
this.sourceAddress
}
return PendingTransaction(
if (address == "unknown") null else address,
this.amount.value,
this.amount.value?.toFormattedString(amount.decimals),
this.amount.currencySymbol,
type
)
return PendingTransaction(this, type)
}
fun List<TransactionData>.toPendingTransactions(walletAddress: String): List<PendingTransaction> {
@ -55,27 +43,58 @@ fun List<PendingTransaction>.removeUnknownTransactions(): List<PendingTransactio
return this.filter { it.type != PendingTransactionType.Unknown }
}
fun List<PendingTransaction>.filterByCoin(): List<PendingTransaction> {
return this.filter { it.transactionData.amount.type == AmountType.Coin }
}
fun List<PendingTransaction>.filterByToken(token: Token): List<PendingTransaction> {
return this.filter { it.currency == token.symbol }
}
fun TransactionData.toPendingTransactionForToken(token: Token, walletAddress: String): PendingTransaction? {
if (this.amount.currencySymbol != token.symbol) return null
return this.toPendingTransaction(walletAddress)
}
fun List<TransactionData>.toPendingTransactionsForToken(token: Token, walletAddress: String): List<PendingTransaction> {
return this.mapNotNull { it.toPendingTransactionForToken(token, walletAddress) }
}
fun Wallet.getPendingTransactions(type: PendingTransactionType? = null): List<PendingTransaction> {
val txs = recentTransactions.toPendingTransactions(address)
return when(type) {
return when (type) {
null -> txs
else -> txs.filter { it.type == type }
}
}
fun Wallet.getPendingTransactions(token: Token): List<PendingTransaction> {
return recentTransactions.mapNotNull { it.toPendingTransactionForToken(token, address) }
}
fun Wallet.hasPendingTransactions(): Boolean {
return getPendingTransactions().isNotEmpty()
}
fun Wallet.hasPendingTransactions(token: Token): Boolean {
return getPendingTransactions(token).isNotEmpty()
}
fun Wallet.getSendableAmounts(): List<Amount> {
return amounts.values
.filter { it.type != AmountType.Reserve }
.filter { it.isAboveZero() }
}
fun Wallet.hasSendableAmounts(): Boolean {
return getSendableAmounts().isNotEmpty()
}
fun Wallet.hasSendableAmountsOrPendingTransactions(): Boolean {
return hasPendingTransactions() || amounts.toSendableAmounts().isNotEmpty()
return hasPendingTransactions() || hasSendableAmounts()
}
fun Wallet.isSendableAmount(type: AmountType): Boolean {
return amounts[type]?.isAboveZero() == true
}
fun Wallet.isSendableAmount(token: Token): Boolean {
return isSendableAmount(AmountType.Token(token))
}

View file

@ -1,15 +1,8 @@
package com.tangem.tap.features.wallet.redux
import android.graphics.Bitmap
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.address.AddressType
import com.tangem.blockchain.extensions.isAboveZero
import com.tangem.common.extensions.isZero
import com.tangem.domain.common.extensions.canHandleToken
import com.tangem.domain.common.extensions.toCoinId
@ -23,24 +16,16 @@ import com.tangem.tap.common.toggleWidget.WidgetState
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.extensions.buyIsAllowed
import com.tangem.tap.domain.extensions.sellIsAllowed
import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.models.WalletRent
import com.tangem.tap.features.wallet.models.WalletWarning
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.models.toPendingTransactionsForToken
import com.tangem.tap.features.wallet.redux.reducers.calculateTotalFiatAmount
import com.tangem.tap.features.wallet.redux.reducers.findTotalBalanceState
import com.tangem.tap.features.wallet.models.*
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.store
import java.math.BigDecimal
import org.rekotlin.StateType
import java.math.BigDecimal
import kotlin.properties.ReadOnlyProperty
data class WalletState(
@ -153,16 +138,13 @@ data class WalletState(
val wallet = walletManager.wallet
if (walletData.currency is Currency.Blockchain) {
return wallet.recentTransactions.toPendingTransactions(wallet.address).isEmpty() &&
wallet.amounts.toSendableAmounts().isEmpty()
} else if (walletData.currency is Currency.Token) (
return wallet.recentTransactions.toPendingTransactionsForToken(
walletData.currency.token, wallet.address
).isEmpty()
&& wallet.amounts[AmountType.Token(token = walletData.currency.token)]
?.isAboveZero() != true
)
return when (walletData.currency) {
is Currency.Blockchain -> !wallet.hasPendingTransactions() && !wallet.hasSendableAmounts()
is Currency.Token -> {
val token = walletData.currency.token
!wallet.hasPendingTransactions(token) && !wallet.isSendableAmount(token)
}
}
}
return false
}
@ -387,7 +369,11 @@ data class WalletData(
return listOfAddresses.size > 1
}
fun shouldEnableTokenSendButton(): Boolean = !blockchainAmountIsEmpty() || !tokenAmountIsEmpty()
fun shouldEnableTokenSendButton(): Boolean = if (blockchainAmountIsEmpty()) {
false
} else {
!tokenAmountIsEmpty()
}
fun assembleWarnings(): List<WalletWarning> {
val blockchain = currency.blockchain
@ -410,10 +396,11 @@ data class WalletData(
val fullName = currency.blockchain.fullName
walletWarnings.add(WalletWarning.BalanceNotEnoughForFee(fullName))
}
return walletWarnings.sortedBy { it.showingPosition }
}
private fun blockchainAmountIsEmpty(): Boolean = currencyData.blockchainAmount?.isZero() ?: false
private fun blockchainAmountIsEmpty(): Boolean = currencyData.blockchainAmount?.isZero() == true
private fun tokenAmountIsEmpty(): Boolean = currencyData.amount?.isZero() == true
}

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.wallet.redux.middlewares
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
import com.tangem.blockchain.common.AmountType
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.navigation.AppScreen
@ -51,7 +52,10 @@ class TradeCryptoMiddleware {
currency is Currency.Token && currency.blockchain.isTestnet()
) {
val walletManager = store.state.walletState.getWalletManager(currency)
if (walletManager !is EthereumWalletManager) return
if (walletManager !is EthereumWalletManager) {
store.dispatchDebugErrorNotification("Testnet tokens available only for the ETH")
return
}
scope.launch { exchangeManager.buyErc20Tokens(walletManager, currency.token) }
return

View file

@ -21,11 +21,11 @@ import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.onCardScanned
import com.tangem.tap.common.extensions.shareText
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.domain.failedRates
import com.tangem.tap.domain.loadedRates
import com.tangem.tap.features.demo.DemoHelper
@ -33,17 +33,12 @@ import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.send.redux.PrepareSendScreen
import com.tangem.tap.features.wallet.models.PendingTransactionType
import com.tangem.tap.features.wallet.models.getPendingTransactions
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletStore
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.network.NetworkStateChanged
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import java.math.BigDecimal
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
@ -51,6 +46,7 @@ import org.rekotlin.Action
import org.rekotlin.DispatchFunction
import org.rekotlin.Middleware
import timber.log.Timber
import java.math.BigDecimal
class WalletMiddleware {
private val tradeCryptoMiddleware = TradeCryptoMiddleware()
@ -257,7 +253,7 @@ class WalletMiddleware {
PrepareSendScreen(amount, selectedWalletData?.fiatRate, walletStore?.walletManager)
}
} else {
val amounts = walletStore?.walletManager?.wallet?.amounts?.toSendableAmounts()
val amounts = walletStore?.walletManager?.wallet?.getSendableAmounts()
if (currency != null && state.isMultiwalletAllowed) {
when (currency) {
is Currency.Blockchain -> {
@ -336,7 +332,7 @@ class WalletMiddleware {
val show = if (outgoingTxs.isEmpty()) {
isNeedToShowWarning(balance, rentExempt)
} else {
val outgoingAmount = outgoingTxs.sumOf { it.amount ?: BigDecimal.ZERO }
val outgoingAmount = outgoingTxs.sumOf { it.amountValue ?: BigDecimal.ZERO }
val rest = balance.minus(outgoingAmount)
isNeedToShowWarning(rest, rentExempt)
}

View file

@ -2,12 +2,14 @@ package com.tangem.tap.features.wallet.redux.reducers
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.extensions.isAboveZero
import com.tangem.common.extensions.guard
import com.tangem.common.extensions.isZero
import com.tangem.tap.common.extensions.toFiatString
import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.wallet.models.filterByToken
import com.tangem.tap.features.wallet.models.getPendingTransactions
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.redux.Currency
@ -111,17 +113,16 @@ class MultiWalletReducer {
throw NullPointerException("MultiWallet.TokenLoaded: WalletManager must be no NULL")
}
val pendingTransactions = wallet.recentTransactions.toPendingTransactions(wallet.address)
val sendButtonEnabled =
action.amount.value?.isZero() == false && pendingTransactions.isEmpty()
val tokenPendingTransactions = pendingTransactions
.filter { it.currency == action.amount.currencySymbol }
val pendingTransactions = wallet.getPendingTransactions()
val tokenPendingTransactions = pendingTransactions.filterByToken(action.token)
val tokenBalanceStatus = when {
tokenPendingTransactions.isNotEmpty() -> BalanceStatus.TransactionInProgress
pendingTransactions.isNotEmpty() -> BalanceStatus.SameCurrencyTransactionInProgress
else -> BalanceStatus.VerifiedOnline
}
val tokenWalletData = state.getWalletData(currency)
val isTokenSendButtonEnabled = action.amount.isAboveZero() && pendingTransactions.isEmpty()
val newTokenWalletData = tokenWalletData?.copy(
currencyData = tokenWalletData.currencyData.copy(
status = tokenBalanceStatus,
@ -136,7 +137,7 @@ class MultiWalletReducer {
blockchainAmount = wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO
),
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
mainButton = WalletMainButton.SendButton(isTokenSendButtonEnabled),
currency = Currency.Token(
token = action.token,
blockchain = action.blockchain.blockchain,

View file

@ -9,6 +9,8 @@ import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.wallet.models.filterByToken
import com.tangem.tap.features.wallet.models.getPendingTransactions
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.redux.Currency
@ -48,10 +50,8 @@ class OnWalletLoadedReducer {
wallet.blockchain.currency
)
val pendingTransactions = wallet.recentTransactions
.toPendingTransactions(wallet.address)
val coinSendButton = coinAmountValue?.isZero() == false && pendingTransactions.isEmpty()
val pendingTransactions = wallet.getPendingTransactions()
val isCoinSendButtonEnabled = coinAmountValue?.isZero() == false && pendingTransactions.isEmpty()
val balanceStatus = if (pendingTransactions.isNotEmpty()) {
BalanceStatus.TransactionInProgress
} else {
@ -70,7 +70,7 @@ class OnWalletLoadedReducer {
fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrency.symbol)
),
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(coinSendButton),
mainButton = WalletMainButton.SendButton(isCoinSendButtonEnabled),
currency = Currency.fromBlockchainNetwork(blockchainNetwork),
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletData),
)
@ -78,8 +78,7 @@ class OnWalletLoadedReducer {
val tokens = wallet.getTokens().mapNotNull { token ->
val currency = Currency.fromBlockchainNetwork(blockchainNetwork, token)
val tokenWalletData = walletState.getWalletData(currency)
val tokenPendingTransactions =
pendingTransactions.filter { it.currency == token.symbol }
val tokenPendingTransactions = pendingTransactions.filterByToken(token)
val tokenBalanceStatus = when {
tokenPendingTransactions.isNotEmpty() -> BalanceStatus.TransactionInProgress
pendingTransactions.isNotEmpty() -> BalanceStatus.SameCurrencyTransactionInProgress
@ -89,8 +88,8 @@ class OnWalletLoadedReducer {
val tokenFiatAmount =
tokenWalletData?.fiatRate?.let { rate -> tokenAmountValue?.toFiatValue(rate) }
val tokenSendButton = newWalletData.shouldEnableTokenSendButton()
&& tokenPendingTransactions.isEmpty()
val isTokenSendButtonEnabled = newWalletData.shouldEnableTokenSendButton()
&& tokenPendingTransactions.isEmpty()
tokenWalletData?.copy(
currencyData = tokenWalletData.currencyData.copy(
status = tokenBalanceStatus,
@ -104,7 +103,7 @@ class OnWalletLoadedReducer {
fiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrency.symbol)
),
pendingTransactions = tokenPendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(tokenSendButton),
mainButton = WalletMainButton.SendButton(isTokenSendButtonEnabled),
tradeCryptoState = TradeCryptoState.from(exchangeManager, tokenWalletData),
)
}
@ -158,7 +157,7 @@ class OnWalletLoadedReducer {
val fiatAmountRaw = fiatRate?.multiply(amount)?.setScale(2, RoundingMode.DOWN)
val fiatAmount = fiatRate?.let { amount?.toFiatString(it, fiatCurrencyName) }
val pendingTransactions = wallet.recentTransactions.toPendingTransactions(wallet.address)
val pendingTransactions = wallet.getPendingTransactions()
val sendButtonEnabled = amount?.isZero() == false && pendingTransactions.isEmpty()
val balanceStatus = if (pendingTransactions.isNotEmpty()) {
BalanceStatus.TransactionInProgress

View file

@ -63,7 +63,7 @@ class PendingTransactionsAdapter
}
binding.tvPendingTransaction.text = binding.root.getString(transactionDescriptionRes)
transaction.amountUi?.let { binding.tvPendingTransactionAmount.text = "$it " }
transaction.amountValueUi?.let { binding.tvPendingTransactionAmount.text = "$it " }
binding.tvPendingTransactionCurrency.text = "${transaction.currency}"
if (transaction.address != null) {