Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-12 15:47:36 +04:00
parent 1443cbb961
commit 8a31a90b02
6 changed files with 362 additions and 29 deletions

View file

@ -17,6 +17,7 @@ import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.data.card.TransactionSignerFactory
import com.tangem.data.common.account.WalletAccountsFetcher
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
@ -154,4 +155,6 @@ interface ApplicationEntryPoint {
fun getABTestsManager(): ABTestsManager
fun getAppsFlyerClientFactory(): AppsFlyerClient.Factory
fun getWalletAccountsFetcher(): WalletAccountsFetcher
}

View file

@ -31,6 +31,7 @@ import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.settings.SettingsManager
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.data.card.TransactionSignerFactory
import com.tangem.data.common.account.WalletAccountsFetcher
import com.tangem.datasource.api.common.MoshiConverter
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
@ -246,6 +247,9 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
private val appsFlyerClientFactory: AppsFlyerClient.Factory
get() = entryPoint.getAppsFlyerClientFactory()
private val walletAccountsFetcher: WalletAccountsFetcher
get() = entryPoint.getWalletAccountsFetcher()
// endregion
private val appScope = MainScope()
@ -349,7 +353,7 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
}
derivationsFinder = DerivationsFinder(
userTokensResponseStore = userTokensResponseStore,
walletAccountsFetcher = walletAccountsFetcher,
dispatchers = dispatchers,
)

View file

