Updated on 2026-08-14

This commit is contained in:
Tangem 2022-04-07 13:32:11 +03:00
parent 1a2d0bfc01
commit 5f0b16e1ee
45 changed files with 294 additions and 204 deletions

View file

@ -17,6 +17,7 @@ import com.tangem.common.core.Config
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.domain.common.ScanResponse
import com.tangem.operations.CommandResponse
import com.tangem.operations.derivation.DerivationTaskResponse
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
@ -30,7 +31,6 @@ import com.tangem.tap.common.analytics.AnalyticsParam
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
import com.tangem.tap.domain.tasks.product.ScanProductTask
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.tokens.CurrenciesRepository
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.wallet.R

View file

@ -3,6 +3,9 @@ package com.tangem.tap.domain
import com.tangem.blockchain.blockchains.solana.RentProvider
import com.tangem.blockchain.common.*
import com.tangem.common.services.Result
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.ThrottlerWithValues
import com.tangem.tap.common.extensions.dispatchOnMain
@ -11,12 +14,10 @@ import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.TapWorkarounds.isStart2Coin
import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.domain.configurable.config.ConfigManager
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.domain.extensions.makeWalletManagersForApp
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.domain.tokens.CardCurrencies
import com.tangem.tap.features.demo.isDemoCard
import com.tangem.tap.features.wallet.models.PendingTransactionType

View file

@ -1,78 +0,0 @@
package com.tangem.tap.domain
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.card.Card
import com.tangem.common.card.EllipticCurve
import com.tangem.common.card.FirmwareVersion
import com.tangem.tap.domain.TapWorkarounds.isStart2Coin
import com.tangem.tap.domain.TapWorkarounds.isTangemNote
import com.tangem.tap.domain.extensions.getSingleWallet
import com.tangem.tap.domain.twins.isTangemTwin
import java.util.*
object TapWorkarounds {
fun isStart2CoinIssuer(cardIssuer: String?): Boolean {
return cardIssuer?.toLowerCase(Locale.US) == START_2_COIN_ISSUER
}
val Card.isStart2Coin: Boolean
get() = isStart2CoinIssuer(issuer.name)
val Card.isTestCard: Boolean
get() = batchId == TEST_CARD_BATCH && cardId.startsWith(TEST_CARD_ID_STARTS_WITH)
fun Card.isExcluded(): Boolean {
val excludedBatch = excludedBatches.contains(batchId)
val excludedIssuerName = excludedIssuers.contains(issuer.name.uppercase(Locale.ROOT))
return excludedBatch || excludedIssuerName
}
fun Card.isNotSupportedInThatRelease():Boolean {
return false
}
@Deprecated("Use ScanResponse.isTangemNote")
fun isTangemNote(card: Card): Boolean = tangemNoteBatches.contains(card.batchId)
fun isTangemWalletBatch(card: Card): Boolean = tangemWalletBatches.contains(card.batchId)
fun getTangemNoteBlockchain(card: Card): Blockchain? = tangemNoteBatches[card.batchId]
private const val START_2_COIN_ISSUER = "start2coin"
private const val TEST_CARD_BATCH = "99FF"
private const val TEST_CARD_ID_STARTS_WITH = "FF99"
private val excludedBatches = listOf(
"0027",
"0030",
"0031",
"0035"
)
private val excludedIssuers = listOf(
"TTM BANK"
)
private val tangemWalletBatches = listOf("AC01")
private val tangemNoteBatches = mapOf(
"AB01" to Blockchain.Bitcoin,
"AB02" to Blockchain.Ethereum,
"AB03" to Blockchain.CardanoShelley,
"AB04" to Blockchain.Dogecoin,
"AB05" to Blockchain.BSC,
"AB06" to Blockchain.XRP,
"AB07" to Blockchain.Bitcoin,
"AB08" to Blockchain.Ethereum,
)
}
val DELAY_SDK_DIALOG_CLOSE = 1400L
val Card.isMultiwalletAllowed: Boolean
get() {
return !isTangemTwin() && !isStart2Coin && !isTangemNote(this)
&& (firmwareVersion >= FirmwareVersion.MultiWalletAvailable ||
getSingleWallet()?.curve == EllipticCurve.Secp256k1)
}

View file

