From b3957a170c8e3caa03ccf23d396e68d56c99544b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 13 Dec 2021 14:41:32 +0300 Subject: [PATCH 01/10] Updated on 2026-08-14 --- .../java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index 258131199b..b16fbe21c7 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -94,8 +94,8 @@ private val globalMiddlewareHandler: Middleware = { dispatch, appState action.shouldDeriveWC, action.messageResId ) - store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) withMainContext { + store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) when (result) { is CompletionResult.Success -> { tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data) From 2c35a0b39ac0754b78e78576f99a97d75e97cf4c Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 13 Dec 2021 15:44:40 +0300 Subject: [PATCH 02/10] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt | 3 +++ .../java/com/tangem/tap/features/home/redux/HomeMiddleware.kt | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index f28523564f..fae5ee9060 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -13,6 +13,7 @@ import com.tangem.tap.common.transitions.FrontCardExitTransition import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.home.redux.HomeState import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment +import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_onboarding_main.* @@ -36,6 +37,8 @@ class HomeFragment : BaseOnboardingFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + store.dispatch(BackupAction.CheckForUnfinishedBackup) + toolbar.hide() val shareTransition = FragmentShareTransition( listOf( diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index b93b3a2e7e..341aa3e64b 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -15,7 +15,6 @@ import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE import com.tangem.tap.features.onboarding.OnboardingHelper import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep -import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.tap.preferencesStorage import com.tangem.tap.scope @@ -39,7 +38,6 @@ private val homeMiddleware: Middleware = { dispatch, state -> store.dispatch(GlobalAction.RestoreAppCurrency) store.dispatch(GlobalAction.GetMoonPayStatus) store.dispatch(HomeAction.SetTermsOfUseState(preferencesStorage.wasDisclaimerAccepted())) - store.dispatch(BackupAction.CheckForUnfinishedBackup) } is HomeAction.ShouldScanCardOnResume -> { if (action.shouldScanCard) { From f3d868577657090df4adb8c25f200a721b196bc2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 13 Dec 2021 21:38:37 +0300 Subject: [PATCH 03/10] Updated on 2026-08-14 --- .../com/tangem/tap/domain/TangemSdkManager.kt | 5 +- .../tasks/product/CreateProductWalletTask.kt | 109 +++++++++++++----- .../domain/tasks/product/ScanProductTask.kt | 49 +++++++- .../note/redux/OnboardingNoteMiddleware.kt | 2 +- .../redux/OnboardingOtherCardsMiddleware.kt | 4 +- .../wallet/redux/OnboardingWalletAction.kt | 2 + .../redux/OnboardingWalletMiddleware.kt | 13 ++- .../wallet/redux/OnboardingWalletReducer.kt | 3 +- .../tap/persistence/UsedCardsPrefStorage.kt | 4 + 9 files changed, 149 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 7131feb7b7..af6b04b1e9 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -1,6 +1,7 @@ package com.tangem.tap.domain import CreateProductWalletTask +import CreateProductWalletTaskResponse import android.content.Context import com.tangem.Message import com.tangem.TangemSdk @@ -48,7 +49,9 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co .also { sendScanFailuresToAnalytics(analyticsHandler, it) } } - suspend fun createProductWallet(scanResponse: ScanResponse): CompletionResult { + suspend fun createProductWallet( + scanResponse: ScanResponse + ): CompletionResult { return runTaskAsync( CreateProductWalletTask(scanResponse.productType), scanResponse.card.cardId, diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt index 530b53ac4c..99e8cd0f62 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt @@ -1,33 +1,42 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.CompletionResult import com.tangem.common.card.Card -import com.tangem.common.card.CardWallet import com.tangem.common.card.EllipticCurve import com.tangem.common.card.FirmwareVersion import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard +import com.tangem.common.hdWallet.ExtendedPublicKey +import com.tangem.operations.CommandResponse +import com.tangem.operations.backup.PrimaryCard +import com.tangem.operations.backup.StartPrimaryCardLinkingTask import com.tangem.operations.derivation.DeriveWalletPublicKeysTask -import com.tangem.operations.derivation.ExtendedPublicKeyList import com.tangem.operations.wallet.CreateWalletResponse import com.tangem.operations.wallet.CreateWalletTask import com.tangem.tap.common.extensions.toMapKey import com.tangem.tap.domain.ProductType import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain import com.tangem.tap.domain.tasks.product.CreateWalletsTask +import com.tangem.tap.domain.tasks.product.KeyWalletPublicKey import com.tangem.tap.domain.tasks.product.ProductCommandProcessor import com.tangem.tap.domain.tasks.product.getCurvesForNonCreatedWallets -import com.tangem.tap.store -/** -[REDACTED_AUTHOR] - */ + +data class CreateProductWalletTaskResponse( + val card: Card, + val derivedKeys: Map> = mapOf(), + val primaryCard: PrimaryCard? = null +) : CommandResponse + class CreateProductWalletTask( private val type: ProductType, -) : CardSessionRunnable { +) : CardSessionRunnable { - override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + override fun run( + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { val card = session.environment.card.guard { callback(CompletionResult.Failure(TangemSdkError.CardError())) return @@ -41,7 +50,15 @@ class CreateProductWalletTask( } commandProcessor.proceed(card, session) { when (it) { - is CompletionResult.Success -> callback(CompletionResult.Success(session.environment.card!!)) + is CompletionResult.Success -> { + val result = when (commandProcessor) { + is CreateWalletTangemWallet -> { + it.data as CreateProductWalletTaskResponse + } + else -> CreateProductWalletTaskResponse(card = session.environment.card!!) + } + callback(CompletionResult.Success(result)) + } is CompletionResult.Failure -> callback(CompletionResult.Failure(it.error)) } } @@ -79,44 +96,74 @@ private class CreateWalletTangemNote : ProductCommandProcessor { +private class CreateWalletTangemWallet : ProductCommandProcessor { + + private var primaryCard: PrimaryCard? = null + private var createWalletResponse: CreateWalletResponse? = null override fun proceed( card: Card, session: CardSession, - callback: (result: CompletionResult) -> Unit, + callback: (result: CompletionResult) -> Unit, ) { val supportedCurves = setOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519) val curves = card.getCurvesForNonCreatedWallets().intersect(supportedCurves).toList() CreateWalletsTask(curves).run(session) { result -> when (result) { is CompletionResult.Success -> { - val response = result.data.createWalletResponses[0] - val derivationPaths = listOf(Blockchain.Bitcoin, Blockchain.Ethereum) - .mapNotNull { it.derivationPath() } - - DeriveWalletPublicKeysTask(response.wallet.publicKey, derivationPaths).run(session) { - when (it) { - is CompletionResult.Success -> { - updateDerivedKeys(response.wallet, it.data) - callback(CompletionResult.Success(response)) - } - is CompletionResult.Failure -> callback(CompletionResult.Failure(it.error)) - } - } + createWalletResponse = result.data.createWalletResponses[0] + linkPrimaryCard(card, session, callback) } is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } } -} -//TODO: updating derived wallet public keys - make it better later -private fun updateDerivedKeys(wallet: CardWallet, derivedKeys: ExtendedPublicKeyList) { - val onboardingManager = store.state.globalState.onboardingState.onboardingManager ?: return - onboardingManager.scanResponse = onboardingManager.scanResponse.copy( - derivedKeys = mapOf(wallet.publicKey.toMapKey() to derivedKeys) - ) + private fun linkPrimaryCard( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit, + ) { + StartPrimaryCardLinkingTask().run(session) { result -> + when (result) { + is CompletionResult.Success -> { + primaryCard = result.data + deriveKeys(card, session, callback) + } + is CompletionResult.Failure -> { + callback(CompletionResult.Failure(result.error)) + } + } + } + } + + private fun deriveKeys( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit, + ) { + val derivationPaths = listOf(Blockchain.Bitcoin, Blockchain.Ethereum) + .mapNotNull { it.derivationPath() } + val response = createWalletResponse.guard { + callback(CompletionResult.Failure(TangemSdkError.UnknownError())) + return + } + + DeriveWalletPublicKeysTask(response.wallet.publicKey, derivationPaths) + .run(session) { result -> + when (result) { + is CompletionResult.Success -> { + val derivedKeys = mapOf(response.wallet.publicKey.toMapKey() to result.data) + callback(CompletionResult.Success(CreateProductWalletTaskResponse( + card = session.environment.card!!, + derivedKeys = derivedKeys, + primaryCard = primaryCard + ))) + } + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } } private class CreateWalletOtherCards : ProductCommandProcessor { 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 4aa99403d2..278e424a2a 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 @@ -20,6 +20,8 @@ import com.tangem.operations.CommandResponse import com.tangem.operations.PreflightReadMode import com.tangem.operations.PreflightReadTask import com.tangem.operations.ScanTask +import com.tangem.operations.backup.PrimaryCard +import com.tangem.operations.backup.StartPrimaryCardLinkingTask import com.tangem.operations.derivation.DeriveWalletPublicKeysTask import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand import com.tangem.tap.common.extensions.ByteArrayKey @@ -33,13 +35,15 @@ import com.tangem.tap.domain.TapWorkarounds.isNotSupportedInThatRelease import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tap.domain.tokens.CurrenciesRepository import com.tangem.tap.domain.twins.TwinsHelper +import com.tangem.tap.preferencesStorage data class ScanResponse( val card: Card, val productType: ProductType, val walletData: WalletData?, val secondTwinPublicKey: String? = null, - val derivedKeys: Map> = mapOf() + val derivedKeys: Map> = mapOf(), + val primaryCard: PrimaryCard? = null ) : CommandResponse { fun getBlockchain(): Blockchain { @@ -134,16 +138,48 @@ private class ScanWalletProcessor( private val shouldDeriveWC: Boolean ) : ProductCommandProcessor { + var primaryCard: PrimaryCard? = null + override fun proceed( card: Card, session: CardSession, callback: (result: CompletionResult) -> Unit + ) { + val activationIsFinished = preferencesStorage.usedCardsPrefStorage.isActivationFinished(card.cardId) + + if (card.backupStatus?.isActive != true && !activationIsFinished && card.wallets.isNotEmpty()) { + StartPrimaryCardLinkingTask().run(session) { linkingResult -> + when (linkingResult) { + is CompletionResult.Success -> { + primaryCard = linkingResult.data + deriveKeysIfNeeded(card, session, callback) + } + is CompletionResult.Failure -> { + deriveKeysIfNeeded(card, session, callback) + } + } + } + } else { + deriveKeysIfNeeded(card, session, callback) + } + + } + + private fun deriveKeysIfNeeded( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit ) { val derivationPaths = collectDerivationPaths(card)?.distinct() val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } if (derivationPaths.isNullOrEmpty() || wallet == null || wallet.chainCode == null) { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Wallet, session.environment.walletData))) + callback(CompletionResult.Success(ScanResponse( + card = card, + productType = ProductType.Wallet, + walletData = session.environment.walletData, + primaryCard = primaryCard + ))) return } @@ -152,10 +188,11 @@ private class ScanWalletProcessor( is CompletionResult.Success -> { val derivedKeys = mapOf(wallet.publicKey.toMapKey() to result.data) val response = ScanResponse( - card, - ProductType.Wallet, - session.environment.walletData, - derivedKeys = derivedKeys + card = card, + productType = ProductType.Wallet, + walletData = session.environment.walletData, + derivedKeys = derivedKeys, + primaryCard = primaryCard ) callback(CompletionResult.Success(response)) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 36e9c93cc1..6a8cc146b7 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -80,7 +80,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { withMainContext { when (result) { is CompletionResult.Success -> { - val updatedResponse = scanResponse.copy(card = result.data) + val updatedResponse = scanResponse.copy(card = result.data.card) onboardingManager.scanResponse = updatedResponse onboardingManager.activationStarted(updatedResponse.card.cardId) store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.TopUpWallet)) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt index f3419e67fa..29ca1fbb3b 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -67,7 +67,9 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) { withMainContext { when (result) { is CompletionResult.Success -> { - val updatedResponse = onboardingManager.scanResponse.copy(card = result.data) + val updatedResponse = onboardingManager.scanResponse.copy( + card = result.data.card + ) onboardingManager.scanResponse = updatedResponse onboardingManager.activationStarted(updatedResponse.card.cardId) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt index b3056d7bdd..d900eaf35f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt @@ -25,6 +25,8 @@ sealed class BackupAction : Action { object IntroduceBackup : BackupAction() object StartBackup : BackupAction() object DismissBackup : BackupAction() + + object StartAddingPrimaryCard : BackupAction() object ScanPrimaryCard : BackupAction() object CheckForUnfinishedBackup : BackupAction() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index f9465751c3..14a8788714 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -76,7 +76,11 @@ private fun handleWalletAction(action: Action) { is CompletionResult.Success -> { //here we must use updated scanResponse after createWallet & derivation val updatedResponse = - globalState.onboardingState.onboardingManager.scanResponse.copy(card = result.data) + globalState.onboardingState.onboardingManager.scanResponse.copy( + card = result.data.card, + derivedKeys = result.data.derivedKeys, + primaryCard = result.data.primaryCard + ) onboardingManager.scanResponse = updatedResponse onboardingManager.activationStarted(updatedResponse.card.cardId) store.dispatch(OnboardingWalletAction.ProceedBackup) @@ -164,6 +168,13 @@ private fun handleBackupAction(action: BackupAction) { when (action) { is BackupAction.StartBackup -> { backupService.discardSavedBackup() + val primaryCard = scanResponse?.primaryCard + if (primaryCard != null) { + backupService.setPrimaryCard(primaryCard) + store.dispatch(BackupAction.StartAddingBackupCards) + } else { + store.dispatch(BackupAction.StartAddingPrimaryCard) + } } is BackupAction.ScanPrimaryCard -> { backupService.readPrimaryCard(cardId = card?.cardId) { result -> diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt index 3947cd5d3f..09aad69d48 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt @@ -44,7 +44,7 @@ class BackupReducer { canSkipBackup = state.canSkipBackup ) - BackupAction.StartBackup -> state.copy(backupStep = BackupStep.ScanOriginCard) + BackupAction.StartAddingPrimaryCard -> state.copy(backupStep = BackupStep.ScanOriginCard) BackupAction.StartAddingBackupCards -> { state.copy(backupStep = BackupStep.AddBackupCards) @@ -127,6 +127,7 @@ class BackupReducer { BackupAction.DiscardBackup -> state BackupAction.ResumeBackup -> state BackupAction.DiscardSavedBackup -> state + BackupAction.StartBackup -> state } } diff --git a/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt index 17238689f5..9d8d877f32 100644 --- a/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt @@ -44,6 +44,10 @@ class UsedCardsPrefStorage( save(foundItem, restoredList) } + fun isActivationFinished(cardId: String): Boolean { + return !(findCardInfo(cardId)?.isActivationStarted ?: true) + } + fun activationIsStarted(cardId: String): Boolean { return findCardInfo(cardId)?.isActivationStarted ?: false } From deed5020b641c41afc82a14ce14d72bb5adf9dbe Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 13 Dec 2021 21:46:20 +0300 Subject: [PATCH 04/10] Updated on 2026-08-14 --- .../com/tangem/tap/domain/topup/TradeCryptoHelper.kt | 3 +++ .../redux/middlewares/TradeCryptoMiddleware.kt | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt b/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt index c8599f6d4c..ec92f9f0fe 100644 --- a/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt @@ -38,6 +38,8 @@ class TradeCryptoHelper { private const val TRANSACTION_RECEIPT_PATH = "transaction_receipt?transactionId=" + private const val BASE_CURRENCY_USD = "USD" + fun getUrl( action: Action, blockchain: Blockchain?, @@ -56,6 +58,7 @@ class TradeCryptoHelper { baseUrl = BASE_URL_BUY originalQuery = API_KEY_PATH + apiKey.urlEncode() + CURRENCY_PATH + cryptoCurrencyName.urlEncode() + + BASE_CURRENCY_PATH + BASE_CURRENCY_USD.urlEncode() + WALLET_ADDRESS_PATH + walletAddress.urlEncode() + REDIRECT_URL_PATH + REDIRECT_URL_BUY.urlEncode() } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 2b2087dd25..2cd5627545 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -58,12 +58,12 @@ class TradeCryptoMiddleware { } val url = TradeCryptoHelper.getUrl( - exchangeAction, - currency?.blockchain, - currencySymbol, - defaultAddress, - config.moonPayApiKey, - config.moonPayApiSecretKey + action = exchangeAction, + blockchain = currency?.blockchain, + cryptoCurrencyName = currencySymbol, + walletAddress = defaultAddress, + apiKey = config.moonPayApiKey, + secretKey = config.moonPayApiSecretKey ) Timber.d("Moonpay $exchangeAction URL: $url") store.dispatchOnMain(NavigationAction.OpenUrl(url)) From 9a2b966ca63dd783270b6f76863dfd12649c9e01 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 14 Dec 2021 19:35:10 +0300 Subject: [PATCH 05/10] Updated on 2026-08-14 --- .../details/ui/DetailsSecurityFragment.kt | 4 ++++ .../res/layout/fragment_details_security.xml | 17 +++++++++++++++++ .../main/res/values/strings_untranslated.xml | 1 + 3 files changed, 22 insertions(+) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsSecurityFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsSecurityFragment.kt index fe017dfec7..8ef33711e7 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsSecurityFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsSecurityFragment.kt @@ -5,6 +5,7 @@ import android.view.View import androidx.activity.OnBackPressedCallback import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater +import com.tangem.common.card.Card import com.tangem.tap.common.extensions.show import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.features.details.redux.DetailsAction @@ -72,6 +73,9 @@ class DetailsSecurityFragment : Fragment(R.layout.fragment_details_security), ?: EnumSet.noneOf(SecurityOption::class.java) ) } + tv_access_code_unavailable_disclaimer.show( + state.scanResponse?.card?.backupStatus == Card.BackupStatus.NoBackup + ) } private fun selectSecurityOption(securityOption: SecurityOption?) { diff --git a/app/src/main/res/layout/fragment_details_security.xml b/app/src/main/res/layout/fragment_details_security.xml index 3e232e82d8..98f442ad29 100644 --- a/app/src/main/res/layout/fragment_details_security.xml +++ b/app/src/main/res/layout/fragment_details_security.xml @@ -183,6 +183,23 @@ app:layout_constraintBottom_toBottomOf="@id/tv_access_code_description" app:layout_constraintTop_toTopOf="@id/tv_access_code_title" /> + + + Reset to factory settings This action is irreversible. If, after resetting the card, someone sends funds to it, then you will not be able to withdraw them. + Access code is available only for cards with a backup. From 3b05c5db10d1d0de1432b2932190ee4e262ca0d0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 15 Dec 2021 15:14:17 +0300 Subject: [PATCH 06/10] Updated on 2026-08-14 --- .../java/com/tangem/tap/domain/ProductType.kt | 3 +- .../com/tangem/tap/domain/TangemSdkManager.kt | 2 +- .../com/tangem/tap/domain/TapWorkarounds.kt | 3 +- .../tasks/product/CreateProductWalletTask.kt | 76 +++---- .../domain/tasks/product/ScanProductTask.kt | 193 ++++++++++++------ .../tap/domain/twins/FinalizeTwinTask.kt | 3 +- .../features/details/redux/DetailsReducer.kt | 6 +- .../features/onboarding/OnboardingHelper.kt | 7 +- .../wallet/redux/OnboardingWalletAction.kt | 2 +- .../redux/OnboardingWalletMiddleware.kt | 9 +- .../wallet/redux/OnboardingWalletReducer.kt | 5 +- .../wallet/redux/OnboardingWalletState.kt | 3 +- .../products/wallet/ui/BackupCardsWidget.kt | 27 ++- .../wallet/ui/OnboardingWalletFragment.kt | 42 ++-- .../features/tokens/redux/TokensMiddleware.kt | 4 +- .../middlewares/MultiWalletMiddleware.kt | 4 +- 16 files changed, 240 insertions(+), 149 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/ProductType.kt b/app/src/main/java/com/tangem/tap/domain/ProductType.kt index 07cbea8905..4f57e8949d 100644 --- a/app/src/main/java/com/tangem/tap/domain/ProductType.kt +++ b/app/src/main/java/com/tangem/tap/domain/ProductType.kt @@ -4,5 +4,6 @@ package com.tangem.tap.domain [REDACTED_AUTHOR] */ enum class ProductType { - Note, Wallet, Twins, Other + Note, Twins, Wallet + } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index af6b04b1e9..455a6b973d 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -45,7 +45,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header)) - return runTaskAsyncReturnOnMain(ScanProductTask(null, currenciesRepository, shouldDeriveWC), null, message) + return runTaskAsyncReturnOnMain(ScanProductTask(null, currenciesRepository), null, message) .also { sendScanFailuresToAnalytics(analyticsHandler, it) } } diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index 45e19eb81a..ffc8df824c 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -35,8 +35,7 @@ object TapWorkarounds { @Deprecated("Use ScanResponse.isTangemNote") fun isTangemNote(card: Card): Boolean = tangemNoteBatches.contains(card.batchId) - @Deprecated("Use ScanResponse.isTangemWallet") - fun isTangemWallet(card: Card): Boolean = tangemWalletBatches.contains(card.batchId) + fun isTangemWalletBatch(card: Card): Boolean = tangemWalletBatches.contains(card.batchId) fun getTangemNoteBlockchain(card: Card): Blockchain? = tangemNoteBatches[card.batchId] diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt index 99e8cd0f62..25e3d98836 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt @@ -2,7 +2,6 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.CompletionResult import com.tangem.common.card.Card import com.tangem.common.card.EllipticCurve -import com.tangem.common.card.FirmwareVersion import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.TangemSdkError @@ -45,8 +44,7 @@ class CreateProductWalletTask( val commandProcessor = when (type) { ProductType.Note -> CreateWalletTangemNote() ProductType.Twins -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet") - ProductType.Wallet -> CreateWalletTangemWallet() - else -> CreateWalletOtherCards() + else -> CreateWalletTangemWallet() } commandProcessor.proceed(card, session) { when (it) { @@ -106,13 +104,27 @@ private class CreateWalletTangemWallet : ProductCommandProcessor) -> Unit, ) { - val supportedCurves = setOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519) - val curves = card.getCurvesForNonCreatedWallets().intersect(supportedCurves).toList() + val curves = card.getCurvesForNonCreatedWallets() CreateWalletsTask(curves).run(session) { result -> when (result) { is CompletionResult.Success -> { createWalletResponse = result.data.createWalletResponses[0] - linkPrimaryCard(card, session, callback) + when { + card.settings.isBackupAllowed -> { + linkPrimaryCard(session, callback) + } + card.settings.isHDWalletAllowed -> { + deriveKeys(session, callback) + } + else -> { + callback( + CompletionResult.Success( + CreateProductWalletTaskResponse(card = session.environment.card!!) + ) + ) + } + } + } is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } @@ -120,7 +132,6 @@ private class CreateWalletTangemWallet : ProductCommandProcessor) -> Unit, ) { @@ -128,7 +139,7 @@ private class CreateWalletTangemWallet : ProductCommandProcessor { primaryCard = result.data - deriveKeys(card, session, callback) + deriveKeys(session, callback) } is CompletionResult.Failure -> { callback(CompletionResult.Failure(result.error)) @@ -138,7 +149,6 @@ private class CreateWalletTangemWallet : ProductCommandProcessor) -> Unit, ) { @@ -149,42 +159,34 @@ private class CreateWalletTangemWallet : ProductCommandProcessor when (result) { is CompletionResult.Success -> { val derivedKeys = mapOf(response.wallet.publicKey.toMapKey() to result.data) - callback(CompletionResult.Success(CreateProductWalletTaskResponse( - card = session.environment.card!!, - derivedKeys = derivedKeys, - primaryCard = primaryCard - ))) + callback( + CompletionResult.Success( + CreateProductWalletTaskResponse( + card = session.environment.card!!, + derivedKeys = derivedKeys, + primaryCard = primaryCard + ) + ) + ) } is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } } -} - -private class CreateWalletOtherCards : ProductCommandProcessor { - - override fun proceed( - card: Card, - session: CardSession, - callback: (result: CompletionResult) -> Unit, - ) { - val firmwareVersion = card.firmwareVersion - val task = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) { - CreateWalletsTask(listOf(card.supportedCurves.first())) - } else { - CreateWalletsTask(card.getCurvesForNonCreatedWallets()) - } - - task.run(session) { result -> - when (result) { - is CompletionResult.Success -> callback(CompletionResult.Success(result.data.createWalletResponses[0])) - is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) - } - } - } } \ 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 278e424a2a..40c7a00ded 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 @@ -47,7 +47,8 @@ data class ScanResponse( ) : CommandResponse { fun getBlockchain(): Blockchain { - if (productType == ProductType.Note) return getTangemNoteBlockchain(card) ?: return Blockchain.Unknown + if (productType == ProductType.Note) return getTangemNoteBlockchain(card) + ?: return Blockchain.Unknown val blockchainName: String = walletData?.blockchain ?: return Blockchain.Unknown return Blockchain.fromId(blockchainName) } @@ -66,17 +67,19 @@ data class ScanResponse( fun isTangemNote(): Boolean = productType == ProductType.Note fun isTangemWallet(): Boolean = productType == ProductType.Wallet fun isTangemTwins(): Boolean = productType == ProductType.Twins - fun isTangemOtherCards(): Boolean = productType == ProductType.Other - fun twinsIsTwinned(): Boolean = card.isTangemTwins() && walletData != null && secondTwinPublicKey != null + fun supportsHdWallet(): Boolean = card.settings.isHDWalletAllowed + fun supportsBackup(): Boolean = card.settings.isBackupAllowed + + fun twinsIsTwinned(): Boolean = + card.isTangemTwins() && walletData != null && secondTwinPublicKey != null } typealias KeyWalletPublicKey = ByteArrayKey class ScanProductTask( val card: Card? = null, - private val currenciesRepository: CurrenciesRepository?, - private val shouldDeriveWC: Boolean + private val currenciesRepository: CurrenciesRepository? ) : CardSessionRunnable { override fun run( @@ -97,15 +100,22 @@ class ScanProductTask( val commandProcessor = when { TapWorkarounds.isTangemNote(card) -> ScanNoteProcessor() card.isTangemTwins() -> ScanTwinProcessor() - TapWorkarounds.isTangemWallet(card) -> ScanWalletProcessor(currenciesRepository, shouldDeriveWC) - else -> ScanOtherCardsProcessor() + else -> ScanWalletProcessor(currenciesRepository) } commandProcessor.proceed(card, session) { processorResult -> when (processorResult) { is CompletionResult.Success -> ScanTask().run(session) { scanTaskResult -> when (scanTaskResult) { - is CompletionResult.Success -> callback(CompletionResult.Success(processorResult.data)) - is CompletionResult.Failure -> callback(CompletionResult.Failure(scanTaskResult.error)) + is CompletionResult.Success -> callback( + CompletionResult.Success( + processorResult.data + ) + ) + is CompletionResult.Failure -> callback( + CompletionResult.Failure( + scanTaskResult.error + ) + ) } } is CompletionResult.Failure -> callback(CompletionResult.Failure(processorResult.error)) @@ -129,13 +139,20 @@ private class ScanNoteProcessor : ProductCommandProcessor { session: CardSession, callback: (result: CompletionResult) -> Unit ) { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Note, session.environment.walletData))) + callback( + CompletionResult.Success( + ScanResponse( + card, + ProductType.Note, + session.environment.walletData + ) + ) + ) } } private class ScanWalletProcessor( - private val currenciesRepository: CurrenciesRepository?, - private val shouldDeriveWC: Boolean + private val currenciesRepository: CurrenciesRepository? ) : ProductCommandProcessor { var primaryCard: PrimaryCard? = null @@ -145,9 +162,60 @@ private class ScanWalletProcessor( session: CardSession, callback: (result: CompletionResult) -> Unit ) { - val activationIsFinished = preferencesStorage.usedCardsPrefStorage.isActivationFinished(card.cardId) + createMissingWalletsIfNeeded(card, session, callback) + } - if (card.backupStatus?.isActive != true && !activationIsFinished && card.wallets.isNotEmpty()) { + private fun createMissingWalletsIfNeeded( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + if (card.wallets.isEmpty() || card.firmwareVersion < FirmwareVersion.MultiWalletAvailable) { + startLinkingForBackupIfNeeded(card, session, callback) + return + } + + val curvesToCreate = card.getCurvesForNonCreatedWallets() + if (curvesToCreate.isEmpty()) { + startLinkingForBackupIfNeeded(card, session, callback) + return + } + + CreateWalletsTask(curvesToCreate).run(session) { result -> + when (result) { + is CompletionResult.Success -> { + PreflightReadTask( + PreflightReadMode.FullCardRead, + card.cardId + ).run(session) { readResult -> + when (readResult) { + is CompletionResult.Success -> { + startLinkingForBackupIfNeeded(card, session, callback) + } + is CompletionResult.Failure -> callback( + CompletionResult.Failure( + readResult.error + ) + ) + } + } + } + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } + + private fun startLinkingForBackupIfNeeded( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + val activationIsFinished = + preferencesStorage.usedCardsPrefStorage.isActivationFinished(card.cardId) + + if (card.backupStatus == Card.BackupStatus.NoBackup && + !activationIsFinished && card.wallets.isNotEmpty() + ) { StartPrimaryCardLinkingTask().run(session) { linkingResult -> when (linkingResult) { is CompletionResult.Success -> { @@ -162,7 +230,6 @@ private class ScanWalletProcessor( } else { deriveKeysIfNeeded(card, session, callback) } - } private fun deriveKeysIfNeeded( @@ -174,12 +241,16 @@ private class ScanWalletProcessor( val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } if (derivationPaths.isNullOrEmpty() || wallet == null || wallet.chainCode == null) { - callback(CompletionResult.Success(ScanResponse( - card = card, - productType = ProductType.Wallet, - walletData = session.environment.walletData, - primaryCard = primaryCard - ))) + callback( + CompletionResult.Success( + ScanResponse( + card = card, + productType = ProductType.Wallet, + walletData = session.environment.walletData, + primaryCard = primaryCard + ) + ) + ) return } @@ -213,8 +284,14 @@ private class ScanWalletProcessor( (cardCurrencies.blockchains + tokenBlockchains).toMutableList() } - if (shouldDeriveWC) { - blockchainsToDerive.addAll(listOf(Blockchain.Ethereum, Blockchain.Binance, Blockchain.EthereumTestnet)) + if (card.settings.isHDWalletAllowed) { + blockchainsToDerive.addAll( + listOf( + Blockchain.Ethereum, + Blockchain.Binance, + Blockchain.EthereumTestnet + ) + ) } return blockchainsToDerive.toSet() @@ -234,17 +311,39 @@ private class ScanTwinProcessor : ProductCommandProcessor { is CompletionResult.Success -> { val publicKey = card.getSingleWallet()?.publicKey if (publicKey == null) { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Twins, null))) + callback( + CompletionResult.Success( + ScanResponse( + card, + ProductType.Twins, + null + ) + ) + ) return@run } - val verified = TwinsHelper.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey) + val verified = + TwinsHelper.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey) if (verified) { val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) val walletData = session.environment.walletData - val response = ScanResponse(card, ProductType.Twins, walletData, twinPublicKey.toHexString()) + val response = ScanResponse( + card, + ProductType.Twins, + walletData, + twinPublicKey.toHexString() + ) callback(CompletionResult.Success(response)) } else { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Twins, null))) + callback( + CompletionResult.Success( + ScanResponse( + card, + ProductType.Twins, + null + ) + ) + ) } } is CompletionResult.Failure -> @@ -255,43 +354,9 @@ private class ScanTwinProcessor : ProductCommandProcessor { } -private class ScanOtherCardsProcessor : ProductCommandProcessor { - - override fun proceed(card: Card, session: CardSession, callback: (result: CompletionResult) -> Unit) { - val walletData = session.environment.walletData - - if (card.wallets.isEmpty() || card.firmwareVersion < FirmwareVersion.MultiWalletAvailable) { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData))) - return - } - - val curvesToCreate = card.getCurvesForNonCreatedWallets() - if (curvesToCreate.isEmpty()) { - callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData))) - return - } - - CreateWalletsTask(curvesToCreate).run(session) { result -> - when (result) { - is CompletionResult.Success -> { - PreflightReadTask(PreflightReadMode.FullCardRead, card.cardId).run(session) { readResult -> - when (readResult) { - is CompletionResult.Success -> { - val response = ScanResponse(readResult.data, ProductType.Other, walletData) - callback(CompletionResult.Success(response)) - } - is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error)) - } - } - } - is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) - } - } - } -} - fun Card.getCurvesForNonCreatedWallets(): List { - val curvesPresent = wallets.map { it.curve } - val curvesForNonCreatedWallets = supportedCurves.subtract(curvesPresent) + val curvesPresent = wallets.map { it.curve }.toSet() + val curvesForNonCreatedWallets = supportedCurves + .subtract(curvesPresent + EllipticCurve.Secp256r1) return curvesForNonCreatedWallets.toList() } \ No newline at end of file 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 6fb4bb28c6..51ade7b4c3 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 @@ -23,7 +23,8 @@ class FinalizeTwinTask( PreflightReadTask(PreflightReadMode.FullCardRead).run(session) { readResult -> when (readResult) { is CompletionResult.Success -> - ScanProductTask(readResult.data, null, false).run(session, callback) + ScanProductTask(readResult.data, null) + .run(session, callback) is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error)) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 71f4bd5fd7..7ea35d3284 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -51,7 +51,7 @@ private fun handlePrepareScreen( cardInfo = action.scanResponse.card.toCardInfo(), appCurrencyState = AppCurrencyState(action.fiatCurrencyName), cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card), - createBackupAllowed = action.scanResponse.isTangemWallet() && !backupIsActive, + createBackupAllowed = action.scanResponse.card.backupStatus == Card.BackupStatus.NoBackup, ) } @@ -65,7 +65,7 @@ private fun handleEraseWallet( val notAllowedByAnyWallet = card?.wallets?.any { it.settings.isPermanent } ?: false val notAllowedByCard = notAllowedByAnyWallet || (card?.isWalletDataSupported == true && - (!state.scanResponse.isTangemNote() && !state.scanResponse.isTangemWallet())) + (!state.scanResponse.isTangemNote() && !state.scanResponse.supportsBackup())) val notEmpty = state.wallets.any { it.hasPendingTransactions() || it.amounts.toSendableAmounts().isNotEmpty() @@ -139,7 +139,7 @@ private fun handleSecurityAction( state.scanResponse?.isTangemNote() == true -> { EnumSet.of(SecurityOption.LongTap) } - state.scanResponse?.isTangemWallet() == true -> { + state.scanResponse?.supportsBackup() == true -> { EnumSet.of(state.securityScreenState?.currentOption) } else -> prepareAllowedSecurityOptions( diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt index 6a097f6511..258701cabc 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -32,9 +32,12 @@ class OnboardingHelper { fun whereToNavigate(scanResponse: ScanResponse): AppScreen { return when (scanResponse.productType) { ProductType.Note -> AppScreen.OnboardingNote - ProductType.Wallet -> AppScreen.OnboardingWallet + ProductType.Wallet -> if (scanResponse.card.settings.isBackupAllowed) { + AppScreen.OnboardingWallet + } else { + AppScreen.OnboardingOther + } ProductType.Twins -> AppScreen.OnboardingTwins - ProductType.Other -> AppScreen.OnboardingOther } } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt index d900eaf35f..9bfdaaf381 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt @@ -22,7 +22,7 @@ sealed class OnboardingWalletAction : Action { sealed class BackupAction : Action { object DetermineBackupStep : BackupAction() - object IntroduceBackup : BackupAction() + data class IntroduceBackup(val buyCardsUrl: String? = null) : BackupAction() object StartBackup : BackupAction() object DismissBackup : BackupAction() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 14a8788714..1816465f6e 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -110,7 +110,14 @@ private fun handleWalletAction(action: Action) { BackupService.State.FinalizingPrimaryCard -> BackupAction.PrepareToWritePrimaryCard is BackupService.State.FinalizingBackupCard -> BackupAction.PrepareToWriteBackupCard(backupState.index) - else -> BackupAction.IntroduceBackup + else -> { + val url = if (card?.issuer?.name?.lowercase()?.contains("tangem") == true) { + BUY_WALLET_URL + } else { + null + } + BackupAction.IntroduceBackup(url) + } } store.dispatch(newAction) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt index 09aad69d48..89542c532e 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt @@ -39,9 +39,10 @@ class BackupReducer { ): BackupState { return when (action) { - BackupAction.IntroduceBackup -> BackupState( + is BackupAction.IntroduceBackup -> BackupState( backupStep = BackupStep.InitBackup, - canSkipBackup = state.canSkipBackup + canSkipBackup = state.canSkipBackup, + buyAdditionalCardsUrl = action.buyCardsUrl ) BackupAction.StartAddingPrimaryCard -> state.copy(backupStep = BackupStep.ScanOriginCard) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt index d8d0c687b7..a048261a6d 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt @@ -52,7 +52,8 @@ data class BackupState( val accessCodeError: AccessCodeError? = null, val backupStep: BackupStep = BackupStep.InitBackup, val maxBackupCards: Int = 2, - val canSkipBackup: Boolean = true + val canSkipBackup: Boolean = true, + val buyAdditionalCardsUrl: String? = null ) enum class AccessCodeError { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupCardsWidget.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupCardsWidget.kt index d5b5065f36..e4de53c9a4 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupCardsWidget.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupCardsWidget.kt @@ -15,7 +15,13 @@ class BackupCardsWidget( val getTopOfAnchorViewForActivateState: () -> Float, ) { + var currentState: WidgetState? = null + fun toWelcome(animate: Boolean = true, onEnd: () -> Unit = {}) { + if (currentState == WidgetState.WELCOME) return + + currentState = WidgetState.WELCOME + val animator = createAnimator(animate, onEnd) animator.playTogether( createAnimator(BackupCardType.ORIGIN, createWelcomeProperties(BackupCardType.ORIGIN)), @@ -26,6 +32,10 @@ class BackupCardsWidget( } fun toFolded(animate: Boolean = true, onEnd: () -> Unit = {}) { + if (currentState == WidgetState.FOLDED) return + + currentState = WidgetState.FOLDED + val animator = createAnimator(animate, onEnd) animator.playTogether( createAnimator(BackupCardType.ORIGIN, createLeapfrogProperties(BackupCardType.ORIGIN)), @@ -36,6 +46,10 @@ class BackupCardsWidget( } fun toFan(animate: Boolean = true, onEnd: () -> Unit = {}) { + if (currentState == WidgetState.FAN) return + + currentState = WidgetState.FAN + val animator = createAnimator(animate, onEnd) animator.playTogether( createAnimator(BackupCardType.ORIGIN, createFanProperties(BackupCardType.ORIGIN)), @@ -46,6 +60,8 @@ class BackupCardsWidget( } fun toLeapfrog(animate: Boolean = true, onEnd: () -> Unit = {}) { + currentState = WidgetState.LEAPFROG + val animator = createAnimator(animate, onEnd) animator.playTogether( createAnimator(BackupCardType.ORIGIN, createLeapfrogProperties(BackupCardType.ORIGIN)), @@ -60,15 +76,6 @@ class BackupCardsWidget( animator.start() } } -// -// fun toActivate(animate: Boolean = true, onEnd: () -> Unit = {}) { -// val animator = createAnimator(animate, onEnd) -// animator.playTogether( -// createAnimator(TwinCardNumber.First, createActivateProperties(TwinCardNumber.First)), -// createAnimator(TwinCardNumber.Second, createActivateProperties(TwinCardNumber.Second)) -// ) -// animator.start() -// } private fun createAnimator(animate: Boolean, onEnd: () -> Unit): AnimatorSet { return AnimatorSet().apply { @@ -196,6 +203,8 @@ class BackupCardsWidget( fun getSecondBackupCardView(): ImageView { return getLeapViewByCardNumber(BackupCardType.SECOND_BACKUP).view as ImageView } + + enum class WidgetState { WELCOME, FOLDED, FAN, LEAPFROG } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index f415e176b9..5a1ccf6684 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -148,19 +148,10 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), } private fun showScanOriginCard(state: BackupState) { - - toolbar.title = getText(R.string.onboarding_navbar_title_creating_backup) + prepareBackupView() cardsWidget.toFolded() - tv_header.show() - tv_body.show() - view_pager_backup_info.hide() - tab_layout_backup_info.hide() - - imv_first_backup_card.show() - imv_second_backup_card.show() - tv_header.text = getText(R.string.onboarding_title_scan_origin_card) tv_body.text = getString( R.string.onboarding_subtitle_scan_origin_card, @@ -171,9 +162,20 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), btn_main_action.setOnClickListener { store.dispatch(BackupAction.ScanPrimaryCard) } } - private fun showAddBackupCards(state: BackupState) { + private fun prepareBackupView() { + toolbar.title = getText(R.string.onboarding_navbar_title_creating_backup) + + tv_header.show() + tv_body.show() + view_pager_backup_info.hide() + tab_layout_backup_info.hide() + imv_first_backup_card.show() imv_second_backup_card.show() + } + + private fun showAddBackupCards(state: BackupState) { + prepareBackupView() accessCodeDialog?.dismiss() accessCodeDialog = null @@ -190,11 +192,12 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), when (state.backupCardsNumber) { 0 -> { - cardsWidget.toFan() + cardsWidget.toFan() { + cardsWidget.getFirstBackupCardView().animate().alpha(0.6f).setDuration(200) + cardsWidget.getSecondBackupCardView().animate().alpha(0.2f).setDuration(200) + } tv_header.text = getText(R.string.onboarding_title_no_backup_cards) tv_body.text = getText(R.string.onboarding_subtitle_no_backup_cards) - cardsWidget.getFirstBackupCardView().alpha = 0.6f - cardsWidget.getSecondBackupCardView().alpha = 0.2f } 1 -> { tv_header.text = getText(R.string.onboarding_title_one_backup_card) @@ -283,10 +286,6 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), cardsWidget.toLeapfrog() -// imv_front_card.alpha = 1f -// imv_first_backup_card.alpha = 1f -// imv_second_backup_card.alpha = 1f - btn_alternative_action.hide() } @@ -373,9 +372,12 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.shop, menu) - val backupStep = store.state.onboardingWalletState.backupState.backupStep + val backupState = store.state.onboardingWalletState.backupState + val backupStep = backupState.backupStep + val shopMenuShouldBeVisible = - backupStep == BackupStep.ScanOriginCard || backupStep == BackupStep.AddBackupCards + (backupStep == BackupStep.ScanOriginCard || backupStep == BackupStep.AddBackupCards) && + backupState.buyAdditionalCardsUrl != null menu.getItem(0).isVisible = shopMenuShouldBeVisible } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt index 2176e4ea51..92fe81b661 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt @@ -46,7 +46,7 @@ class TokensMiddleware { isTestNet = isTestcard ) val currencies = CurrencyListItem.createListOfCurrencies(blockchains, tokens).toMutableList() - if (scanResponse.isTangemWallet()) { + if (scanResponse.supportsHdWallet()) { currencies.forEach { when (it) { is CurrencyListItem.TitleListItem -> { @@ -81,7 +81,7 @@ class TokensMiddleware { .map { it.token } if (blockchains.isEmpty() && tokens.isEmpty()) return - if (scanResponse.isTangemWallet()) { + if (scanResponse.supportsHdWallet()) { deriveMissingBlockchains(scanResponse, blockchains, tokens) } else { submitAdd(blockchains, tokens) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index 11d7232cd3..ace2084261 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -85,7 +85,7 @@ class MultiWalletMiddleware { } is WalletAction.MultiWallet.FindBlockchainsInUse -> { val scanResponse = globalState.scanResponse ?: return - if (scanResponse.isTangemWallet()) return + if (scanResponse.supportsHdWallet()) return val cardFirmware = scanResponse.card.firmwareVersion val blockchains = currenciesRepository.getBlockchains(cardFirmware) @@ -125,7 +125,7 @@ class MultiWalletMiddleware { } is WalletAction.MultiWallet.FindTokensInUse -> { val scanResponse = globalState.scanResponse ?: return - if (scanResponse.isTangemWallet()) return + if (scanResponse.supportsHdWallet()) return val walletFactory = tapWalletManager.walletManagerFactory val card = scanResponse.card From 38b7c1d0422cc30073c96378b180b5cb1dd48606 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 15 Dec 2021 15:14:56 +0300 Subject: [PATCH 07/10] Updated on 2026-08-14 --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 041c1aa018..a8830f8137 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -109,7 +109,7 @@ You may experience NFC problems with some iPhone 7/7+ during the extraction This card has been already topped up and signed transactions in the past. Consider immediate withdrawal of all funds if you have received this card from an untrusted source. Warning - This card it is not designed to work with Tangem + This card is not designed to work with this app The card you scanned is a development card. Don’t accept it as a payment Tangem cards manufactured before September 2019 cannot currently be extracted with an iPhone. We’re working hard with Apple to make it possible in future versions of iOS. From e922364e1adc9ddee8e12ecbf952e8f8ee8dba70 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 15 Dec 2021 15:16:58 +0300 Subject: [PATCH 08/10] Updated on 2026-08-14 --- .../java/com/tangem/tap/features/feedback/FeedbackManager.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt b/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt index 0e4fe0a875..0ae3d143a8 100644 --- a/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt +++ b/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt @@ -218,7 +218,6 @@ interface EmailData { fun joinTogether(infoHolder: AdditionalEmailInfo): String { return "$mainMessage\n\n\n\n" + - "Following information is optional. You can erase it if you don’t want to share it.\n" + createOptionalMessage(infoHolder) } } From 1f0173686fbfd6631553f9fc0f6af41d784cae50 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 15 Dec 2021 20:46:37 +0300 Subject: [PATCH 09/10] Updated on 2026-08-14 --- .../com/tangem/tap/domain/TangemSdkManager.kt | 12 +-- .../tap/domain/extensions/Blockchain.kt | 15 +++ .../tangem/tap/domain/tasks/DerivationTask.kt | 50 ++++++++++ .../tasks/product/CreateProductWalletTask.kt | 7 +- .../domain/tasks/product/ScanProductTask.kt | 45 ++++++--- .../features/tokens/redux/CurrencyListItem.kt | 2 +- .../features/tokens/redux/TokensMiddleware.kt | 95 ++++++++++++------- .../tokens/ui/adapters/CurrenciesAdapter.kt | 2 +- 8 files changed, 167 insertions(+), 61 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 455a6b973d..449e1735ff 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -15,15 +15,16 @@ import com.tangem.common.core.Config import com.tangem.common.core.TangemSdkError import com.tangem.common.hdWallet.DerivationPath import com.tangem.operations.CommandResponse -import com.tangem.operations.derivation.DeriveWalletPublicKeysTask -import com.tangem.operations.derivation.ExtendedPublicKeyList import com.tangem.operations.pins.CheckUserCodesCommand import com.tangem.operations.pins.CheckUserCodesResponse import com.tangem.operations.pins.SetUserCodeCommand import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.AnalyticsHandler import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.ByteArrayKey import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask +import com.tangem.tap.domain.tasks.DerivationTask +import com.tangem.tap.domain.tasks.DerivationTaskResponse import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask import com.tangem.tap.domain.tasks.product.ScanProductTask import com.tangem.tap.domain.tasks.product.ScanResponse @@ -77,10 +78,9 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co suspend fun derivePublicKeys( cardId: String, - walletPublicKey: ByteArray, - derivationPaths: List - ): CompletionResult { - return runTaskAsyncReturnOnMain(DeriveWalletPublicKeysTask(walletPublicKey, derivationPaths), cardId) + derivations: Map> + ): CompletionResult { + return runTaskAsyncReturnOnMain(DerivationTask(derivations), cardId) } suspend fun resetToFactorySettings(card: Card): CompletionResult { diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt index 5ec3ea7672..0563eb8482 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt @@ -2,6 +2,7 @@ package com.tangem.tap.domain.extensions import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token +import com.tangem.common.card.EllipticCurve import java.math.BigDecimal @@ -25,4 +26,18 @@ fun Blockchain.minimalAmount(): BigDecimal { return 1.toBigDecimal().movePointLeft(decimals()) } +fun Blockchain.getPrimaryCurve(): EllipticCurve? { + return when { + getSupportedCurves().contains(EllipticCurve.Secp256k1) -> { + EllipticCurve.Secp256k1 + } + getSupportedCurves().contains(EllipticCurve.Ed25519) -> { + EllipticCurve.Ed25519 + } + else -> { + null + } + } +} + private const val NODL = "NODL" \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt new file mode 100644 index 0000000000..8f03e068b5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tasks/DerivationTask.kt @@ -0,0 +1,50 @@ +package com.tangem.tap.domain.tasks + +import com.tangem.common.CompletionResult +import com.tangem.common.core.CardSession +import com.tangem.common.core.CardSessionRunnable +import com.tangem.common.core.CompletionCallback +import com.tangem.common.hdWallet.DerivationPath +import com.tangem.operations.CommandResponse +import com.tangem.operations.derivation.DeriveWalletPublicKeysTask +import com.tangem.operations.derivation.ExtendedPublicKeyList +import com.tangem.tap.common.extensions.ByteArrayKey + +class DerivationTaskResponse( + val entries: Map +): CommandResponse + +class DerivationTask( + private val derivations: Map> +) : CardSessionRunnable { + + val response: MutableMap = mutableMapOf() + + override fun run(session: CardSession, callback: CompletionCallback) { + derive(keys = derivations.keys.toList(), index = 0, session = session, callback = callback) + } + + private fun derive( + keys: List, + index: Int, + session: CardSession, + callback: CompletionCallback + ) { + if (index == keys.count()) { + callback(CompletionResult.Success(DerivationTaskResponse(response.toMap()))) + return + } + + val key = keys[index] + val paths = derivations[key]!! + DeriveWalletPublicKeysTask(key.bytes, paths).run(session) { result -> + when (result) { + is CompletionResult.Success -> { + response[key] = result.data + derive(keys = keys, index = index + 1, session = session, callback = callback) + } + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt index 25e3d98836..89bdb12dd2 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt @@ -10,12 +10,12 @@ import com.tangem.common.hdWallet.ExtendedPublicKey import com.tangem.operations.CommandResponse import com.tangem.operations.backup.PrimaryCard import com.tangem.operations.backup.StartPrimaryCardLinkingTask -import com.tangem.operations.derivation.DeriveWalletPublicKeysTask import com.tangem.operations.wallet.CreateWalletResponse import com.tangem.operations.wallet.CreateWalletTask import com.tangem.tap.common.extensions.toMapKey import com.tangem.tap.domain.ProductType import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain +import com.tangem.tap.domain.tasks.DerivationTask import com.tangem.tap.domain.tasks.product.CreateWalletsTask import com.tangem.tap.domain.tasks.product.KeyWalletPublicKey import com.tangem.tap.domain.tasks.product.ProductCommandProcessor @@ -170,16 +170,15 @@ private class CreateWalletTangemWallet : ProductCommandProcessor when (result) { is CompletionResult.Success -> { - val derivedKeys = mapOf(response.wallet.publicKey.toMapKey() to result.data) callback( CompletionResult.Success( CreateProductWalletTaskResponse( card = session.environment.card!!, - derivedKeys = derivedKeys, + derivedKeys = result.data.entries, primaryCard = primaryCard ) ) 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 40c7a00ded..506f5aa9c3 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 @@ -22,7 +22,6 @@ import com.tangem.operations.PreflightReadTask import com.tangem.operations.ScanTask import com.tangem.operations.backup.PrimaryCard import com.tangem.operations.backup.StartPrimaryCardLinkingTask -import com.tangem.operations.derivation.DeriveWalletPublicKeysTask import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand import com.tangem.tap.common.extensions.ByteArrayKey import com.tangem.tap.common.extensions.toMapKey @@ -32,7 +31,9 @@ import com.tangem.tap.domain.TapWorkarounds import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain import com.tangem.tap.domain.TapWorkarounds.isExcluded import com.tangem.tap.domain.TapWorkarounds.isNotSupportedInThatRelease +import com.tangem.tap.domain.extensions.getPrimaryCurve import com.tangem.tap.domain.extensions.getSingleWallet +import com.tangem.tap.domain.tasks.DerivationTask import com.tangem.tap.domain.tokens.CurrenciesRepository import com.tangem.tap.domain.twins.TwinsHelper import com.tangem.tap.preferencesStorage @@ -237,10 +238,8 @@ private class ScanWalletProcessor( session: CardSession, callback: (result: CompletionResult) -> Unit ) { - val derivationPaths = collectDerivationPaths(card)?.distinct() - val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } - - if (derivationPaths.isNullOrEmpty() || wallet == null || wallet.chainCode == null) { + val derivations = collectDerivations(card) + if (derivations.isEmpty()) { callback( CompletionResult.Success( ScanResponse( @@ -254,15 +253,14 @@ private class ScanWalletProcessor( return } - DeriveWalletPublicKeysTask(wallet.publicKey, derivationPaths).run(session) { result -> + DerivationTask(derivations).run(session) { result -> when (result) { is CompletionResult.Success -> { - val derivedKeys = mapOf(wallet.publicKey.toMapKey() to result.data) val response = ScanResponse( card = card, productType = ProductType.Wallet, walletData = session.environment.walletData, - derivedKeys = derivedKeys, + derivedKeys = result.data.entries, primaryCard = primaryCard ) callback(CompletionResult.Success(response)) @@ -273,8 +271,8 @@ private class ScanWalletProcessor( } } - private fun collectDerivationPaths(card: Card): List? { - val currenciesRepository = currenciesRepository ?: return null + private fun getBlockchainsToDerive(card: Card): List { + val currenciesRepository = currenciesRepository ?: return emptyList() val cardCurrencies = currenciesRepository.loadCardCurrencies(card.cardId) val blockchainsToDerive = if (cardCurrencies == null) { @@ -293,10 +291,31 @@ private class ScanWalletProcessor( ) ) } + return blockchainsToDerive.distinct() + } - return blockchainsToDerive.toSet() - .filter { it.getSupportedCurves()?.contains(EllipticCurve.Secp256k1) == true } - .mapNotNull { it.derivationPath() } + private fun collectDerivations(card: Card): Map> { + val blockchains = getBlockchainsToDerive(card) + val derivations = mutableMapOf>() + + blockchains.forEach { blockchain -> + val curve = blockchain.getPrimaryCurve() + + val wallet = card.wallets.firstOrNull { it.curve == curve } ?: return@forEach + if (wallet.chainCode == null) return@forEach + + val key = wallet.publicKey.toMapKey() + val path = blockchain.derivationPath() + if (path != null) { + val addedDerivations = derivations[key] + if (addedDerivations != null) { + derivations[key] = addedDerivations + path + } else { + derivations[key] = listOf(path) + } + } + } + return derivations } } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt index bcf0bf5644..53acef169a 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/CurrencyListItem.kt @@ -7,7 +7,7 @@ import com.tangem.wallet.R sealed class CurrencyListItem { var isAdded: Boolean = false - var isLock: Boolean = false + var isLocked: Boolean = false data class TokenListItem(val token: Token) : CurrencyListItem() data class BlockchainListItem(val blockchain: Blockchain) : CurrencyListItem() diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt index 92fe81b661..30db14cfbe 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt @@ -4,6 +4,9 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.common.CompletionResult import com.tangem.common.card.EllipticCurve +import com.tangem.common.hdWallet.DerivationPath +import com.tangem.common.hdWallet.ExtendedPublicKey +import com.tangem.tap.common.extensions.ByteArrayKey import com.tangem.tap.common.extensions.dispatchErrorNotification import com.tangem.tap.common.extensions.toMapKey import com.tangem.tap.common.redux.AppState @@ -13,6 +16,7 @@ import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE import com.tangem.tap.domain.TapError import com.tangem.tap.domain.TapWorkarounds.isTestCard +import com.tangem.tap.domain.tasks.product.KeyWalletPublicKey import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.scope @@ -45,27 +49,8 @@ class TokensMiddleware { cardFirmware = scanResponse.card.firmwareVersion, isTestNet = isTestcard ) - val currencies = CurrencyListItem.createListOfCurrencies(blockchains, tokens).toMutableList() - if (scanResponse.supportsHdWallet()) { - currencies.forEach { - when (it) { - is CurrencyListItem.TitleListItem -> { - } - is CurrencyListItem.BlockchainListItem -> { - val firstCurve = it.blockchain.getSupportedCurves()?.firstOrNull() - if (firstCurve == EllipticCurve.Ed25519) { - it.isLock = true - } - } - is CurrencyListItem.TokenListItem -> { - val firstCurve = it.token.blockchain.getSupportedCurves()?.firstOrNull() - if (firstCurve == EllipticCurve.Ed25519) { - it.isLock = true - } - } - } - } - } + val currencies = + CurrencyListItem.createListOfCurrencies(blockchains, tokens).toMutableList() store.dispatch(TokensAction.LoadCurrencies.Success(currencies)) } @@ -94,26 +79,33 @@ class TokensMiddleware { blockchains: List, tokens: List ) { - val wallet = scanResponse.card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: return - - val tokenBlockchains = tokens.map { it.blockchain } - val derivationPathsCandidates = (blockchains + tokenBlockchains).distinct() - .mapNotNull { it.derivationPath() } - - val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey() - val alreadyDerivedKeys = scanResponse.derivedKeys[mapKeyOfWalletPublicKey]?.toMutableList() ?: mutableListOf() - val alreadyDerivedPaths = alreadyDerivedKeys.map { it.derivationPath } - - val toDerive = derivationPathsCandidates.filterNot { alreadyDerivedPaths.contains(it) } + val derivationDataList = listOfNotNull( + getDerivations(EllipticCurve.Secp256k1, scanResponse, blockchains, tokens), + getDerivations(EllipticCurve.Ed25519, scanResponse, blockchains, tokens) + ) + val derivations = derivationDataList.map { it.derivations }.toMap() scope.launch { - val result = tangemSdkManager.derivePublicKeys(scanResponse.card.cardId, wallet.publicKey, toDerive) + val result = tangemSdkManager.derivePublicKeys( + scanResponse.card.cardId, + derivations + ) when (result) { is CompletionResult.Success -> { - val newDerivedKeys = result.data - alreadyDerivedKeys.addAll(newDerivedKeys) + val newDerivedKeys = result.data.entries + val updatedDerivedKeys = + mutableMapOf>() + + newDerivedKeys.forEach { entry -> + val derivationData = derivationDataList.find { + it.mapKeyOfWalletPublicKey == entry.key + } ?: return@forEach + updatedDerivedKeys[entry.key] = + derivationData.alreadyDerivedKeys + entry.value.toList() + } + val updatedScanResponse = scanResponse.copy( - derivedKeys = mapOf(mapKeyOfWalletPublicKey to alreadyDerivedKeys.toList()) + derivedKeys = updatedDerivedKeys ) store.dispatch(GlobalAction.SaveScanNoteResponse(updatedScanResponse)) submitAdd(blockchains, tokens) @@ -128,6 +120,37 @@ class TokensMiddleware { } } + private fun getDerivations( + curve: EllipticCurve, + scanResponse: ScanResponse, + blockchains: List, + tokens: List + ): DerivationData? { + val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null + + val tokenBlockchains = tokens.map { it.blockchain } + val derivationPathsCandidates = (blockchains + tokenBlockchains).distinct() + .mapNotNull { it.derivationPath() } + + val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey() + val alreadyDerivedKeys = + scanResponse.derivedKeys[mapKeyOfWalletPublicKey]?.toMutableList() ?: mutableListOf() + val alreadyDerivedPaths = alreadyDerivedKeys.map { it.derivationPath } + + val toDerive = derivationPathsCandidates.filterNot { alreadyDerivedPaths.contains(it) } + return DerivationData( + derivations = mapKeyOfWalletPublicKey to toDerive, + alreadyDerivedKeys = alreadyDerivedKeys, + mapKeyOfWalletPublicKey = mapKeyOfWalletPublicKey + ) + } + + private class DerivationData( + val derivations: Pair>, + val alreadyDerivedKeys: List, + val mapKeyOfWalletPublicKey: ByteArrayKey + ) + private fun submitAdd(blockchains: List, tokens: List) { (blockchains.map { WalletAction.MultiWallet.AddBlockchain(it) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt b/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt index b3d0ecdc04..f0046353d1 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt @@ -196,7 +196,7 @@ class CurrenciesAdapter : ListAdapter } private fun modifyAddTokenButton(currency: CurrencyListItem) { - if (currency.isLock) { + if (currency.isLocked) { view.btn_add_token.setText(R.string.common_add) view.btn_add_token.isEnabled = false } else { From 3b0554ec5038a7da329e57aa7e82a5df50b26131 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 16 Dec 2021 11:53:22 +0300 Subject: [PATCH 10/10] Updated on 2026-08-14 --- app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c8e2711c22..f9fe70e593 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,9 +77,9 @@ dependencies { implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:develop-51' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-106' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-106' + implementation 'com.tangem:blockchain:develop-52' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-114' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-114' // WebView implementation "androidx.browser:browser:1.3.0"