Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-08 12:37:31 +03:00
parent e6b4d2ae7b
commit c2625a0de7
7 changed files with 29 additions and 12 deletions

View file

@ -9,7 +9,6 @@ import com.tangem.core.error.UniversalError
import com.tangem.data.common.currency.CryptoCurrencyFactory import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.data.common.network.NetworkFactory import com.tangem.data.common.network.NetworkFactory
import com.tangem.data.pay.util.TangemPayErrorConverter import com.tangem.data.pay.util.TangemPayErrorConverter
import com.tangem.data.pay.util.TangemPayWalletsManager
import com.tangem.datasource.di.NetworkMoshi import com.tangem.datasource.di.NetworkMoshi
import com.tangem.domain.models.ReceiveAddressModel import com.tangem.domain.models.ReceiveAddressModel
import com.tangem.domain.models.ReceiveAddressModel.NameService import com.tangem.domain.models.ReceiveAddressModel.NameService
@ -32,7 +31,6 @@ private const val TOKEN_DECIMALS = 6
internal class DefaultTangemPaySwapDataFactory @Inject constructor( internal class DefaultTangemPaySwapDataFactory @Inject constructor(
@NetworkMoshi moshi: Moshi, @NetworkMoshi moshi: Moshi,
private val tangemPayWalletsManager: TangemPayWalletsManager,
excludedBlockchains: ExcludedBlockchains, excludedBlockchains: ExcludedBlockchains,
) : TangemPaySwapDataFactory { ) : TangemPaySwapDataFactory {
@ -63,17 +61,17 @@ internal class DefaultTangemPaySwapDataFactory @Inject constructor(
} }
override fun create( override fun create(
userWallet: UserWallet,
depositAddress: String, depositAddress: String,
chainId: Int, chainId: Int,
cryptoBalance: BigDecimal, cryptoBalance: BigDecimal,
fiatBalance: BigDecimal, fiatBalance: BigDecimal,
): Either<UniversalError, TangemPayTopUpData> { ): Either<UniversalError, TangemPayTopUpData> {
return catch { return catch {
val wallet = tangemPayWalletsManager.getDefaultWalletForTangemPayBlocking() val currency = getCurrency(userWallet, chainId)
val currency = getCurrency(wallet, chainId)
TangemPayTopUpData( TangemPayTopUpData(
currency = currency, currency = currency,
walletId = wallet.walletId, walletId = userWallet.walletId,
cryptoBalance = cryptoBalance, cryptoBalance = cryptoBalance,
fiatBalance = fiatBalance, fiatBalance = fiatBalance,
depositAddress = depositAddress, depositAddress = depositAddress,

View file

@ -21,11 +21,11 @@ import com.tangem.domain.visa.model.TangemPayAuthTokens
import com.tangem.domain.visa.model.getAuthHeader import com.tangem.domain.visa.model.getAuthHeader
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -40,7 +40,7 @@ internal class TangemPayRequestPerformer @Inject constructor(
private val tangemPayStorage: TangemPayStorage, private val tangemPayStorage: TangemPayStorage,
) { ) {
private val customerWalletAddress = MutableStateFlow<String?>(null) private val customerWalletAddresses = ConcurrentHashMap<UserWalletId, String>()
private val tokensMutex = Mutex() private val tokensMutex = Mutex()
private val errorConverter = TangemPayErrorConverter(moshi) private val errorConverter = TangemPayErrorConverter(moshi)
@ -110,7 +110,7 @@ internal class TangemPayRequestPerformer @Inject constructor(
} }
suspend fun getCustomerWalletAddress(userWalletId: UserWalletId): String { suspend fun getCustomerWalletAddress(userWalletId: UserWalletId): String {
val existingAddress = customerWalletAddress.value val existingAddress = customerWalletAddresses[userWalletId]
if (existingAddress != null) { if (existingAddress != null) {
return existingAddress return existingAddress
} }
@ -118,7 +118,7 @@ internal class TangemPayRequestPerformer @Inject constructor(
userWalletId = userWalletId, userWalletId = userWalletId,
) ?: error("Can not find customer address") ) ?: error("Can not find customer address")
customerWalletAddress.value = storedAddress customerWalletAddresses[userWalletId] = storedAddress
return storedAddress return storedAddress
} }

View file

@ -15,12 +15,14 @@ class TangemPayWalletsManager @Inject constructor(
private val hotWalletFeatureToggles: HotWalletFeatureToggles, private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) { ) {
@Deprecated("Don't use and put userWallet in features that need it")
suspend fun getDefaultWalletForTangemPay(): UserWallet.Cold { suspend fun getDefaultWalletForTangemPay(): UserWallet.Cold {
val userWalletsFlow = if (useNewRepository()) repository.userWallets else manager.userWallets val userWalletsFlow = if (useNewRepository()) repository.userWallets else manager.userWallets
val userWallets = userWalletsFlow.filter { !it.isNullOrEmpty() }.first() val userWallets = userWalletsFlow.filter { !it.isNullOrEmpty() }.first()
return findColdWallet(userWallets) return findColdWallet(userWallets)
} }
@Deprecated("Don't use and put userWallet in features that need it")
fun getDefaultWalletForTangemPayBlocking(): UserWallet.Cold { fun getDefaultWalletForTangemPayBlocking(): UserWallet.Cold {
val userWallets = if (useNewRepository()) repository.userWallets.value else manager.userWalletsSync val userWallets = if (useNewRepository()) repository.userWallets.value else manager.userWalletsSync
return findColdWallet(userWallets) return findColdWallet(userWallets)
@ -29,7 +31,9 @@ class TangemPayWalletsManager @Inject constructor(
private fun useNewRepository(): Boolean = hotWalletFeatureToggles.isHotWalletEnabled private fun useNewRepository(): Boolean = hotWalletFeatureToggles.isHotWalletEnabled
private fun findColdWallet(userWallets: List<UserWallet>?): UserWallet.Cold { private fun findColdWallet(userWallets: List<UserWallet>?): UserWallet.Cold {
return userWallets?.find { it is UserWallet.Cold } as? UserWallet.Cold return userWallets?.find {
it is UserWallet.Cold && it.isMultiCurrency
} as? UserWallet.Cold
?: error("Cannot find cold user wallet") ?: error("Cannot find cold user wallet")
} }
} }

View file

@ -4,12 +4,14 @@ import arrow.core.Either
import com.tangem.core.error.UniversalError import com.tangem.core.error.UniversalError
import com.tangem.domain.models.ReceiveAddressModel import com.tangem.domain.models.ReceiveAddressModel
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import java.math.BigDecimal import java.math.BigDecimal
interface TangemPaySwapDataFactory { interface TangemPaySwapDataFactory {
fun create( fun create(
userWallet: UserWallet,
depositAddress: String, depositAddress: String,
chainId: Int, chainId: Int,
cryptoBalance: BigDecimal, cryptoBalance: BigDecimal,

View file

@ -5,6 +5,7 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.pay.TangemPaySwapDataFactory import com.tangem.domain.pay.TangemPaySwapDataFactory
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.tangempay.components.TangemPayAddFundsComponent import com.tangem.features.tangempay.components.TangemPayAddFundsComponent
import com.tangem.features.tangempay.entity.TangemPayAddFundsUM import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
import com.tangem.features.tangempay.model.transformers.TangemPayAddFundsUMConverter import com.tangem.features.tangempay.model.transformers.TangemPayAddFundsUMConverter
@ -17,6 +18,7 @@ internal class TangemPayAddFundsModel @Inject constructor(
paramsContainer: ParamsContainer, paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider, override val dispatchers: CoroutineDispatcherProvider,
private val tangemPaySwapDataFactory: TangemPaySwapDataFactory, private val tangemPaySwapDataFactory: TangemPaySwapDataFactory,
private val getUserWalletUseCase: GetUserWalletUseCase,
) : Model() { ) : Model() {
private val params = paramsContainer.require<TangemPayAddFundsComponent.Params>() private val params = paramsContainer.require<TangemPayAddFundsComponent.Params>()
@ -24,7 +26,11 @@ internal class TangemPayAddFundsModel @Inject constructor(
val uiState: TangemPayAddFundsUM = getInitialState() val uiState: TangemPayAddFundsUM = getInitialState()
private fun getInitialState(): TangemPayAddFundsUM { private fun getInitialState(): TangemPayAddFundsUM {
val userWallet = requireNotNull(
getUserWalletUseCase(params.walletId).getOrNull(),
) { "User wallet not found for id: ${params.walletId}" }
val data = tangemPaySwapDataFactory.create( val data = tangemPaySwapDataFactory.create(
userWallet = userWallet,
depositAddress = params.depositAddress, depositAddress = params.depositAddress,
chainId = params.chainId, chainId = params.chainId,
cryptoBalance = params.cryptoBalance, cryptoBalance = params.cryptoBalance,

View file

@ -18,14 +18,15 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.SnackbarMessage import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.models.TokenReceiveConfig import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.pay.TangemPayTopUpData
import com.tangem.domain.pay.TangemPaySwapDataFactory import com.tangem.domain.pay.TangemPaySwapDataFactory
import com.tangem.domain.pay.TangemPayTopUpData
import com.tangem.domain.pay.model.TangemPayCardBalance import com.tangem.domain.pay.model.TangemPayCardBalance
import com.tangem.domain.pay.repository.CustomerOrderRepository import com.tangem.domain.pay.repository.CustomerOrderRepository
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.domain.visa.model.TangemPayCardFrozenState import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.domain.visa.model.TangemPayTxHistoryItem import com.tangem.domain.visa.model.TangemPayTxHistoryItem
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.tangempay.TangemPayConstants import com.tangem.features.tangempay.TangemPayConstants
import com.tangem.features.tangempay.components.AddFundsListener import com.tangem.features.tangempay.components.AddFundsListener
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
@ -55,7 +56,7 @@ import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@Suppress("LongParameterList") @Suppress("LongParameterList", "LargeClass")
@Stable @Stable
@ModelScoped @ModelScoped
internal class TangemPayDetailsModel @Inject constructor( internal class TangemPayDetailsModel @Inject constructor(
@ -71,6 +72,7 @@ internal class TangemPayDetailsModel @Inject constructor(
private val txHistoryUpdateListener: TangemPayTxHistoryUpdateListener, private val txHistoryUpdateListener: TangemPayTxHistoryUpdateListener,
private val tangemPaySwapDataFactory: TangemPaySwapDataFactory, private val tangemPaySwapDataFactory: TangemPaySwapDataFactory,
private val orderRepository: CustomerOrderRepository, private val orderRepository: CustomerOrderRepository,
private val getUserWalletUseCase: GetUserWalletUseCase,
) : Model(), TangemPayTxHistoryUiActions, TangemPayDetailIntents, AddFundsListener { ) : Model(), TangemPayTxHistoryUiActions, TangemPayDetailIntents, AddFundsListener {
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require() private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
@ -234,7 +236,11 @@ internal class TangemPayDetailsModel @Inject constructor(
if (hasActiveWithdrawal) { if (hasActiveWithdrawal) {
showBottomSheetError(TangemPayDetailsErrorType.WithdrawInProgress) showBottomSheetError(TangemPayDetailsErrorType.WithdrawInProgress)
} else { } else {
val userWallet = requireNotNull(
getUserWalletUseCase(params.userWalletId).getOrNull(),
) { "User wallet not found: ${params.userWalletId}" }
val data = tangemPaySwapDataFactory.create( val data = tangemPaySwapDataFactory.create(
userWallet = userWallet,
depositAddress = depositAddress, depositAddress = depositAddress,
chainId = params.config.chainId, chainId = params.config.chainId,
cryptoBalance = currentBalance.cryptoBalance, cryptoBalance = currentBalance.cryptoBalance,

View file

@ -46,6 +46,7 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
.distinctUntilChanged() .distinctUntilChanged()
.onEach { data -> .onEach { data ->
val userWalletId = userWallet.walletId val userWalletId = userWallet.walletId
Timber.tag("scan===").e("onEach $userWalletId $data")
val mainInfoData = data[userWalletId] ?: return@onEach val mainInfoData = data[userWalletId] ?: return@onEach
mainInfoData.onLeft { tangemPayError -> mainInfoData.onLeft { tangemPayError ->
when (tangemPayError) { when (tangemPayError) {