@ -2,14 +2,33 @@ package com.tangem.tap.domain.extensions
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.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.domain.common.TapWorkarounds
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.getTwinCardNumber
import com.tangem.domain.common.isTangemTwin
import com.tangem.operations.attestation.CardVerifyAndGetInfo
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.domain.twins.getTwinCardNumber
import com.tangem.tap.features.wallet.redux.Artwork
val Card.remainingSignatures: Int?
get() = this.getSingleWallet()?.remainingSignatures
val Card.isWalletDataSupported: Boolean
get() = this.firmwareVersion.major >= 4
val Card.isMultiwalletAllowed: Boolean
get() {
return !isTangemTwin() && !isStart2Coin && !TapWorkarounds.isTangemNote(this)
&& (firmwareVersion >= FirmwareVersion.MultiWalletAvailable ||
getSingleWallet()?.curve == EllipticCurve.Secp256k1)
}
fun Card.getSingleWallet(): CardWallet? {
return wallets.firstOrNull()
}
@ -66,10 +85,4 @@ fun Card.getArtworkUrl(artworkId: String?): String? {
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
else -> null
}
}
val Card.remainingSignatures: Int?
get() = this.getSingleWallet()?.remainingSignatures
val Card.isWalletDataSupported: Boolean
get() = this.firmwareVersion.major >= 4
}

View file

