Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-10 16:33:07 +04:00
parent 2f2d6755f9
commit 5f4dfefc2e
20 changed files with 190 additions and 311 deletions

View file

@ -53,18 +53,6 @@ internal object TokensDomainModule {
return DefaultTokensFeatureToggles(featureTogglesManager = featureTogglesManager) return DefaultTokensFeatureToggles(featureTogglesManager = featureTogglesManager)
} }
@Provides
@Singleton
fun provideGetCurrencyUseCase(
baseCurrencyStatusOperations: BaseCurrencyStatusOperations,
dispatchers: CoroutineDispatcherProvider,
): GetSingleCryptoCurrencyStatusUseCase {
return GetSingleCryptoCurrencyStatusUseCase(
currencyStatusOperations = baseCurrencyStatusOperations,
dispatchers = dispatchers,
)
}
@Provides @Provides
@Singleton @Singleton
fun provideGetCurrencyWarningsUseCase( fun provideGetCurrencyWarningsUseCase(

View file

@ -1,13 +1,13 @@
package com.tangem.tap.di.domain package com.tangem.tap.di.domain
import com.tangem.data.wallets.hot.TangemHotWalletSigner import com.tangem.data.wallets.hot.TangemHotWalletSigner
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
import com.tangem.domain.notifications.repository.PushNotificationsRepository import com.tangem.domain.notifications.repository.PushNotificationsRepository
import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.GetViewedTokenReceiveWarningUseCase import com.tangem.domain.tokens.GetViewedTokenReceiveWarningUseCase
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository
@ -344,13 +344,13 @@ internal object TransactionDomainModule {
fun provideCreateAndSendGaslessTransactionUseCase( fun provideCreateAndSendGaslessTransactionUseCase(
walletManagersFacade: WalletManagersFacade, walletManagersFacade: WalletManagersFacade,
gaslessTransactionRepository: GaslessTransactionRepository, gaslessTransactionRepository: GaslessTransactionRepository,
getSingCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, singleAccountListSupplier: SingleAccountListSupplier,
cardSdkConfigRepository: CardSdkConfigRepository, cardSdkConfigRepository: CardSdkConfigRepository,
tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory, tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory,
): CreateAndSendGaslessTransactionUseCase { ): CreateAndSendGaslessTransactionUseCase {
return CreateAndSendGaslessTransactionUseCase( return CreateAndSendGaslessTransactionUseCase(
walletManagersFacade = walletManagersFacade, walletManagersFacade = walletManagersFacade,
getSingleCryptoCurrencyStatusUseCase = getSingCryptoCurrencyStatusUseCase, singleAccountListSupplier = singleAccountListSupplier,
gaslessTransactionRepository = gaslessTransactionRepository, gaslessTransactionRepository = gaslessTransactionRepository,
cardSdkConfigRepository = cardSdkConfigRepository, cardSdkConfigRepository = cardSdkConfigRepository,
getHotWalletSigner = tangemHotWalletSignerFactory::create, getHotWalletSigner = tangemHotWalletSignerFactory::create,

View file

@ -1,5 +1,6 @@
package com.tangem.data.walletconnect.network.ethereum package com.tangem.data.walletconnect.network.ethereum
import arrow.core.getOrElse
import com.domain.blockaid.models.transaction.CheckTransactionResult import com.domain.blockaid.models.transaction.CheckTransactionResult
import com.domain.blockaid.models.transaction.SimulationResult import com.domain.blockaid.models.transaction.SimulationResult
import com.domain.blockaid.models.transaction.simultation.ApproveInfo import com.domain.blockaid.models.transaction.simultation.ApproveInfo
@ -17,16 +18,17 @@ import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.blockchainsdk.utils.toCoinId import com.tangem.blockchainsdk.utils.toCoinId
import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.hexToBytes
import com.tangem.data.common.currency.getCoinId import com.tangem.data.common.currency.getCoinId
import com.tangem.domain.account.status.utils.CryptoCurrencyOperations.getCryptoCurrency
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.transaction.usecase.GetEthSpecificFeeUseCase import com.tangem.domain.transaction.usecase.GetEthSpecificFeeUseCase
import com.tangem.domain.walletconnect.model.WcApprovedAmount import com.tangem.domain.walletconnect.model.WcApprovedAmount
import com.tangem.domain.walletconnect.model.WcEthTransactionParams import com.tangem.domain.walletconnect.model.WcEthTransactionParams
import javax.inject.Inject import javax.inject.Inject
internal class WcEthTxHelper @Inject constructor( internal class WcEthTxHelper @Inject constructor(
private val getSingleCryptoCurrency: GetSingleCryptoCurrencyStatusUseCase, private val singleAccountListSupplier: SingleAccountListSupplier,
private val ethSpecificFee: GetEthSpecificFeeUseCase, private val ethSpecificFee: GetEthSpecificFeeUseCase,
) { ) {
@ -34,10 +36,19 @@ internal class WcEthTxHelper @Inject constructor(
val gasLimit = txParams.gas?.hexToBigInteger() ?: return null val gasLimit = txParams.gas?.hexToBigInteger() ?: return null
val gasPrice = txParams.gasPrice?.hexToBigInteger() val gasPrice = txParams.gasPrice?.hexToBigInteger()
val coinId = getCoinId(network, network.toBlockchain().toCoinId()) val coinId = getCoinId(network, network.toBlockchain().toCoinId())
val currency = getSingleCryptoCurrency.invokeMultiWalletSync(userWallet.walletId, coinId)
.map { it.currency } val currency = singleAccountListSupplier.getSyncOrNull(userWalletId = userWallet.walletId)
.getOrNull() ?: return null .getCryptoCurrency(currencyId = coinId, network = network)
return ethSpecificFee(userWallet, currency, gasLimit, gasPrice) .getOrElse {
return null
}
return ethSpecificFee(
userWallet = userWallet,
cryptoCurrency = currency,
gasLimit = gasLimit,
gasPrice = gasPrice,
)
.map { it.minimum } .map { it.minimum }
.getOrNull() .getOrNull()
} }

View file

@ -21,4 +21,10 @@ abstract class SingleAccountListSupplier(
params = SingleAccountListProducer.Params(userWalletId = userWalletId), params = SingleAccountListProducer.Params(userWalletId = userWalletId),
) )
} }
suspend fun getSyncOrNull(userWalletId: UserWalletId): AccountList? {
return getSyncOrNull(
params = SingleAccountListProducer.Params(userWalletId = userWalletId),
)
}
} }

View file

@ -20,4 +20,9 @@ abstract class SingleAccountStatusListSupplier(
val params = SingleAccountStatusListProducer.Params(userWalletId) val params = SingleAccountStatusListProducer.Params(userWalletId)
return this.invoke(params) return this.invoke(params)
} }
suspend fun getSyncOrNull(userWalletId: UserWalletId): AccountStatusList? {
val params = SingleAccountStatusListProducer.Params(userWalletId)
return this.getSyncOrNull(params)
}
} }

