Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-25 13:47:05 +03:00
commit 467442724d
595 changed files with 7210 additions and 3334 deletions

View file

@ -18,7 +18,7 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
@Suppress("ComplexCondition")
internal class DefaultSellRedirectDeepLinkHandler @AssistedInject constructor(
@ -40,13 +40,13 @@ internal class DefaultSellRedirectDeepLinkHandler @AssistedInject constructor(
getSelectedWalletSyncUseCase()
.fold(
ifLeft = {
Timber.e("Error on getting user wallet: $it")
TangemLogger.e("Error on getting user wallet: $it")
},
ifRight = { userWallet ->
if (currencyId.isNullOrEmpty() || transactionId.isNullOrEmpty() ||
amount.isNullOrEmpty() || destinationAddress.isNullOrEmpty()
) {
Timber.e(
TangemLogger.e(
"""
Invalid parameters for SELL deeplink
|- Params: $queryParams
@ -57,7 +57,7 @@ internal class DefaultSellRedirectDeepLinkHandler @AssistedInject constructor(
scope.launch {
val cryptoCurrency = getCryptoCurrency(userWallet.walletId, currencyId).getOrElse {
Timber.e("Error on getting cryptoCurrency: $currencyId")
TangemLogger.e("Error on getting cryptoCurrency: $currencyId")
return@launch
}
// Convert using universal parser to account for regional separators

View file

@ -43,7 +43,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
@Suppress("LongParameterList")
internal class FeeSelectorLogic @AssistedInject constructor(
@ -277,7 +277,7 @@ internal class FeeSelectorLogic @AssistedInject constructor(
val selectedToken = getSelectedTokenStatus(fee.feeTokenId).bind()
val availableTokens = getAvailableFeeTokens().fold(
ifLeft = { error ->
Timber.e("Failed to get available fee tokens: $error")
TangemLogger.e("Failed to get available fee tokens: $error")
if (selectedToken.currency !is CryptoCurrency.Coin) {
raise(error)
}

View file

@ -77,7 +77,7 @@ import com.tangem.utils.extensions.stripZeroPlainString
import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import java.math.BigDecimal
import javax.inject.Inject
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
@ -374,7 +374,7 @@ internal class SendConfirmModel @Inject constructor(
network = cryptoCurrency.network,
).fold(
ifLeft = { error ->
Timber.e(error)
TangemLogger.e("Error", error)
_uiState.update(SendConfirmSendingStateTransformer(isSending = false))
alertFactory.getGenericErrorState(
onFailedTxEmailClick = {
@ -470,7 +470,7 @@ internal class SendConfirmModel @Inject constructor(
val tokenToAdd = currenciesRepository.createTokenCurrency(cryptoCurrency, network)
manageCryptoCurrenciesUseCase(accountId = accountId, add = tokenToAdd)
.onLeft(Timber::e)
.onLeft { TangemLogger.e("Error", it) }
}
}

View file

@ -67,7 +67,7 @@ import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import javax.inject.Inject
import kotlin.properties.Delegates
@ -271,7 +271,7 @@ internal class SendModel @Inject constructor(
}
override fun onError(error: GetUserWalletError) {
Timber.w(error.toString())
TangemLogger.w(error.toString())
showAlertError()
}
@ -430,7 +430,7 @@ internal class SendModel @Inject constructor(
.launchIn(modelScope)
},
ifLeft = { error ->
Timber.w(error.toString())
TangemLogger.w(error.toString())
showAlertError()
return@launch
},

View file

@ -57,7 +57,7 @@ import com.tangem.utils.extensions.stripZeroPlainString
import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import java.math.BigDecimal
import javax.inject.Inject
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
@ -273,7 +273,7 @@ internal class NFTSendConfirmModel @Inject constructor(
network = cryptoCurrency.network,
).fold(
ifLeft = { error ->
Timber.e(error)
TangemLogger.e("Error", error)
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = false))
alertFactory.getGenericErrorState(
onFailedTxEmailClick = { onFailedTxEmailClick(error.localizedMessage.orEmpty()) },
@ -297,7 +297,7 @@ internal class NFTSendConfirmModel @Inject constructor(
result.fold(
ifLeft = { error ->
Timber.e(error.toString())
TangemLogger.e(error.toString())
alertFactory.getSendTransactionErrorState(
error = error,
popBack = appRouter::pop,

View file

@ -13,9 +13,13 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.models.account.filterCryptoPortfolio
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.CryptoCurrencyAddress
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase
@ -65,6 +69,7 @@ internal class SendDestinationModel @Inject constructor(
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
private val parseQrCodeUseCase: ParseQrCodeUseCase,
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val tangemPayCryptoCurrencyFactory: TangemPayCryptoCurrencyFactory,
private val analyticsEventHandler: AnalyticsEventHandler,
private val multiAccountStatusListSupplier: MultiAccountStatusListSupplier,
) : Model(), SendDestinationClickIntents {
@ -217,8 +222,6 @@ internal class SendDestinationModel @Inject constructor(
flow = getWalletsUseCase().conflate(),
flow2 = multiAccountStatusListSupplier().conflate(),
) { wallets, accountStatusLists ->
val cryptoCurrencyNetwork = cryptoCurrency.network
coroutineScope {
accountStatusLists.mapNotNull { accountStatusList ->
val wallet = wallets
@ -227,35 +230,55 @@ internal class SendDestinationModel @Inject constructor(
?: return@mapNotNull null
async {
accountStatusList.flattenCurrencies()
.filter { it.currency.network.rawId == cryptoCurrencyNetwork.rawId }
.mapNotNull { cryptoCurrencyStatus ->
val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
?: return@mapNotNull null
// Find the corresponding account from accountStatuses
val account = accountStatusList.accountStatuses
.filterCryptoPortfolio()
.firstOrNull { accountStatus ->
accountStatus.tokenList
.flattenCurrencies()
.any { it.currency.id == cryptoCurrencyStatus.currency.id }
}?.account
DestinationWalletUM(
name = wallet.name,
address = address,
cryptoCurrency = cryptoCurrencyStatus.currency,
userWalletId = wallet.walletId,
account = account,
)
accountStatusList.accountStatuses.flatMap { accountStatus ->
when (accountStatus) {
is AccountStatus.CryptoPortfolio -> accountStatus.getDestinationWalletUM(wallet)
is AccountStatus.Payment -> listOfNotNull(accountStatus.getDestinationWalletUM(wallet))
}
}
}
}.awaitAll().flatten()
}
}.flowOn(dispatchers.default)
}
private fun AccountStatus.CryptoPortfolio.getDestinationWalletUM(wallet: UserWallet): List<DestinationWalletUM> {
return this.flattenCurrencies()
.filter { it.currency.network.rawId == cryptoCurrency.network.rawId }
.mapNotNull { cryptoCurrencyStatus ->
val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
?: return@mapNotNull null
DestinationWalletUM(
name = wallet.name,
address = address,
cryptoCurrency = cryptoCurrencyStatus.currency,
userWalletId = wallet.walletId,
account = account,
)
}
}
private fun AccountStatus.Payment.getDestinationWalletUM(wallet: UserWallet): DestinationWalletUM? {
val contractAddress = (cryptoCurrency as? CryptoCurrency.Token)?.contractAddress ?: return null
val address = when (val status = this.value) {
is PaymentAccountStatusValue.Loaded -> status.cryptoBalance.depositAddress
is PaymentAccountStatusValue.Locked -> status.cryptoBalance.depositAddress
else -> return null
}
val currency = tangemPayCryptoCurrencyFactory.create(wallet).getOrNull() ?: return null
return if (contractAddress.equals(currency.contractAddress, true)) {
DestinationWalletUM(
name = wallet.name,
address = address,
cryptoCurrency = currency,
userWalletId = wallet.walletId,
account = account,
)
} else {
null
}
}
private fun validate(address: String, memo: String?, type: EnterAddressSource? = null) {
modelScope.launch {
_uiState.update(SendDestinationValidationStartedTransformer)

View file

@ -35,10 +35,11 @@ internal class SendRecipientWalletListConverter(
return this.filterNotNull()
.filter { destinationWallet ->
val isCoin = destinationWallet.cryptoCurrency is CryptoCurrency.Coin
val isPaymentAccount = destinationWallet.account is Account.Payment
val isNotSameAddress = destinationWallet.address != senderAddress
val isNotBlankAddress = destinationWallet.address.isNotBlank()
isNotBlankAddress && isCoin && (isNotSameAddress || isSelfSendAvailable)
isNotBlankAddress && (isCoin || isPaymentAccount) && (isNotSameAddress || isSelfSendAvailable)
}
.groupBy { item -> item.name }
.values.map { wallets ->