From 39e8abe1bbee37298cb68276d5d8e92e4d56607c Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 15 Mar 2024 13:30:57 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../tangem/tap/di/domain/CardDomainModule.kt | 8 +++++ .../com/tangem/tap/domain/TangemSdkManager.kt | 13 ++++++++ .../card/DefaultDerivationsRepository.kt | 25 ++++++++++++++++ .../configs/feature_toggles_config.json | 4 +++ .../GetExtendedPublicKeyForCurrencyUseCase.kt | 30 +++++++++++++++++++ .../card/repository/DerivationsRepository.kt | 5 ++++ .../TokenDetailsFeatureToggles.kt | 5 ++++ features/tokendetails/impl/build.gradle.kts | 4 +++ .../di/TokenDetailsFeatureTogglesModule.kt | 21 +++++++++++++ .../DefaultTokenDetailsFeatureToggles.kt | 10 +++++++ .../router/DefaultTokenDetailsRouter.kt | 4 +++ .../router/InnerTokenDetailsRouter.kt | 3 ++ .../TokenDetailsSkeletonStateConverter.kt | 22 ++++++++++---- .../state/factory/TokenDetailsStateFactory.kt | 29 +++++++++++++----- .../viewmodels/TokenDetailsClickIntents.kt | 2 ++ .../viewmodels/TokenDetailsViewModel.kt | 29 +++++++++++++++++- features/wallet/impl/build.gradle.kts | 2 ++ .../com/tangem/lib/crypto/BlockchainUtils.kt | 6 ++++ 18 files changed, 208 insertions(+), 14 deletions(-) create mode 100644 domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt create mode 100644 features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/featuretoggles/TokenDetailsFeatureToggles.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsFeatureTogglesModule.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/featuretoggles/DefaultTokenDetailsFeatureToggles.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt index 76aa092682..084134986d 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt @@ -69,4 +69,12 @@ internal object CardDomainModule { fun provideIsNeedToBackupUseCase(walletStateHolder: WalletsStateHolder): IsNeedToBackupUseCase { return IsNeedToBackupUseCase(walletStateHolder) } + + @Provides + @ViewModelScoped + fun provideGetExtendedPublicKeyForCurrencyUseCase( + derivationsRepository: DerivationsRepository, + ): GetExtendedPublicKeyForCurrencyUseCase { + return GetExtendedPublicKeyForCurrencyUseCase(derivationsRepository) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 94b72fcf1e..9c452259d9 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -26,6 +26,8 @@ import com.tangem.operations.derivation.DerivationTaskResponse import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask import com.tangem.operations.pins.SetUserCodeCommand import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask +import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey +import com.tangem.operations.derivation.DeriveWalletPublicKeyTask import com.tangem.tap.derivationsFinder import com.tangem.tap.domain.tasks.product.CreateProductWalletTask import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse @@ -138,6 +140,17 @@ class TangemSdkManager( return runTaskAsyncReturnOnMain(DeriveMultipleWalletPublicKeysTask(derivations), cardId) } + suspend fun deriveExtendedPublicKey( + cardId: String?, + walletPublicKey: ByteArray, + derivation: DerivationPath, + ): CompletionResult = withContext(Dispatchers.Main) { + runTaskAsyncReturnOnMain( + DeriveWalletPublicKeyTask(walletPublicKey, derivation), + cardId, + ) + } + suspend fun resetToFactorySettings( cardId: String, allowsRequestAccessCodeFromRepository: Boolean, diff --git a/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt b/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt index 4b2f3852b6..84d55370ed 100644 --- a/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt @@ -1,12 +1,15 @@ package com.tangem.tap.domain.card +import com.tangem.common.CompletionResult import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess import com.tangem.common.extensions.ByteArrayKey import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.operations.derivation.ExtendedPublicKeysMap @@ -51,6 +54,28 @@ internal class DefaultDerivationsRepository( error("This code should never be reached") } + override suspend fun deriveExtendedPublicKey( + userWalletId: UserWalletId, + derivation: DerivationPath, + ): ExtendedPublicKey? { + val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found") + val walletCard = userWallet.scanResponse.card.wallets.firstOrNull { + UserWalletIdBuilder.scanResponse(userWallet.scanResponse).build()?.value + .contentEquals(userWallet.walletId.value) + } ?: return null + + val result = tangemSdkManager.deriveExtendedPublicKey( + cardId = null, + walletPublicKey = walletCard.publicKey, + derivation = derivation, + ) + + return when (result) { + is CompletionResult.Failure -> throw result.error + is CompletionResult.Success -> result.data + } + } + private suspend fun updatePublicKeys(userWalletId: UserWalletId, keys: DerivedKeys): Result { return runCatching(dispatchers.io) { userWalletsStore.update( diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index ce4c2d9448..8f979c41f0 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -18,5 +18,9 @@ { "name": "LOCAL_USER_LOGS_ENABLED", "version": "5.8.0" + }, + { + "name": "GENERATE_XPUB_ENABLED", + "version": "undefined" } ] diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt new file mode 100644 index 0000000000..4f107a0948 --- /dev/null +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt @@ -0,0 +1,30 @@ +package com.tangem.domain.card + +import arrow.core.Either +import com.tangem.crypto.NetworkType +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.domain.card.repository.DerivationsRepository +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.wallets.models.UserWalletId + +/** + * Derivates an exteneded public key (xpub) based on blockchain hardened derivation + */ +class GetExtendedPublicKeyForCurrencyUseCase( + private val derivationsRepository: DerivationsRepository, +) { + suspend operator fun invoke( + userWalletId: UserWalletId, + derivation: Network.DerivationPath, + ): Either { + return Either.catch { + val derivationPath = requireNotNull(derivation.value?.let { DerivationPath(it) }) { + error("Derivation is null") + } + val hardenedNodes = derivationPath.nodes.filter { it.isHardened } + val hardenedDerivation = DerivationPath(hardenedNodes) + derivationsRepository.deriveExtendedPublicKey(userWalletId, hardenedDerivation) + ?.serialize(NetworkType.Mainnet).orEmpty() + } + } +} \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/repository/DerivationsRepository.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/repository/DerivationsRepository.kt index cd1af0a789..209b68817a 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/repository/DerivationsRepository.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/repository/DerivationsRepository.kt @@ -1,5 +1,7 @@ package com.tangem.domain.card.repository +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId @@ -7,4 +9,7 @@ interface DerivationsRepository { @Throws suspend fun derivePublicKeys(userWalletId: UserWalletId, currencies: List) + + @Throws + suspend fun deriveExtendedPublicKey(userWalletId: UserWalletId, derivation: DerivationPath): ExtendedPublicKey? } \ No newline at end of file diff --git a/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/featuretoggles/TokenDetailsFeatureToggles.kt b/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/featuretoggles/TokenDetailsFeatureToggles.kt new file mode 100644 index 0000000000..d64095a963 --- /dev/null +++ b/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/featuretoggles/TokenDetailsFeatureToggles.kt @@ -0,0 +1,5 @@ +package com.tangem.features.tokendetails.featuretoggles + +interface TokenDetailsFeatureToggles { + fun isGenerateXPubEnabled(): Boolean +} \ No newline at end of file diff --git a/features/tokendetails/impl/build.gradle.kts b/features/tokendetails/impl/build.gradle.kts index 81f6868424..6db0120517 100644 --- a/features/tokendetails/impl/build.gradle.kts +++ b/features/tokendetails/impl/build.gradle.kts @@ -53,10 +53,14 @@ dependencies { implementation(projects.core.datasource) implementation(projects.core.deepLinks) implementation(projects.core.deepLinks.global) + implementation(projects.core.featuretoggles) + + implementation(projects.libs.crypto) /** Domain modules */ implementation(projects.domain.appCurrency) implementation(projects.domain.appCurrency.models) + implementation(projects.domain.card) implementation(projects.domain.demo) implementation(projects.domain.legacy) implementation(projects.domain.models) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsFeatureTogglesModule.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsFeatureTogglesModule.kt new file mode 100644 index 0000000000..21b9d911cb --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/di/TokenDetailsFeatureTogglesModule.kt @@ -0,0 +1,21 @@ +package com.tangem.feature.tokendetails.di + +import com.tangem.core.featuretoggle.manager.FeatureTogglesManager +import com.tangem.feature.tokendetails.featuretoggles.DefaultTokenDetailsFeatureToggles +import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object TokenDetailsFeatureTogglesModule { + + @Provides + @Singleton + fun provideTokenDetailsFeatureToggles(featureTogglesManager: FeatureTogglesManager): TokenDetailsFeatureToggles { + return DefaultTokenDetailsFeatureToggles(featureTogglesManager) + } +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/featuretoggles/DefaultTokenDetailsFeatureToggles.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/featuretoggles/DefaultTokenDetailsFeatureToggles.kt new file mode 100644 index 0000000000..1df3fcf7be --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/featuretoggles/DefaultTokenDetailsFeatureToggles.kt @@ -0,0 +1,10 @@ +package com.tangem.feature.tokendetails.featuretoggles + +import com.tangem.core.featuretoggle.manager.FeatureTogglesManager +import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles + +internal class DefaultTokenDetailsFeatureToggles( + private val featureTogglesManager: FeatureTogglesManager, +) : TokenDetailsFeatureToggles { + override fun isGenerateXPubEnabled() = featureTogglesManager.isFeatureEnabled(name = "GENERATE_XPUB_ENABLED") +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt index 00693c486c..cadf3b9549 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/DefaultTokenDetailsRouter.kt @@ -24,6 +24,10 @@ internal class DefaultTokenDetailsRouter( reduxNavController.navigate(NavigationAction.OpenUrl(url = url)) } + override fun share(text: String) { + reduxNavController.navigate(NavigationAction.Share(text)) + } + override fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) { reduxNavController.navigate( action = NavigationAction.NavigateTo( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/InnerTokenDetailsRouter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/InnerTokenDetailsRouter.kt index 0c870f833a..56eead22a5 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/InnerTokenDetailsRouter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/router/InnerTokenDetailsRouter.kt @@ -12,5 +12,8 @@ internal interface InnerTokenDetailsRouter : TokenDetailsRouter { /** Open website by [url] */ fun openUrl(url: String) + /** Share [text] */ + fun share(text: String) + fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt index 30d410450e..dbc7ff632b 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt @@ -5,20 +5,25 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.networkIconResId +import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.feature.tokendetails.presentation.tokendetails.state.* import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsPullToRefreshConfig import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents +import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles import com.tangem.features.tokendetails.impl.R +import com.tangem.lib.crypto.BlockchainUtils.isBitcoin import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.MutableStateFlow internal class TokenDetailsSkeletonStateConverter( private val clickIntents: TokenDetailsClickIntents, + private val featureToggles: TokenDetailsFeatureToggles, ) : Converter { private val iconStateConverter by lazy { TokenDetailsIconStateConverter() } @@ -27,7 +32,7 @@ internal class TokenDetailsSkeletonStateConverter( return TokenDetailsState( topAppBarConfig = TokenDetailsTopAppBarConfig( onBackClick = clickIntents::onBackClick, - tokenDetailsAppBarMenuConfig = createMenu(), + tokenDetailsAppBarMenuConfig = createMenu(value), ), tokenInfoBlockState = TokenInfoBlockState( name = value.name, @@ -60,14 +65,21 @@ internal class TokenDetailsSkeletonStateConverter( ) } - private fun createMenu(): TokenDetailsAppBarMenuConfig = TokenDetailsAppBarMenuConfig( - items = persistentListOf( + private fun createMenu(cryptoCurrency: CryptoCurrency): TokenDetailsAppBarMenuConfig = TokenDetailsAppBarMenuConfig( + items = buildList { + if (featureToggles.isGenerateXPubEnabled() && isBitcoin(cryptoCurrency.network.id.value)) { + TokenDetailsAppBarMenuConfig.MenuItem( + title = resourceReference(R.string.token_details_generate_xpub), + textColorProvider = { TangemTheme.colors.text.primary1 }, + onClick = clickIntents::onGenerateExtendedKey, + ).let(::add) + } TokenDetailsAppBarMenuConfig.MenuItem( title = TextReference.Res(id = R.string.token_details_hide_token), textColorProvider = { TangemTheme.colors.text.warning }, onClick = clickIntents::onHideClick, - ), - ), + ).let(::add) + }.toImmutableList(), ) private fun createButtons(): ImmutableList { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index c0e14ea621..06b0c838c7 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -11,6 +11,7 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.event.triggeredEvent import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.common.CardTypesResolver @@ -32,9 +33,9 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.t import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter.TokenDetailsLoadingTxHistoryModel import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.ExchangeStatusBottomSheetConfig import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents +import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles import com.tangem.features.tokendetails.impl.R import com.tangem.utils.Provider -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -44,12 +45,16 @@ internal class TokenDetailsStateFactory( private val currentStateProvider: Provider, private val appCurrencyProvider: Provider, private val clickIntents: TokenDetailsClickIntents, + private val featureToggles: TokenDetailsFeatureToggles, symbol: String, decimals: Int, ) { private val skeletonStateConverter by lazy { - TokenDetailsSkeletonStateConverter(clickIntents = clickIntents) + TokenDetailsSkeletonStateConverter( + clickIntents = clickIntents, + featureToggles = featureToggles, + ) } private val notificationConverter by lazy { @@ -282,12 +287,12 @@ internal class TokenDetailsStateFactory( ) } - fun getStateWithUpdatedMenu(cardTypesResolver: CardTypesResolver): TokenDetailsState { + fun getStateWithUpdatedMenu(cardTypesResolver: CardTypesResolver, isBitcoin: Boolean): TokenDetailsState { return with(currentStateProvider()) { copy( topAppBarConfig = topAppBarConfig.copy( tokenDetailsAppBarMenuConfig = topAppBarConfig.tokenDetailsAppBarMenuConfig - ?.updateMenu(cardTypesResolver), + ?.updateMenu(cardTypesResolver, isBitcoin), ), ) } @@ -295,17 +300,25 @@ internal class TokenDetailsStateFactory( private fun TokenDetailsAppBarMenuConfig.updateMenu( cardTypesResolver: CardTypesResolver, + isBitcoin: Boolean, ): TokenDetailsAppBarMenuConfig? { if (cardTypesResolver.isSingleWalletWithToken()) return null - + val showGenerateExtendedKey = featureToggles.isGenerateXPubEnabled() && isBitcoin return copy( - items = persistentListOf( + items = buildList { + if (showGenerateExtendedKey) { + TokenDetailsAppBarMenuConfig.MenuItem( + title = resourceReference(R.string.token_details_generate_xpub), + textColorProvider = { TangemTheme.colors.text.primary1 }, + onClick = clickIntents::onGenerateExtendedKey, + ).let(::add) + } TokenDetailsAppBarMenuConfig.MenuItem( title = TextReference.Res(id = R.string.token_details_hide_token), textColorProvider = { TangemTheme.colors.text.warning }, onClick = clickIntents::onHideClick, - ), - ), + ).let(::add) + }.toImmutableList(), ) } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt index d049e3bf31..712afdeb80 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt @@ -47,4 +47,6 @@ interface TokenDetailsClickIntents { fun onSwapPromoDismiss() fun onSwapPromoClick() + + fun onGenerateExtendedKey() } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index a8a0978de4..f4c38aca5c 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -18,6 +18,7 @@ import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase +import com.tangem.domain.card.GetExtendedPublicKeyForCurrencyUseCase import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.redux.ReduxStateHolder @@ -50,8 +51,10 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTrans import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.ExchangeStatusBottomSheetConfig +import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles import com.tangem.features.tokendetails.impl.R import com.tangem.features.tokendetails.navigation.TokenDetailsRouter +import com.tangem.lib.crypto.BlockchainUtils.isBitcoin import com.tangem.utils.Provider import com.tangem.utils.coroutines.* import dagger.hilt.android.lifecycle.HiltViewModel @@ -85,6 +88,7 @@ internal class TokenDetailsViewModel @Inject constructor( private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val shouldShowSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase, private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase, + private val getExtendedPublicKeyForCurrencyUseCase: GetExtendedPublicKeyForCurrencyUseCase, private val swapRepository: SwapRepository, private val swapTransactionRepository: SwapTransactionRepository, private val quotesRepository: QuotesRepository, @@ -92,6 +96,7 @@ internal class TokenDetailsViewModel @Inject constructor( private val isDemoCardUseCase: IsDemoCardUseCase, private val reduxStateHolder: ReduxStateHolder, private val analyticsEventsHandler: AnalyticsEventHandler, + featureToggles: TokenDetailsFeatureToggles, deepLinksRegistry: DeepLinksRegistry, savedStateHandle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents { @@ -121,6 +126,7 @@ internal class TokenDetailsViewModel @Inject constructor( clickIntents = this, symbol = cryptoCurrency.symbol, decimals = cryptoCurrency.decimals, + featureToggles = featureToggles, ) private val exchangeStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) { @@ -359,7 +365,10 @@ internal class TokenDetailsViewModel @Inject constructor( viewModelScope.launch(dispatchers.main) { val wallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch } - uiState = stateFactory.getStateWithUpdatedMenu(wallet.scanResponse.cardTypesResolver) + uiState = stateFactory.getStateWithUpdatedMenu( + cardTypesResolver = wallet.scanResponse.cardTypesResolver, + isBitcoin = isBitcoin(cryptoCurrency.network.id.value), + ) } } @@ -496,6 +505,24 @@ internal class TokenDetailsViewModel @Inject constructor( } } + override fun onGenerateExtendedKey() { + viewModelScope.launch(dispatchers.main) { + val extendedKey = getExtendedPublicKeyForCurrencyUseCase( + userWalletId, + cryptoCurrency.network.derivationPath, + ).fold( + ifLeft = { + Timber.e(it.cause?.localizedMessage.orEmpty()) + "" + }, + ifRight = { it }, + ) + if (extendedKey.isNotBlank()) { + router.share(extendedKey) + } + } + } + override fun onSellClick() { analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonSell(cryptoCurrency.symbol)) diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index db4d102187..6b46d5c5dc 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -56,6 +56,8 @@ dependencies { implementation(projects.core.deepLinks) implementation(projects.core.deepLinks.global) + implementation(projects.libs.crypto) + /** Domain modules */ implementation(projects.domain.card) implementation(projects.domain.demo) diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt index 1e7a2e6a6d..a6e7acf14d 100644 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt @@ -26,4 +26,10 @@ object BlockchainUtils { null } } + + /** If current [networkId] is Bitcoin */ + fun isBitcoin(networkId: String): Boolean { + val blockchain = Blockchain.fromId(networkId) + return blockchain == Blockchain.Bitcoin || blockchain == Blockchain.BitcoinTestnet + } } \ No newline at end of file