View file

@ -1,106 +0,0 @@
package com.tangem.domain.tokens
import arrow.core.Either
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
/**
* Use case for fetching the status of a cryptocurrency associated with a user wallet.
*
*/
class GetSingleCryptoCurrencyStatusUseCase(
private val currencyStatusOperations: BaseCurrencyStatusOperations,
private val dispatchers: CoroutineDispatcherProvider,
) {
/**
* Returns cryptocurrency status flow for Multi-Currency wallet
*
* @param userWalletId The unique identifier of the user's wallet.
* @param currencyId The unique identifier of the cryptocurrency.
* @param isSingleWalletWithTokens Indicates whether the user wallet contains only one token on card (old cards)
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
fun invokeMultiWallet(
userWalletId: UserWalletId,
currencyId: CryptoCurrency.ID,
isSingleWalletWithTokens: Boolean,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return flow {
emitAll(
getCurrencyStatus(
userWalletId = userWalletId,
currencyId = currencyId,
isSingleWalletWithTokens = isSingleWalletWithTokens,
),
)
}.flowOn(dispatchers.io)
}
/**
* Returns cryptocurrency status flow for primary currency for Single-Currency wallet
*
* @param userWalletId The unique identifier of the user's wallet.
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
fun invokeSingleWallet(userWalletId: UserWalletId): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return flow {
emitAll(
currencyStatusOperations.getPrimaryCurrencyStatusFlow(userWalletId).map { maybeCurrency ->
maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError)
},
)
}.flowOn(dispatchers.io)
}
/**
* Returns synchronously cryptocurrency status for Multi-Currency wallet
*
* @param userWalletId The unique identifier of the user's wallet.
* @param cryptoCurrencyId The unique identifier of the cryptocurrency.
* @param isSingleWalletWithTokens Indicates whether the user wallet contains only one token on card (old cards)
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
suspend fun invokeMultiWalletSync(
userWalletId: UserWalletId,
cryptoCurrencyId: CryptoCurrency.ID,
isSingleWalletWithTokens: Boolean = false,
): Either<CurrencyStatusError, CryptoCurrencyStatus> {
return currencyStatusOperations.getCurrencyStatusSync(userWalletId, cryptoCurrencyId, isSingleWalletWithTokens)
.mapLeft { error -> error.mapToCurrencyError() }
}
/**
* Returns synchronously cryptocurrency status for primary currency for Single-Currency wallet
*
* @param userWalletId The unique identifier of the user's wallet.
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
*/
suspend fun invokeSingleWalletSync(userWalletId: UserWalletId): Either<CurrencyStatusError, CryptoCurrencyStatus> {
return currencyStatusOperations.getPrimaryCurrencyStatusSync(userWalletId)
.mapLeft { error -> error.mapToCurrencyError() }
}
private suspend fun getCurrencyStatus(
userWalletId: UserWalletId,
currencyId: CryptoCurrency.ID,
isSingleWalletWithTokens: Boolean,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
val currencyFlow = currencyStatusOperations.getCurrencyStatusFlow(
userWalletId = userWalletId,
currencyId = currencyId,
isSingleWalletWithTokens = isSingleWalletWithTokens,
)
return currencyFlow.map { maybeCurrency ->
maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError)
}
}
}

View file

