Updated on 2026-08-14

This commit is contained in:
Tangem 2023-01-19 18:27:45 +04:00
commit e6fcb9e2fe
35 changed files with 332 additions and 207 deletions

View file

@ -2,11 +2,11 @@ package com.tangem.tap.common.analytics.converters
import com.tangem.common.Converter
import com.tangem.common.extensions.isZero
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.analytics.filters.BasicTopUpFilter
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletData
@ -21,8 +21,8 @@ class BasicSignInEventConverter(
) : Converter<WalletState, Basic.SignedIn?> {
override fun convert(value: WalletState): Basic.SignedIn? {
if (!statesIsReadyToCreateEvent(scanResponse, value)) return null
val cardCurrency = ParamCardCurrencyConverter().convert(scanResponse) ?: return null
if (!statesIsReadyToCreateEvent(scanResponse.cardTypesResolver, value)) return null
val cardCurrency = ParamCardCurrencyConverter().convert(scanResponse.cardTypesResolver) ?: return null
return Basic.SignedIn(
state = AnalyticsParam.CardBalanceState.from(value.walletsDataFromStores),
@ -41,8 +41,8 @@ class BasicTopUpEventConverter(
) : Converter<WalletState, Basic.ToppedUp?> {
override fun convert(value: WalletState): Basic.ToppedUp? {
if (!statesIsReadyToCreateEvent(scanResponse, value)) return null
val cardCurrency = ParamCardCurrencyConverter().convert(scanResponse) ?: return null
if (!statesIsReadyToCreateEvent(scanResponse.cardTypesResolver, value)) return null
val cardCurrency = ParamCardCurrencyConverter().convert(scanResponse.cardTypesResolver) ?: return null
val data = BasicTopUpFilter.Data(
walletId = UserWalletIdBuilder.scanResponse(scanResponse).build()?.stringValue ?: "",
@ -63,8 +63,8 @@ private fun AnalyticsParam.CardBalanceState.Companion.from(
}
}
private fun statesIsReadyToCreateEvent(scanResponse: ScanResponse, state: WalletState): Boolean {
if (scanResponse.card.isMultiwalletAllowed && state.missingDerivations.isNotEmpty()) return false
private fun statesIsReadyToCreateEvent(cardTypesResolver: CardTypesResolver, state: WalletState): Boolean {
if (cardTypesResolver.isMultiwalletAllowed() && state.missingDerivations.isNotEmpty()) return false
if (state.walletsDataFromStores.isEmpty()) return false
val totalBalanceState = state.totalBalance?.state ?: return false

View file

@ -2,24 +2,20 @@ package com.tangem.tap.common.analytics.converters
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.Converter
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.SaltPayWorkaround
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.getTangemNoteBlockchain
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
/**
[REDACTED_AUTHOR]
*/
class ParamCardCurrencyConverter : Converter<ScanResponse, AnalyticsParam.CardCurrency?> {
class ParamCardCurrencyConverter : Converter<CardTypesResolver, AnalyticsParam.CardCurrency?> {
override fun convert(value: ScanResponse): AnalyticsParam.CardCurrency? {
if (value.card.isMultiwalletAllowed) return AnalyticsParam.CardCurrency.MultiCurrency
override fun convert(value: CardTypesResolver): AnalyticsParam.CardCurrency? {
if (value.isMultiwalletAllowed()) return AnalyticsParam.CardCurrency.MultiCurrency
val type = when {
value.isTangemNote() -> {
value.card.getTangemNoteBlockchain()?.let { AnalyticsParam.CurrencyType.Blockchain(it) }
}
value.isTangemNote() -> AnalyticsParam.CurrencyType.Blockchain(value.getBlockchain())
value.isTangemTwins() -> AnalyticsParam.CurrencyType.Blockchain(Blockchain.Bitcoin)
value.isSaltPay() -> AnalyticsParam.CurrencyType.Token(SaltPayWorkaround.tokenFrom(Blockchain.SaltPay))
value.getPrimaryToken() != null -> AnalyticsParam.CurrencyType.Token(value.getPrimaryToken()!!)

View file

@ -99,7 +99,7 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di
// if config not set -> try to get it based on a scanResponse.productType
val unsafeZendeskConfig = action.zendeskConfig ?: when {
scanResponse?.isSaltPay() == true -> config.saltPayConfig?.zendesk
scanResponse?.cardTypesResolver?.isSaltPay() == true -> config.saltPayConfig?.zendesk
else -> config.zendesk
}

View file

@ -85,7 +85,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
scanResponse: ScanResponse,
): CompletionResult<CreateProductWalletTaskResponse> {
return runTaskAsync(
CreateProductWalletTask(scanResponse.productType),
CreateProductWalletTask(scanResponse.cardTypesResolver),
scanResponse.card.cardId,
Message(context.getString(R.string.initial_message_create_wallet_body)),
)
@ -215,8 +215,8 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
tangemSdk.config.cardIdDisplayFormat = when {
scanResponse == null -> CardIdDisplayFormat.Full
scanResponse.isTangemTwins() -> CardIdDisplayFormat.LastLuhn(4)
scanResponse.isSaltPay() -> CardIdDisplayFormat.None
scanResponse.cardTypesResolver.isTangemTwins() -> CardIdDisplayFormat.LastLuhn(4)
scanResponse.cardTypesResolver.isSaltPay() -> CardIdDisplayFormat.None
else -> CardIdDisplayFormat.Full
}
}

View file

@ -11,7 +11,6 @@ import com.tangem.common.doOnSuccess
import com.tangem.common.services.Result
import com.tangem.domain.common.CardDTO
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.ThrottlerWithValues
import com.tangem.domain.common.extensions.withMainContext
@ -20,7 +19,6 @@ import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.common.redux.global.GlobalAction
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.model.UserWallet
@ -161,7 +159,11 @@ class TapWalletManager {
store.dispatch(WalletConnectAction.ResetState)
store.dispatch(GlobalAction.SaveScanResponse(data))
store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard))
store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed))
store.dispatch(
WalletAction.MultiWallet.SetIsMultiwalletAllowed(
data.cardTypesResolver.isMultiwalletAllowed(),
),
)
store.dispatch(WalletConnectAction.RestoreSessions(data))
store.dispatch(
WalletAction.MultiWallet.ShowWalletBackupWarning(
@ -175,8 +177,8 @@ class TapWalletManager {
fun updateConfigManager(data: ScanResponse) {
val configManager = store.state.globalState.configManager
val blockchain = data.getBlockchain()
if (data.card.isStart2Coin) {
val blockchain = data.cardTypesResolver.getBlockchain()
if (data.cardTypesResolver.isStart2Coin()) {
configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled)
configManager?.turnOff(ConfigManager.isTopUpEnabled)
} else if (blockchain == Blockchain.Bitcoin
@ -197,7 +199,7 @@ class TapWalletManager {
return
}
if (data.card.isMultiwalletAllowed) {
if (data.cardTypesResolver.isMultiwalletAllowed()) {
loadMultiWalletData(data)
} else {
loadSingleWalletData(data)
@ -226,11 +228,11 @@ class TapWalletManager {
}
private suspend fun loadSingleWalletData(data: ScanResponse) {
val blockchain = data.getBlockchain()
val blockchain = data.cardTypesResolver.getBlockchain()
val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data)
if (blockchain != Blockchain.Unknown && primaryWalletManager != null) {
val primaryToken = data.getPrimaryToken()
val primaryToken = data.cardTypesResolver.getPrimaryToken()
dispatchOnMain(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain))
if (primaryToken != null) {
@ -292,10 +294,11 @@ class TapWalletManager {
private fun getActionIfUnknownBlockchainOrEmptyWallet(data: ScanResponse): WalletAction? {
return when {
// check order is important
data.isTangemTwins() && !data.twinsIsTwinned() -> {
data.cardTypesResolver.isTangemTwins() && !data.twinsIsTwinned() -> {
WalletAction.EmptyWallet
}
data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
data.cardTypesResolver.getBlockchain() == Blockchain.Unknown &&
!data.cardTypesResolver.isMultiwalletAllowed() -> {
WalletAction.LoadData.Failure(TapError.UnknownBlockchain)
}
data.isDemoCard() -> {

View file

@ -1,16 +1,12 @@
package com.tangem.tap.domain.extensions
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.CardDTO
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TapWorkarounds.isTangemNote
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.features.wallet.redux.Artwork
@ -21,22 +17,12 @@ val CardDTO.remainingSignatures: Int?
val CardDTO.isWalletDataSupported: Boolean
get() = this.firmwareVersion.major >= 4
val CardDTO.isMultiwalletAllowed: Boolean
get() {
return !isTangemTwin() && !isStart2Coin && !isTangemNote && !isSaltPay &&
(firmwareVersion >= FirmwareVersion.MultiWalletAvailable ||
wallets.firstOrNull()?.curve == EllipticCurve.Secp256k1)
}
val CardDTO.isFirmwareMultiwalletAllowed: Boolean
get() = firmwareVersion >= FirmwareVersion.MultiWalletAvailable && settings.maxWalletsCount > 1
val CardDTO.isHdWalletAllowedByApp: Boolean
get() = settings.isHDWalletAllowed && !isSaltPay
val CardDTO.isTangemWallet: Boolean
get() = settings.isBackupAllowed
&& settings.isHDWalletAllowed
&& firmwareVersion >= FirmwareVersion.MultiWalletAvailable
&& !isSaltPay
@Suppress("UnnecessaryParentheses")
fun CardDTO.hasSignedHashes(): Boolean {
return wallets.any { (it.totalSignedHashes ?: 0) > 0 }

View file

@ -37,7 +37,7 @@ fun WalletManagerFactory.makeWalletManagerForApp(
val seedKey = wallet.extendedPublicKey
return when {
scanResponse.isTangemTwins() && scanResponse.secondTwinPublicKey != null -> {
scanResponse.cardTypesResolver.isTangemTwins() && scanResponse.secondTwinPublicKey != null -> {
makeTwinWalletManager(
walletPublicKey = wallet.publicKey,
pairPublicKey = scanResponse.secondTwinPublicKey!!.hexToBytes(),
@ -119,9 +119,9 @@ fun WalletManagerFactory.makePrimaryWalletManager(
scanResponse: ScanResponse,
): WalletManager? {
val blockchain = if (scanResponse.card.isTestCard) {
scanResponse.getBlockchain().getTestnetVersion() ?: return null
scanResponse.cardTypesResolver.getBlockchain().getTestnetVersion() ?: return null
} else {
scanResponse.getBlockchain()
scanResponse.cardTypesResolver.getBlockchain()
}
val derivationParams = getDerivationParams(null, scanResponse.card)
return makeWalletManagerForApp(
@ -142,7 +142,7 @@ private fun selectWallet(wallets: List<CardDTO.Wallet>): CardDTO.Wallet? {
fun WalletManagerFactory.makeSaltPayWalletManager(
scanResponse: ScanResponse,
): EthereumWalletManager {
val blockchain = scanResponse.getBlockchain()
val blockchain = scanResponse.cardTypesResolver.getBlockchain()
if (blockchain != Blockchain.SaltPay) {
error("WalletManager for the SaltPay can be created based only on Blockchain.SaltPay")
}

View file

@ -1,7 +1,5 @@
package com.tangem.tap.domain.model.builders
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.CardDTO
@ -37,24 +35,6 @@ class UserWalletBuilder(
}
}
private val ScanResponse.isMultiCurrency: Boolean
get() = when (productType) {
ProductType.Note -> false
ProductType.Twins -> false
ProductType.SaltPay -> false
ProductType.Start2Coin -> false
ProductType.Wallet -> when {
card.isStart2Coin -> false
card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable -> true
else -> {
val cardWallets = card.wallets
require(cardWallets.isNotEmpty()) { "Card wallets must not be empty" }
cardWallets.first().curve == EllipticCurve.Secp256k1
}
}
}
fun backupCardsIds(backupCardsIds: Set<String>?) = this.apply {
if (backupCardsIds != null) {
this.backupCardsIds = backupCardsIds
@ -72,7 +52,7 @@ class UserWalletBuilder(
artworkUrl = loadArtworkUrl(card.cardId, card.cardPublicKey),
cardsInWallet = backupCardsIds.plus(card.cardId),
scanResponse = this,
isMultiCurrency = isMultiCurrency,
isMultiCurrency = cardTypesResolver.isMultiwalletAllowed(),
)
}
}

View file

@ -122,7 +122,7 @@ object ScanCardProcessor {
?.let { it == scanResponse.card.cardId }
?: false
if (scanResponse.isSaltPayWallet() || !isTheSamePrimaryCard) {
if (scanResponse.cardTypesResolver.isSaltPayWallet() || !isTheSamePrimaryCard) {
onProgressStateChange(false)
showSaltPayTapVisaLogoCardDialog()
} else {
@ -186,8 +186,8 @@ object ScanCardProcessor {
store.dispatchOnMain(TwinCardsAction.IfTwinsPrepareState(scanResponse))
if (scanResponse.isSaltPay()) {
if (scanResponse.isSaltPayVisa()) {
if (scanResponse.cardTypesResolver.isSaltPay()) {
if (scanResponse.cardTypesResolver.isSaltPayVisa()) {
val (manager, config) = OnboardingSaltPayState.initDependency(scanResponse)
val result = OnboardingSaltPayHelper.isOnboardingCase(scanResponse, manager)
delay(500)

View file

@ -13,10 +13,9 @@ import com.tangem.common.extensions.toMapKey
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.common.map
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.KeyWalletPublicKey
import com.tangem.domain.common.ProductType
import com.tangem.domain.common.TapWorkarounds.derivationStyle
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
@ -56,7 +55,7 @@ private data class CreateWalletResponse(
}
class CreateProductWalletTask(
private val type: ProductType,
private val cardTypesResolver: CardTypesResolver,
) : CardSessionRunnable<CreateProductWalletTaskResponse> {
override val allowsRequestAccessCodeFromRepository: Boolean = false
@ -71,9 +70,10 @@ class CreateProductWalletTask(
}
val cardDto = CardDTO(card)
val commandProcessor = when (type) {
ProductType.Note -> CreateWalletTangemNote()
ProductType.Twins -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet")
val commandProcessor = when {
cardTypesResolver.isTangemNote() -> CreateWalletTangemNote(cardTypesResolver)
cardTypesResolver.isTangemTwins() ->
throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet")
else -> CreateWalletTangemWallet()
}
commandProcessor.proceed(cardDto, session) {
@ -95,7 +95,8 @@ class CreateProductWalletTask(
}
}
private class CreateWalletTangemNote : ProductCommandProcessor<CreateWalletResponse> {
private class CreateWalletTangemNote(private val cardTypesResolver: CardTypesResolver) :
ProductCommandProcessor<CreateWalletResponse> {
override fun proceed(
card: CardDTO,
session: CardSession,
@ -106,8 +107,8 @@ private class CreateWalletTangemNote : ProductCommandProcessor<CreateWalletRespo
return
}
val curvesSupportedByBlockchain = card.getTangemNoteBlockchain()?.getSupportedCurves()
if (curvesSupportedByBlockchain == null || curvesSupportedByBlockchain.isEmpty()) {
val curvesSupportedByBlockchain = cardTypesResolver.getBlockchain().getSupportedCurves().toSet()
if (curvesSupportedByBlockchain.isEmpty()) {
callback(CompletionResult.Failure(TangemSdkError.CardError()))
return
}
@ -183,8 +184,8 @@ private class CreateWalletTangemWallet : ProductCommandProcessor<CreateProductWa
else -> {
callback(
CompletionResult.Success(
CreateProductWalletTaskResponse(card = session.environment.card!!)
)
CreateProductWalletTaskResponse(card = session.environment.card!!),
),
)
}
}
@ -209,9 +210,9 @@ private class CreateWalletTangemWallet : ProductCommandProcessor<CreateProductWa
callback(
CompletionResult.Success(
CreateProductWalletTaskResponse(
card = session.environment.card!!, primaryCard = primaryCard
)
)
card = session.environment.card!!, primaryCard = primaryCard,
),
),
)
}
}

View file

@ -8,19 +8,23 @@ import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.TangemError
import com.tangem.common.core.TangemSdkError
import com.tangem.common.deserialization.WalletDataDeserializer
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.guard
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toByteArray
import com.tangem.common.extensions.toHexString
import com.tangem.common.extensions.toMapKey
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.common.tlv.Tlv
import com.tangem.common.tlv.TlvDecoder
import com.tangem.crypto.CryptoUtils
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.ProductType
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.isExcluded
import com.tangem.domain.common.TapWorkarounds.isNotSupportedInThatRelease
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TapWorkarounds.isTangemNote
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.domain.common.TwinsHelper
@ -30,10 +34,11 @@ 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.files.ReadFilesTask
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
import com.tangem.tap.domain.TapSdkError
import com.tangem.tap.domain.extensions.getPrimaryCurve
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
import com.tangem.tap.domain.extensions.isFirmwareMultiwalletAllowed
import com.tangem.tap.domain.tokens.UserTokensRepository
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.preferencesStorage
@ -66,7 +71,6 @@ class ScanProductTask(
}
val commandProcessor = when {
cardDto.isTangemNote -> ScanNoteProcessor()
cardDto.isTangemTwins -> ScanTwinProcessor()
else -> ScanWalletProcessor(userTokensRepository, additionalBlockchainsToDerive)
}
@ -126,15 +130,84 @@ private class ScanWalletProcessor(
session: CardSession,
callback: (result: CompletionResult<ScanResponse>) -> Unit,
) {
if (card.firmwareVersion.doubleValue >= 4.39) {
if (card.settings.maxWalletsCount == 1) {
readFile(card, session, callback)
return
}
}
createMissingWalletsIfNeeded(card, session, callback)
}
private fun readFile(
card: CardDTO,
session: CardSession,
callback: (result: CompletionResult<ScanResponse>) -> Unit,
) {
val funToContinue = { createMissingWalletsIfNeeded(card, session, callback) }
ReadFilesTask(fileName = "blockchainInfo", walletPublicKey = null).run(session) { result ->
when (result) {
is CompletionResult.Success -> {
val file = result.data.firstOrNull()
val counter = file?.counter
val signature = file?.signature
val tlv = file?.data?.let { Tlv.deserialize(it) }
val walletData = tlv?.let { WalletDataDeserializer.deserialize(TlvDecoder(tlv)) }
if (walletData == null || counter == null || signature == null) {
funToContinue()
return@run
}
val dataToVerify = card.cardId.hexToBytes() + file.data + counter.toByteArray()
val isVerified = CryptoUtils.verify(
publicKey = card.issuer.publicKey,
message = dataToVerify,
signature = signature,
)
if (!isVerified) {
funToContinue()
return@run
}
if (walletData.blockchain != "ANY") {
callback(
CompletionResult.Success(
ScanResponse(
card = card,
productType = ProductType.Note,
walletData = walletData,
),
),
)
} else {
funToContinue()
}
}
is CompletionResult.Failure -> {
when (result.error) {
is TangemSdkError.FileNotFound, is TangemSdkError.InsNotSupported -> {
funToContinue()
}
else -> callback(CompletionResult.Failure(result.error))
}
}
}
}
}
private fun createMissingWalletsIfNeeded(
card: CardDTO,
session: CardSession,
callback: (result: CompletionResult<ScanResponse>) -> Unit,
) {
if (card.wallets.isEmpty() || !card.isMultiwalletAllowed) {
if (card.wallets.isEmpty() || !card.isFirmwareMultiwalletAllowed) {
startLinkingForBackupIfNeeded(card, session, callback)
return
}
@ -195,9 +268,8 @@ private class ScanWalletProcessor(
session: CardSession,
callback: (result: CompletionResult<ScanResponse>) -> Unit,
) {
val productType = when {
card.isSaltPay -> ProductType.SaltPay
card.isStart2Coin -> ProductType.Start2Coin
val productType = when (card.isSaltPay) {
true -> ProductType.SaltPay
else -> ProductType.Wallet
}
scope.launch {
@ -267,7 +339,12 @@ private class ScanWalletProcessor(
}
if (additionalBlockchainsToDerive != null) {
blockchainsToDerive.addAll(
additionalBlockchainsToDerive.map { BlockchainNetwork(it, card) },
additionalBlockchainsToDerive.map {
BlockchainNetwork(
blockchain = it,
card = card,
)
},
)
}
if (!card.useOldStyleDerivation) {
@ -278,7 +355,12 @@ private class ScanWalletProcessor(
Blockchain.RSK,
Blockchain.Fantom, Blockchain.FantomTestnet,
Blockchain.Avalanche, Blockchain.AvalancheTestnet,
).map { BlockchainNetwork(it, card) },
).map {
BlockchainNetwork(
blockchain = it,
card = card,
)
},
)
}
return blockchainsToDerive.distinct()

View file

@ -66,7 +66,7 @@ internal class DefaultWalletManagersRepository(
): CompletionResult<WalletManager> {
val scanResponse = userWallet.scanResponse
val blockchain = blockchainNetwork?.blockchain
?: scanResponse.getBlockchain().let { blockchain ->
?: scanResponse.cardTypesResolver.getBlockchain().let { blockchain ->
if (scanResponse.card.isTestCard) blockchain.getTestnetVersion() else blockchain
}
val derivationParams = getDerivationParams(
@ -156,7 +156,7 @@ internal class DefaultWalletManagersRepository(
): CompletionResult<WalletManager> {
val walletManager = this
return catching {
val tokens = blockchainNetwork?.tokens ?: listOfNotNull(scanResponse.getPrimaryToken())
val tokens = blockchainNetwork?.tokens ?: listOfNotNull(scanResponse.cardTypesResolver.getPrimaryToken())
if (tokens != cardTokens) {
cardTokens.clear()

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.details.redux
import com.tangem.blockchain.common.Wallet
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.entities.FiatCurrency
import org.rekotlin.Action
@ -27,7 +28,7 @@ sealed class DetailsAction : Action {
object ScanCard : DetailsAction()
data class PrepareCardSettingsData(val card: CardDTO) : DetailsAction()
data class PrepareCardSettingsData(val card: CardDTO, val cardTypesResolver: CardTypesResolver) : DetailsAction()
object ResetCardSettingsData : DetailsAction()
sealed class ManageSecurity : DetailsAction() {

View file

@ -82,7 +82,12 @@ class DetailsMiddleware {
val isSameWallet = currentUserWalletId == scannedUserWalletId
if (isSameWallet) {
store.dispatchOnMain(DetailsAction.PrepareCardSettingsData(scanResponse.card))
store.dispatchOnMain(
DetailsAction.PrepareCardSettingsData(
scanResponse.card,
scanResponse.cardTypesResolver,
),
)
} else {
store.dispatchDialogShow(
AppDialog.SimpleOkDialogRes(

View file

@ -1,11 +1,8 @@
package com.tangem.tap.features.details.redux
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TapWorkarounds.isTangemNote
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.isTangemTwin
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.extensions.isWalletDataSupported
import com.tangem.tap.domain.extensions.signedHashesCount
@ -27,7 +24,11 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
handlePrepareScreen(action)
}
is DetailsAction.PrepareCardSettingsData -> {
handlePrepareCardSettingsScreen(action.card, detailsState)
handlePrepareCardSettingsScreen(
card = action.card,
cardTypesResolver = action.cardTypesResolver,
state = detailsState,
)
}
is DetailsAction.ResetCardSettingsData -> detailsState.copy(cardSettingsState = null)
is DetailsAction.ResetToFactory -> {
@ -59,17 +60,22 @@ private fun handlePrepareScreen(
)
}
private fun handlePrepareCardSettingsScreen(card: CardDTO, state: DetailsState): DetailsState {
private fun handlePrepareCardSettingsScreen(
card: CardDTO,
cardTypesResolver: CardTypesResolver,
state: DetailsState,
):
DetailsState {
val cardSettingsState = CardSettingsState(
cardInfo = card.toCardInfo(),
manageSecurityState = prepareSecurityOptions(card),
cardInfo = card.toCardInfo(cardTypesResolver),
manageSecurityState = prepareSecurityOptions(card, cardTypesResolver),
card = card,
resetCardAllowed = isResetToFactoryAllowedByCard(card),
resetCardAllowed = isResetToFactoryAllowedByCard(card, cardTypesResolver),
)
return state.copy(cardSettingsState = cardSettingsState)
}
private fun prepareSecurityOptions(card: CardDTO): ManageSecurityState {
private fun prepareSecurityOptions(card: CardDTO, cardTypesResolver: CardTypesResolver): ManageSecurityState {
val securityOption = when {
card.isAccessCodeSet -> {
SecurityOption.AccessCode
@ -84,13 +90,14 @@ private fun prepareSecurityOptions(card: CardDTO): ManageSecurityState {
}
}
val allowedSecurityOptions = when {
card.isStart2Coin || card.isTangemNote || card.isSaltPay -> {
cardTypesResolver.isStart2Coin() || cardTypesResolver.isTangemNote() || cardTypesResolver.isSaltPay() -> {
EnumSet.of(SecurityOption.LongTap)
}
card.settings.isBackupAllowed -> {
EnumSet.of(securityOption)
}
else -> prepareAllowedSecurityOptions(card, securityOption)
else ->
prepareAllowedSecurityOptions(cardTypesResolver = cardTypesResolver, currentSecurityOption = securityOption)
}
return ManageSecurityState(
currentOption = securityOption,
@ -99,10 +106,11 @@ private fun prepareSecurityOptions(card: CardDTO): ManageSecurityState {
)
}
private fun isResetToFactoryAllowedByCard(card: CardDTO): Boolean {
private fun isResetToFactoryAllowedByCard(card: CardDTO, cardTypesResolver: CardTypesResolver): Boolean {
val notAllowedByAnyWallet = card.wallets.any { it.settings.isPermanent }
val notAllowedByCard = notAllowedByAnyWallet ||
card.isWalletDataSupported && !card.isTangemNote && !card.settings.isBackupAllowed || card.isSaltPay
card.isWalletDataSupported && !cardTypesResolver.isTangemNote() && !card.settings.isBackupAllowed ||
card.isSaltPay
return !notAllowedByCard
}
@ -132,7 +140,7 @@ private fun handleSecurityAction(
// Setting options to show only LongTap from now on for non-twins
val manageSecurityState = state.cardSettingsState?.manageSecurityState?.copy(
currentOption = state.cardSettingsState.manageSecurityState.selectedOption,
allowedOptions = state.scanResponse?.card?.let {
allowedOptions = state.scanResponse?.cardTypesResolver?.let {
prepareAllowedSecurityOptions(
it, state.cardSettingsState.manageSecurityState.selectedOption,
)
@ -168,12 +176,12 @@ private fun handlePrivacyAction(
}
private fun prepareAllowedSecurityOptions(
card: CardDTO?,
cardTypesResolver: CardTypesResolver,
currentSecurityOption: SecurityOption?,
): EnumSet<SecurityOption> {
val allowedSecurityOptions = EnumSet.of(SecurityOption.LongTap)
if (card?.isTangemTwin() == true) {
if (cardTypesResolver.isTangemTwins()) {
allowedSecurityOptions.add(SecurityOption.PassCode)
}
if (currentSecurityOption == SecurityOption.AccessCode) {
@ -186,7 +194,7 @@ private fun prepareAllowedSecurityOptions(
}
@Suppress("MagicNumber")
private fun CardDTO.toCardInfo(): CardInfo {
private fun CardDTO.toCardInfo(cardTypesResolver: CardTypesResolver): CardInfo {
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
val issuer = this.issuer.name
val signedHashes = this.signedHashesCount()
@ -195,7 +203,7 @@ private fun CardDTO.toCardInfo(): CardInfo {
cardId = cardId,
issuer = issuer,
signedHashes = signedHashes,
isTwin = isTangemTwins,
isTwin = cardTypesResolver.isTangemTwins(),
hasBackup = backupStatus?.isActive == true,
)
}

View file

@ -29,9 +29,6 @@ data class DetailsState(
val twinCardsState: TwinCardsState by ReadOnlyProperty<Any, TwinCardsState> { _, _ ->
store.state.twinCardsState
}
val isTangemTwins: Boolean
get() = store.state.globalState.scanResponse?.isTangemTwins() == true
}
data class CardInfo(

View file

@ -14,7 +14,6 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.domain.walletconnect.BnbHelper
import com.tangem.tap.domain.walletconnect.WalletConnectManager
@ -290,7 +289,7 @@ class WalletConnectMiddleware {
blockchain: Blockchain,
) {
val card = scanResponse.card
if (!card.isMultiwalletAllowed) {
if (!scanResponse.cardTypesResolver.isMultiwalletAllowed()) {
store.dispatchOnMain(WalletConnectAction.UnsupportedCard)
return
}

View file

@ -9,7 +9,6 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.details.redux.DetailsState
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
@ -23,22 +22,24 @@ class DetailsViewModel(private val store: Store<AppState>) {
@Suppress("ComplexMethod")
fun updateState(state: DetailsState): DetailsScreenState {
val cardTypesResolver = state.scanResponse?.cardTypesResolver
val settings = SettingsElement.values().mapNotNull {
when (it) {
SettingsElement.WalletConnect -> {
if (state.scanResponse?.card?.isMultiwalletAllowed == true) it else null
if (cardTypesResolver?.isMultiwalletAllowed() == true) it else null
}
SettingsElement.SendFeedback -> if (state.scanResponse?.card?.isSaltPay != true) it else null
SettingsElement.SendFeedback ->
if (cardTypesResolver?.isSaltPay() != true) it else null
SettingsElement.LinkMoreCards -> {
// if (state.createBackupAllowed) it else null
// TODO: SaltPay: temporary excluding backup process for Visa cards
if (state.createBackupAllowed && state.scanResponse?.card?.isSaltPay != true) it else null
if (state.createBackupAllowed && cardTypesResolver?.isSaltPay() != true) it else null
}
SettingsElement.PrivacyPolicy -> {
if (state.privacyPolicyUrl != null) it else null
}
SettingsElement.AppSettings -> if (state.isBiometricsAvailable) it else null
SettingsElement.AppCurrency -> if (state.scanResponse?.card?.isMultiwalletAllowed != true) it else null
SettingsElement.AppCurrency -> if (cardTypesResolver?.isMultiwalletAllowed() != true) it else null
// SettingsElement.ReferralProgram -> if (state.scanResponse?.card?.isTangemWallet == true) it else null
else -> it
}

View file

@ -22,7 +22,7 @@ object OnboardingHelper {
val cardInfoStorage = preferencesStorage.usedCardsPrefStorage
val cardId = response.card.cardId
return when {
response.isTangemTwins() -> {
response.cardTypesResolver.isTangemTwins() -> {
if (!response.twinsIsTwinned()) {
true
} else {

View file

@ -87,9 +87,9 @@ private fun handleOtherCardsAction(action: Action) {
onboardingManager.scanResponse = updatedResponse
onboardingManager.activationStarted(updatedCard.cardId)
val primaryBlockchain = updatedResponse.getBlockchain()
val primaryBlockchain = updatedResponse.cardTypesResolver.getBlockchain()
val blockchainNetworks = if (primaryBlockchain != Blockchain.Unknown) {
val primaryToken = updatedResponse.getPrimaryToken()
val primaryToken = updatedResponse.cardTypesResolver.getPrimaryToken()
val blockchainNetwork =
BlockchainNetwork(
blockchain = primaryBlockchain,

View file

@ -16,7 +16,7 @@ private fun internalReduce(action: Action, state: AppState): TwinCardsState {
when (action) {
is TwinCardsAction.IfTwinsPrepareState -> {
state = if (action.scanResponse.isTangemTwins()) {
state = if (action.scanResponse.cardTypesResolver.isTangemTwins()) {
val cardNumber = action.scanResponse.card.getTwinCardNumber() ?: return state
TwinCardsState(

View file

@ -80,11 +80,11 @@ private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch:
card.wallets.isNotEmpty() && card.backupStatus?.isActive == true -> {
when {
// check for unfinished backup for saltPay cards. See more
scanResponse.isSaltPay() && backupService.hasIncompletedBackup -> {
scanResponse.cardTypesResolver.isSaltPay() && backupService.hasIncompletedBackup -> {
store.dispatch(OnboardingWalletAction.ResumeBackup)
}
scanResponse.isSaltPay() -> {
scanResponse.cardTypesResolver.isSaltPay() -> {
store.dispatch(OnboardingWalletAction.GetToSaltPayStep)
store.dispatch(BackupAction.FinishBackup)
}

View file

@ -35,30 +35,31 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingWallet
}
private object ReducerForGlobalAction {
fun reduce(action: GlobalAction.Onboarding, state: OnboardingWalletState): OnboardingWalletState {
return when (action) {
is GlobalAction.Onboarding.Start -> {
OnboardingWalletState(
backupState = state.backupState.copy(
maxBackupCards = if (action.scanResponse.isSaltPay()) 1 else 2,
canSkipBackup = if (action.scanResponse.isSaltPay()) false else action.canSkipBackup,
),
isSaltPay = action.scanResponse.isSaltPay(),
)
fun reduce(action: GlobalAction.Onboarding, state: OnboardingWalletState): OnboardingWalletState {
return when (action) {
is GlobalAction.Onboarding.Start -> {
OnboardingWalletState(
backupState = state.backupState.copy(
maxBackupCards = if (action.scanResponse.cardTypesResolver.isSaltPay()) 1 else 2,
canSkipBackup =
if (action.scanResponse.cardTypesResolver.isSaltPay()) false else action.canSkipBackup,
),
isSaltPay = action.scanResponse.cardTypesResolver.isSaltPay(),
)
}
is GlobalAction.Onboarding.StartForUnfinishedBackup -> {
OnboardingWalletState(
backupState = state.backupState.copy(
maxBackupCards = action.addedBackupCardsCount,
canSkipBackup = false,
),
isSaltPay = false,
)
}
else -> state
}
is GlobalAction.Onboarding.StartForUnfinishedBackup -> {
OnboardingWalletState(
backupState = state.backupState.copy(
maxBackupCards = action.addedBackupCardsCount,
canSkipBackup = false,
),
isSaltPay = false,
)
}
else -> state
}
}
}
private object BackupReducer {
@Suppress("ComplexMethod")

View file

@ -64,7 +64,7 @@ data class OnboardingSaltPayState(
paymentologyService: PaymentologyApiService,
kycProvider: KYCProvider,
): SaltPayActivationManager {
if (!scanResponse.isSaltPay()) {
if (!scanResponse.cardTypesResolver.isSaltPay()) {
throw IllegalArgumentException("Can't initialize the OnboardingSaltPayMiddleware if card is not SalPay")
}
val saltPaySingleWallet = scanResponse.card.wallets.firstOrNull().guard {

View file

@ -63,7 +63,7 @@ data class WalletState(
}
val isTangemTwins: Boolean
get() = store.state.globalState.scanResponse?.isTangemTwins() == true
get() = store.state.globalState.scanResponse?.cardTypesResolver?.isTangemTwins() == true
val isExchangeServiceFeatureOn: Boolean
get() = store.state.globalState.exchangeManager.featureIsSwitchedOn()

View file

@ -14,7 +14,6 @@ import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
import com.tangem.tap.domain.extensions.hasSignedHashes
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
import com.tangem.tap.features.demo.isDemoCard
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.network.NetworkConnectivity
@ -120,9 +119,9 @@ class WarningsMiddleware {
private fun checkIfWarningNeeded(
scanResponse: ScanResponse,
): WarningMessage? {
if (scanResponse.isTangemTwins() || scanResponse.isDemoCard()) return null
if (scanResponse.cardTypesResolver.isTangemTwins() || scanResponse.isDemoCard()) return null
if (scanResponse.card.isMultiwalletAllowed) {
if (scanResponse.cardTypesResolver.isMultiwalletAllowed()) {
val isBackupForbidden = with(scanResponse.card.settings) { !(isBackupAllowed || isHDWalletAllowed) }
return if (scanResponse.card.hasSignedHashes() && isBackupForbidden) {
WarningMessagesManager.signedHashesMultiWalletWarning()
@ -154,7 +153,9 @@ class WarningsMiddleware {
val card = scanResponse?.card
if (card == null || preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) return
if (scanResponse.isTangemTwins() || card.isMultiwalletAllowed) return
if (scanResponse.cardTypesResolver.isTangemTwins() || scanResponse.cardTypesResolver.isMultiwalletAllowed()) {
return
}
val validator = store.state.walletState.walletManagers.firstOrNull()
as? SignatureCountValidator

View file

@ -16,7 +16,6 @@ import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.getArtworkUrl
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.Currency
@ -382,7 +381,6 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
private fun CardDTO.findCardsCount(): Int? {
return (this.backupStatus as? CardDTO.BackupStatus.Active)?.cardCount?.inc()
?.takeIf { this.isMultiwalletAllowed }
}
fun createAddressList(wallet: Wallet?, walletAddresses: WalletAddresses? = null): WalletAddresses? {

View file

@ -218,7 +218,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
private fun setupCardImage(state: WalletState) {
//TODO: SaltPay: remove hardCode
if (store.state.globalState.scanResponse?.isSaltPay() == true) {
if (store.state.globalState.scanResponse?.cardTypesResolver?.isSaltPay() == true) {
binding.ivCard.load(R.drawable.img_salt_pay_visa) {
scale(Scale.FIT)
crossfade(enable = true)

View file

@ -108,7 +108,7 @@ internal object WalletSelectorReducer {
)
} else {
UserWalletModel.Type.SingleCurrency(
blockchainName = scanResponse.getBlockchain().fullName,
blockchainName = scanResponse.cardTypesResolver.getBlockchain().fullName,
)
}
}

View file

@ -61,7 +61,10 @@ object Versions {
// region Tangem
const val tangemBlockchainSdk = "develop-151"
const val tangemCardSdk = "develop-179"
// const val tangemBlockchainSdk = "0.0.1" // Keep it! - used for local builds
const val tangemCardSgk = "develop-180"
// const val tangemCardSgk = "0.0.1" // Keep it! - used for local builds
// endregion Tangem
// region Tools

View file

@ -135,6 +135,9 @@ data class CardDTO(
type = firmwareVersion.type,
)
val doubleValue: Double
get() = "$major.$minor".toDouble()
override fun compareTo(other: SdkFirmwareVersion): Int = when {
major != other.major -> major.compareTo(other.major)
minor != other.minor -> minor.compareTo(other.minor)

View file

@ -0,0 +1,19 @@
package com.tangem.domain.common
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
interface CardTypesResolver {
fun isTangemNote(): Boolean
fun isTangemWallet(): Boolean
fun isSaltPay(): Boolean
fun isSaltPayVisa(): Boolean
fun isSaltPayWallet(): Boolean
fun isTangemTwins(): Boolean
fun isStart2Coin(): Boolean
fun isMultiwalletAllowed(): Boolean
fun getBlockchain(): Blockchain
fun getPrimaryToken(): Token?
}

View file

@ -1,16 +1,11 @@
package com.tangem.domain.common
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.common.card.EllipticCurve
import com.tangem.common.card.WalletData
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.toMapKey
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.domain.common.TapWorkarounds.getTangemNoteBlockchain
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.domain.common.TapWorkarounds.isSaltPayVisa
import com.tangem.domain.common.TapWorkarounds.isSaltPayWallet
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.operations.CommandResponse
@ -28,36 +23,13 @@ data class ScanResponse(
val derivedKeys: Map<KeyWalletPublicKey, ExtendedPublicKeysMap> = mapOf(),
val primaryCard: PrimaryCard? = null,
) : CommandResponse {
fun getBlockchain(): Blockchain {
return when (productType) {
ProductType.Start2Coin -> if (card.isTestCard) Blockchain.BitcoinTestnet else Blockchain.Bitcoin
ProductType.SaltPay -> Blockchain.SaltPay
ProductType.Note -> card.getTangemNoteBlockchain() ?: Blockchain.Unknown
else -> {
val blockchainName: String = walletData?.blockchain ?: return Blockchain.Unknown
Blockchain.fromId(blockchainName)
}
}
}
fun getPrimaryToken(): Token? {
if (card.isSaltPay) return SaltPayWorkaround.tokenFrom(getBlockchain())
val cardTypesResolver: CardTypesResolver = TangemCardTypesResolver(
card = card,
productType = productType,
walletData = walletData,
)
val cardToken = walletData?.token ?: return null
return Token(
cardToken.name,
cardToken.symbol,
cardToken.contractAddress,
cardToken.decimals,
)
}
fun isTangemNote(): Boolean = productType == ProductType.Note
fun isTangemWallet(): Boolean = productType == ProductType.Wallet
fun isSaltPay(): Boolean = productType == ProductType.SaltPay
fun isSaltPayVisa(): Boolean = card.isSaltPayVisa
fun isSaltPayWallet(): Boolean = card.isSaltPayWallet
fun isTangemTwins(): Boolean = productType == ProductType.Twins
fun twinsIsTwinned(): Boolean = card.isTangemTwins && walletData != null && secondTwinPublicKey != null
fun supportsHdWallet(): Boolean = card.settings.isHDWalletAllowed
fun supportsBackup(): Boolean = card.settings.isBackupAllowed
@ -88,8 +60,4 @@ data class ScanResponse(
}
}
enum class ProductType {
Note, Twins, Wallet, SaltPay, Start2Coin
}
typealias KeyWalletPublicKey = ByteArrayKey
typealias KeyWalletPublicKey = ByteArrayKey

View file

@ -0,0 +1,69 @@
package com.tangem.domain.common
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.common.card.EllipticCurve
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.card.WalletData
import com.tangem.common.extensions.guard
import com.tangem.domain.common.TapWorkarounds.getTangemNoteBlockchain
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.domain.common.TapWorkarounds.isSaltPayVisa
import com.tangem.domain.common.TapWorkarounds.isSaltPayWallet
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TapWorkarounds.isTestCard
class TangemCardTypesResolver(
private val card: CardDTO, private val productType: ProductType, private val walletData: WalletData?,
) : CardTypesResolver {
override fun isTangemNote(): Boolean = productType == ProductType.Note
override fun isTangemWallet(): Boolean = card.settings.isBackupAllowed
&& card.settings.isHDWalletAllowed
&& card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable
&& !card.isSaltPay
override fun isSaltPay(): Boolean = productType == ProductType.SaltPay
override fun isSaltPayVisa(): Boolean = card.isSaltPayVisa
override fun isSaltPayWallet(): Boolean = card.isSaltPayWallet
override fun isTangemTwins(): Boolean = productType == ProductType.Twins
override fun isStart2Coin(): Boolean = card.isStart2Coin
override fun isMultiwalletAllowed(): Boolean =
!isTangemTwins() && !card.isStart2Coin && !isTangemNote() && !isSaltPay() &&
(card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable ||
card.wallets.firstOrNull()?.curve == EllipticCurve.Secp256k1)
override fun getBlockchain(): Blockchain {
return when (productType) {
ProductType.Start2Coin -> if (card.isTestCard) Blockchain.BitcoinTestnet else Blockchain.Bitcoin
ProductType.SaltPay -> Blockchain.SaltPay
else -> {
val blockchainName: String = walletData?.blockchain.guard {
if (productType == ProductType.Note) {
return card.getTangemNoteBlockchain() ?: Blockchain.Unknown
} else {
return Blockchain.Unknown
}
}
Blockchain.fromId(blockchainName)
}
}
}
override fun getPrimaryToken(): Token? {
if (isSaltPay()) return SaltPayWorkaround.tokenFrom(getBlockchain())
val cardToken = walletData?.token ?: return null
return Token(
cardToken.name,
cardToken.symbol,
cardToken.contractAddress,
cardToken.decimals,
)
}
}
enum class ProductType {
Note, Twins, Wallet, SaltPay, Start2Coin
}

View file

@ -75,6 +75,10 @@ object TapWorkarounds {
private val excludedIssuers = listOf("TTM BANK")
@Deprecated(
"Now blockchain is read form files (CardTypesResolver.getBlockchain), " +
"but for previously saved cards this method is still used",
)
fun CardDTO.getTangemNoteBlockchain(): Blockchain? =
tangemNoteBatches[batchId] ?: if (isSaltPay) Blockchain.Gnosis else null