Updated on 2026-08-14
This commit is contained in:
commit
bf2cea8a46
9 changed files with 149 additions and 42 deletions
|
|
@ -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<Card> {
|
||||
suspend fun createProductWallet(
|
||||
scanResponse: ScanResponse
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
return runTaskAsync(
|
||||
CreateProductWalletTask(scanResponse.productType),
|
||||
scanResponse.card.cardId,
|
||||
|
|
|
|||
|
|
@ -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<KeyWalletPublicKey, List<ExtendedPublicKey>> = mapOf(),
|
||||
val primaryCard: PrimaryCard? = null
|
||||
) : CommandResponse
|
||||
|
||||
class CreateProductWalletTask(
|
||||
private val type: ProductType,
|
||||
) : CardSessionRunnable<Card> {
|
||||
) : CardSessionRunnable<CreateProductWalletTaskResponse> {
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<Card>) -> Unit) {
|
||||
override fun run(
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<CreateProductWalletTaskResponse>) -> 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<CreateWalletRespo
|
|||
}
|
||||
}
|
||||
|
||||
private class CreateWalletTangemWallet : ProductCommandProcessor<CreateWalletResponse> {
|
||||
private class CreateWalletTangemWallet : ProductCommandProcessor<CreateProductWalletTaskResponse> {
|
||||
|
||||
private var primaryCard: PrimaryCard? = null
|
||||
private var createWalletResponse: CreateWalletResponse? = null
|
||||
|
||||
override fun proceed(
|
||||
card: Card,
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<CreateWalletResponse>) -> Unit,
|
||||
callback: (result: CompletionResult<CreateProductWalletTaskResponse>) -> 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<CreateProductWalletTaskResponse>) -> 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<CreateProductWalletTaskResponse>) -> 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<CreateWalletResponse> {
|
||||
|
|
|
|||
|
|
@ -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<KeyWalletPublicKey, List<ExtendedPublicKey>> = mapOf()
|
||||
val derivedKeys: Map<KeyWalletPublicKey, List<ExtendedPublicKey>> = mapOf(),
|
||||
val primaryCard: PrimaryCard? = null
|
||||
) : CommandResponse {
|
||||
|
||||
fun getBlockchain(): Blockchain {
|
||||
|
|
@ -134,16 +138,48 @@ private class ScanWalletProcessor(
|
|||
private val shouldDeriveWC: Boolean
|
||||
) : ProductCommandProcessor<ScanResponse> {
|
||||
|
||||
var primaryCard: PrimaryCard? = null
|
||||
|
||||
override fun proceed(
|
||||
card: Card,
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<ScanResponse>) -> 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<ScanResponse>) -> 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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue