From e960e9b0681b1a9c57c97e6c402a354accfde34c Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 1 Jun 2026 16:42:27 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../tangem/tap/di/TangemSdkManagerModule.kt | 6 - .../sdk/impl/DefaultTangemSdkManager.kt | 8 +- .../tasks/product/BlockchainToDeriveFinder.kt | 74 ----- .../domain/tasks/product/ScanProductTask.kt | 69 ++--- .../tap/domain/twins/FinalizeTwinTask.kt | 3 - .../DefaultUserWalletsListRepository.kt | 6 +- .../tap/domain/userWalletList/utils/Mapper.kt | 13 +- .../product/BlockchainToDeriveFinderTest.kt | 252 ------------------ 8 files changed, 21 insertions(+), 410 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/domain/tasks/product/BlockchainToDeriveFinder.kt delete mode 100644 app/src/test/kotlin/com/tangem/tap/domain/tasks/product/BlockchainToDeriveFinderTest.kt diff --git a/app/src/main/java/com/tangem/tap/di/TangemSdkManagerModule.kt b/app/src/main/java/com/tangem/tap/di/TangemSdkManagerModule.kt index 61da4b0ea5..37929d7d54 100644 --- a/app/src/main/java/com/tangem/tap/di/TangemSdkManagerModule.kt +++ b/app/src/main/java/com/tangem/tap/di/TangemSdkManagerModule.kt @@ -5,12 +5,10 @@ import com.tangem.core.analytics.api.AnalyticsErrorHandler import com.tangem.domain.card.BuildConfig import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository -import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles import com.tangem.sdk.api.TangemSdkManager import com.tangem.tap.domain.sdk.impl.DefaultTangemSdkManager import com.tangem.tap.domain.sdk.impl.MockTangemSdkManager -import com.tangem.tap.domain.tasks.product.BlockchainToDeriveFinder import com.tangem.tap.domain.tasks.visa.TangemPayGenerateAddressAndSignChallengeTask import com.tangem.tap.domain.tasks.visa.VisaCardActivationTask import com.tangem.tap.domain.visa.VisaCardScanHandler @@ -34,8 +32,6 @@ internal class TangemSdkManagerModule { visaCardActivationTaskFactory: VisaCardActivationTask.Factory, tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory, onboardingV2FeatureToggles: OnboardingV2FeatureToggles, - dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles, - blockchainToDeriveFinder: BlockchainToDeriveFinder, analyticsErrorHandler: AnalyticsErrorHandler, cardRepository: CardRepository, ): TangemSdkManager { @@ -49,8 +45,6 @@ internal class TangemSdkManagerModule { visaCardActivationTaskFactory = visaCardActivationTaskFactory, tangemPayChallengeTaskFactory = tangemPayChallengeTaskFactory, onboardingV2FeatureToggles = onboardingV2FeatureToggles, - dynamicAddressesFeatureToggles = dynamicAddressesFeatureToggles, - blockchainToDeriveFinder = blockchainToDeriveFinder, analyticsErrorHandler = analyticsErrorHandler, cardRepository = cardRepository, ) diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt index 797c3ce6da..4ed0eef964 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt @@ -27,7 +27,6 @@ import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository -import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWalletId @@ -58,6 +57,7 @@ import com.tangem.tap.domain.twins.CreateFirstTwinWalletTask import com.tangem.tap.domain.twins.CreateSecondTwinWalletTask import com.tangem.tap.domain.twins.FinalizeTwinTask import com.tangem.tap.domain.visa.VisaCardScanHandler +import com.tangem.utils.logging.TangemLogger import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.coroutineScope @@ -73,8 +73,6 @@ internal class DefaultTangemSdkManager( private val visaCardActivationTaskFactory: VisaCardActivationTask.Factory, private val tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory, private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles, - private val dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles, - private val blockchainToDeriveFinder: BlockchainToDeriveFinder, private val analyticsErrorHandler: AnalyticsErrorHandler, private val cardRepository: CardRepository, ) : TangemSdkManager { @@ -145,12 +143,10 @@ internal class DefaultTangemSdkManager( runTaskAsyncReturnOnMain( runnable = ScanProductTask( card = null, - blockchainToDeriveFinder = blockchainToDeriveFinder, allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository, visaCardScanHandler = visaCardScanHandler, visaCoroutineScope = this, shouldCheckIsAlreadyActivated = shouldCheckIsAlreadyActivated, - isDynamicAddressesEnabled = dynamicAddressesFeatureToggles.isDynamicAddressesEnabled, onboardingV2FeatureToggles = onboardingV2FeatureToggles, cardRepository = cardRepository, ), @@ -242,6 +238,7 @@ internal class DefaultTangemSdkManager( Analytics.send(event = analyticsEvent.withParams(params.toMap())) } .doOnFailure { tangemError -> + TangemLogger.e("scanProduct failed: code=${tangemError.code}, message=${tangemError.customMessage}") (tangemError as? TangemSdkError)?.let { error -> Analytics.sendErrorEvent(TangemSdkErrorEvent(error)) } @@ -470,7 +467,6 @@ internal class DefaultTangemSdkManager( runnable = FinalizeTwinTask( twinPublicKey = secondCardPublicKey, issuerKeys = issuerKeyPair, - isDynamicAddressesEnabled = dynamicAddressesFeatureToggles.isDynamicAddressesEnabled, cardRepository = cardRepository, ), cardId = cardId, diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/BlockchainToDeriveFinder.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/BlockchainToDeriveFinder.kt deleted file mode 100644 index 9118279751..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/BlockchainToDeriveFinder.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.tangem.tap.domain.tasks.product - -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.data.common.account.WalletAccountsFetcher -import com.tangem.data.wallets.derivations.BlockchainToDerive -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 javax.inject.Inject - -/** - * Finder of blockchains to derive. - * Returns only saved, default or demo blockchains without any additional logic - * (no cardano/ethereum additions or unnecessary blockchain removals). - */ -class BlockchainToDeriveFinder @Inject constructor( - private val walletAccountsFetcher: WalletAccountsFetcher, -) { - - suspend fun find(card: CardDTO): Set { - if (!card.settings.isHDWalletAllowed || card.wallets.isEmpty()) return emptySet() - val userWalletId = UserWalletIdBuilder.card(card).build() ?: return emptySet() - - val derivationStyle = card.derivationStyleProvider.getDerivationStyle() - - val blockchains = getBlockchains(userWalletId).ifEmpty { - if (DemoHelper.isDemoCardId(card.cardId)) { - getDemoBlockchains(derivationStyle, card.cardId) - } else { - getDefaultBlockchains(derivationStyle) - } - } - - return blockchains - } - - private suspend fun getBlockchains(userWalletId: UserWalletId): Set { - 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) ?: return@mapNotNull null - - BlockchainToDerive(blockchain, derivationPath) - } - .toSet() - } - - private fun getDemoBlockchains(derivationStyle: DerivationStyle?, cardId: String): Set { - return DemoHelper.config.getDemoBlockchains(cardId).mapToBlockchainsWithDerivations(derivationStyle) - } - - private fun getDefaultBlockchains(derivationStyle: DerivationStyle?): Set { - val defaultBlockchains = setOf(Blockchain.Bitcoin, Blockchain.Ethereum) - return defaultBlockchains.mapToBlockchainsWithDerivations(derivationStyle) - } - - private fun Set.mapToBlockchainsWithDerivations( - derivationStyle: DerivationStyle?, - ): Set { - return mapNotNullTo(hashSetOf()) { blockchain -> - val derivationPath = blockchain.derivationPath(derivationStyle) ?: return@mapNotNullTo null - BlockchainToDerive(blockchain, derivationPath) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index 78d3151844..7476e9dde4 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -12,8 +12,6 @@ import com.tangem.common.extensions.* import com.tangem.common.tlv.Tlv import com.tangem.common.tlv.TlvDecoder import com.tangem.crypto.CryptoUtils -import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.data.wallets.derivations.MissedDerivationsFinder import com.tangem.domain.card.common.TapWorkarounds.isExcluded import com.tangem.domain.card.common.TapWorkarounds.isNotSupportedInThatRelease import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin @@ -32,25 +30,21 @@ import com.tangem.operations.PreflightReadMode import com.tangem.operations.ScanTask import com.tangem.operations.backup.PrimaryCard import com.tangem.operations.backup.StartPrimaryCardLinkingTask -import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask import com.tangem.operations.files.ReadFilesTask import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand import com.tangem.tap.domain.TapSdkError import com.tangem.tap.domain.visa.VisaCardScanHandler import com.tangem.tap.mainScope -import com.tangem.tap.scope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @Suppress("LongParameterList") internal class ScanProductTask( private val card: Card?, - private val blockchainToDeriveFinder: BlockchainToDeriveFinder?, private val visaCardScanHandler: VisaCardScanHandler?, private val visaCoroutineScope: CoroutineScope?, private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles?, private val shouldCheckIsAlreadyActivated: Boolean, - private val isDynamicAddressesEnabled: Boolean, private val cardRepository: CardRepository, override val allowsRequestAccessCodeFromRepository: Boolean = false, ) : CardSessionRunnable { @@ -80,8 +74,6 @@ internal class ScanProductTask( session = session, cardDto = cardDto, scanWalletProcessor = ScanWalletProcessor( - blockchainToDeriveFinder = blockchainToDeriveFinder, - isDynamicAddressesEnabled = isDynamicAddressesEnabled, cardRepository = cardRepository, ), callback = callback, @@ -92,8 +84,6 @@ internal class ScanProductTask( val commandProcessor = when { cardDto.isTangemTwins -> ScanTwinProcessor() else -> ScanWalletProcessor( - blockchainToDeriveFinder = blockchainToDeriveFinder, - isDynamicAddressesEnabled = isDynamicAddressesEnabled, cardRepository = cardRepository, ) } @@ -102,8 +92,8 @@ internal class ScanProductTask( is CompletionResult.Success -> ScanTask().run(session) { scanTaskResult -> when (scanTaskResult) { is CompletionResult.Success -> { - // it needed because processorResult.data.card doesn't contains attestation result - // and CardWallet.derivedKeys + // It's needed because processorResult.data.card doesn't contain the attestation + // result or the existing CardWallet.derivedKeys read from the card. val processorScanResponseWithNewCard = processorResult.data.copy( card = CardDTO(scanTaskResult.data), ) @@ -176,8 +166,6 @@ internal class ScanProductTask( } private class ScanWalletProcessor( - private val blockchainToDeriveFinder: BlockchainToDeriveFinder?, - private val isDynamicAddressesEnabled: Boolean, private val cardRepository: CardRepository, ) : ProductCommandProcessor { @@ -281,48 +269,34 @@ private class ScanWalletProcessor( when (linkingResult) { is CompletionResult.Success -> { primaryCard = linkingResult.data - deriveKeysIfNeeded(card, session, callback) + completeScan(card, session, callback) } is CompletionResult.Failure -> { - deriveKeysIfNeeded(card, session, callback) + completeScan(card, session, callback) } } } } else { - deriveKeysIfNeeded(card, session, callback) + completeScan(card, session, callback) } } } - private fun deriveKeysIfNeeded( + // Keys are no longer derived during scan: default derivations are created up front in + // CreateProductWalletTask, and derivations for additional tokens are handled by + // DefaultColdMapDerivationsRepository when the user explicitly adds a token. + private fun completeScan( card: CardDTO, session: CardSession, callback: (result: CompletionResult) -> Unit, ) { - val productType = getWalletProductType(card) - scope.launch { - val scanResponse = ScanResponse( - card = card, - productType = productType, - walletData = session.environment.walletData, - primaryCard = primaryCard, - ) - val derivations = collectDerivations(card, scanResponse) - if (derivations.isEmpty() || !card.settings.isHDWalletAllowed) { - callback(CompletionResult.Success(scanResponse)) - return@launch - } - - DeriveMultipleWalletPublicKeysTask(derivations).run(session) { result -> - when (result) { - is CompletionResult.Success -> { - val response = scanResponse.copy(derivedKeys = result.data.entries) - callback(CompletionResult.Success(response)) - } - is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) - } - } - } + val scanResponse = ScanResponse( + card = card, + productType = getWalletProductType(card), + walletData = session.environment.walletData, + primaryCard = primaryCard, + ) + callback(CompletionResult.Success(scanResponse)) } private fun getWalletProductType(card: CardDTO): ProductType { @@ -334,17 +308,6 @@ private class ScanWalletProcessor( else -> ProductType.Wallet } } - - private suspend fun collectDerivations( - card: CardDTO, - scanResponse: ScanResponse, - ): Map> { - val blockchains = blockchainToDeriveFinder - ?.find(card) - ?: return emptyMap() - - return MissedDerivationsFinder(scanResponse, isDynamicAddressesEnabled).findByBlockchainsToDerive(blockchains) - } } @Suppress("MagicNumber") diff --git a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt index c2c044dd95..cba0726923 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt @@ -13,7 +13,6 @@ import com.tangem.tap.domain.tasks.product.ScanProductTask class FinalizeTwinTask( private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair, - private val isDynamicAddressesEnabled: Boolean, private val cardRepository: CardRepository, ) : CardSessionRunnable { @@ -31,11 +30,9 @@ class FinalizeTwinTask( is CompletionResult.Success -> ScanProductTask( card = readResult.data, - blockchainToDeriveFinder = null, visaCardScanHandler = null, visaCoroutineScope = null, shouldCheckIsAlreadyActivated = false, - isDynamicAddressesEnabled = isDynamicAddressesEnabled, onboardingV2FeatureToggles = null, cardRepository = cardRepository, ).run(session, callback) diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt index 411869a6cd..0cab26cf50 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt @@ -325,11 +325,7 @@ internal class DefaultUserWalletsListRepository( sensitiveInformationRepository.getAll(listOf(encryptionKey)) .doOnSuccess { 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), - ) + wallets?.updateWith(walletIdToSensitiveInformation = sensitiveInfo) } trackSignInEvent(userWallet, AnalyticsParam.SignInType.Card) } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/Mapper.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/Mapper.kt index 51aefb10ab..4e14c06473 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/Mapper.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/Mapper.kt @@ -1,10 +1,8 @@ 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 @@ -74,10 +72,7 @@ internal fun List.toUserWallets(): List return this.map { it.toUserWallet() } } -internal fun UserWallet.updateWith( - sensitiveInformation: UserWalletSensitiveInformation, - derivedKeys: Map?, -): UserWallet { +internal fun UserWallet.updateWith(sensitiveInformation: UserWalletSensitiveInformation): UserWallet { return when (this) { is UserWallet.Cold -> { copy( @@ -85,7 +80,6 @@ internal fun UserWallet.updateWith( card = scanResponse.card.copy( wallets = requireNotNull(sensitiveInformation.wallets), ), - derivedKeys = derivedKeys ?: scanResponse.derivedKeys, // visaCardActivationStatus = sensitiveInformation.visaCardActivationStatus, ), ) @@ -98,17 +92,14 @@ internal fun UserWallet.updateWith( internal fun List.updateWith( walletIdToSensitiveInformation: Map, - walletIdToDerivedKeys: Map>? = null, ): List { return if (walletIdToSensitiveInformation.isEmpty()) { this } else { this.map { wallet -> val sensitiveInformation = walletIdToSensitiveInformation[wallet.walletId] - val derivedKeys = walletIdToDerivedKeys?.get(wallet.walletId) - if (sensitiveInformation != null) { - wallet.updateWith(sensitiveInformation, derivedKeys) + wallet.updateWith(sensitiveInformation) } else { wallet } diff --git a/app/src/test/kotlin/com/tangem/tap/domain/tasks/product/BlockchainToDeriveFinderTest.kt b/app/src/test/kotlin/com/tangem/tap/domain/tasks/product/BlockchainToDeriveFinderTest.kt deleted file mode 100644 index 5be7a5b371..0000000000 --- a/app/src/test/kotlin/com/tangem/tap/domain/tasks/product/BlockchainToDeriveFinderTest.kt +++ /dev/null @@ -1,252 +0,0 @@ -package com.tangem.tap.domain.tasks.product - -import com.google.common.truth.Truth -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.derivation.DerivationStyle -import com.tangem.blockchainsdk.utils.toNetworkId -import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.data.common.account.WalletAccountsFetcher -import com.tangem.data.wallets.derivations.BlockchainToDerive -import com.tangem.datasource.api.tangemTech.models.UserTokensResponse -import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse -import com.tangem.datasource.api.tangemTech.models.account.WalletAccountDTO -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.models.wallet.UserWalletId -import io.mockk.* -import kotlinx.coroutines.test.runTest -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance - -/** -[REDACTED_AUTHOR] - */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class BlockchainToDeriveFinderTest { - - private val walletAccountsFetcher = mockk() - private val finder = BlockchainToDeriveFinder( - walletAccountsFetcher = walletAccountsFetcher, - ) - - @AfterEach - fun tearDown() { - clearMocks(walletAccountsFetcher) - } - - @Test - fun `GIVEN card is not HD wallet THEN return empty set`() = runTest { - // Arrange - val card = mockk { - every { this@mockk.settings.isHDWalletAllowed } returns false - } - - // Act - val actual = finder.find(card) - - // Assert - Truth.assertThat(actual).isEmpty() - } - - @Test - fun `GIVEN card has empty wallets THEN return empty set`() = runTest { - // Arrange - val card = mockk { - every { this@mockk.settings.isHDWalletAllowed } returns true - every { this@mockk.wallets } returns emptyList() - } - - // Act - val actual = finder.find(card) - - // Assert - Truth.assertThat(actual).isEmpty() - } - - @Test - fun `GIVEN saved bitcoin THEN return only bitcoin`() = runTest { - // Arrange - val card = createCardDTO() - - val response = createResponse(Blockchain.Bitcoin) - coEvery { walletAccountsFetcher.getSaved(userWalletId) } returns response - - // Act - val actual = finder.find(card) - - // Assert - val expected = setOf( - createExpected(Blockchain.Bitcoin), - ) - - Truth.assertThat(actual).containsExactlyElementsIn(expected) - - coVerify(exactly = 1) { walletAccountsFetcher.getSaved(userWalletId) } - } - - @Test - fun `GIVEN empty store and common demo card THEN return demo blockchains`() = runTest { - // Arrange - val demoCardId = "AC01000000045754" - val card = createCardDTO(cardId = demoCardId) - - coEvery { walletAccountsFetcher.getSaved(userWalletId) } returns null - - // Act - val actual = finder.find(card) - - // Assert - val expected = setOf( - createExpected(Blockchain.Bitcoin), - createExpected(Blockchain.Ethereum), - createExpected(Blockchain.Dogecoin), - createExpected(Blockchain.Solana), - ) - - Truth.assertThat(actual).containsExactlyElementsIn(expected) - - coVerify(exactly = 1) { walletAccountsFetcher.getSaved(userWalletId) } - } - - @Test - fun `GIVEN empty store and DE00 demo card THEN return demo blockchains`() = runTest { - // Arrange - val demoCardId = "DE00" - val card = createCardDTO(cardId = demoCardId) - - coEvery { walletAccountsFetcher.getSaved(userWalletId) } returns null - - // Act - val actual = finder.find(card) - - // Assert - val expected = setOf( - createExpected(Blockchain.Bitcoin), - createExpected(Blockchain.Ethereum), - createExpected(Blockchain.Dogecoin), - ) - - Truth.assertThat(actual).containsExactlyElementsIn(expected) - - coVerify(exactly = 1) { walletAccountsFetcher.getSaved(userWalletId) } - } - - @Test - fun `GIVEN empty store THEN return default blockchains`() = runTest { - // Arrange - val card = createCardDTO() - - coEvery { walletAccountsFetcher.getSaved(userWalletId) } returns null - - // Act - val actual = finder.find(card) - - // Assert - val expected = setOf( - createExpected(Blockchain.Bitcoin), - createExpected(Blockchain.Ethereum), - ) - - Truth.assertThat(actual).containsExactlyElementsIn(expected) - - coVerify(exactly = 1) { walletAccountsFetcher.getSaved(userWalletId) } - } - - @Test - fun `GIVEN saved cardano THEN return only cardano`() = runTest { - // Arrange - val card = createCardDTO() - - val response = createResponse(Blockchain.Cardano) - coEvery { walletAccountsFetcher.getSaved(userWalletId) } returns response - - // Act - val actual = finder.find(card) - - // Assert - val expected = setOf( - createExpected(Blockchain.Cardano), - ) - - Truth.assertThat(actual).containsExactlyElementsIn(expected) - - coVerify(exactly = 1) { walletAccountsFetcher.getSaved(userWalletId) } - } - - @Test - fun `GIVEN saved eth-like blockchains THEN return all saved blockchains without filtering`() = runTest { - // Arrange - val card = createCardDTO() - - val blockchains = listOf(Blockchain.Ethereum, Blockchain.BSC, Blockchain.Polygon) - - val response = createResponse(*blockchains.toTypedArray()) - - coEvery { walletAccountsFetcher.getSaved(userWalletId) } returns response - - // Act - val actual = finder.find(card) - - // Assert - val expected = blockchains.mapTo(hashSetOf(), ::createExpected) - - Truth.assertThat(actual).containsExactlyElementsIn(expected) - - coVerify(exactly = 1) { walletAccountsFetcher.getSaved(userWalletId) } - } - - private fun createCardDTO(cardId: String = "0001", batchId: String = "AC10"): CardDTO { - val wallet = mockk { - every { this@mockk.publicKey } returns byteArrayOf(0) - } - - return mockk { - every { this@mockk.cardId } returns cardId - every { this@mockk.batchId } returns batchId - every { this@mockk.settings.isHDWalletAllowed } returns true - every { this@mockk.settings.isKeysImportAllowed } returns true - every { this@mockk.firmwareVersion } returns CardDTO.FirmwareVersion( - major = 6, - minor = 33, - patch = 0, - type = com.tangem.common.card.FirmwareVersion.FirmwareType.Release, - ) - every { this@mockk.wallets } returns listOf(wallet) - } - } - - private fun createResponse(vararg blockchains: Blockchain): GetWalletAccountsResponse { - val tokens = blockchains.map { blockchain -> - mockk { - every { this@mockk.networkId } returns blockchain.toNetworkId() - every { this@mockk.derivationPath } returns blockchain.getDerivationPath().rawPath - every { this@mockk.contractAddress } returns null - } - } - - val account = mockk { - every { this@mockk.tokens } returns tokens - } - - return mockk { - every { this@mockk.accounts } returns listOf(account) - } - } - - private fun createExpected( - blockchain: Blockchain, - derivationPath: DerivationPath = blockchain.getDerivationPath(), - ): BlockchainToDerive { - return BlockchainToDerive(blockchain = blockchain, derivationPath = derivationPath) - } - - private fun Blockchain.getDerivationPath(): DerivationPath { - return derivationPath(DerivationStyle.V3)!! - } - - private companion object { - - // for byteArrayOf(0) - val userWalletId = UserWalletId("41448576B8DA24C7D8F5F0F79863D20D7D8312A7F9E50D3248304136DDB7AAD7") - } -}