From e421ca8d32d91670b85686debd32ec186e3b8025 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 28 Aug 2023 10:51:17 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../presentation/routers/CustomTokenRouter.kt | 8 ++++ .../routers/DefaultCustomTokenRouter.kt | 13 +++++++ .../viewmodels/AddCustomTokenViewModel.kt | 18 ++++++++- .../presentation/models/SupportTokensState.kt | 11 ++++++ .../router/DefaultTokensListRouter.kt | 25 ++++++------ .../presentation/router/TokensListRouter.kt | 5 +++ .../viewmodels/TokensListViewModel.kt | 39 +++++++++++++------ .../domain/common/configs/CardConfig.kt | 2 +- .../common/configs/GenericCardConfig.kt | 3 +- .../common/configs/MultiWalletCardConfig.kt | 35 +++++++++++++++++ .../domain/common/extensions/CardSdk.kt | 32 +++++++++++++-- 11 files changed, 160 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/models/SupportTokensState.kt create mode 100644 domain/legacy/src/main/java/com/tangem/domain/common/configs/MultiWalletCardConfig.kt diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/CustomTokenRouter.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/CustomTokenRouter.kt index bcd32893fe..4444aeea64 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/CustomTokenRouter.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/CustomTokenRouter.kt @@ -1,5 +1,7 @@ package com.tangem.tap.features.customtoken.impl.presentation.routers +import com.tangem.blockchain.common.Blockchain + /** * Custom token feature router * @@ -12,4 +14,10 @@ internal interface CustomTokenRouter { /** Open wallet (main) screen */ fun openWalletScreen() + + /** Open alert if solana network is unsupported + * + * @param blockchain blockchain to show alert + */ + fun openUnsupportedNetworkAlert(blockchain: Blockchain) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/DefaultCustomTokenRouter.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/DefaultCustomTokenRouter.kt index 7afec7d5a5..af1ebb8795 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/DefaultCustomTokenRouter.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/routers/DefaultCustomTokenRouter.kt @@ -1,8 +1,12 @@ package com.tangem.tap.features.customtoken.impl.presentation.routers +import com.tangem.blockchain.common.Blockchain import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction +import com.tangem.tap.common.extensions.dispatchDialogShow +import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.store +import com.tangem.wallet.R /** Default implementation of custom token feature router */ internal class DefaultCustomTokenRouter : CustomTokenRouter { @@ -14,4 +18,13 @@ internal class DefaultCustomTokenRouter : CustomTokenRouter { override fun openWalletScreen() { store.dispatch(NavigationAction.PopBackTo(screen = AppScreen.Wallet)) } + + override fun openUnsupportedNetworkAlert(blockchain: Blockchain) { + val alert = AppDialog.SimpleOkDialogRes( + headerId = R.string.common_warning, + messageId = R.string.alert_manage_tokens_unsupported_curve_message, + args = listOf(blockchain.fullName), + ) + store.dispatchDialogShow(alert) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt index 0a5151fd23..e99d45b564 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt @@ -635,6 +635,15 @@ internal class AddCustomTokenViewModel @Inject constructor( return derivationNetwork.derivationPath(derivationStyle) } + private fun isUnsupportedBlockchain(blockchain: Blockchain): Boolean { + val scanResponse = reduxStateHolder.scanResponse + val canHandleToken = scanResponse?.card?.canHandleBlockchain( + blockchain = blockchain, + cardTypesResolver = scanResponse.cardTypesResolver, + ) ?: false + return !canHandleToken + } + private inner class ActionsHandler(private val featureRouter: CustomTokenRouter) { fun onBackButtonClick() { @@ -747,6 +756,11 @@ internal class AddCustomTokenViewModel @Inject constructor( fun onAddCustomTokenClick() { if (!isNetworkSelected()) return + val blockchain = uiState.form.networkSelectorField.selectedItem.blockchain + if (isUnsupportedBlockchain(blockchain)) { + featureRouter.openUnsupportedNetworkAlert(blockchain) + return + } val currency = when (getCustomTokenType()) { CustomTokenType.TOKEN -> { @@ -758,13 +772,13 @@ internal class AddCustomTokenViewModel @Inject constructor( decimals = requireNotNull(uiState.form.decimalsInputField.value.toIntOrNull()), id = foundToken?.id, ), - network = uiState.form.networkSelectorField.selectedItem.blockchain, + network = blockchain, derivationPath = getDerivationPath(), ) } CustomTokenType.BLOCKCHAIN -> { CustomCurrency.CustomBlockchain( - network = uiState.form.networkSelectorField.selectedItem.blockchain, + network = blockchain, derivationPath = getDerivationPath(), ) } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/models/SupportTokensState.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/models/SupportTokensState.kt new file mode 100644 index 0000000000..e815d5d1e7 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/models/SupportTokensState.kt @@ -0,0 +1,11 @@ +package com.tangem.tap.features.tokens.impl.presentation.models + +/** + * State that shows is token support by given card + */ +sealed class SupportTokensState { + + object SolanaNetworkUnsupported : SupportTokensState() + object UnsupportedCurve : SupportTokensState() + object SupportedToken : SupportTokensState() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt index 54cca1b8dc..93ae4e1b17 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt @@ -51,17 +51,20 @@ internal class DefaultTokensListRouter( } override fun openUnsupportedNetworkAlert(blockchain: Blockchain) { - val alert = when (blockchain) { - Blockchain.Solana -> AppDialog.SimpleOkDialogRes( - headerId = R.string.common_warning, - messageId = R.string.alert_manage_tokens_unsupported_message, - ) - else -> AppDialog.SimpleOkDialogRes( - headerId = R.string.common_warning, - messageId = R.string.alert_manage_tokens_unsupported_curve_message, - args = listOf(blockchain.fullName), - ) - } + val alert = AppDialog.SimpleOkDialogRes( + headerId = R.string.common_warning, + messageId = R.string.alert_manage_tokens_unsupported_curve_message, + args = listOf(blockchain.fullName), + ) store.dispatchDialogShow(alert) } + + override fun openSolanaTokensNotSupportAlert() { + store.dispatchDialogShow( + AppDialog.SimpleOkDialogRes( + headerId = R.string.common_warning, + messageId = R.string.alert_manage_tokens_unsupported_message, + ), + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/TokensListRouter.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/TokensListRouter.kt index b80e835962..ee87445d1d 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/TokensListRouter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/TokensListRouter.kt @@ -35,4 +35,9 @@ internal interface TokensListRouter { * @param blockchain blockchain to show alert */ fun openUnsupportedNetworkAlert(blockchain: Blockchain) + + /** + * Open alert with Solana tokens error + */ + fun openSolanaTokensNotSupportAlert() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt index ffb1de2c52..1cac67b7b2 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt @@ -17,6 +17,7 @@ import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.common.extensions.canHandleBlockchain import com.tangem.domain.common.extensions.canHandleToken import com.tangem.domain.common.extensions.fromNetworkId +import com.tangem.domain.common.extensions.supportedTokens import com.tangem.domain.common.util.cardTypesResolver import com.tangem.tap.common.extensions.fullNameWithoutTestnet import com.tangem.tap.common.extensions.getGreyedOutIconRes @@ -24,6 +25,7 @@ import com.tangem.tap.common.extensions.getNetworkName import com.tangem.tap.features.tokens.impl.domain.TokensListInteractor import com.tangem.tap.features.tokens.impl.domain.models.Token import com.tangem.tap.features.tokens.impl.domain.models.Token.Network +import com.tangem.tap.features.tokens.impl.presentation.models.SupportTokensState import com.tangem.tap.features.tokens.impl.presentation.models.TokensListArgs import com.tangem.tap.features.tokens.impl.presentation.router.TokensListRouter import com.tangem.tap.features.tokens.impl.presentation.states.NetworkItemState @@ -42,6 +44,7 @@ import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.plus +import timber.log.Timber import javax.inject.Inject import com.tangem.blockchain.common.Token as BlockchainToken @@ -355,24 +358,38 @@ internal class TokensListViewModel @Inject constructor( toggledNetwork.changeToggleState() } } else { - if (isUnsupportedToken(token.blockchain)) { - router.openUnsupportedNetworkAlert(token.blockchain) - } else { - analyticsSender.sendWhenTokenAdded(token.token) - changedTokensList.add(token) - toggledNetwork.changeToggleState() + when (isUnsupportedToken(token.blockchain)) { + SupportTokensState.SolanaNetworkUnsupported -> router.openSolanaTokensNotSupportAlert() + SupportTokensState.SupportedToken -> { + analyticsSender.sendWhenTokenAdded(token.token) + changedTokensList.add(token) + toggledNetwork.changeToggleState() + } + SupportTokensState.UnsupportedCurve -> router.openUnsupportedNetworkAlert(token.blockchain) + null -> Timber.e("Something went wrong in isUnsupportedToken (no scanResponse found)") } } } } - private fun isUnsupportedToken(blockchain: Blockchain): Boolean { + private fun isUnsupportedToken(blockchain: Blockchain): SupportTokensState? { val scanResponse = reduxStateHolder.scanResponse - val canHandleToken = scanResponse?.card?.canHandleToken( + val cardTypesResolver = scanResponse?.cardTypesResolver ?: return null + val supportedTokens = scanResponse.card.supportedTokens(cardTypesResolver) + + // refactor this later by moving all this logic in card config + if (!supportedTokens.contains(Blockchain.Solana)) { + return SupportTokensState.SolanaNetworkUnsupported + } + val canHandleToken = scanResponse.card.canHandleToken( + supportedTokens = supportedTokens, blockchain = blockchain, - cardTypesResolver = scanResponse.cardTypesResolver, - ) ?: false - return !canHandleToken + cardTypesResolver = cardTypesResolver, + ) + if (!canHandleToken) { + return SupportTokensState.UnsupportedCurve + } + return SupportTokensState.SupportedToken } private fun isUnsupportedBlockchain(blockchain: Blockchain): Boolean { diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt index cb34af8d75..51ff7de9b3 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt @@ -20,7 +20,7 @@ sealed interface CardConfig { if (cardDTO.settings.isBackupAllowed && cardDTO.settings.isHDWalletAllowed && cardDTO.firmwareVersion >= FirmwareVersion.MultiWalletAvailable ) { - return GenericCardConfig + return MultiWalletCardConfig } return GenericCardConfig } diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt index 8a57584f27..4955dbdb11 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt @@ -9,11 +9,10 @@ object GenericCardConfig : CardConfig { get() = listOf( EllipticCurve.Secp256k1, EllipticCurve.Ed25519, - EllipticCurve.Bls12381G2Aug, ) /** - * Old logic to determine primary curve for blockchain in TangemWallet + * Old logic to determine primary curve for blockchain */ override fun primaryCurve(blockchain: Blockchain): EllipticCurve? { return when { diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/MultiWalletCardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/MultiWalletCardConfig.kt new file mode 100644 index 0000000000..414d7a528a --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/MultiWalletCardConfig.kt @@ -0,0 +1,35 @@ +package com.tangem.domain.common.configs + +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.card.EllipticCurve +import timber.log.Timber + +object MultiWalletCardConfig : CardConfig { + override val mandatoryCurves: List + get() = listOf( + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Bls12381G2Aug, + ) + + /** + * Old logic to determine primary curve for blockchain in TangemWallet + */ + override fun primaryCurve(blockchain: Blockchain): EllipticCurve? { + return when { + blockchain.getSupportedCurves().contains(EllipticCurve.Secp256k1) -> { + EllipticCurve.Secp256k1 + } + blockchain.getSupportedCurves().contains(EllipticCurve.Ed25519) -> { + EllipticCurve.Ed25519 + } + blockchain.getSupportedCurves().contains(EllipticCurve.Bls12381G2Aug) -> { + EllipticCurve.Bls12381G2Aug + } + else -> { + Timber.e("Unsupported blockchain, curve not found") + null + } + } + } +} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt index 8420054345..66d0a0f12d 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt @@ -5,6 +5,7 @@ import com.tangem.common.card.EllipticCurve import com.tangem.common.card.FirmwareVersion import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.TapWorkarounds.isTestCard +import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.models.scan.CardDTO /** @@ -52,10 +53,32 @@ fun CardDTO.supportedTokens(cardTypesResolver: CardTypesResolver): List, + blockchain: Blockchain, + cardTypesResolver: CardTypesResolver, +): Boolean { + val cardConfig = CardConfig.createConfig(this) + val primaryCurveForBlockchain = cardConfig.primaryCurve(blockchain) + val isContainsBlockchain = supportedTokens.contains(blockchain) + val isWalletForCurveExists = wallets.any { it.curve == primaryCurveForBlockchain } + // fixme: check for first wallets with 1 curve and remove condition + return if (cardTypesResolver.isTangemWallet() || cardTypesResolver.isWallet2()) { + // if there's no wallet on card for blockchain with given curve + isContainsBlockchain && isWalletForCurveExists + } else { + isContainsBlockchain + } +} + fun CardDTO.canHandleToken(blockchain: Blockchain, cardTypesResolver: CardTypesResolver): Boolean { + val cardConfig = CardConfig.createConfig(this) + val primaryCurveForBlockchain = cardConfig.primaryCurve(blockchain) val isContainsBlockchain = this.supportedTokens(cardTypesResolver).contains(blockchain) - val isWalletForCurveExists = - wallets.map { it.curve }.intersect(blockchain.getSupportedCurves().toSet()).isNotEmpty() + val isWalletForCurveExists = wallets.any { it.curve == primaryCurveForBlockchain } // fixme: check for first wallets with 1 curve and remove condition return if (cardTypesResolver.isTangemWallet() || cardTypesResolver.isWallet2()) { // if there's no wallet on card for blockchain with given curve @@ -66,9 +89,10 @@ fun CardDTO.canHandleToken(blockchain: Blockchain, cardTypesResolver: CardTypesR } fun CardDTO.canHandleBlockchain(blockchain: Blockchain, cardTypesResolver: CardTypesResolver): Boolean { + val cardConfig = CardConfig.createConfig(this) + val primaryCurveForBlockchain = cardConfig.primaryCurve(blockchain) val isContainsBlockchain = this.supportedBlockchains(cardTypesResolver).contains(blockchain) - val isWalletForCurveExists = - wallets.map { it.curve }.intersect(blockchain.getSupportedCurves().toSet()).isNotEmpty() + val isWalletForCurveExists = wallets.any { it.curve == primaryCurveForBlockchain } // fixme: check for first wallets with 1 curve and remove condition return if (cardTypesResolver.isTangemWallet() || cardTypesResolver.isWallet2()) { // if there's no wallet on card for blockchain with given curve