@ -5,12 +5,12 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.datasource.local.token.UserTokensResponseStore
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.data.common.account.WalletAccountsFetcher
import com.tangem.domain.card.common.TapWorkarounds.hasOldStyleDerivation
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
@ -22,7 +22,7 @@ internal data class BlockchainToDerive(
// FIXME: May be move to DI, currently unnecessary
internal class DerivationsFinder(
private val userTokensResponseStore: UserTokensResponseStore,
private val walletAccountsFetcher: WalletAccountsFetcher,
private val dispatchers: CoroutineDispatcherProvider,
) {
@ -54,19 +54,19 @@ internal class DerivationsFinder(
}
// pay attention to this
if (!card.hasOldStyleDerivation) {
blockchains.removeUnnecessaryBlockchains()
return if (!card.hasOldStyleDerivation) {
blockchains.removeUnnecessaryBlockchains(derivationStyle)
} else {
blockchains
}
return blockchains
}
private suspend fun getBlockchains(userWalletId: UserWalletId): MutableSet<BlockchainToDerive> {
val responseTokens = userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId)?.tokens
?: return hashSetOf()
return responseTokens.asSequence()
.filter { it.contractAddress == null }
return walletAccountsFetcher.getSaved(userWalletId)?.accounts.orEmpty()
.flatMap { accountDTO ->
accountDTO.tokens.orEmpty()
.filter { it.contractAddress == null }
}
.mapNotNull { coin ->
val blockchain = Blockchain.fromNetworkId(coin.networkId) ?: return@mapNotNull null
val derivationPath = coin.derivationPath?.let(::DerivationPath)
@ -90,22 +90,29 @@ internal class DerivationsFinder(
}
private fun MutableSet<BlockchainToDerive>.addEthereumBlockchains(derivationStyle: DerivationStyle?) {
val ethereumBlockchains = setOf(Blockchain.Ethereum, Blockchain.EthereumTestnet)
val ethereumBlockchains = setOf(Blockchain.Ethereum)
.mapToBlockchainsWithDerivations(derivationStyle)
addAll(ethereumBlockchains)
}
private fun MutableSet<BlockchainToDerive>.removeUnnecessaryBlockchains() {
val unnecessaryBlockchains = listOf(
Blockchain.BSC, Blockchain.BSCTestnet,
Blockchain.Polygon, Blockchain.PolygonTestnet,
Blockchain.RSK,
Blockchain.Fantom, Blockchain.FantomTestnet,
Blockchain.Avalanche, Blockchain.AvalancheTestnet,
private fun Set<BlockchainToDerive>.removeUnnecessaryBlockchains(
derivationStyle: DerivationStyle?,
): Set<BlockchainToDerive> {
val defaultEthereum = BlockchainToDerive(
blockchain = Blockchain.Ethereum,
derivationPath = Blockchain.Ethereum.derivationPath(derivationStyle),
)
removeAll { it.blockchain in unnecessaryBlockchains }
val addedEthereum = this.firstOrNull { it == defaultEthereum }
return if (addedEthereum != null) {
filterNot { it.derivationPath == defaultEthereum.derivationPath && it.blockchain != Blockchain.Ethereum }
} else {
// Impossible case because Ethereum was added at the last stage
distinctBy(BlockchainToDerive::derivationPath)
}
.toSet()
}
private fun MutableSet<BlockchainToDerive>.addSecondCardanoDerivationIfPresent() {

View file

@ -88,7 +88,7 @@ internal class DefaultUserWalletsListRepository(
.map { wallets.updateWith(it) }
}
.doOnSuccess { loadedWallets ->
userWallets.update { toUpdate ->
userWallets.update { _ ->
val selectedUserWalletId = selectedUserWalletRepository.get()
selectedUserWallet.value = loadedWallets.firstOrNull { it.walletId == selectedUserWalletId }
?: loadedWallets.firstOrNull()?.also {
@ -240,7 +240,7 @@ internal class DefaultUserWalletsListRepository(
}
}
@Suppress("CyclomaticComplexMethod")
@Suppress("CyclomaticComplexMethod", "LongMethod")
override suspend fun unlock(
userWalletId: UserWalletId,
unlockMethod: UserWalletsListRepository.UnlockMethod,
@ -315,7 +315,13 @@ internal class DefaultUserWalletsListRepository(
sensitiveInformationRepository.getAll(listOf(encryptionKey))
.doOnSuccess { sensitiveInfo ->
updateWallets { it?.updateWith(sensitiveInfo) }
updateWallets { wallets ->
// It is necessary to update derivations because when scanning we obtain the missing keys
wallets?.updateWith(
walletIdToSensitiveInformation = sensitiveInfo,
walletIdToDerivedKeys = mapOf(userWallet.walletId to scanResponse.derivedKeys),
)
}
trackSignInEvent(userWallet, Basic.SignedIn.SignInType.Card)
}
.doOnFailure { error -> raise(UnlockWalletError.UnableToUnlock.RawException(error)) }

View file

@ -1,8 +1,10 @@
package com.tangem.tap.domain.userWalletList.utils
import com.tangem.domain.models.scan.KeyWalletPublicKey
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.tap.domain.userWalletList.model.UserWalletPublicInformation
import com.tangem.tap.domain.userWalletList.model.UserWalletSensitiveInformation
@ -72,7 +74,10 @@ internal fun List<UserWalletPublicInformation>.toUserWallets(): List<UserWallet>
return this.map { it.toUserWallet() }
}
internal fun UserWallet.updateWith(sensitiveInformation: UserWalletSensitiveInformation): UserWallet {
internal fun UserWallet.updateWith(
sensitiveInformation: UserWalletSensitiveInformation,
derivedKeys: Map<KeyWalletPublicKey, ExtendedPublicKeysMap>?,
): UserWallet {
return when (this) {
is UserWallet.Cold -> {
copy(
@ -80,6 +85,7 @@ internal fun UserWallet.updateWith(sensitiveInformation: UserWalletSensitiveInfo
card = scanResponse.card.copy(
wallets = requireNotNull(sensitiveInformation.wallets),
),
derivedKeys = derivedKeys ?: scanResponse.derivedKeys,
// visaCardActivationStatus = sensitiveInformation.visaCardActivationStatus,
),
)
@ -92,14 +98,20 @@ internal fun UserWallet.updateWith(sensitiveInformation: UserWalletSensitiveInfo
internal fun List<UserWallet>.updateWith(
walletIdToSensitiveInformation: Map<UserWalletId, UserWalletSensitiveInformation>,
walletIdToDerivedKeys: Map<UserWalletId, Map<KeyWalletPublicKey, ExtendedPublicKeysMap>>? = null,
): List<UserWallet> {
return if (walletIdToSensitiveInformation.isEmpty()) {
this
} else {
this.map { wallet ->
walletIdToSensitiveInformation[wallet.walletId]
?.let(wallet::updateWith)
?: wallet
val sensitiveInformation = walletIdToSensitiveInformation[wallet.walletId]
val derivedKeys = walletIdToDerivedKeys?.get(wallet.walletId)
if (sensitiveInformation != null) {
wallet.updateWith(sensitiveInformation, derivedKeys)
} else {
wallet
}
}
}
}