Updated on 2026-08-14
This commit is contained in:
parent
53926e0843
commit
4d8930951c
6 changed files with 122 additions and 43 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<Throwable, Unit> {
|
||||
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<CryptoCurrency.Token>) {
|
||||
if (tokens.isEmpty()) return
|
||||
|
||||
|
|
|
|||
|
|
@ -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<List<WalletManager>>
|
||||
|
||||
suspend fun validateSignatureCount(
|
||||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
signedHashes: Int,
|
||||
): Either<Throwable, Unit>
|
||||
}
|
||||
|
|
@ -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<Boolean> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CryptoCurrencyStatus>,
|
||||
): Flow<ImmutableList<WalletNotification>> {
|
||||
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<CryptoCurrencyStatus>,
|
||||
): Flow<Boolean> {
|
||||
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<WalletNotification>.addCriticalNotifications(cardTypesResolver: CardTypesResolver) {
|
||||
addIf(
|
||||
element = WalletNotification.Critical.DevCard,
|
||||
|
|
@ -97,7 +110,7 @@ internal class WalletNotificationsListFactory(
|
|||
private fun MutableList<WalletNotification>.addWarningNotifications(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
cryptoCurrencyList: List<CryptoCurrencyStatus>,
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<AppCurrency> = 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 -> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue