Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-28 09:36:24 +03:00
commit 4b78bf0d94
429 changed files with 10253 additions and 1865 deletions

View file

@ -3,7 +3,6 @@ package com.tangem.tap
import androidx.hilt.work.HiltWorkerFactory
import com.tangem.TangemSdkLogger
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.data.card.TransactionSignerFactory
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.filter.OneTimeEventFilter
@ -15,6 +14,7 @@ import com.tangem.core.navigation.settings.SettingsManager
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.data.card.TransactionSignerFactory
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
import com.tangem.datasource.local.config.issuers.IssuersConfigStorage
@ -32,13 +32,13 @@ import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
import com.tangem.domain.onboarding.repository.OnboardingRepository
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.features.onramp.OnrampFeatureToggles
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
@ -94,8 +94,6 @@ interface ApplicationEntryPoint {
fun getSaveTwinsOnboardingShownUseCase(): SaveTwinsOnboardingShownUseCase
fun getWalletNameGenerateUseCase(): GenerateWalletNameUseCase
fun getCardRepository(): CardRepository
fun getTangemSdkLogger(): TangemSdkLogger
@ -118,8 +116,6 @@ interface ApplicationEntryPoint {
fun getTransactionSignerFactory(): TransactionSignerFactory
fun getGetUserCountryCodeUseCase(): GetUserCountryUseCase
fun getOnrampFeatureToggles(): OnrampFeatureToggles
fun getOnboardingV2FeatureToggles(): OnboardingV2FeatureToggles
@ -142,4 +138,8 @@ interface ApplicationEntryPoint {
fun getUiMessageSender(): UiMessageSender
fun getWorkerFactory(): HiltWorkerFactory
fun getOnlineCardVerifier(): OnlineCardVerifier
fun getUserWalletBuilderFactory(): UserWalletBuilder.Factory
}

View file

@ -8,6 +8,7 @@ import coil.ImageLoaderFactory
import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.tangem.Log
import com.tangem.TangemSdkLogger
import com.tangem.blockchain.common.ExceptionHandler
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
@ -46,15 +47,16 @@ import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
import com.tangem.domain.onboarding.repository.OnboardingRepository
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.common.analytics.AnalyticsFactory
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.handlers.BlockchainExceptionHandler
import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler
import com.tangem.tap.common.images.createCoilImageLoader
@ -146,9 +148,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
private val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase
get() = entryPoint.getSaveTwinsOnboardingShownUseCase()
private val generateWalletNameUseCase: GenerateWalletNameUseCase
get() = entryPoint.getWalletNameGenerateUseCase()
private val cardRepository: CardRepository
get() = entryPoint.getCardRepository()
@ -182,9 +181,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
private val transactionSignerFactory: TransactionSignerFactory
get() = entryPoint.getTransactionSignerFactory()
private val getUserCountryUseCase: GetUserCountryUseCase
get() = entryPoint.getGetUserCountryCodeUseCase()
private val onrampFeatureToggles: OnrampFeatureToggles
get() = entryPoint.getOnrampFeatureToggles()
@ -212,6 +208,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
private val uiMessageSender: UiMessageSender
get() = entryPoint.getUiMessageSender()
private val blockchainExceptionHandler: BlockchainExceptionHandler
get() = entryPoint.getBlockchainExceptionHandler()
private val workerFactory: HiltWorkerFactory
get() = entryPoint.getWorkerFactory()
@ -220,6 +219,12 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
.setWorkerFactory(workerFactory)
.build()
private val onlineCardVerifier: OnlineCardVerifier
get() = entryPoint.getOnlineCardVerifier()
private val userWalletBuilderFactory: UserWalletBuilder.Factory
get() = entryPoint.getUserWalletBuilderFactory()
// endregion
override fun onCreate() {
@ -265,7 +270,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
}
loadNativeLibraries()
// ExceptionHandler.append(blockchainExceptionHandler) // TODO [REDACTED_TASK_KEY] Send only to Firebase
ExceptionHandler.append(blockchainExceptionHandler)
if (LogConfig.network.blockchainSdkNetwork) {
BlockchainSdkRetrofitBuilder.interceptors = listOf(
createNetworkLoggingInterceptor(),
@ -301,7 +306,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
generalUserWalletsListManager = generalUserWalletsListManager,
wasTwinsOnboardingShownUseCase = wasTwinsOnboardingShownUseCase,
saveTwinsOnboardingShownUseCase = saveTwinsOnboardingShownUseCase,
generateWalletNameUseCase = generateWalletNameUseCase,
cardRepository = cardRepository,
settingsRepository = settingsRepository,
blockchainSDKFactory = blockchainSDKFactory,
@ -312,7 +316,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
shareManager = shareManager,
appRouter = appRouter,
transactionSignerFactory = transactionSignerFactory,
getUserCountryUseCase = getUserCountryUseCase,
onrampFeatureToggles = onrampFeatureToggles,
environmentConfigStorage = environmentConfigStorage,
onboardingV2FeatureToggles = onboardingV2FeatureToggles,
@ -322,6 +325,8 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
clipboardManager = clipboardManager,
settingsManager = settingsManager,
uiMessageSender = uiMessageSender,
onlineCardVerifier = onlineCardVerifier,
userWalletBuilderFactory = userWalletBuilderFactory,
),
),
)

View file

@ -1,20 +1,20 @@
package com.tangem.tap.common.analytics
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.core.analytics.api.ErrorEventLogger
import com.tangem.core.analytics.api.ExceptionLogger
import com.tangem.core.analytics.api.EventLogger
import timber.log.Timber
class AnalyticsEventsLogger(
private val name: String,
private val jsonConverter: MoshiJsonConverter,
) : EventLogger, ErrorEventLogger {
) : EventLogger, ExceptionLogger {
override fun logEvent(event: String, params: Map<String, String>) {
Timber.d(jsonConverter.prettyPrint(PrintEventModel(name, event, params)))
}
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
override fun logException(error: Throwable, params: Map<String, String>) {
Timber.e(error, jsonConverter.prettyPrint(PrintEventModel(name, "error", params)))
}
}

View file

@ -2,7 +2,7 @@ package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.models.AnalyticsEvent
class BlockchainExceptionEvent(
class BlockchainApiExceptionEvent(
selectedHost: String,
exceptionHost: String,
error: String,

View file

@ -6,7 +6,6 @@ internal sealed class Push(event: String) : AnalyticsEvent(
category = "Push",
event = event,
params = emptyMap(),
error = null,
) {
data object PushNotificationOpened : Push(event = "Push Notification Opened")

View file

@ -9,8 +9,7 @@ sealed class Settings(
category: String = "Settings",
event: String,
params: Map<String, String> = mapOf(),
error: Throwable? = null,
) : AnalyticsEvent(category, event, params, error) {
) : AnalyticsEvent(category, event, params) {
class ScreenOpened : Settings(event = "Settings Screen Opened")
class ButtonStartWalletConnectSession : Settings(event = "Button - Start Wallet Connect Session")
@ -19,16 +18,14 @@ sealed class Settings(
sealed class CardSettings(
event: String,
params: Map<String, String> = mapOf(),
error: Throwable? = null,
) : Settings("Settings / Card Settings", event, params, error) {
) : Settings("Settings / Card Settings", event, params) {
class ButtonFactoryReset : CardSettings("Button - Factory Reset")
class FactoryResetFinished(cardsCount: Int? = null, error: Throwable? = null) : CardSettings(
class FactoryResetFinished(cardsCount: Int? = null) : CardSettings(
event = "Factory Reset Finished",
params = buildMap {
cardsCount?.let { put("Cards Count", "$it") }
},
error = error,
)
class FactoryResetCanceled(cardsCount: Int) : CardSettings(
@ -44,10 +41,9 @@ sealed class Settings(
params = mapOf("Type" to type.value),
)
class SecurityModeChanged(mode: AnalyticsParam.SecurityMode, error: Throwable? = null) : CardSettings(
class SecurityModeChanged(mode: AnalyticsParam.SecurityMode) : CardSettings(
event = "Security Mode Changed",
params = mapOf("Mode" to mode.value),
error = error,
)
class AccessCodeRecoveryChanged(status: AnalyticsParam.AccessCodeRecoveryStatus) : CardSettings(

View file

@ -8,8 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
sealed class SignIn(
event: String,
params: Map<String, String> = mapOf(),
error: Throwable? = null,
) : AnalyticsEvent("Sign In", event, params, error) {
) : AnalyticsEvent("Sign In", event, params) {
class ScreenOpened : SignIn(event = "Sign In Screen Opened")

View file

@ -0,0 +1,18 @@
package com.tangem.tap.common.analytics.events
import com.tangem.common.core.TangemSdkError
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter
class TangemSdkErrorEvent(
val exception: TangemSdkError,
) : AnalyticsEvent(
category = "TangemSdk",
event = "Tangem Sdk Error",
params = errorConverter.convert(exception),
) {
companion object {
private val errorConverter = AnalyticsErrorConverter()
}
}

View file

@ -10,8 +10,7 @@ sealed class Token(
category: String,
event: String,
params: Map<String, String> = mapOf(),
error: Throwable? = null,
) : AnalyticsEvent(category, event, params, error) {
) : AnalyticsEvent(category, event, params) {
sealed class Receive(
event: String,

View file

@ -9,8 +9,7 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.O
internal sealed class WalletConnect(
event: String,
params: Map<String, String> = mapOf(),
error: Throwable? = null,
) : AnalyticsEvent("Wallet Connect", event, params, error) {
) : AnalyticsEvent("Wallet Connect", event, params) {
class ScreenOpened : WalletConnect(event = "WC Screen Opened")
class NewSessionInitiated(source: SourceType) : WalletConnect(

View file

@ -1,16 +1,16 @@
package com.tangem.tap.common.analytics.handlers
import com.tangem.blockchain.common.ExceptionHandlerOutput
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.tap.common.analytics.events.BlockchainExceptionEvent
import com.tangem.core.analytics.api.AnalyticsErrorHandler
import com.tangem.tap.common.analytics.events.BlockchainApiExceptionEvent
import javax.inject.Inject
class BlockchainExceptionHandler @Inject constructor(
private val analyticsHandler: AnalyticsEventHandler,
private val analyticsErrorHandler: AnalyticsErrorHandler,
) : ExceptionHandlerOutput {
override fun handleApiSwitch(currentHost: String, nextHost: String, message: String) {
analyticsHandler.send(
BlockchainExceptionEvent(
analyticsErrorHandler.sendErrorEvent(
BlockchainApiExceptionEvent(
selectedHost = nextHost,
exceptionHost = currentHost,
error = message,

View file

@ -1,16 +1,18 @@
package com.tangem.tap.common.analytics.handlers.firebase
import com.google.firebase.analytics.FirebaseAnalytics
import com.tangem.core.analytics.api.AnalyticsErrorHandler
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.ErrorEventHandler
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter
import com.tangem.tap.common.analytics.events.Shop
class FirebaseAnalyticsHandler(
private val client: FirebaseAnalyticsClient,
) : AnalyticsHandler, ErrorEventHandler {
) : AnalyticsHandler, AnalyticsErrorHandler, AnalyticsExceptionHandler {
private val errorConverter = AnalyticsErrorConverter()
override fun id(): String = ID
@ -18,30 +20,17 @@ class FirebaseAnalyticsHandler(
client.logEvent(eventId, params)
}
override fun send(event: AnalyticsEvent) {
val error = event.error
when {
error != null -> {
val errorConverter = AnalyticsErrorConverter()
if (!errorConverter.canBeHandled(error)) return
override fun sendException(event: ExceptionAnalyticsEvent) {
if (!errorConverter.canBeHandled(event.exception)) return
val errorParams = errorConverter.convert(error).toMutableMap()
errorParams["Category"] = event.category
errorParams["Event"] = event.event
errorParams.putAll(event.params)
send(error, errorParams)
}
event is Shop.Purchased -> {
send(FirebaseAnalytics.Event.PURCHASE, event.params)
}
else -> {
super.send(event)
}
}
val errorParams = errorConverter.convert(event.exception).toMutableMap()
errorParams.putAll(event.params)
client.logException(event.exception, event.params)
}
override fun send(error: Throwable, params: Map<String, String>) {
client.logErrorEvent(error, params)
override fun sendErrorEvent(event: AnalyticsEvent) {
send(event)
}
companion object {

View file

@ -4,14 +4,15 @@ import android.os.Bundle
import androidx.core.os.bundleOf
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.crashlytics.recordException
import com.google.firebase.ktx.Firebase
import com.tangem.core.analytics.api.ErrorEventLogger
import com.tangem.core.analytics.api.ExceptionLogger
import com.tangem.core.analytics.api.EventLogger
/**
[REDACTED_AUTHOR]
*/
interface FirebaseAnalyticsClient : EventLogger, ErrorEventLogger
interface FirebaseAnalyticsClient : EventLogger, ExceptionLogger
internal class FirebaseClient : FirebaseAnalyticsClient {
@ -27,11 +28,11 @@ internal class FirebaseClient : FirebaseAnalyticsClient {
)
}
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
eventConverter.convertEventParams(params)
.forEach { fbCrashlytics.setCustomKey(it.key, it.value) }
fbCrashlytics.recordException(error)
override fun logException(error: Throwable, params: Map<String, String>) {
fbCrashlytics.recordException(error) {
eventConverter.convertEventParams(params)
.forEach { key(it.key, it.value) }
}
}
private fun Map<String, String>.toBundle(): Bundle = bundleOf(*this.toList().toTypedArray())

View file

@ -16,7 +16,7 @@ internal class FirebaseLogClient(
logger.logEvent(event, params)
}
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
logger.logErrorEvent(error, params)
override fun logException(error: Throwable, params: Map<String, String>) {
logger.logException(error, params)
}
}

View file

@ -10,11 +10,13 @@ import com.tangem.common.authentication.AuthenticationManager
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.core.Config
import com.tangem.common.services.secure.SecureStorage
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.crypto.bip39.Wordlist
import com.tangem.data.card.sdk.CardSdkOwner
import com.tangem.data.card.sdk.CardSdkProvider
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
@ -32,8 +34,9 @@ import javax.inject.Singleton
*/
@Singleton
internal class DefaultCardSdkProvider @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
private val dispatchers: CoroutineDispatcherProvider,
private val cardSdkFeatureToggles: CardSdkFeatureToggles,
) : CardSdkProvider, CardSdkOwner {
private val observer = Observer()
@ -46,7 +49,12 @@ internal class DefaultCardSdkProvider @Inject constructor(
override fun register(activity: FragmentActivity) = runBlocking(dispatchers.mainImmediate) {
if (activity.isDestroyed || activity.isFinishing || activity.isChangingConfigurations) {
val message = "Tangem SDK owner registration skipped: activity is destroyed or finishing"
analyticsEventHandler.send(TangemSdkWarningEvent(message))
analyticsExceptionHandler.sendException(
ExceptionAnalyticsEvent(
exception = IllegalStateException(message),
params = errorParams,
),
)
Log.info { message }
return@runBlocking
}
@ -64,14 +72,24 @@ internal class DefaultCardSdkProvider @Inject constructor(
private fun tryToRegisterWithForegroundActivity(): TangemSdk = runBlocking(dispatchers.mainImmediate) {
val warning = "Tangem SDK holder is null, trying to recreate it with foreground activity"
analyticsEventHandler.send(TangemSdkWarningEvent(warning))
analyticsExceptionHandler.sendException(
ExceptionAnalyticsEvent(
exception = IllegalStateException(warning),
params = errorParams,
),
)
Log.warning { warning }
val activity = foregroundActivityObserver.foregroundActivity
if (activity == null) {
val error = "Tangem SDK holder is null and foreground activity is null"
analyticsEventHandler.send(TangemSdkWarningEvent(error))
analyticsExceptionHandler.sendException(
ExceptionAnalyticsEvent(
exception = IllegalStateException(error),
params = errorParams,
),
)
Log.error { error }
error(error)
}
@ -82,7 +100,12 @@ internal class DefaultCardSdkProvider @Inject constructor(
if (sdk == null) {
val error = "Tangem SDK is null after re-registering with foreground activity"
analyticsEventHandler.send(TangemSdkWarningEvent(error))
analyticsExceptionHandler.sendException(
ExceptionAnalyticsEvent(
exception = IllegalStateException(error),
params = errorParams,
),
)
Log.error { error }
error(error)
}
@ -108,7 +131,9 @@ internal class DefaultCardSdkProvider @Inject constructor(
authenticationManager = authenticationManager,
keystoreManager = keystoreManager,
wordlist = Wordlist.getWordlist(activity),
config = config,
config = config.apply {
isNewOnlineAttestationEnabled = cardSdkFeatureToggles.isNewAttestationEnabled
},
)
holder = Holder(
@ -170,5 +195,10 @@ internal class DefaultCardSdkProvider @Inject constructor(
),
),
)
val errorParams = mapOf(
"Category" to "Tangem SDK",
"Event" to "Warning",
)
}
}

View file

@ -1,9 +0,0 @@
package com.tangem.tap.data
import com.tangem.core.analytics.models.AnalyticsEvent
internal class TangemSdkWarningEvent(message: String) : AnalyticsEvent(
category = "Tangem SDK",
event = "Warning",
error = IllegalStateException(message),
)

View file

@ -0,0 +1,40 @@
package com.tangem.tap.di.domain
import com.tangem.domain.nft.FetchNFTCollectionsUseCase
import com.tangem.domain.nft.GetNFTCollectionsUseCase
import com.tangem.domain.nft.repository.NFTRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository
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 NFTDomainModule {
@Provides
@Singleton
fun providesGetNFTCollectionsUseCase(
currenciesRepository: CurrenciesRepository,
nftRepository: NFTRepository,
): GetNFTCollectionsUseCase {
return GetNFTCollectionsUseCase(
currenciesRepository = currenciesRepository,
nftRepository = nftRepository,
)
}
@Provides
@Singleton
fun providesFetchNFTCollectionsUseCase(
currenciesRepository: CurrenciesRepository,
nftRepository: NFTRepository,
): FetchNFTCollectionsUseCase {
return FetchNFTCollectionsUseCase(
currenciesRepository = currenciesRepository,
nftRepository = nftRepository,
)
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.tap.di.domain
import com.tangem.domain.onramp.repositories.LegacyTopUpRepository
import com.tangem.domain.onramp.*
import com.tangem.domain.onramp.repositories.HotCryptoRepository
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
@ -242,4 +243,10 @@ internal object OnrampDomainModule {
fun provideFetchHotCryptoUseCase(hotCryptoRepository: HotCryptoRepository): FetchHotCryptoUseCase {
return FetchHotCryptoUseCase(hotCryptoRepository)
}
@Provides
@Singleton
fun provideGetLegacyTopUpUrlUseCase(legacyTopUpRepository: LegacyTopUpRepository): GetLegacyTopUpUrlUseCase {
return GetLegacyTopUpUrlUseCase(legacyTopUpRepository)
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.tap.di.domain
import com.tangem.domain.staking.*
import com.tangem.domain.staking.repositories.*
import com.tangem.domain.walletmanager.WalletManagersFacade
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -209,4 +210,12 @@ internal object StakingDomainModule {
fun provideGetStakingIntegrationIdUseCase(stakingRepository: StakingRepository): GetStakingIntegrationIdUseCase {
return GetStakingIntegrationIdUseCase(stakingRepository)
}
@Provides
@Singleton
fun provideCheckAccountInitializedUseCase(
walletManagersFacade: WalletManagersFacade,
): CheckAccountInitializedUseCase {
return CheckAccountInitializedUseCase(walletManagersFacade)
}
}

View file

@ -5,10 +5,11 @@ import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.promo.PromoRepository
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.operations.*
import com.tangem.domain.tokens.operations.BaseCurrenciesStatusesOperations
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
import com.tangem.domain.tokens.operations.CachedCurrenciesStatusesOperations
import com.tangem.domain.tokens.repository.*
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.features.swap.SwapFeatureToggles
import com.tangem.tap.domain.tokens.DefaultTokensFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
@ -209,7 +210,6 @@ internal object TokensDomainModule {
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
promoRepository: PromoRepository,
swapFeatureToggles: SwapFeatureToggles,
dispatchers: CoroutineDispatcherProvider,
currencyStatusOperations: BaseCurrencyStatusOperations,
): GetCryptoCurrencyActionsUseCase {
@ -219,7 +219,6 @@ internal object TokensDomainModule {
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
promoRepository = promoRepository,
swapFeatureToggles = swapFeatureToggles,
dispatchers = dispatchers,
currencyStatusOperations = currencyStatusOperations,
)
@ -351,21 +350,13 @@ internal object TokensDomainModule {
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
): BaseCurrenciesStatusesOperations {
return if (tokensFeatureToggles.isBalancesCachingEnabled) {
CachedCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
)
} else {
LceCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
)
}
return CachedCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@Provides
@ -377,20 +368,12 @@ internal object TokensDomainModule {
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
): BaseCurrencyStatusOperations {
return if (tokensFeatureToggles.isBalancesCachingEnabled) {
CachedCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
)
} else {
CurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
)
}
return CachedCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
tokensFeatureToggles = tokensFeatureToggles,
)
}
}

View file

@ -10,7 +10,10 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
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.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
@ -18,6 +21,7 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Suppress("TooManyFunctions")
@Module
@InstallIn(SingletonComponent::class)
internal object WalletsDomainModule {
@ -165,4 +169,22 @@ internal object WalletsDomainModule {
fun providesSeedPhraseNotificationUseCase(walletsRepository: WalletsRepository): SeedPhraseNotificationUseCase {
return SeedPhraseNotificationUseCase(walletsRepository = walletsRepository)
}
@Provides
@Singleton
fun provideGetCardImageUseCase(onlineCardVerifier: OnlineCardVerifier): GetCardImageUseCase {
return GetCardImageUseCase(verifier = onlineCardVerifier)
}
@Provides
@Singleton
fun providesIsWalletNFTEnabledSyncUseCase(
walletsRepository: WalletsRepository,
nftFeatureToggles: NFTFeatureToggles,
): IsWalletNFTEnabledSyncUseCase {
return IsWalletNFTEnabledSyncUseCase(
walletsRepository = walletsRepository,
nftFeatureToggles = nftFeatureToggles,
)
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.tap.di.routing
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.tap.routing.ProxyAppRouter
import com.tangem.tap.routing.configurator.AppRouterConfig
import com.tangem.tap.routing.configurator.MutableAppRouterConfig
@ -17,8 +18,15 @@ internal object AppRouterModule {
@Provides
@Singleton
fun provideAppRouter(config: AppRouterConfig, dispatchers: CoroutineDispatcherProvider): AppRouter =
ProxyAppRouter(config, dispatchers)
fun provideAppRouter(
config: AppRouterConfig,
dispatchers: CoroutineDispatcherProvider,
analyticsExceptionHandler: AnalyticsExceptionHandler,
): AppRouter = ProxyAppRouter(
config = config,
dispatchers = dispatchers,
analyticsExceptionHandler = analyticsExceptionHandler,
)
@Provides
@Singleton

View file

@ -6,14 +6,17 @@ 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.CardVerifyAndGetInfo
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): String {
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
@ -28,7 +31,7 @@ suspend fun CardDTO.getOrLoadCardArtworkUrl(cardInfo: Result<CardVerifyAndGetInf
}
}
return when (val cardInfoResult = cardInfo ?: OnlineCardVerifier().getCardInfo(cardId, cardPublicKey)) {
return when (val cardInfoResult = cardInfo ?: onlineCardVerifier.getCardInfo(cardId, cardPublicKey)) {
is Result.Success -> {
val artworkId = cardInfoResult.data.artwork?.id
if (artworkId.isNullOrEmpty()) {

View file

@ -235,13 +235,25 @@ internal class LegacyScanProcessor @Inject constructor(
} else {
Analytics.setContext(scanResponse)
val isTwinRefactoringEnabled =
store.inject(DaggerGraphState::onboardingV2FeatureToggles).isTwinRefactoringEnabled
val wasTwinsOnboardingShown =
store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase).invokeSync()
if (scanResponse.twinsIsTwinned() && !wasTwinsOnboardingShown) {
onWalletNotCreated()
store.dispatchOnMain(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(scanResponse)))
navigateTo(AppRoute.OnboardingTwins) { onProgressStateChange(it) }
if (isTwinRefactoringEnabled) {
navigateTo(
AppRoute.Onboarding(
scanResponse = scanResponse,
mode = AppRoute.Onboarding.Mode.WelcomeOnlyTwin,
),
) { onProgressStateChange(it) }
} else {
store.dispatchOnMain(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(scanResponse)))
navigateTo(AppRoute.OnboardingTwins) { onProgressStateChange(it) }
}
} else {
delay(DELAY_SDK_DIALOG_CLOSE)
onSuccess(scanResponse)

View file

@ -7,9 +7,11 @@ import com.tangem.common.routing.AppRoute
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.domain.card.ScanCardException
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.redux.StateDialog
import com.tangem.tap.common.analytics.events.TangemSdkErrorEvent
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchNavigationAction
import com.tangem.tap.common.extensions.inject
@ -34,7 +36,7 @@ internal object UseCaseScanProcessor {
ifLeft = {
val error = scanCardExceptionConverter.convertBack(it)
Analytics.send(Basic.ScanError(error))
Analytics.sendErrorEvent(TangemSdkErrorEvent(error))
CompletionResult.Failure(error)
},
ifRight = { CompletionResult.Success(it) },
@ -97,7 +99,12 @@ internal object UseCaseScanProcessor {
-> {
val error = scanCardExceptionConverter.convertBack(exception)
Analytics.send(Basic.ScanError(error))
Analytics.sendException(
ExceptionAnalyticsEvent(
exception = error,
params = mapOf("Event" to "Scan"),
),
)
onFailure(error)
}
}

View file

@ -59,15 +59,27 @@ class CheckForOnboardingChain(
else -> {
Analytics.setContext(previousChainResult)
val isTwinRefactoringEnabled =
store.inject(DaggerGraphState::onboardingV2FeatureToggles).isTwinRefactoringEnabled
val wasTwinsOnboardingShown = store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase)
.invokeSync()
// If twins was twinned previously but twins welcome not shown
if (previousChainResult.twinsIsTwinned() && !wasTwinsOnboardingShown) {
store.dispatchOnMain(
TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(previousChainResult)),
)
ScanChainException.OnboardingNeeded(AppRoute.OnboardingTwins).left()
if (isTwinRefactoringEnabled) {
ScanChainException.OnboardingNeeded(
AppRoute.Onboarding(
scanResponse = previousChainResult,
mode = AppRoute.Onboarding.Mode.WelcomeOnlyTwin,
),
).left()
} else {
store.dispatchOnMain(
TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(previousChainResult)),
)
ScanChainException.OnboardingNeeded(AppRoute.OnboardingTwins).left()
}
} else {
delay(DELAY_SDK_DIALOG_CLOSE)
previousChainResult.right()

View file

@ -12,7 +12,7 @@ internal class ScanCardExceptionConverter : TwoWayConverter<TangemError, ScanCar
else -> ScanCardException.UnknownException(value)
}
override fun convertBack(value: ScanCardException): TangemError {
override fun convertBack(value: ScanCardException): TangemSdkError {
return when (value) {
is ScanCardException.UnknownException -> TangemSdkError.ExceptionError(value)
is ScanCardException.UserCancelled -> TangemSdkError.UserCancelled()
@ -31,7 +31,7 @@ internal class ScanCardExceptionConverter : TwoWayConverter<TangemError, ScanCar
else -> ScanCardException.UnknownException(value)
}
private fun concertScanChainException(value: ScanCardException.ChainException): TangemError {
private fun concertScanChainException(value: ScanCardException.ChainException): TangemSdkError {
return when (val e = value as? ScanChainException) {
is ScanChainException.DisclaimerWasCanceled -> TangemSdkError.UserCancelled()
is ScanChainException.OnboardingNeeded,

View file

@ -14,7 +14,6 @@ import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.services.secure.SecureStorage
import com.tangem.common.usersCode.UserCodeRepository
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.Basic
import com.tangem.core.res.getStringSafe
import com.tangem.crypto.bip39.DefaultMnemonic
import com.tangem.crypto.hdWallet.DerivationPath
@ -41,6 +40,7 @@ import com.tangem.sdk.api.CreateProductWalletTaskResponse
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.sdk.api.visa.VisaCardActivationResponse
import com.tangem.sdk.api.visa.VisaCardActivationTaskMode
import com.tangem.tap.common.analytics.events.TangemSdkErrorEvent
import com.tangem.tap.derivationsFinder
import com.tangem.tap.domain.tasks.product.CreateProductWalletTask
import com.tangem.tap.domain.tasks.product.ResetBackupCardTask
@ -224,7 +224,7 @@ internal class DefaultTangemSdkManager(
private fun sendScanResultsToAnalytics(result: CompletionResult<ScanResponse>) {
if (result is CompletionResult.Failure) {
(result.error as? TangemSdkError)?.let { error ->
Analytics.send(Basic.ScanError(error))
Analytics.sendErrorEvent(TangemSdkErrorEvent(error))
}
}
}

View file

@ -7,6 +7,6 @@ internal class DefaultTokensFeatureToggles(
private val featureTogglesManager: FeatureTogglesManager,
) : TokensFeatureToggles {
override val isBalancesCachingEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "BALANCES_CACHING_ENABLED")
override val isNetworksLoadingRefactoringEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "NETWORKS_LOADING_REFACTORING_ENABLED")
}

View file

@ -5,17 +5,10 @@ import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.tap.domain.walletconnect2.domain.WcBlockchainHelper
import com.tangem.tap.domain.walletconnect2.domain.models.Account
import com.tangem.tap.domain.walletconnect2.toggles.WalletConnectFeatureToggles
internal class TangemWcBlockchainHelper(
featureToggles: WalletConnectFeatureToggles,
) : WcBlockchainHelper {
internal class TangemWcBlockchainHelper : WcBlockchainHelper {
private val supportedNonEvmBlockchains = if (featureToggles.isSolanaTxSignEnabled) {
setOf(Blockchain.Solana, Blockchain.SolanaTestnet)
} else {
emptySet()
}
private val supportedNonEvmBlockchains = setOf(Blockchain.Solana, Blockchain.SolanaTestnet)
override fun chainIdToNetworkIdOrNull(chainId: String): String? {
val parsedId = chainId.parseId() ?: return null

View file

@ -8,11 +8,14 @@ import com.reown.android.relay.ConnectionType
import com.reown.walletkit.client.Wallet
import com.reown.walletkit.client.WalletKit
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.data.walletconnect.pair.unsupportedDApps
import com.tangem.tap.common.analytics.events.WalletConnect
import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper
import com.tangem.tap.domain.walletconnect2.domain.*
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
import com.tangem.tap.domain.walletconnect2.domain.WcJrpcMethods
import com.tangem.tap.domain.walletconnect2.domain.WcJrpcRequestsDeserializer
import com.tangem.tap.domain.walletconnect2.domain.WcRequest
import com.tangem.tap.domain.walletconnect2.domain.models.*
import com.tangem.tap.domain.walletconnect2.toggles.WalletConnectFeatureToggles
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
@ -24,7 +27,6 @@ internal class DefaultLegacyWalletConnectRepository(
private val application: Application,
private val wcRequestDeserializer: WcJrpcRequestsDeserializer,
private val analyticsHandler: AnalyticsEventHandler,
private val walletConnectFeatureToggles: WalletConnectFeatureToggles,
) : LegacyWalletConnectRepository {
private var sessionProposal: Wallet.Model.SessionProposal? = null
@ -36,7 +38,7 @@ internal class DefaultLegacyWalletConnectRepository(
private val _activeSessions: MutableSharedFlow<List<WalletConnectSession>> = MutableSharedFlow()
override val activeSessions: Flow<List<WalletConnectSession>> = _activeSessions
private val blockchainHelper by lazy { TangemWcBlockchainHelper(walletConnectFeatureToggles) }
private val blockchainHelper by lazy { TangemWcBlockchainHelper() }
override var currentSessions: List<WalletConnectSession> = emptyList()
private set
@ -555,9 +557,4 @@ internal class DefaultLegacyWalletConnectRepository(
val userChains = userNamespaces.flatMap { it.value.map { account -> account.chainId } }
return wcProvidedChains.intersect(userChains.toSet())
}
private companion object {
val unsupportedDApps = listOf("dYdX", "dYdX v4", "Apex Pro", "The Sandbox")
}
}

View file

@ -33,7 +33,6 @@ internal object WalletConnectInteractorModule {
fun provideWalletConnectInteractor(
wcRepository: LegacyWalletConnectRepository,
wcSessionsRepository: WalletConnectSessionsRepository,
walletConnectFeatureToggles: WalletConnectFeatureToggles,
currenciesRepository: CurrenciesRepository,
walletManagersFacade: WalletManagersFacade,
userWalletsListManager: UserWalletsListManager,
@ -44,7 +43,7 @@ internal object WalletConnectInteractorModule {
walletConnectRepository = wcRepository,
sessionsRepository = wcSessionsRepository,
sdkHelper = WalletConnectSdkHelper(),
blockchainHelper = TangemWcBlockchainHelper(walletConnectFeatureToggles),
blockchainHelper = TangemWcBlockchainHelper(),
currenciesRepository = currenciesRepository,
walletManagersFacade = walletManagersFacade,
userWalletsListManager = userWalletsListManager,
@ -69,13 +68,11 @@ internal object WalletConnectModule {
application: Application,
wcRequestDeserializer: WcJrpcRequestsDeserializer,
analyticsHandler: AnalyticsEventHandler,
walletConnectFeatureToggles: WalletConnectFeatureToggles,
): LegacyWalletConnectRepository {
return DefaultLegacyWalletConnectRepository(
application = application,
wcRequestDeserializer = wcRequestDeserializer,
analyticsHandler = analyticsHandler,
walletConnectFeatureToggles = walletConnectFeatureToggles,
)
}

View file

@ -6,6 +6,6 @@ internal class WalletConnectFeatureToggles(
private val featureTogglesManager: FeatureTogglesManager,
) {
val isSolanaTxSignEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "WC_SOLANA_TX_SIGN_ENABLED")
val isRedesignedWalletConnectEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled("WALLET_CONNECT_REDESIGN_ENABLED")
}

View file

@ -3,7 +3,6 @@ package com.tangem.tap.features.details.redux.walletconnect
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.common.routing.AppRoute
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.tap.common.extensions.dispatchNavigationAction
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.inject
@ -61,7 +60,11 @@ class WalletConnectMiddleware {
if (uri != null && isWalletConnectUri(uri)) {
store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri))
} else {
store.dispatchNavigationAction { push(AppRoute.QrScanning(SourceType.WALLET_CONNECT)) }
store.dispatchNavigationAction {
push(
AppRoute.QrScanning(AppRoute.QrScanning.Source.WALLET_CONNECT),
)
}
}
}
is WalletConnectAction.ShowClipboardOrScanQrDialog -> {

View file

@ -20,6 +20,7 @@ import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Settings
@ -55,6 +56,7 @@ internal class CardSettingsModel @Inject constructor(
private val getUserWalletUseCase: GetUserWalletUseCase,
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val settingsRepository: SettingsRepository,
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
) : Model() {
private val params = paramsContainer.require<CardSettingsComponent.Params>()
@ -198,13 +200,24 @@ internal class CardSettingsModel @Inject constructor(
}
if (scanResponse.cardTypesResolver.isTangemTwins()) {
// needs to prepare twin state if it's twin cards (depends on onboarding refactoring)
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet(scanResponse)))
if (onboardingV2FeatureToggles.isTwinRefactoringEnabled) {
store.dispatchNavigationAction {
push(
AppRoute.Onboarding(
scanResponse = scanResponse,
mode = AppRoute.Onboarding.Mode.RecreateWalletTwin,
),
)
}
} else {
// needs to prepare twin state if it's twin cards (depends on onboarding refactoring)
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet(scanResponse)))
cardSettingsInteractor.clear()
cardSettingsInteractor.clear()
store.dispatchNavigationAction { push(AppRoute.OnboardingTwins) }
store.dispatchNavigationAction { push(AppRoute.OnboardingTwins) }
}
} else {
val card = scanResponse.card

View file

@ -140,9 +140,9 @@ private fun ConditionCheckBox(checkedState: Boolean, onCheckedChange: (Boolean)
Icon(
painter = painterResource(
if (checked) {
R.drawable.ic_accepted
R.drawable.ic_accepted_20
} else {
R.drawable.ic_unticked
R.drawable.ic_unticked_20
},
),
contentDescription = null,

View file

@ -4,13 +4,15 @@ import androidx.compose.runtime.Stable
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.AnalyticsErrorHandler
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Settings
import com.tangem.tap.common.analytics.events.TangemSdkErrorEvent
import com.tangem.tap.common.extensions.dispatchNavigationAction
import com.tangem.tap.features.details.redux.SecurityOption
import com.tangem.tap.features.details.ui.cardsettings.domain.CardSettingsInteractor
@ -30,6 +32,8 @@ internal class SecurityModeModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val tangemSdkManager: TangemSdkManager,
private val cardSettingsInteractor: CardSettingsInteractor,
private val analyticsEventHandler: AnalyticsEventHandler,
private val analyticsErrorHandler: AnalyticsErrorHandler,
) : Model() {
private val scannedScanResponse = cardSettingsInteractor.scannedScanResponse.value
@ -85,14 +89,14 @@ internal class SecurityModeModel @Inject constructor(
val paramValue = AnalyticsParam.SecurityMode.from(selectedOption)
when (result) {
is CompletionResult.Success -> {
Analytics.send(Settings.CardSettings.SecurityModeChanged(paramValue))
analyticsEventHandler.send(Settings.CardSettings.SecurityModeChanged(paramValue))
store.dispatchNavigationAction(AppRouter::pop)
}
is CompletionResult.Failure -> {
val error = result.error
if (error is TangemSdkError && error !is TangemSdkError.UserCancelled) {
Analytics.send(Settings.CardSettings.SecurityModeChanged(paramValue, error))
analyticsErrorHandler.sendErrorEvent(TangemSdkErrorEvent(error))
}
}
else -> Unit

View file

@ -21,7 +21,7 @@ object ClipboardOrScanQrDialog {
}
setNegativeButton(context.getText(R.string.wallet_connect_scan_new_code)) { _, _ ->
store.dispatchNavigationAction {
push(AppRoute.QrScanning(source = SourceType.WALLET_CONNECT))
push(AppRoute.QrScanning(source = AppRoute.QrScanning.Source.WALLET_CONNECT))
}
}
setOnDismissListener {

View file

@ -19,7 +19,6 @@ import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.tokens.TokensAction
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
import com.tangem.tap.common.analytics.events.IntroductionProcess
@ -44,12 +43,12 @@ import javax.inject.Inject
internal class HomeModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val scanCardProcessor: ScanCardProcessor,
private val generateWalletNameUseCase: GenerateWalletNameUseCase,
private val saveWalletUseCase: SaveWalletUseCase,
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val settingsRepository: SettingsRepository,
private val urlOpener: UrlOpener,
private val analyticsEventHandler: AnalyticsEventHandler,
private val userWalletBuilderFactory: UserWalletBuilder.Factory,
) : Model() {
private val tangemErrorHandler = TangemTangemErrorsHandler(store)
@ -100,10 +99,7 @@ internal class HomeModel @Inject constructor(
}
private suspend fun proceedWithScanResponse(scanResponse: ScanResponse) {
val userWallet = UserWalletBuilder(
scanResponse = scanResponse,
generateWalletNameUseCase = generateWalletNameUseCase,
).build()
val userWallet = userWalletBuilderFactory.create(scanResponse = scanResponse).build()
if (userWallet == null) {
Timber.e("User wallet not created")

View file

@ -10,7 +10,6 @@ import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.extensions.dispatchNavigationAction
@ -91,8 +90,9 @@ private suspend fun readCard() {
}
private fun proceedWithScanResponse(scanResponse: ScanResponse) = scope.launch {
val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
val userWallet = UserWalletBuilder(scanResponse, walletNameGenerateUseCase).build().guard {
val userWalletBuilder = store.inject(DaggerGraphState::userWalletBuilderFactory).create(scanResponse)
val userWallet = userWalletBuilder.build().guard {
Timber.e("User wallet not created")
return@launch
}

View file

@ -30,8 +30,6 @@ import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase
import com.tangem.domain.staking.FetchStakingTokensUseCase
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.feature.swap.analytics.StoriesEvents
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.features.swap.SwapFeatureToggles
import com.tangem.tap.common.extensions.setContext
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.lifecycle.HiltViewModel
@ -60,8 +58,6 @@ internal class MainViewModel @Inject constructor(
private val getStoryContentUseCase: GetStoryContentUseCase,
private val imagePreloader: ImagePreloader,
private val fetchHotCryptoUseCase: FetchHotCryptoUseCase,
private val swapFeatureToggles: SwapFeatureToggles,
onrampFeatureToggles: OnrampFeatureToggles,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
) : ViewModel() {
@ -82,9 +78,7 @@ internal class MainViewModel @Inject constructor(
}
}
if (onrampFeatureToggles.isHotTokensEnabled) {
viewModelScope.launch { fetchHotCryptoUseCase() }
}
viewModelScope.launch { fetchHotCryptoUseCase() }
updateAppCurrencies()
observeFlips()
@ -301,23 +295,21 @@ internal class MainViewModel @Inject constructor(
private fun preloadImages() {
try {
viewModelScope.launch {
if (swapFeatureToggles.isPromoStoriesEnabled) {
val swapStories = getStoryContentUseCase.invokeSync(
id = StoryContentIds.STORY_FIRST_TIME_SWAP.id,
refresh = true,
).getOrNull()
val swapStories = getStoryContentUseCase.invokeSync(
id = StoryContentIds.STORY_FIRST_TIME_SWAP.id,
refresh = true,
).getOrNull()
if (swapStories == null) {
analyticsEventHandler.send(
StoriesEvents.Error(
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
),
)
} else {
val storiesImages = swapStories.getImageUrls()
// try to preload images for stories
storiesImages.forEach(imagePreloader::preload)
}
if (swapStories == null) {
analyticsEventHandler.send(
StoriesEvents.Error(
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
),
)
} else {
val storiesImages = swapStories.getImageUrls()
// try to preload images for stories
storiesImages.forEach(imagePreloader::preload)
}
}
} catch (ex: Exception) {

View file

@ -14,7 +14,6 @@ import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
@ -70,6 +69,7 @@ object OnboardingHelper {
}
}
@Suppress("CyclomaticComplexMethod")
fun whereToNavigate(scanResponse: ScanResponse): AppRoute {
val newOnboardingSupportTypes = scanResponse.productType == ProductType.Wallet2 ||
scanResponse.productType == ProductType.Ring ||
@ -85,7 +85,7 @@ object OnboardingHelper {
scanResponse = scanResponse,
mode = when (backupState.startedSource) {
BackupStartedSource.Onboarding -> AppRoute.Onboarding.Mode.Onboarding
BackupStartedSource.CreateBackup -> AppRoute.Onboarding.Mode.AddBackup
BackupStartedSource.CreateBackup -> AppRoute.Onboarding.Mode.AddBackupWallet1
},
)
}
@ -104,7 +104,11 @@ object OnboardingHelper {
} else {
AppRoute.OnboardingOther
}
ProductType.Twins -> AppRoute.OnboardingTwins
ProductType.Twins -> if (featureToggles.isTwinRefactoringEnabled) {
AppRoute.Onboarding(scanResponse = scanResponse)
} else {
AppRoute.OnboardingTwins
}
ProductType.Start2Coin,
ProductType.Visa,
-> throw UnsupportedOperationException("Onboarding for ${type.name} cards is not supported")
@ -250,8 +254,9 @@ object OnboardingHelper {
hasBackupError: Boolean,
alreadyCreatedWallet: UserWallet? = null,
) {
val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
val userWallet = alreadyCreatedWallet ?: UserWalletBuilder(scanResponse, walletNameGenerateUseCase)
val userWalletBuilderFactory = store.inject(DaggerGraphState::userWalletBuilderFactory)
val userWallet = alreadyCreatedWallet ?: userWalletBuilderFactory.create(scanResponse)
.hasBackupError(hasBackupError)
.backupCardsIds(backupCardsIds?.toSet())
.build()

View file

@ -7,8 +7,8 @@ import com.tangem.common.extensions.isZero
import com.tangem.common.services.Result
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.operations.attestation.CardVerifyAndGetInfo
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.attestation.api.models.CardVerifyAndGetInfo
import com.tangem.tap.common.entities.ProgressState
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.extensions.safeUpdate
@ -26,16 +26,22 @@ import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
*/
@Deprecated("Remove when navigation refactoring will be implemented")
class OnboardingManager(var scanResponse: ScanResponse) {
private val cardRepository: CardRepository = store.inject(DaggerGraphState::cardRepository)
private val cardRepository: CardRepository by lazy { store.inject(DaggerGraphState::cardRepository) }
private val onlineCardVerifier: OnlineCardVerifier by lazy { store.inject(DaggerGraphState::onlineCardVerifier) }
private var cardInfo: Result<CardVerifyAndGetInfo.Response.Item>? = null
suspend fun loadArtworkUrl(): String {
val cardInfo = cardInfo
?: OnlineCardVerifier().getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey)
?: onlineCardVerifier.getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey)
this.cardInfo = cardInfo
return scanResponse.card.getOrLoadCardArtworkUrl(cardInfo)
return scanResponse.card.getOrLoadCardArtworkUrl(cardInfo, onlineCardVerifier)
}
suspend fun updateBalance(walletManager: WalletManager): OnboardingWalletBalance {

View file

@ -26,9 +26,7 @@ import com.tangem.tap.store
import com.tangem.utils.extensions.stripZeroPlainString
import com.tangem.wallet.R
/**
[REDACTED_AUTHOR]
*/
@Deprecated("Use OnboardingNoteComponent")
class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
private var previousStep: OnboardingNoteStep = OnboardingNoteStep.None

View file

@ -1,7 +1,6 @@
package com.tangem.tap.features.onboarding.products.wallet.redux
import android.net.Uri
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.common.CompletionResult
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
@ -12,6 +11,7 @@ import com.tangem.common.routing.AppRouter
import com.tangem.common.services.Result
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.AnalyticsParam.ScreensSources
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.toWrappedList
@ -24,7 +24,6 @@ import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_IDS
import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_PREFIX
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.Artwork
@ -243,7 +242,9 @@ private suspend fun readCard(): CompletionResult<ScanResponse> {
}
private suspend fun loadArtworkForCard(cardId: String, cardPublicKey: ByteArray, defaultArtwork: Uri?): Uri {
return when (val cardInfo = OnlineCardVerifier().getCardInfo(cardId, cardPublicKey)) {
val onlineCardVerifier = store.inject(DaggerGraphState::onlineCardVerifier)
return when (val cardInfo = onlineCardVerifier.getCardInfo(cardId, cardPublicKey)) {
is Result.Success -> {
val artworkId = cardInfo.data.artwork?.id
if (artworkId.isNullOrEmpty()) {
@ -453,8 +454,6 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
store.dispatchOnMain(BackupAction.AddBackupCard.Success(result.data))
}
is CompletionResult.Failure -> {
val crashlytics = FirebaseCrashlytics.getInstance()
when (val error = result.error) {
is TangemSdkError.CardVerificationFailed -> {
Analytics.send(
@ -491,7 +490,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
GlobalAction.ShowDialog(BackupDialog.AttestationFailed),
)
}
else -> crashlytics.recordException(error)
else -> Analytics.sendException(ExceptionAnalyticsEvent(error))
}
}
}
@ -782,9 +781,10 @@ private suspend fun updateWallet(
}
private suspend fun createUserWallet(scanResponse: ScanResponse, backupState: BackupState): UserWallet {
val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
val userWalletBuilder = store.inject(DaggerGraphState::userWalletBuilderFactory).create(scanResponse)
return requireNotNull(
value = UserWalletBuilder(scanResponse, walletNameGenerateUseCase)
value = userWalletBuilder
.backupCardsIds(backupState.backupCardIds.toSet())
.hasBackupError(backupState.hasBackupError)
.build(),

View file

@ -5,7 +5,7 @@ import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tangem.core.analytics.Analytics
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
import com.tangem.features.onboarding.v2.common.analytics.OnboardingEvent
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog

View file

@ -6,7 +6,6 @@ import com.tangem.common.extensions.guard
import com.tangem.common.routing.AppRoute
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
@ -70,8 +69,10 @@ internal class SaveWalletMiddleware {
scope.launch {
val backupInfo = state.backupInfo ?: error("Backup info is null")
val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
val userWallet = UserWalletBuilder(backupInfo.scanResponse, walletNameGenerateUseCase)
val userWalletBuilder = store.inject(DaggerGraphState::userWalletBuilderFactory)
.create(scanResponse = backupInfo.scanResponse)
val userWallet = userWalletBuilder
.backupCardsIds(state.backupInfo.backupCardsIds)
.hasBackupError(hasBackupError)
.build()

View file

@ -11,10 +11,8 @@ import com.tangem.common.routing.utils.popTo
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
import com.tangem.domain.wallets.legacy.unlockIfLockable
import com.tangem.tap.backupService
@ -118,9 +116,9 @@ internal class WelcomeMiddleware {
)
scanCardInternal { scanResponse ->
val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
val userWallet = UserWalletBuilder(scanResponse, walletNameGenerateUseCase).build()
?: return@scanCardInternal
val userWalletBuilder = store.inject(DaggerGraphState::userWalletBuilderFactory).create(scanResponse)
val userWallet = userWalletBuilder.build() ?: return@scanCardInternal
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
userWalletsListManager.save(userWallet, canOverride = true)

View file

@ -1,160 +0,0 @@
package com.tangem.tap.network.exchangeServices.mercuryo
import com.tangem.blockchain.common.Blockchain
/**
* Map [Blockchain] to ids from [link](https://api.mercuryo.io/v1.6/lib/currencies) [crypto_currencies]
*/
@Suppress("CyclomaticComplexMethod")
internal val Blockchain.mercuryoNetwork: String?
get() {
return when (this) {
Blockchain.Algorand -> "ALGORAND"
Blockchain.Arbitrum -> "ARBITRUM"
Blockchain.Avalanche -> "AVALANCHE"
Blockchain.BSC -> "BINANCESMARTCHAIN"
Blockchain.Bitcoin -> "BITCOIN"
Blockchain.BitcoinCash -> "BITCOINCASH"
Blockchain.Cardano -> "CARDANO"
Blockchain.Cosmos -> "COSMOS"
Blockchain.Dash -> "DASH"
Blockchain.Dogecoin -> "DOGECOIN"
Blockchain.Ethereum -> "ETHEREUM"
Blockchain.Fantom -> "FANTOM"
Blockchain.Kusama -> "KUSAMA"
Blockchain.Litecoin -> "LITECOIN"
Blockchain.Near -> "NEAR_PROTOCOL"
Blockchain.TON -> "NEWTON"
Blockchain.Optimism -> "OPTIMISM"
Blockchain.Polkadot -> "POLKADOT"
Blockchain.Polygon -> "POLYGON"
Blockchain.XRP -> "RIPPLE"
Blockchain.Solana -> "SOLANA"
Blockchain.Stellar -> "STELLAR"
Blockchain.Tezos -> "TEZOS"
Blockchain.Tron -> "TRON"
Blockchain.ZkSyncEra -> "ZKSYNC"
Blockchain.Base -> "BASE"
Blockchain.ArbitrumTestnet -> null
Blockchain.AvalancheTestnet -> null
Blockchain.Binance -> null
Blockchain.BinanceTestnet -> null
Blockchain.BSCTestnet -> null
Blockchain.BitcoinTestnet -> null
Blockchain.BitcoinCashTestnet -> null
Blockchain.CosmosTestnet -> null
Blockchain.Ducatus -> null
Blockchain.EthereumTestnet -> null
Blockchain.EthereumClassic -> null
Blockchain.EthereumClassicTestnet -> null
Blockchain.FantomTestnet -> null
Blockchain.NearTestnet -> null
Blockchain.PolkadotTestnet -> null
Blockchain.Kava -> null
Blockchain.KavaTestnet -> null
Blockchain.PolygonTestnet -> null
Blockchain.RSK -> null
Blockchain.Sei -> null
Blockchain.SeiTestnet -> null
Blockchain.StellarTestnet -> null
Blockchain.SolanaTestnet -> null
Blockchain.TronTestnet -> null
Blockchain.Gnosis -> null
Blockchain.OptimismTestnet -> null
Blockchain.Dischain -> null
Blockchain.EthereumPow -> null
Blockchain.EthereumPowTestnet -> null
Blockchain.Kaspa -> null
Blockchain.Telos -> null
Blockchain.TelosTestnet -> null
Blockchain.TONTestnet -> null
Blockchain.Ravencoin -> null
Blockchain.RavencoinTestnet -> null
Blockchain.TerraV1 -> null
Blockchain.TerraV2 -> null
Blockchain.Cronos -> null
Blockchain.AlephZero -> null
Blockchain.AlephZeroTestnet -> null
Blockchain.OctaSpace -> null
Blockchain.OctaSpaceTestnet -> null
Blockchain.Chia -> null
Blockchain.ChiaTestnet -> null
Blockchain.Decimal -> null
Blockchain.DecimalTestnet -> null
Blockchain.XDC -> null
Blockchain.XDCTestnet -> null
Blockchain.VeChain -> null
Blockchain.VeChainTestnet -> null
Blockchain.Aptos -> null
Blockchain.AptosTestnet -> null
Blockchain.Playa3ull -> null
Blockchain.Shibarium -> null
Blockchain.ShibariumTestnet -> null
Blockchain.AlgorandTestnet -> null
Blockchain.Hedera -> null
Blockchain.HederaTestnet -> null
Blockchain.Aurora -> null
Blockchain.AuroraTestnet -> null
Blockchain.Alephium -> null
Blockchain.AlephiumTestnet -> null
Blockchain.Areon -> null
Blockchain.AreonTestnet -> null
Blockchain.PulseChain -> null
Blockchain.PulseChainTestnet -> null
Blockchain.ZkSyncEraTestnet -> null
Blockchain.Nexa -> null
Blockchain.NexaTestnet -> null
Blockchain.Moonbeam -> null
Blockchain.MoonbeamTestnet -> null
Blockchain.Manta -> null
Blockchain.MantaTestnet -> null
Blockchain.PolygonZkEVM -> null
Blockchain.PolygonZkEVMTestnet -> null
Blockchain.Radiant -> null
Blockchain.Fact0rn -> null
Blockchain.BaseTestnet -> null
Blockchain.Moonriver -> null
Blockchain.MoonriverTestnet -> null
Blockchain.Mantle -> null
Blockchain.MantleTestnet -> null
Blockchain.Flare -> null
Blockchain.FlareTestnet -> null
Blockchain.Taraxa -> null
Blockchain.TaraxaTestnet -> null
Blockchain.Koinos -> null
Blockchain.KoinosTestnet -> null
Blockchain.Joystream -> null
Blockchain.Bittensor -> null
Blockchain.Filecoin -> null
Blockchain.Blast -> null
Blockchain.BlastTestnet -> null
Blockchain.Cyber -> null
Blockchain.CyberTestnet -> null
Blockchain.InternetComputer -> null
Blockchain.Sui -> null
Blockchain.SuiTestnet -> null
Blockchain.EnergyWebChain -> null
Blockchain.EnergyWebChainTestnet -> null
Blockchain.EnergyWebX -> null
Blockchain.EnergyWebXTestnet -> null
Blockchain.Casper -> null
Blockchain.CasperTestnet -> null
Blockchain.Core -> null
Blockchain.CoreTestnet -> null
Blockchain.Chiliz -> null
Blockchain.ChilizTestnet -> null
Blockchain.VanarChain -> null
Blockchain.VanarChainTestnet -> null
Blockchain.Unknown -> null
Blockchain.Xodex -> null
Blockchain.Canxium -> null
Blockchain.Clore -> null
Blockchain.OdysseyChain, Blockchain.OdysseyChainTestnet -> null
Blockchain.Bitrock, Blockchain.BitrockTestnet -> null
Blockchain.Sonic, Blockchain.SonicTestnet -> null
Blockchain.ApeChain, Blockchain.ApeChainTestnet -> null
Blockchain.Scroll, Blockchain.ScrollTestnet -> null
Blockchain.ZkLinkNova, Blockchain.ZkLinkNovaTestnet -> null
Blockchain.KaspaTestnet -> null
}
}

View file

@ -6,6 +6,7 @@ import com.tangem.common.extensions.calculateSha512
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.common.services.performRequest
import com.tangem.data.onramp.legacy.mercuryoNetwork
import com.tangem.domain.core.utils.lceContent
import com.tangem.domain.core.utils.lceError
import com.tangem.domain.core.utils.lceLoading

View file

@ -26,13 +26,13 @@ import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
import com.tangem.domain.onboarding.repository.OnboardingRepository
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.features.onramp.OnrampFeatureToggles
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
@ -56,7 +56,6 @@ data class DaggerGraphState(
val generalUserWalletsListManager: UserWalletsListManager? = null,
val wasTwinsOnboardingShownUseCase: WasTwinsOnboardingShownUseCase? = null,
val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase? = null,
val generateWalletNameUseCase: GenerateWalletNameUseCase? = null,
val cardRepository: CardRepository? = null,
val settingsRepository: SettingsRepository? = null,
val blockchainSDKFactory: BlockchainSDKFactory? = null,
@ -67,7 +66,6 @@ data class DaggerGraphState(
val shareManager: ShareManager? = null,
val appRouter: AppRouter? = null,
val transactionSignerFactory: TransactionSignerFactory? = null,
val getUserCountryUseCase: GetUserCountryUseCase? = null,
val onrampFeatureToggles: OnrampFeatureToggles? = null,
val environmentConfigStorage: EnvironmentConfigStorage? = null,
val onboardingV2FeatureToggles: OnboardingV2FeatureToggles? = null,
@ -77,4 +75,6 @@ data class DaggerGraphState(
val clipboardManager: ClipboardManager? = null,
val settingsManager: SettingsManager? = null,
val uiMessageSender: UiMessageSender? = null,
val onlineCardVerifier: OnlineCardVerifier? = null,
val userWalletBuilderFactory: UserWalletBuilder.Factory? = null,
) : StateType

View file

@ -1,8 +1,9 @@
package com.tangem.tap.routing
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.core.decompose.navigation.Router
import com.tangem.tap.routing.configurator.AppRouterConfig
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -15,6 +16,7 @@ import kotlin.reflect.KClass
internal class ProxyAppRouter(
private val config: AppRouterConfig,
private val dispatchers: CoroutineDispatcherProvider,
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
) : AppRouter {
private val routerScope: CoroutineScope
@ -76,7 +78,7 @@ internal class ProxyAppRouter(
override fun defaultCompletionHandler(isSuccess: Boolean, errorMessage: String) {
if (!isSuccess) {
FirebaseCrashlytics.getInstance().recordException(RuntimeException(errorMessage))
analyticsExceptionHandler.sendException(ExceptionAnalyticsEvent(RuntimeException(errorMessage)))
Timber.w(errorMessage)
with(receiver = config.snackbarHandler ?: return) {

View file

@ -2,6 +2,7 @@ package com.tangem.tap.routing.utils
import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.feature.qrscanning.QrScanningComponent
import com.tangem.feature.referral.api.ReferralComponent
import com.tangem.feature.stories.api.StoriesComponent
@ -15,11 +16,13 @@ import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent
import com.tangem.features.onramp.component.*
import com.tangem.features.pushnotifications.api.PushNotificationsComponent
import com.tangem.features.send.api.SendComponent
import com.tangem.features.swap.SwapComponent
import com.tangem.features.send.v2.api.SendFeatureToggles
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.tap.domain.walletconnect2.toggles.WalletConnectFeatureToggles
import com.tangem.tap.features.details.ui.appcurrency.api.AppCurrencySelectorComponent
import com.tangem.tap.features.details.ui.appsettings.api.AppSettingsComponent
import com.tangem.tap.features.details.ui.cardsettings.api.CardSettingsComponent
@ -40,6 +43,7 @@ import com.tangem.utils.Provider
import dagger.hilt.android.scopes.ActivityScoped
import java.util.WeakHashMap
import javax.inject.Inject
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent as RedisegnedWalletConnectComponent
@ActivityScoped
@Suppress("LongParameterList", "LargeClass")
@ -73,8 +77,12 @@ internal class ChildFactory @Inject constructor(
private val referralComponentFactory: ReferralComponent.Factory,
private val pushNotificationsComponentFactory: PushNotificationsComponent.Factory,
private val walletComponentFactory: WalletEntryComponent.Factory,
private val sendComponentFactoryV2: com.tangem.features.send.v2.api.SendComponent.Factory,
private val sendFeatureToggles: SendFeatureToggles,
private val redesignedWalletConnectComponentFactory: RedisegnedWalletConnectComponent.Factory,
private val testerRouter: TesterRouter,
private val routingFeatureToggles: RoutingFeatureToggles,
private val walletConnectFeatureToggles: WalletConnectFeatureToggles,
) {
fun createChild(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
@ -201,9 +209,12 @@ internal class ChildFactory @Inject constructor(
contextProvider = contextProvider(route, contextFactory),
params = OnboardingEntryComponent.Params(
scanResponse = route.scanResponse,
multiWalletMode = when (route.mode) {
AppRoute.Onboarding.Mode.Onboarding -> OnboardingEntryComponent.MultiWalletMode.Onboarding
AppRoute.Onboarding.Mode.AddBackup -> OnboardingEntryComponent.MultiWalletMode.AddBackup
mode = when (route.mode) {
AppRoute.Onboarding.Mode.Onboarding -> OnboardingEntryComponent.Mode.Onboarding
AppRoute.Onboarding.Mode.AddBackupWallet1 -> OnboardingEntryComponent.Mode.AddBackupWallet1
AppRoute.Onboarding.Mode.WelcomeOnlyTwin -> OnboardingEntryComponent.Mode.WelcomeOnlyTwin
AppRoute.Onboarding.Mode.RecreateWalletTwin ->
OnboardingEntryComponent.Mode.RecreateWalletTwin
},
),
componentFactory = onboardingEntryComponentFactory,
@ -255,18 +266,33 @@ internal class ChildFactory @Inject constructor(
)
}
is AppRoute.Send -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = SendComponent.Params(
userWalletId = route.userWalletId,
currency = route.currency,
transactionId = route.transactionId,
amount = route.amount,
tag = route.tag,
destinationAddress = route.destinationAddress,
),
componentFactory = sendComponentFactory,
)
if (sendFeatureToggles.isSendV2Enabled) {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = com.tangem.features.send.v2.api.SendComponent.Params(
userWalletId = route.userWalletId,
currency = route.currency,
transactionId = route.transactionId,
amount = route.amount,
tag = route.tag,
destinationAddress = route.destinationAddress,
),
componentFactory = sendComponentFactoryV2,
)
} else {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = SendComponent.Params(
userWalletId = route.userWalletId,
currency = route.currency,
transactionId = route.transactionId,
amount = route.amount,
tag = route.tag,
destinationAddress = route.destinationAddress,
),
componentFactory = sendComponentFactory,
)
}
}
is AppRoute.Home -> {
createComponentChild(
@ -276,17 +302,29 @@ internal class ChildFactory @Inject constructor(
)
}
is AppRoute.WalletConnectSessions -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = Unit,
componentFactory = walletConnectComponentFactory,
)
if (walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = Unit,
componentFactory = redesignedWalletConnectComponentFactory,
)
} else {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = Unit,
componentFactory = walletConnectComponentFactory,
)
}
}
is AppRoute.QrScanning -> {
val source = when (route.source) {
AppRoute.QrScanning.Source.SEND -> SourceType.SEND
AppRoute.QrScanning.Source.WALLET_CONNECT -> SourceType.WALLET_CONNECT
}
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = QrScanningComponent.Params(
source = route.source,
source = source,
networkName = route.networkName,
),
componentFactory = qrScanningComponentFactory,
@ -399,18 +437,33 @@ internal class ChildFactory @Inject constructor(
route.asFragmentChild(Provider { SaveWalletBottomSheetFragment() })
}
is AppRoute.Send -> {
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = SendComponent.Params(
userWalletId = route.userWalletId,
currency = route.currency,
transactionId = route.transactionId,
amount = route.amount,
tag = route.tag,
destinationAddress = route.destinationAddress,
),
componentFactory = sendComponentFactory,
)
if (sendFeatureToggles.isSendV2Enabled) {
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = com.tangem.features.send.v2.api.SendComponent.Params(
userWalletId = route.userWalletId,
currency = route.currency,
transactionId = route.transactionId,
amount = route.amount,
tag = route.tag,
destinationAddress = route.destinationAddress,
),
componentFactory = sendComponentFactoryV2,
)
} else {
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = SendComponent.Params(
userWalletId = route.userWalletId,
currency = route.currency,
transactionId = route.transactionId,
amount = route.amount,
tag = route.tag,
destinationAddress = route.destinationAddress,
),
componentFactory = sendComponentFactory,
)
}
}
is AppRoute.AppSettings -> {
route.asComponentChild(
@ -482,10 +535,14 @@ internal class ChildFactory @Inject constructor(
route.asFragmentChild(Provider { OnboardingWalletFragment() })
}
is AppRoute.QrScanning -> {
val source = when (route.source) {
AppRoute.QrScanning.Source.SEND -> SourceType.SEND
AppRoute.QrScanning.Source.WALLET_CONNECT -> SourceType.WALLET_CONNECT
}
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = QrScanningComponent.Params(
source = route.source,
source = source,
networkName = route.networkName,
),
componentFactory = qrScanningComponentFactory,
@ -531,11 +588,19 @@ internal class ChildFactory @Inject constructor(
)
}
is AppRoute.WalletConnectSessions -> {
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = Unit,
componentFactory = walletConnectComponentFactory,
)
if (walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) {
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = Unit,
componentFactory = redesignedWalletConnectComponentFactory,
)
} else {
route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = Unit,
componentFactory = walletConnectComponentFactory,
)
}
}
is AppRoute.CurrencyDetails -> {
route.asComponentChild(
@ -645,9 +710,12 @@ internal class ChildFactory @Inject constructor(
contextProvider = contextProvider(route, contextFactory),
params = OnboardingEntryComponent.Params(
scanResponse = route.scanResponse,
multiWalletMode = when (route.mode) {
AppRoute.Onboarding.Mode.Onboarding -> OnboardingEntryComponent.MultiWalletMode.Onboarding
AppRoute.Onboarding.Mode.AddBackup -> OnboardingEntryComponent.MultiWalletMode.AddBackup
mode = when (route.mode) {
AppRoute.Onboarding.Mode.Onboarding -> OnboardingEntryComponent.Mode.Onboarding
AppRoute.Onboarding.Mode.AddBackupWallet1 -> OnboardingEntryComponent.Mode.AddBackupWallet1
AppRoute.Onboarding.Mode.WelcomeOnlyTwin -> OnboardingEntryComponent.Mode.WelcomeOnlyTwin
AppRoute.Onboarding.Mode.RecreateWalletTwin ->
OnboardingEntryComponent.Mode.RecreateWalletTwin
},
),
componentFactory = onboardingEntryComponentFactory,