@ -26,6 +26,7 @@ dependencies {
implementation(projects.libs.blockchainSdk) implementation(projects.libs.blockchainSdk)
implementation(projects.libs.crypto) implementation(projects.libs.crypto)
implementation(projects.domain.account.status)
implementation(projects.domain.models) implementation(projects.domain.models)
implementation(projects.domain.legacy) implementation(projects.domain.legacy)
implementation(projects.domain.walletManager) implementation(projects.domain.walletManager)

View file

@ -1,6 +1,7 @@
package com.tangem.domain.transaction.usecase.gasless package com.tangem.domain.transaction.usecase.gasless
import arrow.core.Either import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.raise.catch import arrow.core.raise.catch
import arrow.core.raise.either import arrow.core.raise.either
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
@ -16,13 +17,13 @@ import com.tangem.blockchain.yieldsupply.providers.ethereum.yield.EthereumYieldS
import com.tangem.common.CompletionResult import com.tangem.common.CompletionResult
import com.tangem.common.extensions.toDecompressedPublicKey import com.tangem.common.extensions.toDecompressedPublicKey
import com.tangem.common.extensions.toHexString import com.tangem.common.extensions.toHexString
import com.tangem.domain.account.status.utils.CryptoCurrencyOperations.getCryptoCurrency
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.card.models.TwinKey import com.tangem.domain.card.models.TwinKey
import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.transaction.GaslessTransactionRepository import com.tangem.domain.transaction.GaslessTransactionRepository
import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.domain.transaction.models.Eip7702Authorization import com.tangem.domain.transaction.models.Eip7702Authorization
@ -33,7 +34,7 @@ import java.math.BigInteger
class CreateAndSendGaslessTransactionUseCase( class CreateAndSendGaslessTransactionUseCase(
private val walletManagersFacade: WalletManagersFacade, private val walletManagersFacade: WalletManagersFacade,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val singleAccountListSupplier: SingleAccountListSupplier,
private val gaslessTransactionRepository: GaslessTransactionRepository, private val gaslessTransactionRepository: GaslessTransactionRepository,
private val cardSdkConfigRepository: CardSdkConfigRepository, private val cardSdkConfigRepository: CardSdkConfigRepository,
private val getHotWalletSigner: (UserWallet.Hot) -> TransactionSigner, private val getHotWalletSigner: (UserWallet.Hot) -> TransactionSigner,
@ -74,18 +75,17 @@ class CreateAndSendGaslessTransactionUseCase(
transactionData: TransactionData.Uncompiled, transactionData: TransactionData.Uncompiled,
fee: TransactionFeeExtended, fee: TransactionFeeExtended,
): GaslessContext { ): GaslessContext {
val tokenForFeeStatus = getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync( val currency = singleAccountListSupplier.getSyncOrNull(userWalletId = userWallet.walletId)
userWallet.walletId, .getCryptoCurrency(currencyId = fee.feeTokenId, network = null)
fee.feeTokenId, .getOrElse { error("Token for fee not found") }
).getOrNull() ?: error("Token for fee not found")
val walletManager = walletManagersFacade.getOrCreateWalletManager( val walletManager = walletManagersFacade.getOrCreateWalletManager(
userWallet.walletId, userWallet.walletId,
tokenForFeeStatus.currency.network, currency.network,
) ?: error("WalletManager not found for network ${tokenForFeeStatus.currency.network.id}") ) ?: error("WalletManager not found for network ${currency.network.id}")
val gaslessDataProvider = walletManager as? EthereumGaslessDataProvider ?: error( val gaslessDataProvider = walletManager as? EthereumGaslessDataProvider ?: error(
"WalletManager for network ${tokenForFeeStatus.currency.network.id} " + "WalletManager for network ${currency.network.id} " +
"does not support gasless transactions", "does not support gasless transactions",
) )
@ -94,16 +94,16 @@ class CreateAndSendGaslessTransactionUseCase(
val gaslessTransactionData = createGaslessTransactionData( val gaslessTransactionData = createGaslessTransactionData(
transactionData = transactionData, transactionData = transactionData,
txFee = fee, txFee = fee,
tokenFeeStatus = tokenForFeeStatus, currency = currency,
nonce = gaslessContractNonce, nonce = gaslessContractNonce,
) )
val chainId = gaslessTransactionRepository.getChainIdForNetwork(tokenForFeeStatus.currency.network) val chainId = gaslessTransactionRepository.getChainIdForNetwork(currency.network)
return GaslessContext( return GaslessContext(
walletManager = walletManager, walletManager = walletManager,
gaslessDataProvider = gaslessDataProvider, gaslessDataProvider = gaslessDataProvider,
tokenForFeeStatus = tokenForFeeStatus, currency = currency,
gaslessTransactionData = gaslessTransactionData, gaslessTransactionData = gaslessTransactionData,
chainId = chainId, chainId = chainId,
) )
@ -189,7 +189,7 @@ class CreateAndSendGaslessTransactionUseCase(
transactionData: TransactionData.Uncompiled, transactionData: TransactionData.Uncompiled,
): String { ): String {
val txHash = gaslessTransactionRepository.signGaslessTransaction( val txHash = gaslessTransactionRepository.signGaslessTransaction(
network = context.tokenForFeeStatus.currency.network, network = context.currency.network,
gaslessTransactionData = context.gaslessTransactionData, gaslessTransactionData = context.gaslessTransactionData,
signature = signedData.eip712Signature, signature = signedData.eip712Signature,
userAddress = transactionData.sourceAddress, userAddress = transactionData.sourceAddress,
@ -244,11 +244,11 @@ class CreateAndSendGaslessTransactionUseCase(
private suspend fun createGaslessTransactionData( private suspend fun createGaslessTransactionData(
transactionData: TransactionData.Uncompiled, transactionData: TransactionData.Uncompiled,
txFee: TransactionFeeExtended, txFee: TransactionFeeExtended,
tokenFeeStatus: CryptoCurrencyStatus, currency: CryptoCurrency,
nonce: BigInteger, nonce: BigInteger,
): GaslessTransactionData { ): GaslessTransactionData {
val transaction = buildTransaction(transactionData) val transaction = buildTransaction(transactionData)
val fee = buildFee(txFee, tokenFeeStatus) val fee = buildFee(txFee, currency)
return GaslessTransactionData( return GaslessTransactionData(
transaction = transaction, transaction = transaction,
@ -272,11 +272,8 @@ class CreateAndSendGaslessTransactionUseCase(
) )
} }
private suspend fun buildFee( private suspend fun buildFee(txFee: TransactionFeeExtended, currency: CryptoCurrency): GaslessTransactionData.Fee {
txFee: TransactionFeeExtended, val tokenForFee = currency as? CryptoCurrency.Token
tokenFeeStatus: CryptoCurrencyStatus,
): GaslessTransactionData.Fee {
val tokenForFee = tokenFeeStatus.currency as? CryptoCurrency.Token
?: error("Fee currency must be a token") ?: error("Fee currency must be a token")
val feeInTokenCurrency = txFee.transactionFee.normal as? Fee.Ethereum.TokenCurrency val feeInTokenCurrency = txFee.transactionFee.normal as? Fee.Ethereum.TokenCurrency
@ -320,7 +317,7 @@ class CreateAndSendGaslessTransactionUseCase(
private data class GaslessContext( private data class GaslessContext(
val walletManager: WalletManager, val walletManager: WalletManager,
val gaslessDataProvider: EthereumGaslessDataProvider, val gaslessDataProvider: EthereumGaslessDataProvider,
val tokenForFeeStatus: CryptoCurrencyStatus, val currency: CryptoCurrency,
val gaslessTransactionData: GaslessTransactionData, val gaslessTransactionData: GaslessTransactionData,
val chainId: Int, val chainId: Int,
) )

View file

@ -8,11 +8,12 @@ import arrow.core.raise.either
import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.models.TransactionFeeExtended import com.tangem.domain.transaction.models.TransactionFeeExtended
import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase
@ -55,7 +56,7 @@ internal class FeeSelectorLogic @AssistedInject constructor(
private val feeSelectorCheckReloadTrigger: FeeSelectorCheckReloadTrigger, private val feeSelectorCheckReloadTrigger: FeeSelectorCheckReloadTrigger,
private val feeSelectorAlertFactory: FeeSelectorAlertFactory, private val feeSelectorAlertFactory: FeeSelectorAlertFactory,
private val analyticsEventHandler: AnalyticsEventHandler, private val analyticsEventHandler: AnalyticsEventHandler,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getUserWalletUseCase: GetUserWalletUseCase, private val getUserWalletUseCase: GetUserWalletUseCase,
private val getAvailableFeeTokensUseCase: GetAvailableFeeTokensUseCase, private val getAvailableFeeTokensUseCase: GetAvailableFeeTokensUseCase,
isGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork, isGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork,
@ -294,11 +295,9 @@ internal class FeeSelectorLogic @AssistedInject constructor(
private suspend fun getSelectedTokenStatus(tokenId: CryptoCurrency.ID): Either<GetFeeError, CryptoCurrencyStatus> = private suspend fun getSelectedTokenStatus(tokenId: CryptoCurrency.ID): Either<GetFeeError, CryptoCurrencyStatus> =
either { either {
if (params.feeCryptoCurrencyStatus.currency.id != tokenId) { if (params.feeCryptoCurrencyStatus.currency.id != tokenId) {
getSingleCryptoCurrencyStatusUseCase singleAccountStatusListSupplier.getSyncOrNull(params.userWalletId)
.invokeMultiWalletSync( .getCryptoCurrencyStatus(currencyId = tokenId, network = null)
userWalletId = params.userWalletId, .getOrElse {
cryptoCurrencyId = tokenId,
).getOrElse {
raise(GetFeeError.DataError(IllegalStateException("No token found for id: $tokenId"))) raise(GetFeeError.DataError(IllegalStateException("No token found for id: $tokenId")))
} }
} else { } else {

View file

@ -35,7 +35,9 @@ import com.tangem.core.ui.utils.InputNumberFormatter
import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.datasource.local.appsflyer.AppsFlyerStore import com.tangem.datasource.local.appsflyer.AppsFlyerStore
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
@ -69,7 +71,6 @@ import com.tangem.domain.tangempay.GetTangemPayCustomerIdUseCase
import com.tangem.domain.tangempay.TangemPayWithdrawUseCase import com.tangem.domain.tangempay.TangemPayWithdrawUseCase
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.models.TransactionFeeExtended import com.tangem.domain.transaction.models.TransactionFeeExtended
@ -136,7 +137,6 @@ internal class SwapModel @Inject constructor(
private val analyticsErrorEventHandler: AnalyticsErrorHandler, private val analyticsErrorEventHandler: AnalyticsErrorHandler,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase, private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase, private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase, private val getUserWalletUseCase: GetUserWalletUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase, private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
@ -153,6 +153,7 @@ internal class SwapModel @Inject constructor(
router: AppRouter, router: AppRouter,
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase, private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase, private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getTangemPayCurrencyStatusUseCase: GetTangemPayCurrencyStatusUseCase, private val getTangemPayCurrencyStatusUseCase: GetTangemPayCurrencyStatusUseCase,
private val tangemPayWithdrawUseCase: TangemPayWithdrawUseCase, private val tangemPayWithdrawUseCase: TangemPayWithdrawUseCase,
private val iGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork, private val iGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork,
@ -386,10 +387,9 @@ internal class SwapModel @Inject constructor(
} else { } else {
val fromStatus = getFromStatus() val fromStatus = getFromStatus()
val toStatus = initialCurrencyTo?.let { currencyTo -> val toStatus = initialCurrencyTo?.let { currencyTo ->
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync( singleAccountStatusListSupplier.getSyncOrNull(params.userWalletId)
userWalletId = userWalletId, .getCryptoCurrencyStatus(currencyTo)
cryptoCurrencyId = currencyTo.id, .getOrNull()
).getOrNull()
} }
if (fromStatus == null) { if (fromStatus == null) {
@ -2305,10 +2305,9 @@ internal class SwapModel @Inject constructor(
depositAddress = tangemPayInput.depositAddress, depositAddress = tangemPayInput.depositAddress,
) )
} else { } else {
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync( singleAccountStatusListSupplier.getSyncOrNull(params.userWalletId)
userWalletId = userWalletId, .getCryptoCurrencyStatus(currency = initialCurrencyFrom)
cryptoCurrencyId = initialCurrencyFrom.id, .getOrNull()
).getOrNull()
} }
} }

View file

@ -36,6 +36,7 @@ dependencies {
implementation(projects.domain.tokens.models) implementation(projects.domain.tokens.models)
implementation(projects.domain.balanceHiding) implementation(projects.domain.balanceHiding)
implementation(projects.domain.balanceHiding.models) implementation(projects.domain.balanceHiding.models)
implementation(projects.domain.account.status)
/* AndroidX */ /* AndroidX */
implementation(deps.androidx.activity.compose) implementation(deps.androidx.activity.compose)

View file

@ -1,22 +1,19 @@
package com.tangem.features.txhistory.model package com.tangem.features.txhistory.model
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import arrow.core.Either import arrow.core.Option
import com.tangem.core.decompose.di.ModelScoped 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.core.navigation.url.UrlOpener import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.domain.txhistory.repository.TxHistoryRepositoryV2 import com.tangem.domain.txhistory.repository.TxHistoryRepositoryV2
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.txhistory.component.TxHistoryComponent import com.tangem.features.txhistory.component.TxHistoryComponent
import com.tangem.features.txhistory.converter.TxHistoryItemToTransactionStateConverter import com.tangem.features.txhistory.converter.TxHistoryItemToTransactionStateConverter
import com.tangem.features.txhistory.entity.TxHistoryUM import com.tangem.features.txhistory.entity.TxHistoryUM
@ -40,8 +37,7 @@ internal class TxHistoryModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider, override val dispatchers: CoroutineDispatcherProvider,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val urlOpener: UrlOpener, private val urlOpener: UrlOpener,
private val txHistoryUpdateListener: TxHistoryUpdateListener, private val txHistoryUpdateListener: TxHistoryUpdateListener,
@ -184,26 +180,20 @@ internal class TxHistoryModel @Inject constructor(
} }
private fun subscribeOnCurrencyStatusUpdates() { private fun subscribeOnCurrencyStatusUpdates() {
val userWallet: UserWallet = requireNotNull(getUserWalletUseCase(params.userWalletId).getOrNull()) { singleAccountStatusListSupplier(params.userWalletId)
"User wallet not found" .map { it.getCryptoCurrencyStatus(currency = params.currency) }
}
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = params.userWalletId,
currencyId = params.currency.id,
isSingleWalletWithTokens = userWallet is UserWallet.Cold &&
userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(),
)
.distinctUntilChanged() .distinctUntilChanged()
.onEach(::handlePendingTxsChanges) .onEach(::handlePendingTxsChanges)
.flowOn(dispatchers.main) .flowOn(dispatchers.default)
.launchIn(modelScope) .launchIn(modelScope)
} }
private fun handlePendingTxsChanges(maybeCurrencyStatus: Either<CurrencyStatusError, CryptoCurrencyStatus>) { private fun handlePendingTxsChanges(maybeCurrencyStatus: Option<CryptoCurrencyStatus>) {
maybeCurrencyStatus.onRight { status -> maybeCurrencyStatus.onSome { status ->
val pendingTxs = status.value.pendingTransactions val pendingTxs = status.value.pendingTransactions
.map(txHistoryItemConverter::convert) .map(txHistoryItemConverter::convert)
.toPersistentList() .toPersistentList()
_uiState.update { state -> _uiState.update { state ->
if (state is TxHistoryUM.NotSupported) { if (state is TxHistoryUM.NotSupported) {
state.copy(pendingTransactions = pendingTxs) state.copy(pendingTransactions = pendingTxs)

View file

@ -8,6 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.models.account.Account import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
@ -18,7 +19,6 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.nft.analytics.NFTAnalyticsEvent import com.tangem.domain.nft.analytics.NFTAnalyticsEvent
import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.details.NavigationAction import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
@ -94,7 +94,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
private val currencyActionsClickIntents: WalletCurrencyActionsClickIntentsImplementor, private val currencyActionsClickIntents: WalletCurrencyActionsClickIntentsImplementor,
private val onrampStatusFactory: OnrampStatusFactory, private val onrampStatusFactory: OnrampStatusFactory,
private val getUserWalletUseCase: GetUserWalletUseCase, private val getUserWalletUseCase: GetUserWalletUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase, private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val shouldShowMarketsTooltipUseCase: ShouldShowMarketsTooltipUseCase, private val shouldShowMarketsTooltipUseCase: ShouldShowMarketsTooltipUseCase,
@ -269,7 +269,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
override fun onTransactionClick(txHash: String) { override fun onTransactionClick(txHash: String) {
modelScope.launch(dispatchers.main) { modelScope.launch(dispatchers.main) {
val currency = getSingleCryptoCurrencyStatusUseCase.unwrap( val currency = singleAccountStatusListSupplier.unwrap(
userWalletId = stateHolder.getSelectedWalletId(), userWalletId = stateHolder.getSelectedWalletId(),
) )
?.currency ?.currency

View file

@ -22,6 +22,7 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.haptic.TangemHapticEffect import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.haptic.VibratorHapticManager import com.tangem.core.ui.haptic.VibratorHapticManager
import com.tangem.core.ui.message.DialogMessage import com.tangem.core.ui.message.DialogMessage
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.extenstions.unwrap import com.tangem.domain.appcurrency.extenstions.unwrap
@ -42,7 +43,10 @@ import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.promo.GetStoryContentUseCase import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.promo.models.StoryContentIds import com.tangem.domain.promo.models.StoryContentIds
import com.tangem.domain.staking.model.StakingOption import com.tangem.domain.staking.model.StakingOption
import com.tangem.domain.tokens.* import com.tangem.domain.tokens.IsCryptoCurrencyCoinCouldHideUseCase
import com.tangem.domain.tokens.NeedShowYieldSupplyDepositedWarningUseCase
import com.tangem.domain.tokens.SaveViewedTokenReceiveWarningUseCase
import com.tangem.domain.tokens.SaveViewedYieldSupplyWarningUseCase
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource
import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent
@ -124,7 +128,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val walletManagersFacade: WalletManagersFacade, private val walletManagersFacade: WalletManagersFacade,
private val isDemoCardUseCase: IsDemoCardUseCase, private val isDemoCardUseCase: IsDemoCardUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getExploreUrlUseCase: GetExploreUrlUseCase, private val getExploreUrlUseCase: GetExploreUrlUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase, private val getStoryContentUseCase: GetStoryContentUseCase,
@ -485,7 +489,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
val userWalletId = stateHolder.getSelectedWalletId() val userWalletId = stateHolder.getSelectedWalletId()
modelScope.launch(dispatchers.main) { modelScope.launch(dispatchers.main) {
val currencyStatus = getSingleCryptoCurrencyStatusUseCase.unwrap(userWalletId) ?: return@launch val currencyStatus = singleAccountStatusListSupplier.unwrap(userWalletId) ?: return@launch
when (val addresses = currencyStatus.value.networkAddress) { when (val addresses = currencyStatus.value.networkAddress) {
is NetworkAddress.Selectable -> { is NetworkAddress.Selectable -> {

View file

@ -1,13 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.domain package com.tangem.feature.wallet.presentation.wallet.domain
import arrow.core.Either import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import kotlinx.coroutines.flow.*
import timber.log.Timber import timber.log.Timber
internal fun GetSelectedWalletSyncUseCase.unwrap(): UserWallet? { internal fun GetSelectedWalletSyncUseCase.unwrap(): UserWallet? {
@ -20,29 +18,9 @@ internal fun GetSelectedWalletSyncUseCase.unwrap(): UserWallet? {
) )
} }
internal suspend fun GetSingleCryptoCurrencyStatusUseCase.unwrap(userWalletId: UserWalletId): CryptoCurrencyStatus? { internal suspend fun SingleAccountStatusListSupplier.unwrap(userWalletId: UserWalletId): CryptoCurrencyStatus? {
return invokeSingleWallet(userWalletId) return getSyncOrNull(params = SingleAccountStatusListProducer.Params(userWalletId))
.conflate() ?.mainAccount
.distinctUntilChanged() ?.flattenCurrencies()
.filter(Either<CurrencyStatusError, CryptoCurrencyStatus>::isRight) ?.firstOrNull()
.firstOrNull()
?.fold(
ifLeft = {
Timber.e("Impossible to get primary currency status $it")
null
},
ifRight = { it },
)
}
internal suspend fun GetSingleCryptoCurrencyStatusUseCase.collectLatest(
userWalletId: UserWalletId,
onRight: suspend (CryptoCurrencyStatus) -> Unit,
) {
invokeSingleWallet(userWalletId = userWalletId)
.conflate()
.distinctUntilChanged()
.collectLatest { maybeStatus ->
maybeStatus.onRight { onRight(it) }
}
} }

View file

@ -43,6 +43,7 @@ dependencies {
implementation(projects.domain.models) implementation(projects.domain.models)
implementation(projects.domain.appCurrency.models) implementation(projects.domain.appCurrency.models)
implementation(projects.domain.appCurrency) implementation(projects.domain.appCurrency)
implementation(projects.domain.account.status)
implementation(projects.domain.wallets.models) implementation(projects.domain.wallets.models)
implementation(projects.domain.wallets) implementation(projects.domain.wallets)
implementation(projects.domain.tokens.models) implementation(projects.domain.tokens.models)

View file

@ -16,12 +16,13 @@ import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.yield.supply.usecase.* import com.tangem.domain.yield.supply.usecase.*
import com.tangem.features.yield.supply.api.YieldSupplyActiveComponent import com.tangem.features.yield.supply.api.YieldSupplyActiveComponent
@ -55,7 +56,7 @@ internal class YieldSupplyActiveModel @Inject constructor(
private val yieldSupplyGetMaxFeeUseCase: YieldSupplyGetMaxFeeUseCase, private val yieldSupplyGetMaxFeeUseCase: YieldSupplyGetMaxFeeUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase, private val getUserWalletUseCase: GetUserWalletUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val urlOpener: UrlOpener, private val urlOpener: UrlOpener,
private val appRouter: AppRouter, private val appRouter: AppRouter,
private val yieldSupplyGetDustMinAmountUseCase: YieldSupplyGetDustMinAmountUseCase, private val yieldSupplyGetDustMinAmountUseCase: YieldSupplyGetDustMinAmountUseCase,
@ -168,44 +169,44 @@ internal class YieldSupplyActiveModel @Inject constructor(
ifRight = { wallet -> ifRight = { wallet ->
userWallet = wallet userWallet = wallet
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( singleAccountStatusListSupplier(params.userWalletId)
userWalletId = params.userWalletId, .map { it.getCryptoCurrencyStatus(currency = cryptoCurrency) }
currencyId = cryptoCurrency.id, .distinctUntilChanged()
isSingleWalletWithTokens = false, .onEach { maybeCryptoCurrency ->
).onEach { maybeCryptoCurrency -> maybeCryptoCurrency.fold(
maybeCryptoCurrency.fold( ifSome = { cryptoCurrencyStatus ->
ifRight = { cryptoCurrencyStatus -> cryptoCurrencyStatusFlow.update { cryptoCurrencyStatus }
cryptoCurrencyStatusFlow.update { cryptoCurrencyStatus }
val protocolBalance = val protocolBalance =
cryptoCurrencyStatus.value.yieldSupplyStatus?.effectiveProtocolBalance cryptoCurrencyStatus.value.yieldSupplyStatus?.effectiveProtocolBalance
?: yieldSupplyGetProtocolBalanceUseCase( ?: yieldSupplyGetProtocolBalanceUseCase(
userWalletId = userWalletId, userWalletId = userWalletId,
cryptoCurrency = cryptoCurrency, cryptoCurrency = cryptoCurrency,
).getOrNull() ).getOrNull()
loadApy() loadApy()
loadMinAmount() loadMinAmount()
loadFees() loadFees()
uiState.update { uiState.update {
it.copy( it.copy(
availableBalance = stringReference( availableBalance = stringReference(
protocolBalance.format { protocolBalance.format {
crypto( crypto(
symbol = AAVEV3_PREFIX + cryptoCurrency.symbol, symbol = AAVEV3_PREFIX + cryptoCurrency.symbol,
decimals = cryptoCurrency.decimals, decimals = cryptoCurrency.decimals,
) )
}, },
), ),
) )
} }
}, },
ifLeft = { ifEmpty = {
Timber.w(it.toString()) Timber.w("No currency status found: ${cryptoCurrency.id}")
}, },
) )
}.flowOn(dispatchers.default) }
.flowOn(dispatchers.default)
.launchIn(modelScope) .launchIn(modelScope)
}, },
ifLeft = { error -> ifLeft = { error ->

View file

@ -5,9 +5,10 @@ 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.core.decompose.navigation.Router import com.tangem.core.decompose.navigation.Router
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.details.NavigationAction import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.yield.supply.usecase.YieldSupplyEnterStatusUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyEnterStatusUseCase
import com.tangem.features.yield.supply.api.YieldSupplyEntryComponent import com.tangem.features.yield.supply.api.YieldSupplyEntryComponent
@ -24,7 +25,7 @@ internal class YieldSupplyEntryModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider, override val dispatchers: CoroutineDispatcherProvider,
private val router: Router, private val router: Router,
private val yieldSupplyEnterStatusUseCase: YieldSupplyEnterStatusUseCase, private val yieldSupplyEnterStatusUseCase: YieldSupplyEnterStatusUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
) : Model() { ) : Model() {
private val params = paramsContainer.require<YieldSupplyEntryComponent.Params>() private val params = paramsContainer.require<YieldSupplyEntryComponent.Params>()
@ -39,22 +40,22 @@ internal class YieldSupplyEntryModel @Inject constructor(
val userWalletId = params.userWalletId val userWalletId = params.userWalletId
val cryptoCurrency = params.cryptoCurrency val cryptoCurrency = params.cryptoCurrency
modelScope.launch(dispatchers.default) { modelScope.launch(dispatchers.default) {
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync( singleAccountStatusListSupplier.getSyncOrNull(userWalletId)
userWalletId = userWalletId, .getCryptoCurrencyStatus(currency = cryptoCurrency)
cryptoCurrencyId = cryptoCurrency.id, .onNone {
).onLeft { error -> Timber.e("Failed to get CryptoCurrencyStatus: ${cryptoCurrency.id}")
Timber.e("Failed to get CryptoCurrencyStatus: $error") withContext(dispatchers.mainImmediate) {
withContext(dispatchers.mainImmediate) { router.pop()
router.pop() }
} }
}.onRight { cryptoCurrencyStatus -> .onSome { cryptoCurrencyStatus ->
withContext(dispatchers.mainImmediate) { withContext(dispatchers.mainImmediate) {
val route = getInitialRoute(cryptoCurrencyStatus) val route = getInitialRoute(cryptoCurrencyStatus)
if (route != null) { if (route != null) {
router.replaceCurrent(route) router.replaceCurrent(route)
}
} }
} }
}
} }
} }

View file

@ -11,6 +11,8 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.combinedReference import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.StatusSource import com.tangem.domain.models.StatusSource
@ -21,7 +23,6 @@ import com.tangem.domain.models.currency.shouldShowNotSuppliedNotification
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.yield.supply.YieldSupplyStatus import com.tangem.domain.models.yield.supply.YieldSupplyStatus
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.yield.supply.models.YieldSupplyPendingStatus import com.tangem.domain.yield.supply.models.YieldSupplyPendingStatus
import com.tangem.domain.yield.supply.usecase.* import com.tangem.domain.yield.supply.usecase.*
@ -35,9 +36,9 @@ import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject import javax.inject.Inject
import kotlin.properties.Delegates import kotlin.properties.Delegates
import java.util.concurrent.atomic.AtomicBoolean
@Suppress("LongParameterList", "LargeClass") @Suppress("LongParameterList", "LargeClass")
@ModelScoped @ModelScoped
@ -48,7 +49,7 @@ internal class YieldSupplyModel @Inject constructor(
private val appRouter: AppRouter, private val appRouter: AppRouter,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase, private val getUserWalletUseCase: GetUserWalletUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher, private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
private val yieldSupplyGetTokenStatusUseCase: YieldSupplyGetTokenStatusUseCase, private val yieldSupplyGetTokenStatusUseCase: YieldSupplyGetTokenStatusUseCase,
private val yieldSupplyIsAvailableUseCase: YieldSupplyIsAvailableUseCase, private val yieldSupplyIsAvailableUseCase: YieldSupplyIsAvailableUseCase,
@ -109,32 +110,33 @@ internal class YieldSupplyModel @Inject constructor(
private fun subscribeOnCurrencyStatusUpdates() { private fun subscribeOnCurrencyStatusUpdates() {
combine( combine(
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( flow = singleAccountStatusListSupplier(params.userWalletId).map {
userWalletId = params.userWalletId, it.getCryptoCurrencyStatus(currency = cryptoCurrency)
currencyId = cryptoCurrency.id, },
isSingleWalletWithTokens = false, flow2 = yieldSupplyEnterStatusFlowUseCase(
),
yieldSupplyEnterStatusFlowUseCase(
userWalletId = params.userWalletId, userWalletId = params.userWalletId,
cryptoCurrency = cryptoCurrency, cryptoCurrency = cryptoCurrency,
), ),
) { maybeCryptoCurrency, _ -> ) { maybeCryptoCurrency, _ ->
maybeCryptoCurrency maybeCryptoCurrency
}.flowOn(dispatchers.io) }
.flowOn(dispatchers.io)
.distinctUntilChanged()
.onEach { maybeCryptoCurrency -> .onEach { maybeCryptoCurrency ->
maybeCryptoCurrency.fold( maybeCryptoCurrency.fold(
ifRight = { cryptoCurrencyStatus -> ifSome = { cryptoCurrencyStatus ->
latestCryptoCurrencyStatus = cryptoCurrencyStatus latestCryptoCurrencyStatus = cryptoCurrencyStatus
if (isFirstCryptoCurrencyStatusEmission.compareAndSet(true, false)) { if (isFirstCryptoCurrencyStatusEmission.compareAndSet(true, false)) {
sendInfoAboutProtocolStatus(cryptoCurrencyStatus) sendInfoAboutProtocolStatus(cryptoCurrencyStatus)
} }
onCryptoCurrencyStatusUpdated(cryptoCurrencyStatus) onCryptoCurrencyStatusUpdated(cryptoCurrencyStatus)
}, },
ifLeft = { ifEmpty = {
Timber.w(it.toString()) Timber.w("Unable to get crypto currency status: ${cryptoCurrency.id}")
}, },
) )
}.launchIn(modelScope) }
.launchIn(modelScope)
} }
private suspend fun loadTokenStatus() { private suspend fun loadTokenStatus() {

View file

@ -13,13 +13,14 @@ import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIco
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.extensions.wrappedList
import com.tangem.datasource.local.appsflyer.AppsFlyerStore import com.tangem.datasource.local.appsflyer.AppsFlyerStore
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusOperations.getCryptoCurrencyStatus
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isHotWallet import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.SendTransactionUseCase import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
@ -55,7 +56,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
paramsContainer: ParamsContainer, paramsContainer: ParamsContainer,
private val analytics: AnalyticsEventHandler, private val analytics: AnalyticsEventHandler,
private val getUserWalletUseCase: GetUserWalletUseCase, private val getUserWalletUseCase: GetUserWalletUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase, private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val sendTransactionUseCase: SendTransactionUseCase, private val sendTransactionUseCase: SendTransactionUseCase,
private val yieldSupplyStartEarningUseCase: YieldSupplyStartEarningUseCase, private val yieldSupplyStartEarningUseCase: YieldSupplyStartEarningUseCase,
@ -346,27 +347,27 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
} }
private fun getCurrenciesStatusUpdates() { private fun getCurrenciesStatusUpdates() {
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( singleAccountStatusListSupplier(params.userWalletId)
userWalletId = userWalletId, .map { it.getCryptoCurrencyStatus(currency = cryptoCurrency) }
currencyId = cryptoCurrency.id, .distinctUntilChanged()
isSingleWalletWithTokens = false, .onEach { maybeCryptoCurrency ->
).onEach { maybeCryptoCurrency -> maybeCryptoCurrency.fold(
maybeCryptoCurrency.fold( ifSome = { cryptoCurrencyStatus ->
ifRight = { cryptoCurrencyStatus -> onDataLoaded(
onDataLoaded( currencyStatus = cryptoCurrencyStatus,
currencyStatus = cryptoCurrencyStatus, feeCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase(
feeCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase( userWalletId = userWalletId,
userWalletId = userWalletId, cryptoCurrencyStatus = cryptoCurrencyStatus,
cryptoCurrencyStatus = cryptoCurrencyStatus, ).getOrNull() ?: cryptoCurrencyStatus,
).getOrNull() ?: cryptoCurrencyStatus, )
) },
}, ifEmpty = {
ifLeft = { Timber.w("Unable to get crypto currency status: ${cryptoCurrency.id}")
Timber.w(it.toString()) showAlertError()
showAlertError() },
}, )
) }
}.launchIn(modelScope) .launchIn(modelScope)
} }
private fun onDataLoaded(currencyStatus: CryptoCurrencyStatus, feeCurrencyStatus: CryptoCurrencyStatus) { private fun onDataLoaded(currencyStatus: CryptoCurrencyStatus, feeCurrencyStatus: CryptoCurrencyStatus) {