Updated on 2026-08-14
This commit is contained in:
commit
e6fcb9e2fe
35 changed files with 332 additions and 207 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()!!)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() -> {
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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? {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ internal object WalletSelectorReducer {
|
|||
)
|
||||
} else {
|
||||
UserWalletModel.Type.SingleCurrency(
|
||||
blockchainName = scanResponse.getBlockchain().fullName,
|
||||
blockchainName = scanResponse.cardTypesResolver.getBlockchain().fullName,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue