Updated on 2026-08-14
This commit is contained in:
parent
10bb769916
commit
bb32839f9d
32 changed files with 521 additions and 77 deletions
|
|
@ -27,6 +27,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import com.tangem.domain.visa.model.VisaActivationInput
|
||||
import com.tangem.domain.visa.model.VisaAuthChallenge
|
||||
import com.tangem.domain.visa.model.VisaCardActivationResponse
|
||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
|
||||
import com.tangem.operations.ScanTask
|
||||
|
|
@ -35,6 +36,7 @@ import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
|
|||
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
|
||||
import com.tangem.operations.pins.SetUserCodeCommand
|
||||
import com.tangem.operations.preflightread.PreflightReadFilter
|
||||
import com.tangem.operations.sign.SignHashResponse
|
||||
import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
||||
|
|
@ -45,6 +47,7 @@ import com.tangem.tap.domain.tasks.product.ResetBackupCardTask
|
|||
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanProductTask
|
||||
import com.tangem.tap.domain.tasks.visa.VisaCardActivationTask
|
||||
import com.tangem.tap.domain.tasks.visa.VisaCustomerWalletApproveTask
|
||||
import com.tangem.tap.domain.twins.CreateFirstTwinWalletTask
|
||||
import com.tangem.tap.domain.twins.CreateSecondTwinWalletTask
|
||||
import com.tangem.tap.domain.twins.FinalizeTwinTask
|
||||
|
|
@ -494,6 +497,17 @@ internal class DefaultTangemSdkManager(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun visaCustomerWalletApprove(
|
||||
visaDataForApprove: VisaDataForApprove,
|
||||
): CompletionResult<SignHashResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = VisaCustomerWalletApproveTask(
|
||||
visaDataForApprove = visaDataForApprove,
|
||||
),
|
||||
initialMessage = Message(resources.getStringSafe(R.string.initial_message_tap_header)),
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -20,9 +20,11 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import com.tangem.domain.visa.model.VisaActivationInput
|
||||
import com.tangem.domain.visa.model.VisaAuthChallenge
|
||||
import com.tangem.domain.visa.model.VisaCardActivationResponse
|
||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.preflightread.PreflightReadFilter
|
||||
import com.tangem.operations.sign.SignHashResponse
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
|
|
@ -207,4 +209,12 @@ class MockTangemSdkManager(
|
|||
): CompletionResult<VisaCardActivationResponse> {
|
||||
error("Not implemented")
|
||||
}
|
||||
|
||||
override suspend fun visaCustomerWalletApprove(
|
||||
visaDataForApprove: VisaDataForApprove,
|
||||
): CompletionResult<SignHashResponse> {
|
||||
error("Not implemented")
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -311,15 +311,12 @@ private class ScanWalletProcessor(
|
|||
}
|
||||
|
||||
private fun getWalletProductType(card: CardDTO): ProductType {
|
||||
if (RING_BATCH_IDS.contains(card.batchId) || card.batchId.startsWith(RING_BATCH_PREFIX)) {
|
||||
return ProductType.Ring
|
||||
}
|
||||
return if (card.firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available &&
|
||||
card.settings.isKeysImportAllowed
|
||||
) {
|
||||
ProductType.Wallet2
|
||||
} else {
|
||||
ProductType.Wallet
|
||||
return when {
|
||||
card.isVisa -> ProductType.Visa
|
||||
RING_BATCH_IDS.contains(card.batchId) || card.batchId.startsWith(RING_BATCH_PREFIX) -> ProductType.Ring
|
||||
card.firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available &&
|
||||
card.settings.isKeysImportAllowed -> ProductType.Wallet2
|
||||
else -> ProductType.Wallet
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package com.tangem.tap.domain.tasks.visa
|
||||
|
||||
import com.reown.util.hexToBytes
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.CompletionCallback
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.map
|
||||
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
|
||||
|
|
|
|||
|
|
@ -0,0 +1,188 @@
|
|||
package com.tangem.tap.domain.tasks.visa
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.CardWallet
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.CompletionCallback
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.common.visa.VisaUtilities
|
||||
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility
|
||||
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility.findKeyWithoutDerivation
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.visa.model.VisaActivationError
|
||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
|
||||
import com.tangem.operations.sign.SignHashCommand
|
||||
import com.tangem.operations.sign.SignHashResponse
|
||||
|
||||
class VisaCustomerWalletApproveTask(
|
||||
private val visaDataForApprove: VisaDataForApprove,
|
||||
) : CardSessionRunnable<SignHashResponse> {
|
||||
|
||||
override fun run(session: CardSession, callback: CompletionCallback<SignHashResponse>) {
|
||||
val card = session.environment.card ?: run {
|
||||
callback(CompletionResult.Failure(TangemSdkError.MissingPreflightRead()))
|
||||
return
|
||||
}
|
||||
|
||||
if (VisaUtilities.isVisaCard(card.firmwareVersion.doubleValue, card.batchId).not()) {
|
||||
callback(CompletionResult.Failure(TangemSdkError.Underlying("Card is identified as not Visa")))
|
||||
return
|
||||
}
|
||||
|
||||
if (card.settings.isHDWalletAllowed) {
|
||||
proceedApprove(card, session, callback)
|
||||
} else {
|
||||
proceedApproveWithLegacyCard(card, session, callback)
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedApprove(card: Card, session: CardSession, callback: CompletionCallback<SignHashResponse>) {
|
||||
val cardDTO = CardDTO(card)
|
||||
|
||||
val derivationStyle = cardDTO.derivationStyleProvider.getDerivationStyle() ?: run {
|
||||
proceedApproveWithLegacyCard(
|
||||
card = card,
|
||||
session = session,
|
||||
callback = callback,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val derivationPath = VisaUtilities.visaDefaultDerivationPath(derivationStyle) ?: run {
|
||||
callback(
|
||||
CompletionResult.Failure(
|
||||
TangemSdkError.Underlying("Failed to generate derivation path with provided derivation style"),
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val wallet = card.wallets.firstOrNull { it.curve == VisaUtilities.mandatoryCurve } ?: run {
|
||||
callback(CompletionResult.Failure(TangemSdkError.Underlying(VisaActivationError.MissingWallet.message)))
|
||||
return
|
||||
}
|
||||
|
||||
val derivationTask = DeriveWalletPublicKeyTask(
|
||||
walletPublicKey = wallet.publicKey,
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
|
||||
derivationTask.run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
processDerivedKey(
|
||||
wallet = wallet,
|
||||
extendedPublicKey = result.data,
|
||||
derivationPath = derivationPath,
|
||||
session = session,
|
||||
callback = callback,
|
||||
)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processDerivedKey(
|
||||
wallet: CardWallet,
|
||||
extendedPublicKey: ExtendedPublicKey,
|
||||
derivationPath: DerivationPath,
|
||||
session: CardSession,
|
||||
callback: CompletionCallback<SignHashResponse>,
|
||||
) {
|
||||
val validationResult = VisaWalletPublicKeyUtility.validateExtendedPublicKey(
|
||||
targetAddress = visaDataForApprove.targetAddress,
|
||||
extendedPublicKey = extendedPublicKey,
|
||||
)
|
||||
|
||||
validationResult.onLeft {
|
||||
callback(CompletionResult.Failure(TangemSdkError.Underlying(it.message)))
|
||||
return
|
||||
}
|
||||
|
||||
signApproveData(
|
||||
targetWalletPublicKey = wallet.publicKey,
|
||||
derivationPath = derivationPath,
|
||||
session = session,
|
||||
callback = callback,
|
||||
)
|
||||
}
|
||||
|
||||
private fun proceedApproveWithLegacyCard(
|
||||
card: Card,
|
||||
session: CardSession,
|
||||
callback: CompletionCallback<SignHashResponse>,
|
||||
) {
|
||||
val publicKey = findKeyWithoutDerivation(
|
||||
targetAddress = visaDataForApprove.targetAddress,
|
||||
card = CardDTO(card),
|
||||
).getOrElse {
|
||||
callback(CompletionResult.Failure(TangemSdkError.Underlying(it.message)))
|
||||
return
|
||||
}
|
||||
|
||||
signApproveData(
|
||||
targetWalletPublicKey = publicKey,
|
||||
derivationPath = null,
|
||||
session = session,
|
||||
callback = callback,
|
||||
)
|
||||
}
|
||||
|
||||
private fun signApproveData(
|
||||
targetWalletPublicKey: ByteArray,
|
||||
derivationPath: DerivationPath?,
|
||||
session: CardSession,
|
||||
callback: CompletionCallback<SignHashResponse>,
|
||||
) {
|
||||
val signTask = SignHashCommand(
|
||||
hash = visaDataForApprove.approveHash.hexToBytes(),
|
||||
walletPublicKey = targetWalletPublicKey,
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
|
||||
signTask.run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
scanCard(
|
||||
signHashResponse = result.data,
|
||||
session = session,
|
||||
callback = callback,
|
||||
)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun scanCard(
|
||||
signHashResponse: SignHashResponse,
|
||||
session: CardSession,
|
||||
callback: CompletionCallback<SignHashResponse>,
|
||||
) {
|
||||
val scanTask = ScanTask()
|
||||
scanTask.run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
callback(CompletionResult.Success(signHashResponse))
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue