From 4d8930951cfe6e21247468e877101fa1d9429982 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 25 Oct 2023 22:28:34 +0800 Subject: [PATCH] Updated on 2026-08-14 --- domain/legacy/build.gradle.kts | 7 ++- .../DefaultWalletManagersFacade.kt | 34 +++++++++-- .../walletmanager/WalletManagersFacade.kt | 7 +++ .../HasSingleWalletSignedHashesUseCase.kt | 50 +++++++++++++++++ .../WalletNotificationsListFactory.kt | 56 +++++++++---------- .../wallet/viewmodels/WalletViewModel.kt | 11 ++-- 6 files changed, 122 insertions(+), 43 deletions(-) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/HasSingleWalletSignedHashesUseCase.kt diff --git a/domain/legacy/build.gradle.kts b/domain/legacy/build.gradle.kts index 69f443892c..9a4c2225c2 100644 --- a/domain/legacy/build.gradle.kts +++ b/domain/legacy/build.gradle.kts @@ -25,12 +25,13 @@ dependencies { } /** Other libraries */ - implementation(deps.reKotlin) + implementation(deps.arrow.core) + implementation(deps.jodatime) + implementation(deps.kotlin.coroutines) implementation(deps.moshi) implementation(deps.moshi.kotlin) + implementation(deps.reKotlin) implementation(deps.timber) - implementation(deps.kotlin.coroutines) - implementation(deps.jodatime) /** Testing libraries */ testImplementation(deps.test.junit) diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index 1f4caa5690..f8aebc28ec 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -1,15 +1,17 @@ package com.tangem.domain.walletmanager +import arrow.core.Either +import arrow.core.raise.either +import arrow.core.raise.ensureNotNull +import arrow.core.right import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider import com.tangem.blockchain.blockchains.solana.RentProvider -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.Address import com.tangem.blockchain.common.address.AddressType import com.tangem.blockchain.common.txhistory.TransactionHistoryRequest import com.tangem.blockchain.extensions.Result +import com.tangem.blockchain.extensions.SimpleResult import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.local.userwallet.UserWalletsStore @@ -24,6 +26,7 @@ import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryState import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult import com.tangem.domain.walletmanager.utils.* +import com.tangem.domain.walletmanager.utils.WalletManagerFactory import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow @@ -318,6 +321,29 @@ class DefaultWalletManagersFacade( return walletManagersStore.getAll(userWalletId) } + override suspend fun validateSignatureCount( + userWalletId: UserWalletId, + network: Network, + signedHashes: Int, + ): Either { + return either { + val walletManager = getOrCreateWalletManager( + userWalletId = userWalletId, + blockchain = Blockchain.fromId(network.id.value), + derivationPath = network.derivationPath.value, + ) + + val validator = ensureNotNull(walletManager as? SignatureCountValidator) { + raise(IllegalStateException("Wallet manager is not a SignatureCountValidator")) + } + + when (val result = validator.validateSignatureCount(signedHashes)) { + is SimpleResult.Failure -> raise(result.error) + is SimpleResult.Success -> Unit.right() + } + } + } + private fun updateWalletManagerTokensIfNeeded(walletManager: WalletManager, tokens: Set) { if (tokens.isEmpty()) return diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index 51e96aa10c..1e33f610e6 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -1,5 +1,6 @@ package com.tangem.domain.walletmanager +import arrow.core.Either import com.tangem.blockchain.blockchains.solana.RentProvider import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager @@ -113,4 +114,10 @@ interface WalletManagersFacade { @Deprecated("Will be removed in future") fun getAll(userWalletId: UserWalletId): Flow> + + suspend fun validateSignatureCount( + userWalletId: UserWalletId, + network: Network, + signedHashes: Int, + ): Either } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/HasSingleWalletSignedHashesUseCase.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/HasSingleWalletSignedHashesUseCase.kt new file mode 100644 index 0000000000..0738db4676 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/HasSingleWalletSignedHashesUseCase.kt @@ -0,0 +1,50 @@ +package com.tangem.feature.wallet.presentation.wallet.domain + +import com.tangem.domain.card.repository.CardRepository +import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.demo.DemoConfig +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.models.UserWallet +import dagger.hilt.android.scopes.ViewModelScoped +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +@ViewModelScoped +class HasSingleWalletSignedHashesUseCase @Inject constructor( + private val cardRepository: CardRepository, + private val walletManagersFacade: WalletManagersFacade, +) { + + operator fun invoke(userWallet: UserWallet, network: Network): Flow { + return cardRepository.wasCardScanned(cardId = userWallet.cardId) + .map { wasCardScanned -> + if (wasCardScanned || !userWallet.isCorrectCardType()) return@map false + + if (!userWallet.scanResponse.cardTypesResolver.hasWalletSignedHashes()) { + cardRepository.setCardWasScanned(cardId = userWallet.cardId) + return@map false + } + + return@map walletManagersFacade.validateSignatureCount( + userWalletId = userWallet.walletId, + network = network, + signedHashes = userWallet.scanResponse.card.wallets.firstOrNull()?.totalSignedHashes ?: 0, + ) + .fold( + ifLeft = { true }, + ifRight = { + cardRepository.setCardWasScanned(cardId = userWallet.cardId) + false + }, + ) + } + } + + private fun UserWallet.isCorrectCardType(): Boolean { + return with(scanResponse.cardTypesResolver) { + !DemoConfig().isDemoCardId(cardId) && isReleaseFirmwareType() && !isMultiwalletAllowed() && !isTangemTwins() + } + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt index dc6fce13fd..6540090c11 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt @@ -1,29 +1,30 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels import arrow.core.Either -import com.tangem.domain.card.WasCardScannedUseCase import com.tangem.domain.common.CardTypesResolver +import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.settings.IsReadyToShowRateAppUseCase import com.tangem.domain.tokens.GetMissedAddressesCryptoCurrenciesUseCase import com.tangem.domain.tokens.error.GetCurrenciesError import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase +import com.tangem.feature.wallet.presentation.wallet.domain.HasSingleWalletSignedHashesUseCase import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flowOf /** * Wallet notifications list factory * * @property isDemoCardUseCase use case that checks if card is demo * @property isReadyToShowRateAppUseCase use case that checks if card is user already rate app - * @property wasCardScannedUseCase use case that checks if card was scanned * @property isNeedToBackupUseCase use case that checks if wallet need backup cards * @property clickIntents screen click intents * @@ -32,38 +33,50 @@ import kotlinx.coroutines.flow.conflate internal class WalletNotificationsListFactory( private val isDemoCardUseCase: IsDemoCardUseCase, private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase, - private val wasCardScannedUseCase: WasCardScannedUseCase, private val isNeedToBackupUseCase: IsNeedToBackupUseCase, private val getMissedAddressCryptoCurrenciesUseCase: GetMissedAddressesCryptoCurrenciesUseCase, + private val hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase, private val clickIntents: WalletClickIntents, ) { private var readyForRateAppNotification = false fun create( - selectedWalletId: UserWalletId, - cardTypesResolver: CardTypesResolver, + selectedWallet: UserWallet, cryptoCurrencyList: List, ): Flow> { + val cardTypesResolver = selectedWallet.scanResponse.cardTypesResolver return combine( - flow = wasCardScannedUseCase(cardTypesResolver.getCardId()).conflate(), + flow = hasSingleWalletSignedHashesFlow(selectedWallet, cryptoCurrencyList), flow2 = isReadyToShowRateAppUseCase().conflate(), - flow3 = isNeedToBackupUseCase(selectedWalletId).conflate(), - flow4 = getMissedAddressCryptoCurrenciesUseCase(selectedWalletId).conflate(), - ) { wasCardScanned, isReadyToShowRating, isNeedToBackup, maybeMissedAddressCurrencies -> + flow3 = isNeedToBackupUseCase(selectedWallet.walletId).conflate(), + flow4 = getMissedAddressCryptoCurrenciesUseCase(selectedWallet.walletId).conflate(), + ) { hasSignedHashes, isReadyToShowRating, isNeedToBackup, maybeMissedAddressCurrencies -> readyForRateAppNotification = true buildList { addCriticalNotifications(cardTypesResolver) addInformationalNotifications(cardTypesResolver, maybeMissedAddressCurrencies) - addWarningNotifications(cardTypesResolver, cryptoCurrencyList, wasCardScanned, isNeedToBackup) + addWarningNotifications(cardTypesResolver, cryptoCurrencyList, hasSignedHashes, isNeedToBackup) addRateTheAppNotification(isReadyToShowRating) }.toImmutableList() } } + private fun hasSingleWalletSignedHashesFlow( + selectedWallet: UserWallet, + cryptoCurrencyList: List, + ): Flow { + return if (selectedWallet.scanResponse.cardTypesResolver.isMultiwalletAllowed()) { + flowOf(value = false) + } else { + val network = requireNotNull(cryptoCurrencyList.firstOrNull()?.currency?.network) + hasSingleWalletSignedHashesUseCase(userWallet = selectedWallet, network = network).conflate() + } + } + private fun MutableList.addCriticalNotifications(cardTypesResolver: CardTypesResolver) { addIf( element = WalletNotification.Critical.DevCard, @@ -97,7 +110,7 @@ internal class WalletNotificationsListFactory( private fun MutableList.addWarningNotifications( cardTypesResolver: CardTypesResolver, cryptoCurrencyList: List, - wasCardScanned: Boolean, + hasSignedHashes: Boolean, isNeedToBackup: Boolean, ) { addIf( @@ -112,7 +125,6 @@ internal class WalletNotificationsListFactory( condition = cardTypesResolver.isTestCard(), ) - val isDemo = isDemoCardUseCase(cardId = cardTypesResolver.getCardId()) if (cardTypesResolver.isMultiwalletAllowed()) { addIf( element = WalletNotification.Warning.SomeNetworksUnreachable, @@ -130,11 +142,7 @@ internal class WalletNotificationsListFactory( element = WalletNotification.Warning.NumberOfSignedHashesIncorrect( onCloseClick = clickIntents::onSignedHashesNotificationCloseClick, ), - condition = checkSignedHashes( - cardTypesResolver = cardTypesResolver, - isDemo = isDemo, - wasCardScanned = wasCardScanned, - ), + condition = hasSignedHashes, ) } } @@ -195,18 +203,6 @@ internal class WalletNotificationsListFactory( } } - /** - * Warning is being shown for single wallet cards only - */ - private fun checkSignedHashes( - cardTypesResolver: CardTypesResolver, - isDemo: Boolean, - wasCardScanned: Boolean, - ): Boolean { - return cardTypesResolver.isReleaseFirmwareType() && !cardTypesResolver.isTangemTwins() && - cardTypesResolver.hasWalletSignedHashes() && !isDemo && !wasCardScanned - } - private companion object { const val MAX_REMAINING_SIGNATURES_COUNT = 10 } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 8c38af790a..fb4bc3de20 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -31,7 +31,6 @@ import com.tangem.domain.balancehiding.IsBalanceHiddenUseCase import com.tangem.domain.balancehiding.ListenToFlipsUseCase import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.card.SetCardWasScannedUseCase -import com.tangem.domain.card.WasCardScannedUseCase import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.util.cardTypesResolver @@ -60,6 +59,7 @@ import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.feature.wallet.presentation.wallet.analytics.PortfolioEvent import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent +import com.tangem.feature.wallet.presentation.wallet.domain.HasSingleWalletSignedHashesUseCase import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletClickHandler import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletError import com.tangem.feature.wallet.presentation.wallet.state.* @@ -127,7 +127,7 @@ internal class WalletViewModel @Inject constructor( private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, private val isDemoCardUseCase: IsDemoCardUseCase, private val scanCardToUnlockWalletUseCase: ScanCardToUnlockWalletClickHandler, - wasCardScannedUseCase: WasCardScannedUseCase, + hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase, isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase, isNeedToBackupUseCase: IsNeedToBackupUseCase, getMissedAddressesCryptoCurrenciesUseCase: GetMissedAddressesCryptoCurrenciesUseCase, @@ -140,11 +140,11 @@ internal class WalletViewModel @Inject constructor( private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() private val notificationsListFactory = WalletNotificationsListFactory( - wasCardScannedUseCase = wasCardScannedUseCase, - isReadyToShowRateAppUseCase = isReadyToShowRateAppUseCase, isDemoCardUseCase = isDemoCardUseCase, + isReadyToShowRateAppUseCase = isReadyToShowRateAppUseCase, isNeedToBackupUseCase = isNeedToBackupUseCase, getMissedAddressCryptoCurrenciesUseCase = getMissedAddressesCryptoCurrenciesUseCase, + hasSingleWalletSignedHashesUseCase = hasSingleWalletSignedHashesUseCase, clickIntents = this, ) @@ -1316,8 +1316,7 @@ internal class WalletViewModel @Inject constructor( private fun updateNotifications(index: Int, tokenList: TokenList? = null) { notificationsListFactory.create( - selectedWalletId = getWallet(index).walletId, - cardTypesResolver = getCardTypeResolver(index = index), + selectedWallet = getWallet(index), cryptoCurrencyList = if (tokenList != null) { when (tokenList) { is TokenList.GroupedByNetwork -> {