Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-18 12:51:39 +03:00
commit a56bcff7dd
438 changed files with 9017 additions and 4452 deletions

View file

@ -39,7 +39,6 @@ import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.common.analytics.handlers.BlockchainExceptionHandler
import com.tangem.tap.common.log.TangemAppLoggerInitializer
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
@ -138,8 +137,6 @@ interface ApplicationEntryPoint {
fun getWorkerFactory(): HiltWorkerFactory
fun getOnlineCardVerifier(): OnlineCardVerifier
fun getColdUserWalletBuilderFactory(): ColdUserWalletBuilder.Factory
fun getApiConfigsManager(): ApiConfigsManager

View file

@ -26,17 +26,13 @@ import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import arrow.core.getOrElse
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.RoutingFeatureToggle
import com.tangem.common.routing.deeplink.DeeplinkConst.WEBLINK_KEY
import com.tangem.common.routing.deeplink.PayloadToDeeplinkConverter
import com.tangem.common.routing.entity.SerializableIntent
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.di.RootAppComponentContext
import com.tangem.core.deeplink.DEEPLINK_KEY
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.core.deeplink.WEBLINK_KEY
import com.tangem.core.navigation.email.EmailSender
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.UiDependencies
import com.tangem.data.balancehiding.DefaultDeviceFlipDetector
import com.tangem.data.card.sdk.CardSdkOwner
import com.tangem.domain.apptheme.model.AppThemeMode
@ -53,6 +49,7 @@ import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import com.tangem.features.tester.api.TesterMenuLauncher
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.google.GoogleServicesHelper
import com.tangem.operations.backup.BackupService
@ -69,7 +66,6 @@ import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandle
import com.tangem.tap.features.intentHandler.handlers.OnPushClickedIntentHandler
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
import com.tangem.tap.features.main.MainViewModel
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.proxy.redux.DaggerGraphAction
import com.tangem.tap.routing.component.RoutingComponent
import com.tangem.tap.routing.configurator.AppRouterConfig
@ -103,9 +99,6 @@ val mainScope = CoroutineScope(mainCoroutineContext)
@AndroidEntryPoint
class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
lateinit var appStateHolder: AppStateHolder
/** Router for opening tester menu */
@Inject
lateinit var cardSdkOwner: CardSdkOwner
@ -122,9 +115,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
lateinit var walletConnectInteractor: WalletConnectInteractor
@Inject
lateinit var deepLinksRegistry: DeepLinksRegistry
@Inject
lateinit var settingsRepository: SettingsRepository
@ -143,9 +133,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
lateinit var userWalletsListManager: UserWalletsListManager
@Inject
lateinit var emailSender: EmailSender
@Inject
@RootAppComponentContext
internal lateinit var rootComponentContext: AppComponentContext
@ -174,15 +161,9 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
lateinit var dispatchers: CoroutineDispatcherProvider
@Inject
internal lateinit var uiDependencies: UiDependencies
@Inject
internal lateinit var defaultDeviceFlipDetector: DefaultDeviceFlipDetector
@Inject
internal lateinit var routingFeatureToggle: RoutingFeatureToggle
@Inject
internal lateinit var deeplinkFactory: DeepLinkFactory
@ -192,6 +173,9 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
internal lateinit var urlOpener: UrlOpener
@Inject
internal lateinit var testerMenuLauncher: TesterMenuLauncher
internal val viewModel: MainViewModel by viewModels()
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode>
@ -244,13 +228,12 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
sendStakingUnsubmittedHashes()
checkGoogleServicesAvailability()
if (routingFeatureToggle.isDeepLinkNavigationEnabled.not() && intent != null && savedInstanceState == null) {
// handle intent only on start, not on recreate
handleDeepLink(intent = intent, isFromOnNewIntent = false)
}
lifecycle.addObserver(WindowObscurationObserver)
lifecycle.addObserver(defaultDeviceFlipDetector)
if (BuildConfig.TESTER_MENU_ENABLED) {
lifecycle.addObserver(testerMenuLauncher.launchOnShakeObserver)
}
}
private fun setRootContent() {
@ -481,7 +464,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
}
}
if (routingFeatureToggle.isDeepLinkNavigationEnabled && intent != null) {
if (intent != null) {
handleDeepLink(intent = intent, isFromOnNewIntent = false)
}
@ -489,26 +472,22 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
}
private fun handleDeepLink(intent: Intent, isFromOnNewIntent: Boolean) {
if (routingFeatureToggle.isDeepLinkNavigationEnabled) {
val deepLinkExtras = intent.getStringExtra(DEEPLINK_KEY)?.toUri()
val webLink = intent.getStringExtra(WEBLINK_KEY)
val deepLinkExtras = PayloadToDeeplinkConverter.convertBundle(intent.extras)?.toUri()
val webLink = intent.getStringExtra(WEBLINK_KEY)
val receivedDeepLink = intent.data ?: deepLinkExtras
val receivedDeepLink = intent.data ?: deepLinkExtras
when {
receivedDeepLink != null -> {
deeplinkFactory.handleDeeplink(
deeplinkUri = receivedDeepLink,
coroutineScope = lifecycleScope,
isFromOnNewIntent = isFromOnNewIntent,
)
}
webLink?.uriValidate() == true -> {
urlOpener.openUrl(webLink)
}
when {
receivedDeepLink != null -> {
deeplinkFactory.handleDeeplink(
deeplinkUri = receivedDeepLink,
coroutineScope = lifecycleScope,
isFromOnNewIntent = isFromOnNewIntent,
)
}
webLink?.uriValidate() == true -> {
urlOpener.openUrl(webLink)
}
} else {
deepLinksRegistry.launch(intent)
}
}

View file

@ -58,7 +58,6 @@ import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.attestation.api.TangemApiServiceSettings
import com.tangem.tap.common.analytics.AnalyticsFactory
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
@ -78,7 +77,6 @@ import com.tangem.wallet.BuildConfig
import dagger.hilt.EntryPoints
import kotlinx.coroutines.*
import org.rekotlin.Store
import kotlin.collections.set
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository
lateinit var store: Store<AppState>
@ -220,9 +218,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
.setWorkerFactory(workerFactory)
.build()
private val onlineCardVerifier: OnlineCardVerifier
get() = entryPoint.getOnlineCardVerifier()
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory
get() = entryPoint.getColdUserWalletBuilderFactory()
@ -367,7 +362,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
clipboardManager = clipboardManager,
settingsManager = settingsManager,
uiMessageSender = uiMessageSender,
onlineCardVerifier = onlineCardVerifier,
coldUserWalletBuilderFactory = coldUserWalletBuilderFactory,
userTokensResponseStore = userTokensResponseStore,
),

View file

@ -3,6 +3,7 @@ package com.tangem.tap.common.extensions
import com.tangem.core.analytics.Analytics
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.common.analytics.paramsInterceptor.LinkedCardContextInterceptor
/**
@ -21,6 +22,15 @@ fun Analytics.setContext(scanResponse: ScanResponse) {
addParamsInterceptor(LinkedCardContextInterceptor(scanResponse))
}
fun Analytics.setContext(userWallet: UserWallet) {
setUserId(userWallet.walletId.stringValue)
// TODO add product type for hot ([REDACTED_TASK_KEY])
if (userWallet is UserWallet.Cold) {
addParamsInterceptor(LinkedCardContextInterceptor(userWallet.scanResponse))
}
}
/**
* Erases the context
*/

View file

@ -15,9 +15,6 @@ import coil.executeBlocking
import coil.request.ImageRequest
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.tangem.core.deeplink.DEEPLINK_KEY
import com.tangem.core.deeplink.WEBLINK_KEY
import com.tangem.core.deeplink.converter.PayloadToDeeplinkConverter
import com.tangem.domain.common.LogConfig
import com.tangem.tap.MainActivity
import com.tangem.tap.common.images.createCoilImageLoader
@ -38,11 +35,11 @@ internal class TangemPushNotificationService : FirebaseMessagingService() {
val notification = message.notification ?: return
val channelId = notification.channelId ?: TANGEM_CHANNEL_ID
val deeplink = PayloadToDeeplinkConverter.convert(message.data)
val intent = Intent(applicationContext, MainActivity::class.java).apply {
putExtra(DEEPLINK_KEY, deeplink)
putExtra(WEBLINK_KEY, message.data[WEBLINK_KEY])
message.data.forEach {
putExtra(it.key, it.value)
}
putExtra(OnPushClickedIntentHandler.OPENED_FROM_GCM_PUSH, true)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}

View file

@ -2,7 +2,7 @@ package com.tangem.tap.common.redux.legacy
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.redux.LegacyAction
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.AppState
@ -37,8 +37,7 @@ internal object LegacyMiddleware {
)
store.dispatchWithMain(
DetailsAction.PrepareScreen(
// TODO [REDACTED_TASK_KEY]
scanResponse = selectedUserWallet.requireColdWallet().scanResponse,
scanResponse = (selectedUserWallet as? UserWallet.Cold)?.scanResponse,
initializedAppSettingsState = initializedAppSettingsStateContent,
),
)

View file

@ -16,16 +16,13 @@ import com.tangem.crypto.bip39.Wordlist
import com.tangem.data.card.sdk.CardSdkOwner
import com.tangem.data.card.sdk.CardSdkProvider
import com.tangem.datasource.api.common.config.ApiConfig
import com.tangem.datasource.api.common.config.ApiEnvironment
import com.tangem.datasource.api.common.config.ApiEnvironmentConfig
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectMap
import com.tangem.datasource.api.common.config.managers.MutableApiConfigsManager
import com.tangem.datasource.utils.AddHeadersInterceptor
import com.tangem.datasource.utils.RequestHeader
import com.tangem.operations.attestation.api.TangemApiServiceSettings
import com.tangem.sdk.DefaultSessionViewDelegate
import com.tangem.sdk.api.featuretoggles.CardSdkFeatureToggles
import com.tangem.sdk.extensions.*
import com.tangem.sdk.nfc.AndroidNfcAvailabilityProvider
import com.tangem.sdk.nfc.NfcManager
@ -34,13 +31,6 @@ import com.tangem.tap.foregroundActivityObserver
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.info.AppInfoProvider
import com.tangem.utils.version.AppVersionProvider
import com.tangem.wallet.BuildConfig
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
import javax.inject.Singleton
@ -55,11 +45,9 @@ import javax.inject.Singleton
internal class DefaultCardSdkProvider @Inject constructor(
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
private val dispatchers: CoroutineDispatcherProvider,
private val cardSdkFeatureToggles: CardSdkFeatureToggles,
private val apiConfigsManager: ApiConfigsManager,
appVersionProvider: AppVersionProvider,
appInfoProvider: AppInfoProvider,
appPreferencesStore: AppPreferencesStore,
) : CardSdkProvider, CardSdkOwner {
private val observer = Observer()
@ -70,27 +58,15 @@ internal class DefaultCardSdkProvider @Inject constructor(
get() = holder?.sdk ?: tryToRegisterWithForegroundActivity()
init {
if (BuildConfig.TESTER_MENU_ENABLED) {
appPreferencesStore.getObjectMap<ApiEnvironment>(PreferencesKeys.apiConfigsEnvironmentKey)
.map {
when (it[ApiConfig.ID.TangemTech.name]) {
ApiEnvironment.DEV,
ApiEnvironment.STAGE,
ApiEnvironment.MOCK,
-> false
ApiEnvironment.PROD,
null,
-> true
}
val mutableManager = apiConfigsManager as? MutableApiConfigsManager
mutableManager?.addListener(
object : MutableApiConfigsManager.ApiConfigEnvChangeListener(id = ApiConfig.ID.TangemTech) {
override fun onChange(environmentConfig: ApiEnvironmentConfig) {
holder?.sdk?.config?.tangemApiBaseUrl = environmentConfig.baseUrl
}
.distinctUntilChanged()
.onEach { isProd ->
holder?.let {
it.sdk.config.isTangemAttestationProdEnv = isProd
}
}
.launchIn(CoroutineScope(SupervisorJob() + dispatchers.main))
}
},
)
TangemApiServiceSettings.addInterceptors(
AddHeadersInterceptor(
@ -188,10 +164,8 @@ internal class DefaultCardSdkProvider @Inject constructor(
keystoreManager = keystoreManager,
wordlist = Wordlist.getWordlist(activity),
config = config.apply {
isNewOnlineAttestationEnabled = cardSdkFeatureToggles.isNewAttestationEnabled
val apiConfig = apiConfigsManager.getEnvironmentConfig(id = ApiConfig.ID.TangemTech)
isTangemAttestationProdEnv = apiConfig.environment == ApiEnvironment.PROD
tangemApiBaseUrl = apiConfig.baseUrl
},
)

View file

@ -5,7 +5,6 @@ import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
import com.tangem.domain.tokens.repository.CurrenciesRepository
@ -49,7 +48,6 @@ internal object ActivityModule {
appStateHolder: AppStateHolder,
expressServiceLoader: ExpressServiceLoader,
currenciesRepository: CurrenciesRepository,
getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
excludedBlockchains: ExcludedBlockchains,
dispatchers: CoroutineDispatcherProvider,
): RampStateManager {
@ -57,7 +55,6 @@ internal object ActivityModule {
sellService = Provider { requireNotNull(appStateHolder.sellService) },
expressServiceLoader = expressServiceLoader,
currenciesRepository = currenciesRepository,
getNetworkCoinStatusUseCase = getNetworkCoinStatusUseCase,
dispatchers = dispatchers,
excludedBlockchains = excludedBlockchains,
)

View file

@ -7,7 +7,6 @@ import com.tangem.domain.managetokens.repository.ManageTokensRepository
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
@ -72,7 +71,6 @@ internal object ManageTokensDomainModule {
walletManagersFacade: WalletManagersFacade,
currenciesRepository: CurrenciesRepository,
derivationsRepository: DerivationsRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
@ -84,7 +82,6 @@ internal object ManageTokensDomainModule {
walletManagersFacade = walletManagersFacade,
currenciesRepository = currenciesRepository,
derivationsRepository = derivationsRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,

View file

@ -10,8 +10,6 @@ import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import dagger.Module
@ -62,21 +60,17 @@ object MarketsDomainModule {
derivationsRepository: DerivationsRepository,
marketsTokenRepository: MarketsTokenRepository,
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
): SaveMarketTokensUseCase {
return SaveMarketTokensUseCase(
derivationsRepository = derivationsRepository,
marketsTokenRepository = marketsTokenRepository,
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}

View file

@ -94,12 +94,10 @@ internal object StakingDomainModule {
@Provides
@Singleton
fun provideFetchStakingYieldBalanceUseCase(
stakingRepository: StakingRepository,
stakingErrorResolver: StakingErrorResolver,
singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
): FetchStakingYieldBalanceUseCase {
return FetchStakingYieldBalanceUseCase(
stakingRepository = stakingRepository,
stakingErrorResolver = stakingErrorResolver,
singleYieldBalanceFetcher = singleYieldBalanceFetcher,
)
@ -224,4 +222,12 @@ internal object StakingDomainModule {
): CheckAccountInitializedUseCase {
return CheckAccountInitializedUseCase(walletManagersFacade)
}
@Provides
@Singleton
fun provideGetActionRequirementAmountUseCase(
stakingRepository: StakingRepository,
): GetActionRequirementAmountUseCase {
return GetActionRequirementAmountUseCase(stakingRepository)
}
}

View file

@ -3,10 +3,7 @@ package com.tangem.tap.di.domain
import com.tangem.domain.swap.SwapErrorResolver
import com.tangem.domain.swap.SwapRepositoryV2
import com.tangem.domain.swap.SwapTransactionRepository
import com.tangem.domain.swap.usecase.GetSwapPairsUseCase
import com.tangem.domain.swap.usecase.GetSwapQuoteUseCase
import com.tangem.domain.swap.usecase.GetSwapSupportedPairsUseCase
import com.tangem.domain.swap.usecase.SelectInitialPairUseCase
import com.tangem.domain.swap.usecase.*
import com.tangem.feature.swap.domain.GetAvailablePairsUseCase
import dagger.Module
import dagger.Provides
@ -73,4 +70,30 @@ internal object SwapDomainModule {
swapErrorResolver = swapErrorResolver,
)
}
@Provides
@Singleton
fun provideGetSwapDataUseCase(
swapRepositoryV2: SwapRepositoryV2,
swapErrorResolver: SwapErrorResolver,
): GetSwapDataUseCase {
return GetSwapDataUseCase(
swapRepositoryV2 = swapRepositoryV2,
swapErrorResolver = swapErrorResolver,
)
}
@Provides
@Singleton
fun provideSwapTransactionSentUseCase(
swapRepositoryV2: SwapRepositoryV2,
swapTransactionRepository: SwapTransactionRepository,
swapErrorResolver: SwapErrorResolver,
): SwapTransactionSentUseCase {
return SwapTransactionSentUseCase(
swapRepositoryV2 = swapRepositoryV2,
swapTransactionRepository = swapTransactionRepository,
swapErrorResolver = swapErrorResolver,
)
}
}

View file

@ -41,7 +41,6 @@ internal object TokensDomainModule {
@Singleton
fun provideAddCryptoCurrenciesUseCase(
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
@ -50,7 +49,6 @@ internal object TokensDomainModule {
): AddCryptoCurrenciesUseCase {
return AddCryptoCurrenciesUseCase(
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
singleYieldBalanceFetcher = singleYieldBalanceFetcher,
@ -63,19 +61,15 @@ internal object TokensDomainModule {
@Singleton
fun provideFetchTokenListUseCase(
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
): FetchTokenListUseCase {
return FetchTokenListUseCase(
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@ -173,7 +167,6 @@ internal object TokensDomainModule {
@Singleton
fun provideFetchCurrencyStatusUseCase(
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
@ -182,7 +175,6 @@ internal object TokensDomainModule {
): FetchCurrencyStatusUseCase {
return FetchCurrencyStatusUseCase(
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
singleYieldBalanceFetcher = singleYieldBalanceFetcher,
@ -195,19 +187,15 @@ internal object TokensDomainModule {
@Singleton
fun provideFetchCardTokenListUseCase(
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
): FetchCardTokenListUseCase {
return FetchCardTokenListUseCase(
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@ -256,20 +244,16 @@ internal object TokensDomainModule {
fun provideGetCryptoCurrencyActionsUseCase(
rampStateManager: RampStateManager,
walletManagersFacade: WalletManagersFacade,
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
promoRepository: PromoRepository,
dispatchers: CoroutineDispatcherProvider,
currencyStatusOperations: BaseCurrencyStatusOperations,
): GetCryptoCurrencyActionsUseCase {
return GetCryptoCurrencyActionsUseCase(
rampManager = rampStateManager,
walletManagersFacade = walletManagersFacade,
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
promoRepository = promoRepository,
dispatchers = dispatchers,
currencyStatusOperations = currencyStatusOperations,
)
}

View file

@ -11,6 +11,7 @@ import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.transaction.usecase.*
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.tap.domain.hot.TangemHotSigner
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -43,6 +44,7 @@ internal object TransactionDomainModule {
transactionRepository: TransactionRepository,
walletManagersFacade: WalletManagersFacade,
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
tangemHotSignerFactory: TangemHotSigner.Factory,
): SendTransactionUseCase {
return SendTransactionUseCase(
demoConfig = DemoConfig(),
@ -50,6 +52,7 @@ internal object TransactionDomainModule {
transactionRepository = transactionRepository,
walletManagersFacade = walletManagersFacade,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
getHotSigner = tangemHotSignerFactory::create,
)
}

View file

@ -15,10 +15,7 @@ import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.*
import com.tangem.feature.wallet.presentation.wallet.domain.IsWalletNFTEnabledSyncUseCase
import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.features.nft.NFTFeatureToggles
import com.tangem.operations.attestation.CardArtworksProvider
import com.tangem.sdk.api.featuretoggles.CardSdkFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
@ -191,28 +188,14 @@ internal object WalletsDomainModule {
@Provides
@Singleton
fun provideGetCardImageUseCase(
onlineCardVerifier: OnlineCardVerifier,
cardArtworksProvider: CardArtworksProvider,
cardSdkFeatureToggles: CardSdkFeatureToggles,
): GetCardImageUseCase {
return GetCardImageUseCase(
verifier = onlineCardVerifier,
cardArtworksProvider = cardArtworksProvider,
cardSdkFeatureToggles = cardSdkFeatureToggles,
)
fun provideGetCardImageUseCase(cardArtworksProvider: CardArtworksProvider): GetCardImageUseCase {
return GetCardImageUseCase(cardArtworksProvider = cardArtworksProvider)
}
@Provides
@Singleton
fun providesIsWalletNFTEnabledSyncUseCase(
walletsRepository: WalletsRepository,
nftFeatureToggles: NFTFeatureToggles,
): IsWalletNFTEnabledSyncUseCase {
return IsWalletNFTEnabledSyncUseCase(
walletsRepository = walletsRepository,
nftFeatureToggles = nftFeatureToggles,
)
fun providesIsWalletNFTEnabledSyncUseCase(walletsRepository: WalletsRepository): IsWalletNFTEnabledSyncUseCase {
return IsWalletNFTEnabledSyncUseCase(walletsRepository = walletsRepository)
}
@Provides

View file

@ -0,0 +1,24 @@
package com.tangem.tap.di.hot
import com.tangem.hot.sdk.TangemHotSdk
import com.tangem.tap.domain.hot.HotWalletPasswordRequester
import com.tangem.tap.features.hot.DefaultHotWalletPasswordRequester
import com.tangem.tap.features.hot.TangemHotSDKProxy
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface TangemHotSdkModule {
@Binds
@Singleton
fun bindTangemHotSdk(proxy: TangemHotSDKProxy): TangemHotSdk
@Binds
@Singleton
fun bindHotWalletPasswordRequester(impl: DefaultHotWalletPasswordRequester): HotWalletPasswordRequester
}

View file

@ -1,9 +1,7 @@
package com.tangem.tap.di.routing
import com.tangem.common.routing.AppRouter
import com.tangem.common.routing.RoutingFeatureToggle
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.tap.routing.ProxyAppRouter
import com.tangem.tap.routing.configurator.AppRouterConfig
import com.tangem.tap.routing.configurator.MutableAppRouterConfig
@ -33,10 +31,4 @@ internal object AppRouterModule {
@Provides
@Singleton
fun provideAppRouterConfigurator(): AppRouterConfig = MutableAppRouterConfig()
@Provides
@Singleton
fun provideRoutingFeatureToggle(featureTogglesManager: FeatureTogglesManager): RoutingFeatureToggle {
return RoutingFeatureToggle(featureTogglesManager)
}
}

View file

@ -5,7 +5,6 @@ import com.tangem.blockchain.common.Wallet
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.store
@ -35,14 +34,15 @@ class TapWalletManager(
}
private suspend fun loadUserWalletData(userWallet: UserWallet) {
Analytics.setContext(userWallet.requireColdWallet().scanResponse) // [REDACTED_TASK_KEY]
val scanResponse = userWallet.scanResponse
Analytics.setContext(userWallet)
tangemSdkManager.changeDisplayedCardIdNumbersCount(scanResponse)
withMainContext {
// Order is important
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
if (userWallet is UserWallet.Cold) {
val scanResponse = userWallet.scanResponse
tangemSdkManager.changeDisplayedCardIdNumbersCount(scanResponse)
withMainContext {
// Order is important
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
}
}
}
}

View file

@ -50,7 +50,7 @@ internal class DefaultDerivationsRepository(
networkFactory.create(
blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null,
extraDerivationPath = null,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
userWallet = userWallet,
)
},
)
@ -61,7 +61,11 @@ internal class DefaultDerivationsRepository(
userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
}
userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY]
if (userWallet is UserWallet.Hot) {
return
}
userWallet.requireColdWallet()
if (!userWallet.scanResponse.card.settings.isHDWalletAllowed) {
Timber.d("Nothing to derive")
@ -84,14 +88,18 @@ internal class DefaultDerivationsRepository(
): Boolean {
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
if (userWallet is UserWallet.Hot) {
return false
}
val derivations =
MissedDerivationsFinder(scanResponse = userWallet.requireColdWallet().scanResponse) // TODO [REDACTED_TASK_KEY]
MissedDerivationsFinder(scanResponse = userWallet.requireColdWallet().scanResponse)
.findByNetworks(
networksWithDerivationPath.mapNotNull { (backendId, extraDerivationPath) ->
networkFactory.create(
blockchain = Blockchain.fromNetworkId(backendId) ?: return@mapNotNull null,
extraDerivationPath = extraDerivationPath,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
userWallet = userWallet,
)
},
)

View file

@ -1,47 +0,0 @@
package com.tangem.tap.domain.extensions
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.getTwinCardNumber
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.wallets.models.Artwork
import com.tangem.operations.attestation.CardArtworksProvider
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.attestation.api.models.CardVerifyAndGetInfo
fun CardDTO.signedHashesCount(): Int {
return wallets.sumOf { it.totalSignedHashes ?: 0 }
}
suspend fun CardDTO.getOrLoadCardArtworkUrl(
cardInfo: Result<CardVerifyAndGetInfo.Response.Item>? = null,
onlineCardVerifier: OnlineCardVerifier,
): String {
fun ifAnyError(): String {
return when {
cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
else -> {
when (getTwinCardNumber()) {
TwinCardNumber.First -> Artwork.TWIN_CARD_1_URL
TwinCardNumber.Second -> Artwork.TWIN_CARD_2_URL
else -> Artwork.DEFAULT_IMG_URL
}
}
}
}
return when (val cardInfoResult = cardInfo ?: onlineCardVerifier.getCardInfo(cardId, cardPublicKey)) {
is Result.Success -> {
val artworkId = cardInfoResult.data.artwork?.id
if (artworkId.isNullOrEmpty()) {
ifAnyError()
} else {
CardArtworksProvider.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
}
}
is Result.Failure -> ifAnyError()
}
}

View file

@ -0,0 +1,44 @@
package com.tangem.tap.domain.hot
import com.tangem.hot.sdk.TangemHotSdk
import com.tangem.hot.sdk.model.*
import javax.inject.Inject
class HotWalletAccessor @Inject constructor(
private val tangemHotSdk: TangemHotSdk,
private val hotWalletPasswordRequester: HotWalletPasswordRequester,
) {
suspend fun signHashes(hotWalletId: HotWalletId, dataToSign: List<DataToSign>): List<SignedData> {
val auth = when (hotWalletId.authType) {
HotWalletId.AuthType.NoPassword -> HotAuth.NoAuth
HotWalletId.AuthType.Password -> {
hotWalletPasswordRequester.requestPassword(hotWalletId)
}
HotWalletId.AuthType.Biometry -> HotAuth.Biometry
}
return runCatching {
tangemHotSdk.signHashes(
unlockHotWallet = UnlockHotWallet(
walletId = hotWalletId,
auth = auth,
),
dataToSign = dataToSign,
)
}.getOrElse {
if (hotWalletId.authType == HotWalletId.AuthType.Biometry) {
val passwordAuth = hotWalletPasswordRequester.requestPassword(hotWalletId)
tangemHotSdk.signHashes(
unlockHotWallet = UnlockHotWallet(
walletId = hotWalletId,
auth = passwordAuth,
),
dataToSign = dataToSign,
)
} else {
throw it
}
}
}
}

View file

@ -0,0 +1,9 @@
package com.tangem.tap.domain.hot
import com.tangem.hot.sdk.model.HotAuth
import com.tangem.hot.sdk.model.HotWalletId
interface HotWalletPasswordRequester {
suspend fun requestPassword(hotWalletId: HotWalletId): HotAuth.Password
}

View file

@ -0,0 +1,78 @@
package com.tangem.tap.domain.hot
import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.common.Wallet
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.map
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.hot.sdk.model.DataToSign
import com.tangem.operations.sign.SignData
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
class TangemHotSigner @AssistedInject constructor(
@Assisted private val userWallet: UserWallet.Hot,
private val hotWalletAccessor: HotWalletAccessor,
) : TransactionSigner {
override suspend fun sign(hash: ByteArray, publicKey: Wallet.PublicKey): CompletionResult<ByteArray> {
return sign(listOf(hash), publicKey).map { it.first() }
}
override suspend fun sign(
hashes: List<ByteArray>,
publicKey: Wallet.PublicKey,
): CompletionResult<List<ByteArray>> {
val wallet = userWallet.wallets.orEmpty().firstOrNull { it.publicKey == publicKey.seedKey }
?: return CompletionResult.Failure(
TangemSdkError.ExceptionError(IllegalStateException("wallet is locked")),
)
val result = hotWalletAccessor.signHashes(
hotWalletId = userWallet.hotWalletId,
dataToSign = listOf(
DataToSign(
curve = wallet.curve,
hashes = hashes,
derivationPath = publicKey.derivationPath,
),
),
)
return CompletionResult.Success(result.map { it.signatures }.flatten())
}
override suspend fun multiSign(
dataToSign: List<SignData>,
publicKey: Wallet.PublicKey,
): CompletionResult<Map<ByteArray, ByteArray>> {
val result = hotWalletAccessor.signHashes(
hotWalletId = userWallet.hotWalletId,
dataToSign = dataToSign.map { signData ->
val wallet = userWallet.wallets.orEmpty().firstOrNull { it.publicKey == signData.publicKey }
?: return CompletionResult.Failure(
TangemSdkError.ExceptionError(IllegalStateException("wallet is locked")),
)
DataToSign(
curve = wallet.curve,
hashes = listOf(signData.hash),
derivationPath = signData.derivationPath,
)
},
)
return CompletionResult.Success(
result.mapIndexed { index, data ->
dataToSign[index].publicKey to data.signatures.first()
}.toMap(),
)
}
@AssistedFactory
interface Factory {
fun create(@Assisted userWallet: UserWallet.Hot): TangemHotSigner
}
}

View file

@ -7,9 +7,6 @@ internal class DefaultTokensFeatureToggles(
private val featureTogglesManager: FeatureTogglesManager,
) : TokensFeatureToggles {
override val isStakingLoadingRefactoringEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "STAKING_LOADING_REFACTORING_ENABLED")
override val isWalletBalanceFetcherEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "WALLET_BALANCE_FETCHER_ENABLED")
}

View file

@ -7,7 +7,6 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockTy
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.isLocked
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
import com.tangem.tap.domain.userWalletList.repository.SelectedUserWalletRepository
import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository
@ -208,8 +207,7 @@ internal class BiometricUserWalletsListManager(
changeSelectedUserWallet: Boolean,
canOverridePublicInfo: Boolean,
): CompletionResult<Unit> {
userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY]
val encryptionKey = userWallet.scanResponse.card.encryptionKey
val encryptionKey = userWallet.encryptionKey
?.let { UserWalletEncryptionKey(userWallet.walletId, it) }
?: return CompletionResult.Success(Unit) // No encryption key, no need to save

View file

@ -2,31 +2,44 @@ package com.tangem.tap.domain.userWalletList.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import com.tangem.domain.models.MobileWallet
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.hot.sdk.model.HotWalletId
@JsonClass(generateAdapter = true)
internal data class UserWalletSensitiveInformation(
// Cold
@Json(name = "wallets")
val wallets: List<CardDTO.Wallet>,
val wallets: List<CardDTO.Wallet>?,
@Json(name = "visaCardActivationStatus")
val visaCardActivationStatus: VisaCardActivationStatus? = null,
// Hot
@Json(name = "mobileWallets")
val mobileWallets: List<MobileWallet>? = null,
)
@JsonClass(generateAdapter = true)
internal data class UserWalletPublicInformation(
// Common
@Json(name = "name")
val name: String,
@Json(name = "walletId")
val walletId: UserWalletId,
// Cold
@Json(name = "cardsInWallet")
val cardsInWallet: Set<String>,
@Json(name = "scanResponse")
val scanResponse: ScanResponse,
val scanResponse: ScanResponse?,
@Json(name = "isMultiCurrency")
val isMultiCurrency: Boolean,
@Json(name = "hasBackupError")
val hasBackupError: Boolean = false,
// Hot
@Json(name = "hotWalletId")
val hotWalletId: HotWalletId? = null,
@Json(name = "backedUp")
val backedUp: Boolean? = null,
)

View file

@ -2,6 +2,7 @@ package com.tangem.tap.domain.userWalletList.utils
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.tap.domain.userWalletList.model.UserWalletPublicInformation
import com.tangem.tap.domain.userWalletList.model.UserWalletSensitiveInformation
@ -10,8 +11,12 @@ internal val UserWallet.sensitiveInformation: UserWalletSensitiveInformation
is UserWallet.Cold -> UserWalletSensitiveInformation(
wallets = scanResponse.card.wallets,
visaCardActivationStatus = scanResponse.visaCardActivationStatus,
mobileWallets = null,
)
is UserWallet.Hot -> UserWalletSensitiveInformation(
wallets = null,
mobileWallets = this.wallets,
)
is UserWallet.Hot -> TODO("[REDACTED_TASK_KEY]")
}
internal val UserWallet.publicInformation: UserWalletPublicInformation
@ -28,19 +33,39 @@ internal val UserWallet.publicInformation: UserWalletPublicInformation
visaCardActivationStatus = null,
),
hasBackupError = hasBackupError,
hotWalletId = null,
backedUp = null,
)
is UserWallet.Hot -> UserWalletPublicInformation(
name = name,
walletId = walletId,
isMultiCurrency = isMultiCurrency,
cardsInWallet = emptySet(),
scanResponse = null,
hotWalletId = hotWalletId,
backedUp = backedUp,
)
is UserWallet.Hot -> TODO("[REDACTED_TASK_KEY]")
}
internal fun UserWalletPublicInformation.toUserWallet(): UserWallet {
return UserWallet.Cold(
name = name,
walletId = walletId,
cardsInWallet = cardsInWallet,
scanResponse = scanResponse,
isMultiCurrency = isMultiCurrency,
hasBackupError = hasBackupError,
)
return if (hotWalletId != null) {
UserWallet.Hot(
name = name,
walletId = walletId,
hotWalletId = hotWalletId,
wallets = null,
backedUp = backedUp!!,
)
} else {
UserWallet.Cold(
name = name,
walletId = walletId,
cardsInWallet = cardsInWallet,
scanResponse = scanResponse!!,
isMultiCurrency = isMultiCurrency,
hasBackupError = hasBackupError,
)
}
}
internal fun List<UserWalletPublicInformation>.toUserWallets(): List<UserWallet> {
@ -53,13 +78,15 @@ internal fun UserWallet.updateWith(sensitiveInformation: UserWalletSensitiveInfo
copy(
scanResponse = scanResponse.copy(
card = scanResponse.card.copy(
wallets = sensitiveInformation.wallets,
wallets = sensitiveInformation.wallets!!,
),
visaCardActivationStatus = sensitiveInformation.visaCardActivationStatus,
),
)
}
is UserWallet.Hot -> TODO("[REDACTED_TASK_KEY]")
is UserWallet.Hot -> copy(
wallets = sensitiveInformation.mobileWallets,
)
}
}

View file

@ -1,11 +1,16 @@
package com.tangem.tap.domain.userWalletList.utils
import com.tangem.common.extensions.calculateSha256
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.common.extensions.calculateHmacSha256
import com.tangem.domain.models.MobileWallet
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.wallets.models.UserWallet
internal val CardDTO.encryptionKey: ByteArray?
get() = findPublicKey(wallets)?.let { calculateEncryptionKey(it) }
internal val UserWallet.encryptionKey: ByteArray?
get() = when (this) {
is UserWallet.Cold -> findPublicKey(this.scanResponse.card.wallets)
is UserWallet.Hot -> findPublicKey(this.wallets.orEmpty())
}?.let { calculateEncryptionKey(it) }
private fun calculateEncryptionKey(publicKey: ByteArray): ByteArray {
val message = MESSAGE_FOR_ENCRYPTION_KEY.toByteArray()
@ -15,8 +20,12 @@ private fun calculateEncryptionKey(publicKey: ByteArray): ByteArray {
}
private fun findPublicKey(wallets: List<CardDTO.Wallet>): ByteArray? {
return wallets.firstOrNull()
?.publicKey
return wallets.firstOrNull()?.publicKey
}
@JvmName("findPublicKeyInMobileWallets")
private fun findPublicKey(wallets: List<MobileWallet>): ByteArray? {
return wallets.firstOrNull()?.publicKey
}
private const val MESSAGE_FOR_ENCRYPTION_KEY = "UserWalletEncryptionKey"

View file

@ -14,7 +14,6 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.tap.common.extensions.dispatchOnMain
@ -422,8 +421,7 @@ class WalletConnectInteractor(
}
private fun getCardId(userWallet: UserWallet): String? {
userWallet.requireColdWallet() // [REDACTED_TASK_KEY]
return if (userWallet.scanResponse.card.backupStatus?.isActive != true) {
return if (userWallet is UserWallet.Cold && userWallet.scanResponse.card.backupStatus?.isActive != true) {
userWallet.cardId
} else { // if wallet has backup, any card from wallet can be used to sign
null

View file

@ -9,7 +9,7 @@ import org.rekotlin.Action
sealed class DetailsAction : Action {
data class PrepareScreen(
val scanResponse: ScanResponse,
val scanResponse: ScanResponse?,
val initializedAppSettingsState: AppSettingsState,
) : DetailsAction()

View file

@ -22,7 +22,6 @@ internal class DefaultCardSettingsComponent @AssistedInject constructor(
@Composable
override fun Content(modifier: Modifier) {
val state by model.screenState.collectAsStateWithLifecycle()
CardSettingsScreen(modifier = modifier, state = state)
}

View file

@ -27,7 +27,6 @@ import com.tangem.tap.common.analytics.events.Settings
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchNavigationAction
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.domain.extensions.signedHashesCount
import com.tangem.tap.features.details.ui.cardsettings.CardInfo
import com.tangem.tap.features.details.ui.cardsettings.CardSettingsScreenState
import com.tangem.tap.features.details.ui.cardsettings.api.CardSettingsComponent
@ -75,6 +74,8 @@ internal class CardSettingsModel @Inject constructor(
override fun onDestroy() {
super.onDestroy()
// Reset card scanned data
cardSettingsInteractor.clear()
// Restore the previous value of access code request policy
cardSdkConfigRepository.isBiometricsRequestPolicy = previousBiometricsRequestPolicy
}
@ -169,6 +170,8 @@ internal class CardSettingsModel @Inject constructor(
}
}
private fun CardDTO.signedHashesCount(): Int = wallets.sumOf { it.totalSignedHashes ?: 0 }
private fun handleClickingItem(item: CardInfo) {
when (item) {
is CardInfo.ChangeAccessCode -> {

View file

@ -5,6 +5,7 @@ import arrow.core.getOrElse
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.qrscanning.models.QrResultSource
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
@ -31,12 +32,18 @@ internal class WalletConnectModel @Inject constructor(
init {
modelScope.launch {
listenToQrScanningUseCase(SourceType.WALLET_CONNECT)
listenToQrScanningUseCase.listen(SourceType.WALLET_CONNECT)
.getOrElse { emptyFlow() }
.map {
.map { result ->
val source = when (result.resultSource) {
QrResultSource.CLIPBOARD -> WalletConnectAction.OpenSession.SourceType.CLIPBOARD
QrResultSource.CAMERA,
QrResultSource.GALLERY,
-> WalletConnectAction.OpenSession.SourceType.QR
}
WalletConnectAction.OpenSession(
wcUri = it,
source = WalletConnectAction.OpenSession.SourceType.QR,
wcUri = result.qrCode,
source = source,
userWalletId = params.userWalletId,
)
}

View file

@ -13,6 +13,7 @@ import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.SystemBarsIconsDisposable
import com.tangem.core.ui.utils.ChangeRootBackgroundColorEffect
import com.tangem.core.ui.utils.findActivity
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.home.api.HomeComponent
import com.tangem.tap.features.home.compose.StoriesScreen
@ -29,6 +30,7 @@ import org.rekotlin.StoreSubscriber
internal class DefaultHomeComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted params: Unit,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : HomeComponent, AppComponentContext by appComponentContext, StoreSubscriber<HomeState> {
private val model: HomeModel = getOrCreateModel()
@ -58,7 +60,7 @@ internal class DefaultHomeComponent @AssistedInject constructor(
val activity = LocalContext.current.findActivity()
BackHandler(onBack = activity::finish)
SystemBarsIconsDisposable(darkIcons = false)
if (homeState.value.isV2StoriesEnabled) {
if (hotWalletFeatureToggles.isHotWalletEnabled) {
StoriesScreenV2(
homeState = homeState,
onCreateNewWalletButtonClick = model::onCreateNewWalletScreen,

View file

@ -7,7 +7,6 @@ import org.rekotlin.StateType
// todo refactor [REDACTED_TASK_KEY]
data class HomeState(
val scanInProgress: Boolean = false,
val isV2StoriesEnabled: Boolean = false,
val stories: ImmutableList<Stories> = getRestrictedStories().toImmutableList(),
) : StateType {

View file

@ -0,0 +1,13 @@
package com.tangem.tap.features.hot
import com.tangem.hot.sdk.model.HotAuth
import com.tangem.hot.sdk.model.HotWalletId
import com.tangem.tap.domain.hot.HotWalletPasswordRequester
import javax.inject.Inject
class DefaultHotWalletPasswordRequester @Inject constructor() : HotWalletPasswordRequester {
override suspend fun requestPassword(hotWalletId: HotWalletId): HotAuth.Password {
return HotAuth.Password("TODO [REDACTED_TASK_KEY]".toCharArray()) // TODO [REDACTED_TASK_KEY]
}
}

View file

@ -0,0 +1,53 @@
package com.tangem.tap.features.hot
import com.tangem.crypto.bip39.Mnemonic
import com.tangem.hot.sdk.TangemHotSdk
import com.tangem.hot.sdk.model.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withTimeout
import javax.inject.Inject
import javax.inject.Singleton
/**
* Proxy for [TangemHotSdk] to allow lazy initialization and provide a way to access the SDK state from domain layer.
* SDK is initialized in the [com.tangem.tap.routing.component.RoutingComponent] and can be accessed through this proxy.
* Be aware that the SDK is initialized on activity creation, so it may not be available immediately.
*/
@Singleton
class TangemHotSDKProxy @Inject constructor() : TangemHotSdk {
val sdkState = MutableStateFlow<TangemHotSdk?>(null)
override suspend fun importWallet(mnemonic: Mnemonic, passphrase: CharArray?, auth: HotAuth): HotWalletId =
callSdk { importWallet(mnemonic, passphrase, auth) }
override suspend fun generateWallet(auth: HotAuth, mnemonicType: MnemonicType): HotWalletId =
callSdk { generateWallet(auth, mnemonicType) }
override suspend fun exportMnemonic(unlockHotWallet: UnlockHotWallet): SeedPhrasePrivateInfo =
callSdk { exportMnemonic(unlockHotWallet) }
override suspend fun exportBackup(unlockHotWallet: UnlockHotWallet): ByteArray =
callSdk { exportBackup(unlockHotWallet) }
override suspend fun delete(id: HotWalletId) = callSdk { delete(id) }
override suspend fun changeAuth(unlockHotWallet: UnlockHotWallet, auth: HotAuth): HotWalletId =
callSdk { changeAuth(unlockHotWallet, auth) }
override suspend fun derivePublicKey(
unlockHotWallet: UnlockHotWallet,
request: DeriveWalletRequest,
): DerivedPublicKeyResponse = callSdk { derivePublicKey(unlockHotWallet, request) }
override suspend fun signHashes(unlockHotWallet: UnlockHotWallet, dataToSign: List<DataToSign>): List<SignedData> =
callSdk { signHashes(unlockHotWallet, dataToSign) }
private suspend fun <T> callSdk(block: suspend TangemHotSdk.() -> T): T {
return withTimeout(timeMillis = 1000) {
sdkState.filterNotNull().first()
}.block()
}
}

View file

@ -4,13 +4,11 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.common.keyboard.KeyboardValidator
import com.tangem.common.routing.RoutingFeatureToggle
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.event.TechAnalyticsEvent
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.core.ui.R
import com.tangem.core.ui.coil.ImagePreloader
import com.tangem.core.ui.extensions.resourceReference
@ -46,7 +44,6 @@ import com.tangem.domain.wallets.usecase.AssociateWalletsWithApplicationIdUseCas
import com.tangem.domain.wallets.usecase.GetSavedWalletChangesUseCase
import com.tangem.domain.wallets.usecase.UpdateRemoteWalletsInfoUseCase
import com.tangem.feature.swap.analytics.StoriesEvents
import com.tangem.features.onramp.deeplink.OnrampDeepLink
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog
@ -85,8 +82,6 @@ internal class MainViewModel @Inject constructor(
private val imagePreloader: ImagePreloader,
private val fetchHotCryptoUseCase: FetchHotCryptoUseCase,
private val onboardingRepository: OnboardingRepository,
private val deepLinksRegistry: DeepLinksRegistry,
private val onrampDeepLinkFactory: OnrampDeepLink.Factory,
private val notificationsToggles: NotificationsFeatureToggles,
private val getApplicationIdUseCase: GetApplicationIdUseCase,
private val subscribeOnWalletsUseCase: GetSavedWalletChangesUseCase,
@ -97,7 +92,6 @@ internal class MainViewModel @Inject constructor(
private val multiQuoteUpdater: MultiQuoteUpdater,
private val appStateHolder: AppStateHolder,
private val environmentConfigStorage: EnvironmentConfigStorage,
routingFeatureToggle: RoutingFeatureToggle,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
) : ViewModel() {
@ -140,10 +134,6 @@ internal class MainViewModel @Inject constructor(
sendKeyboardIdentifierEvent()
preloadImages()
if (!routingFeatureToggle.isDeepLinkNavigationEnabled) {
initializeDeepLinks()
}
}
override fun onCleared() {
@ -200,7 +190,7 @@ internal class MainViewModel @Inject constructor(
userWalletsListManager.selectedUserWallet
.distinctUntilChanged()
.onEach { userWallet ->
Analytics.setContext(userWallet.requireColdWallet().scanResponse) // TODO [REDACTED_TASK_KEY]
Analytics.setContext(userWallet)
}
.flowOn(dispatchers.io)
.launchIn(viewModelScope)
@ -417,7 +407,7 @@ internal class MainViewModel @Inject constructor(
}
}
} catch (ex: Exception) {
Timber.e(ex.message)
Timber.e(ex)
analyticsEventHandler.send(
StoriesEvents.Error(
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
@ -426,10 +416,6 @@ internal class MainViewModel @Inject constructor(
}
}
private fun initializeDeepLinks() {
deepLinksRegistry.register(onrampDeepLinkFactory.create(viewModelScope))
}
private suspend fun initPushNotifications() {
if (notificationsToggles.isNotificationsEnabled) {
getApplicationIdUseCase().onRight { applicationId ->

View file

@ -15,7 +15,7 @@ import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
import com.tangem.domain.wallets.legacy.unlockIfLockable
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.backupService
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
import com.tangem.tap.common.extensions.*
@ -95,7 +95,7 @@ internal class WelcomeMiddleware {
}
.doOnSuccess { selectedUserWallet ->
sendSignedInAnalyticsEvent(
scanResponse = selectedUserWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
userWallet = selectedUserWallet,
signInType = Basic.SignedIn.SignInType.Biometric,
)
@ -129,7 +129,7 @@ internal class WelcomeMiddleware {
store.dispatchWithMain(WelcomeAction.ProceedWithCard.Error(error))
}
.doOnSuccess {
sendSignedInAnalyticsEvent(scanResponse = scanResponse, signInType = Basic.SignedIn.SignInType.Card)
sendSignedInAnalyticsEvent(userWallet, signInType = Basic.SignedIn.SignInType.Card)
store.dispatchNavigationAction { replaceAll(AppRoute.Wallet) }
store.dispatchWithMain(WelcomeAction.ProceedWithCard.Success)
@ -142,7 +142,14 @@ internal class WelcomeMiddleware {
}
}
private fun sendSignedInAnalyticsEvent(scanResponse: ScanResponse, signInType: Basic.SignedIn.SignInType) {
private fun sendSignedInAnalyticsEvent(userWallet: UserWallet, signInType: Basic.SignedIn.SignInType) {
// TODO [REDACTED_TASK_KEY]
if (userWallet !is UserWallet.Cold) {
return
}
val scanResponse = userWallet.scanResponse
val currency = ParamCardCurrencyConverter().convert(
value = scanResponse.cardTypesResolver,
)

View file

@ -4,18 +4,27 @@ import com.tangem.common.extensions.toHexString
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.requireColdWallet
internal class DefaultAuthProvider(private val userWalletsListManager: UserWalletsListManager) : AuthProvider {
override fun getCardPublicKey(): String {
return userWalletsListManager.selectedUserWalletSync
?.requireColdWallet()?.scanResponse?.card?.cardPublicKey?.toHexString() ?: ""
val userWallet = userWalletsListManager.selectedUserWalletSync
if (userWallet !is UserWallet.Cold) {
return ""
}
return userWallet.scanResponse.card.cardPublicKey.toHexString()
}
override fun getCardId(): String {
return userWalletsListManager.selectedUserWalletSync
?.requireColdWallet()?.scanResponse?.card?.cardId ?: ""
val userWallet = userWalletsListManager.selectedUserWalletSync
if (userWallet !is UserWallet.Cold) {
return ""
}
return userWallet.scanResponse.card.cardId
}
override fun getCardsPublicKeys(): Map<String, String> {

View file

@ -6,7 +6,6 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.domain.model.Currency
import com.tangem.tap.proxy.redux.DaggerGraphState
@ -25,10 +24,8 @@ internal class CryptoCurrencyConverter(
cryptoCurrencyFactory.createCoin(
blockchain = value.blockchain,
extraDerivationPath = value.derivationPath,
scanResponse = requireNotNull(
store.inject(DaggerGraphState::generalUserWalletsListManager).selectedUserWalletSync
?.requireColdWallet() // TODO [REDACTED_TASK_KEY]
?.scanResponse,
userWallet = requireNotNull(
store.inject(DaggerGraphState::generalUserWalletsListManager).selectedUserWalletSync,
),
),
)
@ -37,10 +34,8 @@ internal class CryptoCurrencyConverter(
sdkToken = value.token,
blockchain = value.blockchain,
extraDerivationPath = value.derivationPath,
scanResponse = requireNotNull(
store.inject(DaggerGraphState::generalUserWalletsListManager).selectedUserWalletSync
?.requireColdWallet() // TODO [REDACTED_TASK_KEY]
?.scanResponse,
userWallet = requireNotNull(
store.inject(DaggerGraphState::generalUserWalletsListManager).selectedUserWalletSync,
),
),
)

View file

@ -13,8 +13,6 @@ import com.tangem.domain.core.lce.Lce
import com.tangem.domain.exchange.ExpressAvailabilityState
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.repository.CurrenciesRepository
@ -32,7 +30,6 @@ internal class DefaultRampManager(
private val sellService: Provider<ExchangeService>,
private val expressServiceLoader: ExpressServiceLoader,
private val currenciesRepository: CurrenciesRepository,
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
private val dispatchers: CoroutineDispatcherProvider,
excludedBlockchains: ExcludedBlockchains,
) : RampStateManager {
@ -40,11 +37,10 @@ internal class DefaultRampManager(
private val cryptoCurrencyConverter = CryptoCurrencyConverter(excludedBlockchains)
override suspend fun availableForBuy(
scanResponse: ScanResponse,
userWalletId: UserWalletId,
userWallet: UserWallet,
cryptoCurrency: CryptoCurrency,
): ScenarioUnavailabilityReason {
val availabilityState = runCatching { getOnrampAvailableState(userWalletId, cryptoCurrency) }
val availabilityState = runCatching { getOnrampAvailableState(userWallet.walletId, cryptoCurrency) }
.getOrNull()
?: ExpressAvailabilityState.Error
@ -52,8 +48,9 @@ internal class DefaultRampManager(
}
override suspend fun availableForSell(
userWallet: UserWallet,
userWalletId: UserWalletId,
status: CryptoCurrencyStatus,
sendUnavailabilityReason: ScenarioUnavailabilityReason?,
): Either<ScenarioUnavailabilityReason, Unit> {
return either {
val sellSupportedByService = catch(
@ -65,7 +62,8 @@ internal class DefaultRampManager(
catch = { raise(ScenarioUnavailabilityReason.NotSupportedBySellService(status.currency.name)) },
)
val reason = getSendUnavailabilityReason(userWallet = userWallet, cryptoCurrencyStatus = status)
val reason = sendUnavailabilityReason
?: getSendUnavailabilityReason(userWalletId = userWalletId, cryptoCurrencyStatus = status)
ensure(condition = reason is ScenarioUnavailabilityReason.None) {
when (reason) {
@ -111,6 +109,27 @@ internal class DefaultRampManager(
return expressServiceLoader.getInitializationStatus(userWalletId)
}
override suspend fun getSendUnavailabilityReason(
userWalletId: UserWalletId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
): ScenarioUnavailabilityReason {
return when {
cryptoCurrencyStatus.value.amount.isNullOrZero() -> {
ScenarioUnavailabilityReason.EmptyBalance(ScenarioUnavailabilityReason.WithdrawalScenario.SEND)
}
currenciesRepository.isSendBlockedByPendingTransactions(
userWalletId = userWalletId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
) -> {
ScenarioUnavailabilityReason.PendingTransaction(
withdrawalScenario = ScenarioUnavailabilityReason.WithdrawalScenario.SEND,
networkName = cryptoCurrencyStatus.currency.network.name,
)
}
else -> ScenarioUnavailabilityReason.None
}
}
private suspend fun getExchangeableState(
userWalletId: UserWalletId,
cryptoCurrency: CryptoCurrency,
@ -179,33 +198,4 @@ internal class DefaultRampManager(
val contractAddress = (this as? CryptoCurrency.Token)?.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE
return asset.network == network.backendId && asset.contractAddress.equals(contractAddress, ignoreCase = true)
}
private suspend fun getSendUnavailabilityReason(
userWallet: UserWallet,
cryptoCurrencyStatus: CryptoCurrencyStatus,
): ScenarioUnavailabilityReason {
val coinStatus = getNetworkCoinStatusUseCase.invokeSync(
userWallet = userWallet,
networkId = cryptoCurrencyStatus.currency.network.id,
derivationPath = cryptoCurrencyStatus.currency.network.derivationPath,
).getOrNull()
return when {
cryptoCurrencyStatus.value.amount.isNullOrZero() -> {
ScenarioUnavailabilityReason.EmptyBalance(ScenarioUnavailabilityReason.WithdrawalScenario.SEND)
}
currenciesRepository.isSendBlockedByPendingTransactions(
cryptoCurrencyStatus = cryptoCurrencyStatus,
coinStatus = coinStatus,
) -> {
ScenarioUnavailabilityReason.PendingTransaction(
withdrawalScenario = ScenarioUnavailabilityReason.WithdrawalScenario.SEND,
networkName = coinStatus?.currency?.network?.name.orEmpty(),
)
}
else -> {
ScenarioUnavailabilityReason.None
}
}
}
}

View file

@ -33,7 +33,6 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.operations.attestation.CardArtworksProvider
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
@ -75,7 +74,6 @@ data class DaggerGraphState(
val clipboardManager: ClipboardManager? = null,
val settingsManager: SettingsManager? = null,
val uiMessageSender: UiMessageSender? = null,
val onlineCardVerifier: OnlineCardVerifier? = null,
val cardArworksProvider: CardArtworksProvider? = null,
val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory? = null,
val userTokensResponseStore: UserTokensResponseStore? = null,

View file

@ -6,6 +6,7 @@ import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.value.Value
import com.arkivanov.decompose.value.subscribe
import com.arkivanov.essenty.lifecycle.subscribe
import com.google.android.material.snackbar.Snackbar
import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.context.AppComponentContext
@ -16,7 +17,10 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.features.walletconnect.components.WcRoutingComponent
import com.tangem.hot.sdk.TangemHotSdk
import com.tangem.hot.sdk.android.create
import com.tangem.tap.common.SnackbarHandler
import com.tangem.tap.features.hot.TangemHotSDKProxy
import com.tangem.tap.routing.RootContent
import com.tangem.tap.routing.component.RoutingComponent
import com.tangem.tap.routing.component.RoutingComponent.Child
@ -36,6 +40,7 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
private val uiDependencies: UiDependencies,
private val wcRoutingComponentFactory: WcRoutingComponent.Factory,
private val deeplinkFactory: DeepLinkFactory,
private val tangemHotSDKProxy: TangemHotSDKProxy,
) : RoutingComponent,
AppComponentContext by context,
SnackbarHandler {
@ -70,6 +75,8 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
appRouterConfig.stack = stackItems
}
}
configureHotSdk()
}
@Composable
@ -120,6 +127,17 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
initialStack
}
private fun configureHotSdk() {
lifecycle.subscribe(
onCreate = {
tangemHotSDKProxy.sdkState.value = TangemHotSdk.create(activity)
},
onDestroy = {
tangemHotSDKProxy.sdkState.value = null
},
)
}
@AssistedFactory
interface Factory : RoutingComponent.Factory {
override fun create(context: AppComponentContext, initialStack: List<AppRoute>?): DefaultRoutingComponent

View file

@ -26,7 +26,6 @@ import com.tangem.features.send.v2.api.NFTSendComponent
import com.tangem.features.send.v2.api.SendComponent
import com.tangem.features.staking.api.StakingComponent
import com.tangem.features.swap.SwapComponent
import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.TokenDetailsComponent
import com.tangem.features.wallet.WalletEntryComponent
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent
@ -86,7 +85,6 @@ internal class ChildFactory @Inject constructor(
private val createWalletSelectionComponentFactory: CreateWalletSelectionComponent.Factory,
private val createMobileWalletComponentFactory: CreateMobileWalletComponent.Factory,
private val addExistingWalletComponentFactory: AddExistingWalletComponent.Factory,
private val testerRouter: TesterRouter,
private val walletConnectFeatureToggles: WalletConnectFeatureToggles,
) {
@ -132,9 +130,6 @@ internal class ChildFactory @Inject constructor(
componentFactory = welcomeComponentFactory,
)
}
is AppRoute.TesterMenu -> {
Child.LegacyIntent(testerRouter.getEntryIntent())
}
is AppRoute.WalletSettings -> {
createComponentChild(
context = context,
@ -370,7 +365,7 @@ internal class ChildFactory @Inject constructor(
is AppRoute.PushNotification -> {
createComponentChild(
context = context,
params = Unit,
params = PushNotificationsComponent.Params.Route(AppRoute.Home),
componentFactory = pushNotificationsComponentFactory,
)
}