@ -7,11 +7,12 @@ import com.tangem.common.card.CardWallet
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toMapKey
import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.isTestCard
fun WalletManagerFactory.makeWalletManagerForApp(
scanResponse: ScanResponse, blockchain: Blockchain
scanResponse: ScanResponse,
blockchain: Blockchain
): WalletManager? {
val card = scanResponse.card
if (card.isTestCard && blockchain.getTestnetVersion() == null) return null
@ -27,7 +28,7 @@ fun WalletManagerFactory.makeWalletManagerForApp(
scanResponse.isTangemTwins() && scanResponse.secondTwinPublicKey != null -> {
makeTwinWalletManager(
card.cardId,
wallet.publicKey, scanResponse.secondTwinPublicKey.hexToBytes(),
wallet.publicKey, scanResponse.secondTwinPublicKey!!.hexToBytes(),
environmentBlockchain, wallet.curve
)
}

View file

@ -9,6 +9,10 @@ import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.guard
import com.tangem.common.extensions.toMapKey
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.domain.common.KeyWalletPublicKey
import com.tangem.domain.common.ProductType
import com.tangem.domain.common.TapWorkarounds.getTangemNoteBlockchain
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.operations.CommandResponse
import com.tangem.operations.backup.PrimaryCard
import com.tangem.operations.backup.StartPrimaryCardLinkingTask
@ -16,11 +20,7 @@ import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.operations.wallet.CreateWalletTask
import com.tangem.tap.domain.ProductType
import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
import com.tangem.tap.domain.TapWorkarounds.isTestCard
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.features.demo.DemoHelper
@ -78,7 +78,7 @@ private class CreateWalletTangemNote : ProductCommandProcessor<CreateWalletRespo
return
}
val curvesSupportedByBlockchain = getTangemNoteBlockchain(card)?.getSupportedCurves()
val curvesSupportedByBlockchain = card.getTangemNoteBlockchain()?.getSupportedCurves()
if (curvesSupportedByBlockchain == null || curvesSupportedByBlockchain.isEmpty()) {
callback(CompletionResult.Failure(TangemSdkError.CardError()))
return

View file

@ -1,12 +1,10 @@
package com.tangem.tap.domain.tasks.product
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
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.card.WalletData
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.TangemError
@ -16,67 +14,22 @@ import com.tangem.common.extensions.guard
import com.tangem.common.extensions.toHexString
import com.tangem.common.extensions.toMapKey
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.operations.CommandResponse
import com.tangem.domain.common.*
import com.tangem.domain.common.TapWorkarounds.isExcluded
import com.tangem.domain.common.TapWorkarounds.isNotSupportedInThatRelease
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.DeriveMultipleWalletPublicKeysTask
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
import com.tangem.tap.domain.ProductType
import com.tangem.tap.domain.TapSdkError
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.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, ExtendedPublicKeysMap> = mapOf(),
val primaryCard: PrimaryCard? = null
) : CommandResponse {
fun getBlockchain(): Blockchain {
if (productType == ProductType.Note) return getTangemNoteBlockchain(card)
?: return Blockchain.Unknown
val blockchainName: String = walletData?.blockchain ?: return Blockchain.Unknown
return Blockchain.fromId(blockchainName)
}
fun getPrimaryToken(): Token? {
val cardToken = walletData?.token ?: return null
return Token(
cardToken.name,
cardToken.symbol,
cardToken.contractAddress,
cardToken.decimals,
Blockchain.fromId(walletData.blockchain)
)
}
fun isTangemNote(): Boolean = productType == ProductType.Note
fun isTangemWallet(): Boolean = productType == ProductType.Wallet
fun isTangemTwins(): Boolean = productType == ProductType.Twins
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?,
@ -132,8 +85,6 @@ class ScanProductTask(
}
}
private fun Card.isTangemTwins(): Boolean = TwinsHelper.getTwinCardNumber(cardId) != null
private class ScanNoteProcessor : ProductCommandProcessor<ScanResponse> {
override fun proceed(
card: Card,

View file

@ -8,6 +8,7 @@ import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.TangemError
import com.tangem.common.extensions.hexToBytes
import com.tangem.domain.common.TwinsHelper
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.operations.wallet.CreateWalletTask
import com.tangem.operations.wallet.PurgeWalletCommand

View file

@ -4,10 +4,10 @@ import com.tangem.common.CompletionResult
import com.tangem.common.KeyPair
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.domain.common.ScanResponse
import com.tangem.operations.PreflightReadMode
import com.tangem.operations.PreflightReadTask
import com.tangem.tap.domain.tasks.product.ScanProductTask
import com.tangem.tap.domain.tasks.product.ScanResponse
class FinalizeTwinTask(
private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair,

View file

@ -10,11 +10,11 @@ import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.domain.common.ScanResponse
import com.tangem.network.common.MoshiConverter
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsHandler
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.tangemSdkManager
class TwinCardsManager(

View file

@ -6,6 +6,7 @@ import android.animation.PropertyValuesHolder
import android.view.View
import androidx.core.animation.doOnEnd
import com.tangem.common.extensions.VoidCallback
import com.tangem.domain.common.TwinCardNumber
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapView
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapViewState
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget

View file

@ -1,78 +0,0 @@
package com.tangem.tap.domain.twins
import com.tangem.common.card.Card
import com.tangem.crypto.CryptoUtils
class TwinsHelper {
companion object {
const val TWIN_FILE_NAME = "TwinPublicKey"
fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean {
if (issuerData.size < 65 || cardWalletPublicKey == null) return false
val publicKey = issuerData.sliceArray(0 until 65)
val signedKey = issuerData.sliceArray(65 until issuerData.size)
return CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey)
}
fun getTwinCardNumber(cardId: String): TwinCardNumber? {
return when {
firstCardSeries.map { cardId.startsWith(it) }.contains(true) -> {
TwinCardNumber.First
}
secondCardSeries.map { cardId.startsWith(it) }.contains(true) -> {
TwinCardNumber.Second
}
else -> {
null
}
}
}
fun getPairCardSeries(cardId: String): String? {
return when (getTwinCardNumber(cardId) ?: return null) {
TwinCardNumber.First -> {
val index = firstCardSeries.indexOf(cardId.take(4))
secondCardSeries[index]
}
TwinCardNumber.Second -> {
val index = secondCardSeries.indexOf(cardId.take(4))
firstCardSeries[index]
}
}
}
fun getTwinCardIdForUser(cardId: String): String {
if (cardId.length < 16) return cardId
val twinCardId = cardId.substring(11..14)
val twinCardNumber = getTwinCardNumber(cardId)?.number ?: 1
return "$twinCardId #$twinCardNumber"
}
private val firstCardSeries = listOf("CB61", "CB64")
private val secondCardSeries = listOf("CB62", "CB65")
}
}
enum class TwinCardNumber(val number: Int) {
First(1), Second(2);
fun pairNumber(): TwinCardNumber = when (this) {
First -> Second
Second -> First
}
}
@Deprecated("Use ScanResponse.isTangemTwin")
fun Card.isTangemTwin(): Boolean {
return TwinsHelper.getTwinCardNumber(cardId) != null
}
fun Card.getTwinCardNumber(): TwinCardNumber? {
return TwinsHelper.getTwinCardNumber(this.cardId)
}
fun Card.getTwinCardIdForUser(): String {
return TwinsHelper.getTwinCardIdForUser(this.cardId)
}