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 }