Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-20 11:43:43 +04:00
parent eb07388729
commit 3f6bcd3a0e
23 changed files with 226 additions and 101 deletions

View file

@ -24,9 +24,7 @@ import com.tangem.domain.wallets.derivations.derivationStyleProvider
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.VisaActivationInput
import com.tangem.domain.visa.model.VisaDataForApprove
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
import com.tangem.domain.visa.model.*
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.operations.ScanTask
import com.tangem.operations.derivation.DerivationTaskResponse
@ -501,7 +499,12 @@ internal class DefaultTangemSdkManager(
): CompletionResult<VisaSignedDataByCustomerWallet> {
return runTaskAsyncReturnOnMain(
runnable = VisaCustomerWalletApproveTask(
visaDataForApprove = visaDataForApprove,
VisaCustomerWalletApproveTask.Input(
cardId = visaDataForApprove.customerWalletCardId,
targetAddress = visaDataForApprove.targetAddress,
hashToSign = visaDataForApprove.dataToSign.hashToSign,
sign = visaDataForApprove.dataToSign::sign,
),
),
cardId = visaDataForApprove.customerWalletCardId,
initialMessage = Message(resources.getStringSafe(R.string.initial_message_tap_header)),

View file

@ -18,9 +18,7 @@ import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.VisaActivationInput
import com.tangem.domain.visa.model.VisaDataForApprove
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
import com.tangem.domain.visa.model.*
import com.tangem.operations.derivation.DerivationTaskResponse
import com.tangem.operations.preflightread.PreflightReadFilter
import com.tangem.operations.wallet.CreateWalletResponse

View file

@ -1,6 +1,7 @@
package com.tangem.tap.domain.tasks.visa
import arrow.core.getOrElse
import com.tangem.blockchain.blockchains.ethereum.EthereumUtils.toKeccak
import com.tangem.blockchain.common.UnmarshalHelper
import com.tangem.common.CompletionResult
import com.tangem.common.card.Card
@ -10,7 +11,6 @@ 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.toDecompressedPublicKey
import com.tangem.common.extensions.toHexString
import com.tangem.core.error.ext.tangemError
@ -22,15 +22,13 @@ import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility
import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility.findKeyWithoutDerivation
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.visa.error.VisaActivationError
import com.tangem.domain.visa.model.VisaDataForApprove
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
import com.tangem.domain.visa.model.sign
import com.tangem.operations.ScanTask
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
import com.tangem.operations.sign.SignHashCommand
class VisaCustomerWalletApproveTask(
private val visaDataForApprove: VisaDataForApprove,
private val visaDataForApprove: Input,
) : CardSessionRunnable<VisaSignedDataByCustomerWallet> {
override fun run(session: CardSession, callback: CompletionCallback<VisaSignedDataByCustomerWallet>) {
@ -44,7 +42,7 @@ class VisaCustomerWalletApproveTask(
return
}
if (visaDataForApprove.customerWalletCardId != null && card.cardId != visaDataForApprove.customerWalletCardId) {
if (visaDataForApprove.cardId != null && card.cardId != visaDataForApprove.cardId) {
callback(CompletionResult.Failure(VisaActivationError.CardIdNotMatched.tangemError))
return
}
@ -153,6 +151,12 @@ class VisaCustomerWalletApproveTask(
)
}
// TODO: [REDACTED_TASK_KEY] - Get this public function from Blockchain SDK
private fun hashPersonalMessage(message: ByteArray): ByteArray {
val prefix = "\u0019Ethereum Signed Message:\n${message.size}".toByteArray()
return (prefix + message).toKeccak()
}
private fun signApproveData(
targetWalletPublicKey: ByteArray,
derivationPath: DerivationPath?,
@ -160,10 +164,11 @@ class VisaCustomerWalletApproveTask(
session: CardSession,
callback: CompletionCallback<VisaSignedDataByCustomerWallet>,
) {
val hashToSign = visaDataForApprove.dataToSign.hashToSign.hexToBytes()
val content = "Tangem Pay wants to sign in with your account. Nonce: ${visaDataForApprove.hashToSign}"
val hash = hashPersonalMessage(content.toByteArray(Charsets.UTF_8))
val signTask = SignHashCommand(
hash = hashToSign,
hash = hash,
walletPublicKey = targetWalletPublicKey,
derivationPath = derivationPath,
)
@ -173,7 +178,7 @@ class VisaCustomerWalletApproveTask(
is CompletionResult.Success -> {
val rsvSignature = UnmarshalHelper.unmarshalSignatureExtended(
signature = result.data.signature,
hash = hashToSign,
hash = hash,
publicKey = extendedPublicKey?.publicKey?.toDecompressedPublicKey()
?: targetWalletPublicKey.toDecompressedPublicKey(),
).asRSVLegacyEVM().toHexString().lowercase()
@ -181,10 +186,7 @@ class VisaCustomerWalletApproveTask(
scanCard(
session = session,
callback = callback,
signedData = visaDataForApprove.dataToSign.sign(
signature = rsvSignature,
customerWalletAddress = visaDataForApprove.targetAddress,
),
signedData = visaDataForApprove.sign(rsvSignature, visaDataForApprove.targetAddress),
)
}
is CompletionResult.Failure -> {
@ -211,4 +213,11 @@ class VisaCustomerWalletApproveTask(
}
}
}
data class Input(
val cardId: String? = null,
val targetAddress: String,
val hashToSign: String,
val sign: (signature: String, customerWalletAddress: String) -> VisaSignedDataByCustomerWallet,
)
}