Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-04 17:40:24 +03:00
commit d8d2876113
1725 changed files with 30598 additions and 11593 deletions

View file

@ -39,8 +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.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
@ -118,8 +116,6 @@ interface ApplicationEntryPoint {
fun getTransactionSignerFactory(): TransactionSignerFactory
fun getOnrampFeatureToggles(): OnrampFeatureToggles
fun getOnboardingV2FeatureToggles(): OnboardingV2FeatureToggles
fun getOnboardingRepository(): OnboardingRepository
@ -141,8 +137,6 @@ interface ApplicationEntryPoint {
fun getWorkerFactory(): HiltWorkerFactory
fun getOnlineCardVerifier(): OnlineCardVerifier
fun getColdUserWalletBuilderFactory(): ColdUserWalletBuilder.Factory
fun getApiConfigsManager(): ApiConfigsManager

View file

@ -6,6 +6,7 @@ import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.WindowManager
import androidx.activity.SystemBarStyle
@ -26,17 +27,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.DeepLinksRegistry
import com.tangem.core.deeplink.WEBLINK_KEY
import com.tangem.core.deeplink.converter.PayloadToDeeplinkConverter
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
@ -52,6 +49,7 @@ import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
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.tester.api.TesterMenuLauncher
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.google.GoogleServicesHelper
import com.tangem.operations.backup.BackupService
@ -68,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
@ -102,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
@ -121,9 +115,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
lateinit var walletConnectInteractor: WalletConnectInteractor
@Inject
lateinit var deepLinksRegistry: DeepLinksRegistry
@Inject
lateinit var settingsRepository: SettingsRepository
@ -142,9 +133,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
lateinit var userWalletsListManager: UserWalletsListManager
@Inject
lateinit var emailSender: EmailSender
@Inject
@RootAppComponentContext
internal lateinit var rootComponentContext: AppComponentContext
@ -173,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
@ -191,13 +173,25 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
internal lateinit var urlOpener: UrlOpener
@Inject
internal lateinit var testerMenuLauncher: TesterMenuLauncher
@Inject
internal lateinit var intentProcessor: IntentProcessor
@Inject
internal lateinit var walletConnectLinkIntentHandler: WalletConnectLinkIntentHandler
@Inject
internal lateinit var onPushClickedIntentHandler: OnPushClickedIntentHandler
@Inject
internal lateinit var backgroundScanIntentHandler: BackgroundScanIntentHandler
internal val viewModel: MainViewModel by viewModels()
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode>
// TODO: fixme: inject through DI
private val intentProcessor: IntentProcessor = IntentProcessor()
private val dialogManager = DialogManager()
private val onActivityResultCallbacks = mutableListOf<OnActivityResultCallback>()
@ -243,13 +237,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.launchOnKeyEventObserver)
}
}
private fun setRootContent() {
@ -360,12 +353,10 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
}
private fun initIntentHandlers() {
val hasSavedWalletsProvider = { userWalletsListManager.hasUserWallets }
intentProcessor.addHandler(OnPushClickedIntentHandler(analyticsEventsHandler))
intentProcessor.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider, lifecycleScope))
intentProcessor.addHandler(onPushClickedIntentHandler)
if (!walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) {
intentProcessor.addHandler(WalletConnectLinkIntentHandler())
intentProcessor.addHandler(walletConnectLinkIntentHandler)
}
}
@ -426,10 +417,17 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
val result = WindowObscurationObserver.dispatchTouchEvent(event, analyticsEventsHandler)
return if (result) super.dispatchTouchEvent(event) else false
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
return if (BuildConfig.TESTER_MENU_ENABLED) {
testerMenuLauncher.launchOnKeyEventObserver.dispatchKeyEvent(event) || super.dispatchKeyEvent(event)
} else {
super.dispatchKeyEvent(event)
}
}
private fun navigateToInitialScreenIfNeeded(intentWhichStartedActivity: Intent?) {
val backStack = appRouterConfig.stack ?: emptyList()
// TODO move inital navigation to navigation component ([REDACTED_JIRA])
@ -451,9 +449,15 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
}
private fun navigateToInitialScreen(intentWhichStartedActivity: Intent?) {
val launchMode = backgroundScanIntentHandler.getInitScreenLaunchMode(intentWhichStartedActivity)
if (userWalletsListManager.isLockable && userWalletsListManager.hasUserWallets) {
store.dispatchNavigationAction {
replaceAll(AppRoute.Welcome(intentWhichStartedActivity?.let(::SerializableIntent)))
replaceAll(
AppRoute.Welcome(
launchMode = launchMode,
intent = intentWhichStartedActivity?.let(::SerializableIntent),
),
)
}
intentProcessor.handleIntent(
intent = intentWhichStartedActivity,
@ -467,7 +471,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
val route = if (shouldShowTos) {
AppRoute.Disclaimer(isTosAccepted = false)
} else {
AppRoute.Home
AppRoute.Home(launchMode = launchMode)
}
store.dispatchNavigationAction { replaceAll(route) }
@ -479,7 +483,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
}
}
if (routingFeatureToggle.isDeepLinkNavigationEnabled && intent != null) {
if (intent != null) {
handleDeepLink(intent = intent, isFromOnNewIntent = false)
}
@ -487,26 +491,22 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
}
private fun handleDeepLink(intent: Intent, isFromOnNewIntent: Boolean) {
if (routingFeatureToggle.isDeepLinkNavigationEnabled) {
val deepLinkExtras = PayloadToDeeplinkConverter.convertBundle(intent.extras)?.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,8 +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.features.onramp.OnrampFeatureToggles
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
@ -79,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>
@ -186,9 +183,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
private val transactionSignerFactory: TransactionSignerFactory
get() = entryPoint.getTransactionSignerFactory()
private val onrampFeatureToggles: OnrampFeatureToggles
get() = entryPoint.getOnrampFeatureToggles()
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles
get() = entryPoint.getOnboardingV2FeatureToggles()
@ -224,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()
@ -363,7 +354,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
shareManager = shareManager,
appRouter = appRouter,
transactionSignerFactory = transactionSignerFactory,
onrampFeatureToggles = onrampFeatureToggles,
environmentConfigStorage = environmentConfigStorage,
onboardingV2FeatureToggles = onboardingV2FeatureToggles,
onboardingRepository = onboardingRepository,
@ -372,7 +362,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
clipboardManager = clipboardManager,
settingsManager = settingsManager,
uiMessageSender = uiMessageSender,
onlineCardVerifier = onlineCardVerifier,
coldUserWalletBuilderFactory = coldUserWalletBuilderFactory,
userTokensResponseStore = userTokensResponseStore,
),

View file

@ -1,7 +1,7 @@
package com.tangem.tap.common.analytics.converters
import com.tangem.blockchain.common.Blockchain
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.card.CardTypesResolver
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.utils.converter.Converter
import com.tangem.core.analytics.models.AnalyticsParam as CoreAnalyticsParam

View file

@ -1,18 +0,0 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.models.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/
sealed class IntroductionProcess(
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent("Introduction Process", event, params) {
class ScreenOpened : IntroductionProcess("Introduction Process Screen Opened")
class ButtonTokensList : IntroductionProcess("Button - Tokens List")
class ButtonBuyCards : IntroductionProcess("Button - Buy Cards")
class ButtonScanCard : IntroductionProcess("Button - Scan Card")
class ButtonRequestSupport : IntroductionProcess("Button - Request Support")
}

View file

@ -1,30 +0,0 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.extensions.filterNotNull
/**
[REDACTED_AUTHOR]
*/
sealed class Shop(
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent("Shop", event, params) {
class ScreenOpened : Shop("Shop Screen Opened")
class Purchased(sku: String, count: String, amount: String, couponCode: String?) : Shop(
event = "Purchased",
params = mapOf(
"SKU" to sku,
"Count" to count,
"Amount" to amount,
"Coupon Code" to couponCode,
).filterNotNull(),
)
class Redirected(partnerName: String?) : Shop(
event = "Redirected",
params = partnerName?.let { mapOf("Partner" to partnerName) } ?: mapOf(),
)
}

View file

@ -2,13 +2,13 @@ package com.tangem.tap.common.analytics.paramsInterceptor
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.features.home.impl.analytics.IntroductionProcess
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.proxy.redux.DaggerGraphState

View file

@ -1,54 +0,0 @@
package com.tangem.tap.common.compose.extensions
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.Easing
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
/**
[REDACTED_AUTHOR]
*/
typealias AnimatedValue = Pair<Float, Float>
@Composable
fun AnimatedValue.toAnimatable(
isPaused: Boolean,
duration: Int,
easing: Easing = LinearEasing,
): Animatable<Float, AnimationVector1D> {
return animatable(
values = this,
isPaused = isPaused,
duration = duration,
easing = easing,
)
}
@Composable
fun animatable(
values: AnimatedValue,
duration: Int,
isPaused: Boolean = false,
easing: Easing = LinearEasing,
): Animatable<Float, AnimationVector1D> {
val animatable = remember { Animatable(values.first) }
LaunchedEffect(isPaused) {
if (isPaused) {
animatable.stop()
} else {
animatable.animateTo(
targetValue = values.second,
animationSpec = tween(
durationMillis = duration,
easing = easing,
),
)
}
}
return animatable
}

View file

@ -1,17 +0,0 @@
package com.tangem.tap.common.compose.extensions
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
/**
[REDACTED_AUTHOR]
*/
@Composable
fun Dp.toPx(): Float {
val currentDp = this
return with(LocalDensity.current) { currentDp.toPx() }
}
fun DpSize.halfHeight(): Dp = this.height / 2

View file

@ -1,20 +0,0 @@
package com.tangem.tap.common.compose.extensions
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.core.graphics.drawable.toBitmap
/**
[REDACTED_AUTHOR]
*/
@Composable
fun asImageBitmap(@DrawableRes drawableId: Int): ImageBitmap {
val drawable = requireNotNull(AppCompatResources.getDrawable(LocalContext.current, drawableId)) {
"drawable is null"
}
return drawable.toBitmap().asImageBitmap()
}

View file

@ -1,20 +0,0 @@
package com.tangem.tap.common.compose.extensions
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.tangem.sdk.extensions.pxToDp
/**
[REDACTED_AUTHOR]
*/
@Composable
fun Painter.dpSize(): DpSize = DpSize(
intrinsicSize.width.pxToDp().dp,
intrinsicSize.height.pxToDp().dp,
)
@Composable
private fun Float.pxToDp(): Float = LocalContext.current.pxToDp(this)

View file

@ -2,6 +2,7 @@ package com.tangem.tap.common.extensions
import com.tangem.core.analytics.Analytics
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
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] [Hot Wallet] Analytics)
if (userWallet is UserWallet.Cold) {
addParamsInterceptor(LinkedCardContextInterceptor(userWallet.scanResponse))
}
}
/**
* Erases the context
*/

View file

@ -1,3 +0,0 @@
package com.tangem.tap.common.extensions
fun Int.isEven() = this and 1 == 0

View file

@ -4,8 +4,8 @@ import com.tangem.common.routing.AppRouter
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.redux.StateDialog
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError

View file

@ -3,16 +3,11 @@ package com.tangem.tap.common.extensions
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.address.AddressType
import com.tangem.blockchainsdk.utils.amountToCreateAccount
import com.tangem.common.services.Result
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.tap.common.TestActions
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.model.WalletAddressData
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import kotlinx.coroutines.delay
@ -57,43 +52,4 @@ suspend fun WalletManager.safeUpdate(isDemoCard: Boolean): Result<Wallet> = try
}
}
}
}
internal fun WalletManager.getTopUpUrl(cryptoCurrency: CryptoCurrency): String? {
val globalState = store.state.globalState
val defaultAddress = wallet.address
return globalState.exchangeManager.getUrl(
action = CurrencyExchangeManager.Action.Buy,
cryptoCurrency = cryptoCurrency,
fiatCurrencyName = globalState.appCurrency.code,
walletAddress = defaultAddress,
isDarkTheme = MutableAppThemeModeHolder.isDarkThemeActive,
)
}
internal fun WalletManager?.getAddressData(): WalletAddressData? {
val wallet = this?.wallet ?: return null
val addressDataList = wallet.createAddressesData()
return if (addressDataList.isEmpty()) null else addressDataList[0]
}
private fun Wallet.createAddressesData(): List<WalletAddressData> {
val listOfAddressData = mutableListOf<WalletAddressData>()
// put a defaultAddress at the first place
addresses.forEach {
val addressData = WalletAddressData(
it.value,
it.type,
getShareUri(it.value),
getExploreUrl(it.value),
)
if (it.type == AddressType.Default) {
listOfAddressData.add(0, addressData)
} else {
listOfAddressData.add(addressData)
}
}
return listOfAddressData
}

View file

@ -6,6 +6,7 @@ import android.util.Log
import coil.ImageLoader
import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import coil.decode.SvgDecoder
import coil.memory.MemoryCache
import coil.request.CachePolicy
import coil.util.Logger
@ -36,6 +37,7 @@ fun createCoilImageLoader(context: Context, logEnabled: Boolean = false): ImageL
} else {
add(GifDecoder.Factory())
}
add(SvgDecoder.Factory())
}
.memoryCachePolicy(CachePolicy.ENABLED)
.memoryCache {

View file

@ -3,7 +3,6 @@ package com.tangem.tap.common.redux
import com.tangem.tap.common.redux.global.globalReducer
import com.tangem.tap.features.details.redux.DetailsReducer
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer
import com.tangem.tap.features.home.redux.HomeReducer
import com.tangem.tap.features.welcome.redux.WelcomeReducer
import com.tangem.tap.proxy.redux.DaggerGraphReducer
import org.rekotlin.Action
@ -14,7 +13,6 @@ fun appReducer(action: Action, state: AppState?): AppState {
return AppState(
globalState = globalReducer(action, state),
homeState = HomeReducer.reduce(action, state),
detailsState = DetailsReducer.reduce(action, state),
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState),
welcomeState = WelcomeReducer.reduce(action, state),

View file

@ -7,8 +7,6 @@ import com.tangem.tap.features.details.redux.DetailsMiddleware
import com.tangem.tap.features.details.redux.DetailsState
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectMiddleware
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
import com.tangem.tap.features.home.redux.HomeMiddleware
import com.tangem.tap.features.home.redux.HomeState
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupMiddleware
import com.tangem.tap.features.wallet.redux.middlewares.TradeCryptoMiddleware
import com.tangem.tap.features.welcome.redux.WelcomeMiddleware
@ -20,7 +18,6 @@ import org.rekotlin.StateType
data class AppState(
val globalState: GlobalState = GlobalState(),
val homeState: HomeState = HomeState(),
val detailsState: DetailsState = DetailsState(),
val walletConnectState: WalletConnectState = WalletConnectState(),
val welcomeState: WelcomeState = WelcomeState(),
@ -32,7 +29,6 @@ data class AppState(
return listOf(
logMiddleware,
GlobalMiddleware.handler,
HomeMiddleware.handler,
DetailsMiddleware().detailsMiddleware,
WalletConnectMiddleware().walletConnectMiddleware,
BackupMiddleware().backupMiddleware,

View file

@ -30,21 +30,5 @@ sealed class GlobalAction : Action {
data class Success(val appCurrency: AppCurrency) : GlobalAction()
}
data class UpdateWalletSignedHashes(
val walletSignedHashes: Int?,
val remainingSignatures: Int?,
val walletPublicKey: ByteArray,
) : GlobalAction()
data class IsSignWithRing(val isSignWithRing: Boolean) : GlobalAction()
object ExchangeManager : GlobalAction() {
object Init : GlobalAction() {
data class Success(
val exchangeManager: com.tangem.tap.network.exchangeServices.CurrencyExchangeManager,
) : GlobalAction()
}
object Update : GlobalAction()
}
}

View file

@ -2,23 +2,14 @@ package com.tangem.tap.common.redux.global
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.datasource.local.config.environment.EnvironmentConfig
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.LogConfig
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.redux.StateDialog
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.network.exchangeServices.CardExchangeRules
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.network.exchangeServices.ExchangeService
import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoEnvironment
import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService
import com.tangem.tap.network.exchangeServices.moonpay.MoonPayService
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
@ -31,17 +22,17 @@ object GlobalMiddleware {
val handler = globalMiddlewareHandler
}
private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState ->
private val globalMiddlewareHandler: Middleware<AppState> = { _, _ ->
{ nextDispatch ->
{ action ->
handleAction(action, appState)
handleAction(action)
nextDispatch(action)
}
}
}
@Suppress("LongMethod", "ComplexMethod")
private fun handleAction(action: Action, appState: () -> AppState?) {
private fun handleAction(action: Action) {
when (action) {
is GlobalAction.ScanFailsCounter.ChooseBehavior -> {
when (action.result) {
@ -51,42 +42,7 @@ private fun handleAction(action: Action, appState: () -> AppState?) {
}
}
}
is GlobalAction.RestoreAppCurrency -> {
restoreAppCurrency()
}
is GlobalAction.ExchangeManager.Init -> {
val config = store.inject(DaggerGraphState::environmentConfigStorage).getConfigSync()
scope.launch {
val scanResponseProvider: () -> ScanResponse? = {
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
userWalletsListManager.selectedUserWalletSync?.requireColdWallet()?.scanResponse // TODO [REDACTED_TASK_KEY]
}
val cardProvider: () -> CardDTO? = { scanResponseProvider.invoke()?.card }
val buyService = makeBuyExchangeService(config)
val sellService = makeSellExchangeService(config)
val exchangeManager = CurrencyExchangeManager(
buyService = buyService,
sellService = sellService,
primaryRules = CardExchangeRules(cardProvider),
)
// TODO: for refactoring (after remove old design refactor CurrencyExchangeManager and use 1 instance)
store.inject(DaggerGraphState::appStateHolder).buyService = buyService
store.inject(DaggerGraphState::appStateHolder).sellService = sellService
store.inject(DaggerGraphState::appStateHolder).exchangeService = exchangeManager
store.dispatchOnMain(GlobalAction.ExchangeManager.Init.Success(exchangeManager))
store.dispatchOnMain(GlobalAction.ExchangeManager.Update)
}
}
is GlobalAction.ExchangeManager.Init.Success -> {}
is GlobalAction.ExchangeManager.Update -> {
val exchangeManager = appState()?.globalState?.exchangeManager.guard {
store.dispatchDebugErrorNotification("exchangeManager is not initialized")
return
}
scope.launch { exchangeManager.update() }
}
is GlobalAction.RestoreAppCurrency -> restoreAppCurrency()
}
}
@ -119,21 +75,4 @@ private fun restoreAppCurrency() {
store.dispatchWithMain(GlobalAction.RestoreAppCurrency.Success(currency))
}
}
private fun makeSellExchangeService(environmentConfig: EnvironmentConfig): ExchangeService {
return MoonPayService(
apiKey = environmentConfig.moonPayApiKey,
secretKey = environmentConfig.moonPayApiSecretKey,
logEnabled = LogConfig.network.moonPayService,
)
}
private fun makeBuyExchangeService(environmentConfig: EnvironmentConfig): ExchangeService {
return MercuryoService(
environment = MercuryoEnvironment.prod(
widgetId = environmentConfig.mercuryoWidgetId,
secret = environmentConfig.mercuryoSecret,
),
)
}

View file

@ -1,7 +1,6 @@
package com.tangem.tap.common.redux.global
import com.tangem.tap.common.redux.AppState
import com.tangem.utils.extensions.replaceBy
import org.rekotlin.Action
@Suppress("LongMethod", "ComplexMethod")
@ -26,24 +25,6 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
is GlobalAction.RestoreAppCurrency.Success -> {
globalState.copy(appCurrency = action.appCurrency)
}
is GlobalAction.UpdateWalletSignedHashes -> {
val card = globalState.scanResponse?.card ?: return globalState
val wallet = card.wallets
.firstOrNull { it.publicKey.contentEquals(action.walletPublicKey) }
?: return globalState
val newCardInstance = card.copy(
wallets = card.wallets.toMutableList().also { walletsMutable ->
walletsMutable.replaceBy(
item = wallet.copy(
totalSignedHashes = action.walletSignedHashes,
remainingSignatures = action.remainingSignatures,
),
) { it.index == wallet.index }
},
)
globalState.copy(scanResponse = globalState.scanResponse.copy(card = newCardInstance))
}
is GlobalAction.IsSignWithRing -> globalState.copy(isLastSignWithRing = action.isSignWithRing)
is GlobalAction.ShowDialog -> {
globalState.copy(dialog = action.stateDialog)
@ -51,9 +32,6 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
is GlobalAction.HideDialog -> {
globalState.copy(dialog = null)
}
is GlobalAction.ExchangeManager.Init.Success -> {
globalState.copy(exchangeManager = action.exchangeManager)
}
else -> globalState
}
}

View file

@ -4,8 +4,6 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.redux.StateDialog
import com.tangem.tap.domain.TapWalletManager
import com.tangem.tap.features.onboarding.OnboardingManager
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import org.rekotlin.StateType
data class GlobalState(
@ -16,7 +14,6 @@ data class GlobalState(
val appCurrency: AppCurrency = AppCurrency.Default,
val scanCardFailsCounter: Int = 0,
val dialog: StateDialog? = null,
val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(),
val isLastSignWithRing: Boolean = false,
) : StateType
@ -24,6 +21,5 @@ typealias CryptoCurrencyName = String
data class OnboardingState(
val onboardingStarted: Boolean = false,
val onboardingManager: OnboardingManager? = null,
val shouldResetOnCreate: Boolean = false,
)

View file

@ -1,8 +1,8 @@
package com.tangem.tap.common.redux.legacy
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.redux.LegacyAction
import com.tangem.domain.wallets.models.requireColdWallet
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,
),
)
@ -67,7 +66,7 @@ internal object LegacyMiddleware {
?: AppThemeMode.DEFAULT,
isHidingEnabled = store.inject(DaggerGraphState::balanceHidingRepository)
.getBalanceHidingSettings().isHidingEnabledInSettings,
needEnrollBiometrics = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull() ?: false,
needEnrollBiometrics = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull() == true,
)
}
}

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.compose.ui.text.intl.Locale
import androidx.core.view.isVisible
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.AnalyticsParam
@ -14,8 +15,6 @@ import com.tangem.tap.common.analytics.events.ScanFailsDialogAnalytics
import com.tangem.tap.common.extensions.dispatchDialogHide
import com.tangem.tap.common.extensions.dispatchOpenUrl
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.features.home.LocaleRegionProvider
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
@ -29,6 +28,7 @@ internal object ScanFailsDialog {
private const val HOW_TO_SCAN_RU_LINK = "https://tangem.com/ru/blog/post/scan-tangem-card/"
private const val HOW_TO_SCAN_LINK = "https://tangem.com/en/blog/post/scan-tangem-card/"
private const val RUSSIA_LOCALE = "ru"
fun create(context: Context, source: StateDialog.ScanFailsSource, onTryAgain: (() -> Unit)? = null): AlertDialog {
return AlertDialog.Builder(context, R.style.CustomMaterialDialog).apply {
@ -62,8 +62,8 @@ internal object ScanFailsDialog {
source = sourceAnalytics,
),
)
val locale = LocaleRegionProvider().getRegion()
val link = if (locale.lowercase() == RUSSIA_COUNTRY_CODE) HOW_TO_SCAN_RU_LINK else HOW_TO_SCAN_LINK
val locale = Locale.current.region
val link = if (locale.lowercase() == RUSSIA_LOCALE) HOW_TO_SCAN_RU_LINK else HOW_TO_SCAN_LINK
store.dispatchOpenUrl(link)
}
customView.findViewById<TextView>(R.id.request_support_button)?.setOnClickListener {

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,26 +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.Attestation.name]) {
ApiEnvironment.DEV,
ApiEnvironment.STAGE,
-> 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(
@ -187,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.Attestation)
isTangemAttestationProdEnv = apiConfig.environment == ApiEnvironment.PROD
val apiConfig = apiConfigsManager.getEnvironmentConfig(id = ApiConfig.ID.TangemTech)
tangemApiBaseUrl = apiConfig.baseUrl
},
)

View file

@ -2,9 +2,9 @@ package com.tangem.tap.data
import com.tangem.common.CompletionResult
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull

View file

@ -9,7 +9,6 @@ import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository
import com.tangem.tap.network.exchangeServices.DefaultRampManager
@ -51,17 +50,13 @@ internal object ActivityModule {
currenciesRepository: CurrenciesRepository,
excludedBlockchains: ExcludedBlockchains,
dispatchers: CoroutineDispatcherProvider,
onrampFeatureToggles: OnrampFeatureToggles,
): RampStateManager {
return DefaultRampManager(
exchangeService = appStateHolder.exchangeService,
buyService = Provider { requireNotNull(appStateHolder.buyService) },
sellService = Provider { requireNotNull(appStateHolder.sellService) },
expressServiceLoader = expressServiceLoader,
currenciesRepository = currenciesRepository,
excludedBlockchains = excludedBlockchains,
dispatchers = dispatchers,
onrampFeatureToggles = onrampFeatureToggles,
excludedBlockchains = excludedBlockchains,
)
}

View file

@ -0,0 +1,34 @@
package com.tangem.tap.di
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.tap.features.intentHandler.IntentProcessor
import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler
import com.tangem.tap.features.intentHandler.handlers.OnPushClickedIntentHandler
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
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 IntentHandlingModule {
@Provides
@Singleton
fun provideBackgroundScanIntentHandler(): BackgroundScanIntentHandler = BackgroundScanIntentHandler()
@Provides
@Singleton
fun provideWalletConnectLinkIntentHandler(): WalletConnectLinkIntentHandler = WalletConnectLinkIntentHandler()
@Provides
@Singleton
fun provideOnPushClickedIntentHandler(analyticsEventHandler: AnalyticsEventHandler): OnPushClickedIntentHandler =
OnPushClickedIntentHandler(analyticsEventHandler)
@Provides
@Singleton
fun provideIntentProcessor(): IntentProcessor = IntentProcessor()
}

View file

@ -1,34 +0,0 @@
package com.tangem.tap.di.data
import com.tangem.data.common.network.NetworkFactory
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.domain.card.DefaultDerivationsRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
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 CardDataModule {
@Singleton
@Provides
fun providesDerivationsRepository(
tangemSdkManager: TangemSdkManager,
userWalletsStore: UserWalletsStore,
networkFactory: NetworkFactory,
dispatchers: CoroutineDispatcherProvider,
): DerivationsRepository {
return DefaultDerivationsRepository(
tangemSdkManager = tangemSdkManager,
userWalletsStore = userWalletsStore,
networkFactory = networkFactory,
dispatchers = dispatchers,
)
}
}

View file

@ -2,12 +2,16 @@ package com.tangem.tap.di.domain
import com.tangem.domain.card.*
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase
import com.tangem.domain.wallets.usecase.HasMissedDerivationsUseCase
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.domain.card.DefaultDeleteSavedAccessCodesUseCase
import com.tangem.tap.domain.card.DefaultResetCardUseCase

View file

@ -1,14 +1,13 @@
package com.tangem.tap.di.domain
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.domain.managetokens.*
import com.tangem.domain.managetokens.repository.CustomTokensRepository
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.StakingIdFactory
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.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -71,22 +70,20 @@ internal object ManageTokensDomainModule {
walletManagersFacade: WalletManagersFacade,
currenciesRepository: CurrenciesRepository,
derivationsRepository: DerivationsRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
stakingIdFactory: StakingIdFactory,
): SaveManagedTokensUseCase {
return SaveManagedTokensUseCase(
customTokensRepository = customTokensRepository,
walletManagersFacade = walletManagersFacade,
currenciesRepository = currenciesRepository,
derivationsRepository = derivationsRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
stakingIdFactory = stakingIdFactory,
)
}

View file

@ -1,7 +1,7 @@
package com.tangem.tap.di.domain
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.domain.markets.*
import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
@ -9,9 +9,8 @@ import com.tangem.domain.promo.PromoRepository
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.StakingIdFactory
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 +61,19 @@ object MarketsDomainModule {
derivationsRepository: DerivationsRepository,
marketsTokenRepository: MarketsTokenRepository,
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
stakingIdFactory: StakingIdFactory,
): SaveMarketTokensUseCase {
return SaveMarketTokensUseCase(
derivationsRepository = derivationsRepository,
marketsTokenRepository = marketsTokenRepository,
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
stakingIdFactory = stakingIdFactory,
)
}

View file

@ -5,6 +5,8 @@ import com.tangem.domain.nft.*
import com.tangem.domain.nft.repository.NFTRepository
import com.tangem.domain.quotes.single.SingleQuoteStatusFetcher
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -33,9 +35,13 @@ internal object NFTDomainModule {
fun providesFetchNFTCollectionsUseCase(
currenciesRepository: CurrenciesRepository,
nftRepository: NFTRepository,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
): FetchNFTCollectionsUseCase = FetchNFTCollectionsUseCase(
currenciesRepository = currenciesRepository,
nftRepository = nftRepository,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
)
@Provides
@ -43,9 +49,13 @@ internal object NFTDomainModule {
fun providesRefreshAllNFTUseCase(
currenciesRepository: CurrenciesRepository,
nftRepository: NFTRepository,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
): RefreshAllNFTUseCase = RefreshAllNFTUseCase(
currenciesRepository = currenciesRepository,
nftRepository = nftRepository,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
)
@Provides
@ -118,8 +128,16 @@ internal object NFTDomainModule {
walletsRepository: WalletsRepository,
nftRepository: NFTRepository,
currenciesRepository: CurrenciesRepository,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
): DisableWalletNFTUseCase {
return DisableWalletNFTUseCase(walletsRepository, nftRepository, currenciesRepository)
return DisableWalletNFTUseCase(
walletsRepository = walletsRepository,
nftRepository = nftRepository,
currenciesRepository = currenciesRepository,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@Provides

View file

@ -3,6 +3,7 @@ package com.tangem.tap.di.domain
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.domain.notifications.*
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.notifications.repository.PushNotificationsRepository
import com.tangem.domain.notifications.toggles.NotificationsFeatureToggles
import com.tangem.tap.domain.notifications.DefaultNotificationsFeatureToggles
import com.tangem.utils.notifications.PushNotificationsTokenProvider
@ -18,20 +19,22 @@ internal object NotificationsDomainModule {
@Provides
@Singleton
fun providesGetApplicationIdUseCase(notificationsRepository: NotificationsRepository): GetApplicationIdUseCase {
fun providesGetApplicationIdUseCase(
pushNotificationsRepository: PushNotificationsRepository,
): GetApplicationIdUseCase {
return GetApplicationIdUseCase(
notificationsRepository = notificationsRepository,
pushNotificationsRepository = pushNotificationsRepository,
)
}
@Provides
@Singleton
fun providesSendPushTokenUseCase(
notificationsRepository: NotificationsRepository,
pushNotificationsRepository: PushNotificationsRepository,
pushNotificationsTokenProvider: PushNotificationsTokenProvider,
): SendPushTokenUseCase {
return SendPushTokenUseCase(
notificationsRepository = notificationsRepository,
pushNotificationsRepository = pushNotificationsRepository,
pushNotificationsTokenProvider = pushNotificationsTokenProvider,
)
}
@ -56,6 +59,26 @@ internal object NotificationsDomainModule {
)
}
@Provides
@Singleton
fun providesShouldShowNotificationUseCase(
notificationsRepository: NotificationsRepository,
): ShouldShowNotificationUseCase {
return ShouldShowNotificationUseCase(
notificationsRepository = notificationsRepository,
)
}
@Provides
@Singleton
fun providesSetShouldShowNotificationUseCase(
notificationsRepository: NotificationsRepository,
): SetShouldShowNotificationUseCase {
return SetShouldShowNotificationUseCase(
notificationsRepository = notificationsRepository,
)
}
@Provides
@Singleton
fun provideNotificationsFeatureToggles(featureTogglesManager: FeatureTogglesManager): NotificationsFeatureToggles {
@ -65,8 +88,8 @@ internal object NotificationsDomainModule {
@Provides
@Singleton
fun provideGetNetworksAvailableForNotifications(
notificationsRepository: NotificationsRepository,
pushNotificationsRepository: PushNotificationsRepository,
): GetNetworksAvailableForNotificationsUseCase {
return GetNetworksAvailableForNotificationsUseCase(notificationsRepository = notificationsRepository)
return GetNetworksAvailableForNotificationsUseCase(pushNotificationsRepository = pushNotificationsRepository)
}
}

View file

@ -94,14 +94,12 @@ internal object StakingDomainModule {
@Provides
@Singleton
fun provideFetchStakingYieldBalanceUseCase(
stakingRepository: StakingRepository,
stakingErrorResolver: StakingErrorResolver,
singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
stakingIdFactory: StakingIdFactory,
): FetchStakingYieldBalanceUseCase {
return FetchStakingYieldBalanceUseCase(
stakingRepository = stakingRepository,
stakingErrorResolver = stakingErrorResolver,
singleYieldBalanceFetcher = singleYieldBalanceFetcher,
stakingIdFactory = stakingIdFactory,
)
}
@ -175,18 +173,6 @@ internal object StakingDomainModule {
)
}
@Provides
@Singleton
fun provideIsApproveNeededUseCase(
stakingRepository: StakingRepository,
stakingErrorResolver: StakingErrorResolver,
): IsApproveNeededUseCase {
return IsApproveNeededUseCase(
stakingRepository = stakingRepository,
stakingErrorResolver = stakingErrorResolver,
)
}
@Provides
@Singleton
fun provideGetConstructedStakingTransactionUseCase(
@ -211,12 +197,6 @@ internal object StakingDomainModule {
)
}
@Provides
@Singleton
fun provideGetStakingIntegrationIdUseCase(stakingRepository: StakingRepository): GetStakingIntegrationIdUseCase {
return GetStakingIntegrationIdUseCase(stakingRepository)
}
@Provides
@Singleton
fun provideCheckAccountInitializedUseCase(
@ -227,9 +207,13 @@ internal object StakingDomainModule {
@Provides
@Singleton
fun provideGetActionRequirementAmountUseCase(
stakingRepository: StakingRepository,
): GetActionRequirementAmountUseCase {
return GetActionRequirementAmountUseCase(stakingRepository)
fun provideGetActionRequirementAmountUseCase(): GetActionRequirementAmountUseCase {
return GetActionRequirementAmountUseCase()
}
@Provides
@Singleton
fun provideStakingIdFactory(walletManagersFacade: WalletManagersFacade): StakingIdFactory {
return StakingIdFactory(walletManagersFacade = walletManagersFacade)
}
}

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

@ -11,7 +11,9 @@ import com.tangem.domain.promo.PromoRepository
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
@ -22,6 +24,7 @@ import com.tangem.domain.tokens.operations.CachedCurrenciesStatusesOperations
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
import com.tangem.domain.tokens.wallet.WalletBalanceFetcher
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.tap.domain.tokens.DefaultTokensFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -40,19 +43,21 @@ internal object TokensDomainModule {
@Singleton
fun provideAddCryptoCurrenciesUseCase(
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
stakingIdFactory: StakingIdFactory,
): AddCryptoCurrenciesUseCase {
return AddCryptoCurrenciesUseCase(
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
singleYieldBalanceFetcher = singleYieldBalanceFetcher,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
stakingIdFactory = stakingIdFactory,
)
}
@ -60,19 +65,17 @@ internal object TokensDomainModule {
@Singleton
fun provideFetchTokenListUseCase(
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
stakingIdFactory: StakingIdFactory,
): FetchTokenListUseCase {
return FetchTokenListUseCase(
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
stakingIdFactory = stakingIdFactory,
)
}
@ -107,8 +110,15 @@ internal object TokensDomainModule {
fun provideRemoveCurrencyUseCase(
currenciesRepository: CurrenciesRepository,
walletManagersFacade: WalletManagersFacade,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
): RemoveCurrencyUseCase {
return RemoveCurrencyUseCase(currenciesRepository, walletManagersFacade)
return RemoveCurrencyUseCase(
currenciesRepository = currenciesRepository,
walletManagersFacade = walletManagersFacade,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@Provides
@ -145,6 +155,8 @@ internal object TokensDomainModule {
currencyChecksRepository: CurrencyChecksRepository,
dispatchers: CoroutineDispatcherProvider,
baseCurrencyStatusOperations: BaseCurrencyStatusOperations,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
): GetCurrencyWarningsUseCase {
return GetCurrencyWarningsUseCase(
walletManagersFacade = walletManagersFacade,
@ -152,6 +164,8 @@ internal object TokensDomainModule {
dispatchers = dispatchers,
currencyChecksRepository = currencyChecksRepository,
currencyStatusOperations = baseCurrencyStatusOperations,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@ -159,19 +173,21 @@ internal object TokensDomainModule {
@Singleton
fun provideFetchCurrencyStatusUseCase(
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
stakingIdFactory: StakingIdFactory,
): FetchCurrencyStatusUseCase {
return FetchCurrencyStatusUseCase(
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
singleYieldBalanceFetcher = singleYieldBalanceFetcher,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
stakingIdFactory = stakingIdFactory,
)
}
@ -179,26 +195,28 @@ internal object TokensDomainModule {
@Singleton
fun provideFetchCardTokenListUseCase(
currenciesRepository: CurrenciesRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
stakingIdFactory: StakingIdFactory,
): FetchCardTokenListUseCase {
return FetchCardTokenListUseCase(
currenciesRepository = currenciesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
stakingIdFactory = stakingIdFactory,
)
}
@Provides
@Singleton
fun provideGetCryptoCurrencyUseCase(currenciesRepository: CurrenciesRepository): GetCryptoCurrencyUseCase {
return GetCryptoCurrencyUseCase(currenciesRepository)
fun provideGetCryptoCurrencyUseCase(
currenciesRepository: CurrenciesRepository,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
): GetCryptoCurrencyUseCase {
return GetCryptoCurrencyUseCase(currenciesRepository, multiWalletCryptoCurrenciesSupplier, tokensFeatureToggles)
}
@Provides
@ -219,9 +237,16 @@ internal object TokensDomainModule {
@Singleton
fun provideApplyTokenListSortingUseCase(
currenciesRepository: CurrenciesRepository,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
dispatchers: CoroutineDispatcherProvider,
): ApplyTokenListSortingUseCase {
return ApplyTokenListSortingUseCase(currenciesRepository, dispatchers)
return ApplyTokenListSortingUseCase(
currenciesRepository = currenciesRepository,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
dispatchers = dispatchers,
)
}
@Provides
@ -280,9 +305,13 @@ internal object TokensDomainModule {
@Singleton
fun provideIsCryptoCurrencyCoinCouldHideUseCase(
currenciesRepository: CurrenciesRepository,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
): IsCryptoCurrencyCoinCouldHideUseCase {
return IsCryptoCurrencyCoinCouldHideUseCase(
currenciesRepository = currenciesRepository,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@ -300,9 +329,16 @@ internal object TokensDomainModule {
@Singleton
fun provideGetBalanceNotEnoughForFeeWarningUseCase(
currenciesRepository: CurrenciesRepository,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
dispatchers: CoroutineDispatcherProvider,
): GetBalanceNotEnoughForFeeWarningUseCase {
return GetBalanceNotEnoughForFeeWarningUseCase(currenciesRepository, dispatchers)
return GetBalanceNotEnoughForFeeWarningUseCase(
currenciesRepository = currenciesRepository,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
dispatchers = dispatchers,
)
}
@Provides
@ -343,10 +379,14 @@ internal object TokensDomainModule {
fun provideRefreshMultiCurrencyWalletQuotesUseCase(
currenciesRepository: CurrenciesRepository,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
): RefreshMultiCurrencyWalletQuotesUseCase {
return RefreshMultiCurrencyWalletQuotesUseCase(
currenciesRepository = currenciesRepository,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@ -365,7 +405,6 @@ internal object TokensDomainModule {
tokensFeatureToggles: TokensFeatureToggles,
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
stakingRepository: StakingRepository,
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
@ -373,12 +412,14 @@ internal object TokensDomainModule {
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
multiYieldBalanceSupplier: MultiYieldBalanceSupplier,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
stakingIdFactory: StakingIdFactory,
): BaseCurrenciesStatusesOperations {
return CachedCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
stakingRepository = stakingRepository,
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
@ -386,8 +427,11 @@ internal object TokensDomainModule {
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
singleYieldBalanceSupplier = singleYieldBalanceSupplier,
multiYieldBalanceSupplier = multiYieldBalanceSupplier,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
stakingIdFactory = stakingIdFactory,
)
}
@ -397,7 +441,6 @@ internal object TokensDomainModule {
tokensFeatureToggles: TokensFeatureToggles,
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
stakingRepository: StakingRepository,
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
@ -405,12 +448,14 @@ internal object TokensDomainModule {
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
singleQuoteStatusSupplier: SingleQuoteStatusSupplier,
singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
multiYieldBalanceSupplier: MultiYieldBalanceSupplier,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
stakingIdFactory: StakingIdFactory,
): BaseCurrencyStatusOperations {
return CachedCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
stakingRepository = stakingRepository,
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
@ -418,8 +463,11 @@ internal object TokensDomainModule {
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
singleYieldBalanceSupplier = singleYieldBalanceSupplier,
multiYieldBalanceSupplier = multiYieldBalanceSupplier,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
stakingIdFactory = stakingIdFactory,
)
}
@ -428,4 +476,28 @@ internal object TokensDomainModule {
fun provideGetCryptoCurrenciesUseCase(currenciesRepository: CurrenciesRepository): GetCryptoCurrenciesUseCase {
return GetCryptoCurrenciesUseCase(currenciesRepository)
}
@Provides
@Singleton
fun provideWalletBalanceFetcher(
currenciesRepository: CurrenciesRepository,
multiWalletCryptoCurrenciesFetcher: MultiWalletCryptoCurrenciesFetcher,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
stakingIdFactory: StakingIdFactory,
dispatchers: CoroutineDispatcherProvider,
): WalletBalanceFetcher {
return WalletBalanceFetcher(
currenciesRepository = currenciesRepository,
multiWalletCryptoCurrenciesFetcher = multiWalletCryptoCurrenciesFetcher,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
stakingIdFactory = stakingIdFactory,
dispatchers = dispatchers,
)
}
}

View file

@ -1,9 +1,12 @@
package com.tangem.tap.di.domain
import com.tangem.data.wallets.hot.TangemHotWalletSigner
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.transaction.FeeRepository
import com.tangem.domain.transaction.TransactionRepository
@ -41,6 +44,7 @@ internal object TransactionDomainModule {
transactionRepository: TransactionRepository,
walletManagersFacade: WalletManagersFacade,
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory,
): SendTransactionUseCase {
return SendTransactionUseCase(
demoConfig = DemoConfig(),
@ -48,6 +52,7 @@ internal object TransactionDomainModule {
transactionRepository = transactionRepository,
walletManagersFacade = walletManagersFacade,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
getHotWalletSigner = tangemHotWalletSignerFactory::create,
)
}
@ -58,12 +63,16 @@ internal object TransactionDomainModule {
walletManagersFacade: WalletManagersFacade,
currenciesRepository: CurrenciesRepository,
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles: TokensFeatureToggles,
): AssociateAssetUseCase {
return AssociateAssetUseCase(
cardSdkConfigRepository = cardSdkConfigRepository,
walletManagersFacade = walletManagersFacade,
currenciesRepository = currenciesRepository,
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@ -171,8 +180,13 @@ internal object TransactionDomainModule {
fun providePrepareForSendUseCase(
transactionRepository: TransactionRepository,
cardSdkConfigRepository: CardSdkConfigRepository,
tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory,
): PrepareForSendUseCase {
return PrepareForSendUseCase(transactionRepository, cardSdkConfigRepository)
return PrepareForSendUseCase(
transactionRepository = transactionRepository,
cardSdkConfigRepository = cardSdkConfigRepository,
getHotTransactionSigner = { tangemHotWalletSignerFactory.create(it) },
)
}
@Provides
@ -180,8 +194,13 @@ internal object TransactionDomainModule {
fun provideSignUseCase(
walletManagersFacade: WalletManagersFacade,
cardSdkConfigRepository: CardSdkConfigRepository,
tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory,
): SignUseCase {
return SignUseCase(cardSdkConfigRepository, walletManagersFacade)
return SignUseCase(
cardSdkConfigRepository = cardSdkConfigRepository,
walletManagersFacade = walletManagersFacade,
getHotTransactionSigner = { tangemHotWalletSignerFactory.create(it) },
)
}
@Provides

View file

@ -1,37 +0,0 @@
package com.tangem.tap.di.domain
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.datasource.asset.loader.AssetLoader
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.datasource.local.walletmanager.WalletManagersStore
import com.tangem.domain.walletmanager.DefaultWalletManagersFacade
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
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 WalletManagersFacadeModule {
@Provides
@Singleton
fun provideWalletManagersFacade(
walletManagersStore: WalletManagersStore,
userWalletsStore: UserWalletsStore,
assetLoader: AssetLoader,
blockchainSDKFactory: BlockchainSDKFactory,
dispatchers: CoroutineDispatcherProvider,
): WalletManagersFacade {
return DefaultWalletManagersFacade(
walletManagersStore = walletManagersStore,
userWalletsStore = userWalletsStore,
assetLoader = assetLoader,
dispatchers = dispatchers,
blockchainSDKFactory = blockchainSDKFactory,
)
}
}

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,18 @@
package com.tangem.tap.di.hot
import com.tangem.hot.sdk.TangemHotSdk
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
}

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

@ -4,8 +4,7 @@ import com.tangem.blockchain.common.Token
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.domain.models.wallet.UserWallet
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

@ -1,165 +0,0 @@
package com.tangem.tap.domain.card
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.common.CompletionResult
import com.tangem.common.card.EllipticCurve
import com.tangem.common.core.TangemSdkError
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.toMapKey
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.data.common.network.NetworkFactory
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.BackendId
import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.domain.tasks.UserWalletIdPreflightReadFilter
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import timber.log.Timber
internal typealias Derivations = Map<ByteArrayKey, List<DerivationPath>>
private typealias DerivedKeys = Map<ByteArrayKey, ExtendedPublicKeysMap>
internal class DefaultDerivationsRepository(
private val tangemSdkManager: TangemSdkManager,
private val userWalletsStore: UserWalletsStore,
private val networkFactory: NetworkFactory,
private val dispatchers: CoroutineDispatcherProvider,
) : DerivationsRepository {
override suspend fun derivePublicKeys(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
derivePublicKeysByNetworks(userWalletId = userWalletId, networks = currencies.map(CryptoCurrency::network))
}
override suspend fun derivePublicKeysByNetworkIds(userWalletId: UserWalletId, networkIds: List<Network.RawID>) {
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
derivePublicKeysByNetworks(
userWalletId = userWalletId,
networks = networkIds.mapNotNull {
networkFactory.create(
blockchain = Blockchain.fromNetworkId(it.value) ?: return@mapNotNull null,
extraDerivationPath = null,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
)
},
)
}
override suspend fun derivePublicKeysByNetworks(userWalletId: UserWalletId, networks: List<Network>) {
val userWallet = withContext(dispatchers.io) {
userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
}
userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY]
if (!userWallet.scanResponse.card.settings.isHDWalletAllowed) {
Timber.d("Nothing to derive")
return
}
val derivations = MissedDerivationsFinder(scanResponse = userWallet.scanResponse)
.findByNetworks(networks)
.ifEmpty {
Timber.d("Nothing to derive")
return
}
derivePublicKeys(userWalletId = userWalletId, derivations = derivations)
}
override suspend fun hasMissedDerivations(
userWalletId: UserWalletId,
networksWithDerivationPath: Map<BackendId, String?>,
): Boolean {
val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found")
val derivations =
MissedDerivationsFinder(scanResponse = userWallet.requireColdWallet().scanResponse) // TODO [REDACTED_TASK_KEY]
.findByNetworks(
networksWithDerivationPath.mapNotNull { (backendId, extraDerivationPath) ->
networkFactory.create(
blockchain = Blockchain.fromNetworkId(backendId) ?: return@mapNotNull null,
extraDerivationPath = extraDerivationPath,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
)
},
)
return derivations.isNotEmpty()
}
override suspend fun derivePublicKeys(userWalletId: UserWalletId, derivations: Derivations): DerivedKeys {
// todo replace it in task [REDACTED_JIRA]
val preflightReadFilter = UserWalletIdPreflightReadFilter(userWalletId)
tangemSdkManager.derivePublicKeys(
cardId = null,
derivations = derivations,
preflightReadFilter = preflightReadFilter,
).doOnSuccess { response ->
updatePublicKeys(userWalletId = userWalletId, keys = response.entries)
.doOnSuccess {
// TODO [REDACTED_TASK_KEY]
validateDerivations(scanResponse = it.requireColdWallet().scanResponse, derivations = derivations)
return response.entries
}
.doOnFailure { throw it }
}
.doOnFailure { throw it }
error("This code should never be reached")
}
/**
* It throws an exception if any of the provided derivations are invalid
* Validation for NonHardened moved to application layer, to avoid fails when derive multiple paths
* It needs to be called after success [derivePublicKeys] or in same flows
*/
private fun validateDerivations(scanResponse: ScanResponse, derivations: Derivations) {
derivations.entries.forEach { derivationForKey ->
val wallet = scanResponse.card.wallets.firstOrNull { it.publicKey.toMapKey() == derivationForKey.key }
if (wallet == null) return@forEach
val hasHardenedNodes = derivationForKey.value.any { path -> path.nodes.any { node -> !node.isHardened } }
if (wallet.curve == EllipticCurve.Ed25519Slip0010 && hasHardenedNodes) {
throw TangemSdkError.NonHardenedDerivationNotSupported()
}
}
}
private suspend fun updatePublicKeys(userWalletId: UserWalletId, keys: DerivedKeys): CompletionResult<UserWallet> {
return withContext(dispatchers.io) {
userWalletsStore.update(
userWalletId = userWalletId,
update = { userWallet -> userWallet.requireColdWallet().updateDerivedKeys(keys) }, // TODO [REDACTED_TASK_KEY]
)
}
}
private fun UserWallet.Cold.updateDerivedKeys(keys: DerivedKeys): UserWallet {
return copy(
scanResponse = scanResponse.copy(
derivedKeys = getUpdatedDerivedKeys(oldKeys = scanResponse.derivedKeys, newKeys = keys),
),
)
}
private fun getUpdatedDerivedKeys(oldKeys: DerivedKeys, newKeys: DerivedKeys): DerivedKeys {
return (oldKeys.keys + newKeys.keys).toSet()
.associateWith { walletKey ->
val oldDerivations = ExtendedPublicKeysMap(oldKeys[walletKey] ?: emptyMap())
val newDerivations = newKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap())
ExtendedPublicKeysMap(oldDerivations + newDerivations)
}
}
}

View file

@ -13,7 +13,7 @@ import com.tangem.common.core.UserCodeRequestPolicy
import com.tangem.domain.card.ResetCardUseCase
import com.tangem.domain.card.ResetCardUserCodeParams
import com.tangem.domain.card.models.ResetCardError
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.sdk.api.TangemSdkManager
internal class DefaultResetCardUseCase(

View file

@ -1,124 +0,0 @@
package com.tangem.tap.domain.card
import com.tangem.blockchain.blockchains.cardano.CardanoUtils
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.toMapKey
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.configs.CardConfig
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.scan.KeyWalletPublicKey
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.operations.derivation.ExtendedPublicKeysMap
private typealias DerivationData = Pair<ByteArrayKey, List<DerivationPath>>
/**
* Finder of missed derivations
*
* @property scanResponse scanning response
*
[REDACTED_AUTHOR]
*/
internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) {
/** Find missed derivations for given currencies [currencies] */
fun find(currencies: List<CryptoCurrency>): Derivations {
return currencies.map { it.network }.let(::findByNetworks)
}
fun findByNetworks(networks: List<Network>): Derivations {
return buildMap<ByteArrayKey, MutableList<DerivationPath>> {
networks
.mapToNewDerivations()
.forEach { data ->
val current = this[data.first]
if (current != null) {
current.addAll(data.second)
current.distinct()
} else {
this[data.first] = data.second.toMutableList()
}
}
}
}
private fun List<Network>.mapToNewDerivations(): List<DerivationData> {
val config = CardConfig.createConfig(scanResponse.card)
return mapNotNull { network ->
val blockchain = network.toBlockchain()
val curve = config.primaryCurve(blockchain) ?: return@mapNotNull null
findNewDerivations(curve = curve, scanResponse = scanResponse, network = network)
}
}
private fun findNewDerivations(
curve: EllipticCurve,
scanResponse: ScanResponse,
network: Network,
): DerivationData? {
val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null
val publicKey = wallet.publicKey.toMapKey()
val derivationCandidates = network
.getDerivationCandidates(curve)
.ifEmpty { return null }
.filterAlreadyDerivedKeys(publicKey)
.ifEmpty { return null }
return publicKey to derivationCandidates
}
private fun Network.getDerivationCandidates(curve: EllipticCurve): List<DerivationPath> {
val blockchain = this.toBlockchain()
return buildList {
add(blockchain.getDerivationPath(curve = curve))
add(blockchain.getCustomDerivationPath(curve = curve, network = this@getDerivationCandidates))
add(blockchain.getCardanoDerivationPathIfNeeded(network = this@getDerivationCandidates))
}
.filterNotNull()
.distinct()
}
private fun Blockchain.getDerivationPath(curve: EllipticCurve): DerivationPath? {
return if (getSupportedCurves().contains(curve)) {
derivationPath(style = scanResponse.derivationStyleProvider.getDerivationStyle())
} else {
null
}
}
private fun Blockchain.getCustomDerivationPath(curve: EllipticCurve, network: Network): DerivationPath? {
return if (getSupportedCurves().contains(curve)) {
network.derivationPath.value?.let(::DerivationPath)
} else {
null
}
}
private fun Blockchain.getCardanoDerivationPathIfNeeded(network: Network): DerivationPath? {
return if (this == Blockchain.Cardano) {
network.derivationPath.value?.let {
CardanoUtils.extendedDerivationPath(derivationPath = DerivationPath(it))
}
} else {
null
}
}
private fun List<DerivationPath>.filterAlreadyDerivedKeys(publicKey: KeyWalletPublicKey): List<DerivationPath> {
val alreadyDerivedPaths = getAlreadyDerivedKeys(publicKey)
return filterNot(alreadyDerivedPaths::contains)
}
private fun getAlreadyDerivedKeys(publicKey: KeyWalletPublicKey): List<DerivationPath> {
val extendedPublicKeysMap = scanResponse.derivedKeys[publicKey] ?: ExtendedPublicKeysMap(emptyMap())
return extendedPublicKeysMap.keys.toList()
}
}

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

@ -1,6 +1,5 @@
package com.tangem.tap.domain.model
import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.features.addCustomToken.CustomCurrency
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.blockchain.common.Blockchain as SdkBlockchain
@ -32,21 +31,6 @@ sealed interface Currency {
}
companion object {
fun fromBlockchainNetwork(blockchainNetwork: BlockchainNetwork, token: SdkToken? = null): Currency {
return if (token != null) {
Token(
token = token,
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath,
)
} else {
Blockchain(
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath,
)
}
}
fun fromCustomCurrency(customCurrency: CustomCurrency): Currency {
return when (customCurrency) {
is CustomCurrency.CustomBlockchain -> Blockchain(

View file

@ -19,7 +19,7 @@ import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.toWrappedList
import com.tangem.core.ui.message.dialog.Dialogs
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.card.common.util.twinsIsTwinned
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.builder.UserWalletIdBuilder

View file

@ -5,7 +5,7 @@ import arrow.core.right
import com.tangem.common.routing.AppRoute
import com.tangem.core.analytics.Analytics
import com.tangem.domain.card.ScanCardException
import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.card.common.util.twinsIsTwinned
import com.tangem.domain.core.chain.Chain
import com.tangem.domain.core.chain.ResultChain
import com.tangem.domain.models.scan.ScanResponse

View file

@ -19,14 +19,14 @@ import com.tangem.crypto.bip39.DefaultMnemonic
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.wallets.derivations.derivationStyleProvider
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.VisaActivationInput
import com.tangem.domain.visa.model.VisaDataForApprove
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.operations.ScanTask
import com.tangem.operations.derivation.DerivationTaskResponse

View file

@ -17,10 +17,10 @@ import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.VisaActivationInput
import com.tangem.domain.visa.model.VisaDataForApprove
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.operations.derivation.DerivationTaskResponse
import com.tangem.operations.preflightread.PreflightReadFilter
import com.tangem.operations.wallet.CreateWalletResponse

View file

@ -177,6 +177,13 @@ object WalletMockContent : MockContent {
parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0,
),
DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
depth = 0,
parentFingerprint = byteArrayOf(0, 0, 0, 0),
childNumber = 0,
),
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),

View file

@ -4,8 +4,8 @@ import com.tangem.common.card.Card
import com.tangem.common.core.SessionEnvironment
import com.tangem.common.core.TangemSdkError
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.operations.preflightread.PreflightReadFilter
/**

View file

@ -13,10 +13,10 @@ import com.tangem.common.extensions.toMapKey
import com.tangem.common.map
import com.tangem.crypto.bip39.Mnemonic
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.domain.common.configs.CardConfig
import com.tangem.domain.card.CardTypesResolver
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.domain.card.common.TapWorkarounds.isTestCard
import com.tangem.domain.card.configs.CardConfig
import com.tangem.domain.models.scan.CardDTO
import com.tangem.operations.backup.PrimaryCard
import com.tangem.operations.backup.StartPrimaryCardLinkingCommand

View file

@ -6,11 +6,11 @@ import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.datasource.local.token.UserTokensResponseStore
import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.domain.card.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext

View file

@ -4,7 +4,7 @@ import com.tangem.common.CompletionResult
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.CompletionCallback
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.operations.PreflightReadMode
import com.tangem.operations.PreflightReadTask
import com.tangem.tap.domain.tasks.UserWalletIdPreflightReadFilter

View file

@ -14,16 +14,16 @@ import com.tangem.common.tlv.Tlv
import com.tangem.common.tlv.TlvDecoder
import com.tangem.crypto.CryptoUtils
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.common.TapWorkarounds.isExcluded
import com.tangem.domain.common.TapWorkarounds.isNotSupportedInThatRelease
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.TapWorkarounds.isVisa
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
import com.tangem.domain.card.common.TapWorkarounds.isExcluded
import com.tangem.domain.card.common.TapWorkarounds.isNotSupportedInThatRelease
import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.card.common.TapWorkarounds.isVisa
import com.tangem.domain.common.TwinsHelper
import com.tangem.domain.common.configs.CardConfig
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.common.visa.VisaUtilities
import com.tangem.domain.wallets.derivations.derivationStyleProvider
import com.tangem.domain.card.common.visa.VisaUtilities
import com.tangem.domain.card.configs.CardConfig
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

View file

@ -17,7 +17,7 @@ import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import com.tangem.datasource.local.visa.VisaOTPStorage
import com.tangem.datasource.local.visa.VisaOtpData
import com.tangem.datasource.local.visa.hasSavedOTP
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility
import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.visa.error.VisaActivationError
import com.tangem.domain.visa.model.*

View file

@ -16,10 +16,10 @@ import com.tangem.common.extensions.toHexString
import com.tangem.core.error.ext.tangemError
import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.common.visa.VisaUtilities
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility.findKeyWithoutDerivation
import com.tangem.domain.wallets.derivations.derivationStyleProvider
import com.tangem.domain.card.common.visa.VisaUtilities
import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility
import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility.findKeyWithoutDerivation
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.visa.error.VisaActivationError
import com.tangem.domain.visa.model.VisaDataForApprove

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

@ -1,13 +1,12 @@
package com.tangem.tap.domain.userWalletList.implementation
import com.tangem.common.*
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
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
@ -213,8 +212,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

@ -4,9 +4,9 @@ import com.tangem.common.CompletionResult
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.get
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi

View file

@ -2,10 +2,10 @@ package com.tangem.tap.domain.userWalletList.implementation
import com.tangem.common.CompletionResult
import com.tangem.common.catching
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*

View file

@ -1,7 +1,7 @@
package com.tangem.tap.domain.userWalletList.model
import com.squareup.moshi.JsonClass
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
@JsonClass(generateAdapter = true)
internal data class UserWalletEncryptionKey(

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.models.wallet.UserWalletId
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

@ -1,6 +1,6 @@
package com.tangem.tap.domain.userWalletList.repository
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
internal interface SelectedUserWalletRepository {
suspend fun get(): UserWalletId?

View file

@ -1,7 +1,7 @@
package com.tangem.tap.domain.userWalletList.repository
import com.tangem.common.CompletionResult
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
internal interface UserWalletsKeysRepository {

View file

@ -1,8 +1,8 @@
package com.tangem.tap.domain.userWalletList.repository
import com.tangem.common.CompletionResult
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletPublicInformation
internal interface UserWalletsPublicInformationRepository {

View file

@ -1,8 +1,8 @@
package com.tangem.tap.domain.userWalletList.repository
import com.tangem.common.CompletionResult
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
import com.tangem.tap.domain.userWalletList.model.UserWalletSensitiveInformation

View file

@ -10,8 +10,8 @@ import com.tangem.common.services.secure.SecureStorage
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository
import kotlinx.coroutines.Dispatchers

View file

@ -1,7 +1,7 @@
package com.tangem.tap.domain.userWalletList.repository.implementation
import com.tangem.common.services.secure.SecureStorage
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.domain.userWalletList.repository.SelectedUserWalletRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.sync.Mutex

View file

@ -7,8 +7,8 @@ import com.tangem.common.CompletionResult
import com.tangem.common.catching
import com.tangem.common.flatMap
import com.tangem.common.services.secure.SecureStorage
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.domain.userWalletList.model.UserWalletPublicInformation
import com.tangem.tap.domain.userWalletList.repository.UserWalletsPublicInformationRepository
import com.tangem.tap.domain.userWalletList.utils.publicInformation

View file

@ -7,8 +7,8 @@ import com.tangem.common.CompletionResult
import com.tangem.common.catching
import com.tangem.common.services.secure.SecureStorage
import com.tangem.crypto.operations.AESCipherOperations
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.common.extensions.filterNotNull
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
import com.tangem.tap.domain.userWalletList.model.UserWalletSensitiveInformation

View file

@ -1,7 +1,8 @@
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.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.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,
)
}
}
@ -90,5 +117,5 @@ internal fun UserWallet.lock(): UserWallet = when (this) {
),
)
}
is UserWallet.Hot -> TODO("[REDACTED_TASK_KEY]")
is UserWallet.Hot -> copy(wallets = null)
}

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.models.wallet.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

@ -9,7 +9,7 @@ import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.core.error.ext.tangemError
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility
import com.tangem.domain.card.common.visa.VisaWalletPublicKeyUtility
import com.tangem.domain.visa.error.VisaActivationError
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.domain.visa.error.VisaCardScanError

View file

@ -17,7 +17,7 @@ import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.Basic.TransactionSent.MemoType
import com.tangem.data.walletconnect.network.ethereum.LegacySdkHelper
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.operations.sign.SignHashCommand
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.extensions.safeUpdate

View file

@ -8,12 +8,12 @@ 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.data.walletconnect.pair.UnsupportedDApps
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.walletconnect.model.legacy.Account
import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.tap.common.analytics.events.WalletConnect
import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper
@ -216,7 +216,7 @@ internal class DefaultLegacyWalletConnectRepository(
Timber.i("sessionProposal: $sessionProposal")
this@DefaultLegacyWalletConnectRepository.sessionProposal = sessionProposal
if (sessionProposal.name in unsupportedDApps) {
if (sessionProposal.name in UnsupportedDApps.list) {
Timber.i("Unsupported DApp")
scope.launch {
_events.emit(

View file

@ -1,7 +1,7 @@
package com.tangem.tap.domain.walletconnect2.domain
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.walletconnect.model.legacy.Account
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.domain.walletconnect2.domain.models.*
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType
import kotlinx.coroutines.flow.Flow

View file

@ -5,16 +5,15 @@ import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.walletconnect.model.legacy.Account
import com.tangem.domain.walletconnect.model.legacy.Session
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
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

@ -2,7 +2,7 @@ package com.tangem.tap.features.demo
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.AppState
@ -43,9 +43,6 @@ object DemoHelper {
}
private fun getScanResponse(appState: () -> AppState?): ScanResponse? {
val state = appState() ?: return null
return state.globalState.onboardingState.onboardingManager?.scanResponse
?: state.globalState.scanResponse
return appState()?.globalState?.scanResponse
}
}

View file

@ -1,6 +1,6 @@
package com.tangem.tap.features.demo
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.models.scan.ScanResponse
import org.rekotlin.Action

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

@ -233,7 +233,7 @@ class DetailsMiddleware {
deleteSavedAccessCodes()
store.inject(DaggerGraphState::walletsRepository).saveShouldSaveUserWallets(item = false)
store.dispatchNavigationAction { replaceAll(AppRoute.Home) }
store.dispatchNavigationAction { replaceAll(AppRoute.Home()) }
return CompletionResult.Success(Unit)
}

View file

@ -1,6 +1,6 @@
package com.tangem.tap.features.details.redux.walletconnect
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError
import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectEvents

View file

@ -2,7 +2,7 @@ package com.tangem.tap.features.details.ui.cardsettings.api
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
interface CardSettingsComponent : ComposableContentComponent {

View file

@ -6,7 +6,7 @@ import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.Analytics
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.card.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

View file

@ -10,16 +10,16 @@ import com.tangem.core.analytics.Analytics
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.card.CardTypesResolver
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.getBackupCardsCount
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.card.common.util.getBackupCardsCount
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.common.analytics.events.AnalyticsParam
@ -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
@ -88,9 +87,10 @@ internal class CardSettingsModel @Inject constructor(
val userWallet = getUserWalletUseCase(userWalletId)
.getOrElse { error("User wallet $userWalletId not found") }
.requireColdWallet()
cardSdkConfigRepository.isBiometricsRequestPolicy =
userWallet.requireColdWallet().scanResponse.card.isAccessCodeSet && // TODO [REDACTED_TASK_KEY]
userWallet.scanResponse.card.isAccessCodeSet &&
settingsRepository.shouldSaveAccessCodes()
}
}
@ -171,6 +171,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

@ -1,6 +1,6 @@
package com.tangem.tap.features.details.ui.common.utils
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.card.CardTypesResolver
import com.tangem.domain.models.scan.CardDTO
internal fun isAccessCodeRecoveryAllowed(typeResolver: CardTypesResolver): Boolean = typeResolver.isWallet2()

View file

@ -1,6 +1,6 @@
package com.tangem.tap.features.details.ui.common.utils
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.card.CardTypesResolver
import com.tangem.tap.features.details.ui.cardsettings.TextReference
import com.tangem.wallet.R

View file

@ -1,6 +1,6 @@
package com.tangem.tap.features.details.ui.common.utils
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.card.CardTypesResolver
import com.tangem.domain.models.scan.CardDTO
import com.tangem.tap.features.details.redux.SecurityOption
import java.util.EnumSet

View file

@ -2,7 +2,7 @@ package com.tangem.tap.features.details.ui.resetcard.api
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
interface ResetCardComponent : ComposableContentComponent {

View file

@ -11,10 +11,10 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.card.DeleteSavedAccessCodesUseCase
import com.tangem.domain.card.ResetCardUseCase
import com.tangem.domain.card.ResetCardUserCodeParams
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.legacy.asLockable
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.domain.wallets.usecase.DeleteWalletUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
@ -268,7 +268,7 @@ internal class ResetCardModel @Inject constructor(
if (isLocked && userWalletsListManager.hasUserWallets) {
store.dispatchNavigationAction { popTo<AppRoute.Welcome>() }
} else {
store.dispatchNavigationAction { replaceAll(AppRoute.Home) }
store.dispatchNavigationAction { replaceAll(AppRoute.Home()) }
}
}
}

View file

@ -2,7 +2,7 @@ package com.tangem.tap.features.details.ui.securitymode.api
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
interface SecurityModeComponent : ComposableContentComponent {

View file

@ -8,7 +8,7 @@ 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.domain.card.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

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

@ -2,7 +2,7 @@ package com.tangem.tap.features.details.ui.walletconnect.api
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.models.wallet.UserWalletId
interface WalletConnectComponent : ComposableContentComponent {
data class Params(val userWalletId: UserWalletId)

View file

@ -1,88 +0,0 @@
package com.tangem.tap.features.home
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import com.arkivanov.essenty.lifecycle.subscribe
import com.tangem.core.decompose.context.AppComponentContext
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.tap.common.redux.AppState
import com.tangem.tap.features.home.api.HomeComponent
import com.tangem.tap.features.home.compose.StoriesScreen
import com.tangem.tap.features.home.compose.StoriesScreenV2
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.home.redux.HomeState
import com.tangem.tap.store
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import org.rekotlin.StoreSubscriber
@Suppress("UnusedPrivateMember")
internal class DefaultHomeComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted params: Unit,
) : HomeComponent, AppComponentContext by appComponentContext, StoreSubscriber<HomeState> {
private val model: HomeModel = getOrCreateModel()
private var homeState: MutableState<HomeState> = mutableStateOf(store.state.homeState)
init {
lifecycle.subscribe(
onCreate = {
store.dispatch(HomeAction.OnCreate)
},
onStart = {
store.subscribe(subscriber = this) { state ->
state
.skipRepeats { oldState, newState -> oldState.homeState == newState.homeState }
.select(AppState::homeState)
}
},
onStop = {
store.unsubscribe(this)
},
)
}
@Composable
override fun Content(modifier: Modifier) {
val activity = LocalContext.current.findActivity()
BackHandler(onBack = activity::finish)
SystemBarsIconsDisposable(darkIcons = false)
if (homeState.value.isV2StoriesEnabled) {
StoriesScreenV2(
homeState = homeState,
onCreateNewWalletButtonClick = model::onCreateNewWalletScreen,
onAddExistingWalletButtonClick = model::onAddExistingWalletScreen,
onScanButtonClick = model::onScanClick,
)
} else {
StoriesScreen(
homeState = homeState,
onScanButtonClick = model::onScanClick,
onShopButtonClick = model::onShopClick,
onSearchTokensClick = model::onSearchClick,
)
}
ChangeRootBackgroundColorEffect(Color(color = 0xFF010101))
}
override fun newState(state: HomeState) {
homeState.value = state
}
@AssistedFactory
interface Factory : HomeComponent.Factory {
override fun create(context: AppComponentContext, params: Unit): DefaultHomeComponent
}
}

View file

@ -1,166 +0,0 @@
package com.tangem.tap.features.home
import androidx.compose.runtime.Stable
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRoute.ManageTokens.Source
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.tokens.TokensAction
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.analytics.events.Shop
import com.tangem.tap.common.extensions.dispatchNavigationAction
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.features.home.redux.HIDE_PROGRESS_DELAY
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.home.redux.HomeMiddleware.NEW_BUY_WALLET_URL
import com.tangem.tap.store
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.Locale
import javax.inject.Inject
@Suppress("LongParameterList")
@Stable
@ModelScoped
internal class HomeModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val scanCardProcessor: ScanCardProcessor,
private val saveWalletUseCase: SaveWalletUseCase,
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val settingsRepository: SettingsRepository,
private val urlOpener: UrlOpener,
private val analyticsEventHandler: AnalyticsEventHandler,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
private val router: Router,
getUserCountryUseCase: GetUserCountryUseCase,
) : Model() {
private val tangemErrorHandler = TangemTangemErrorsHandler(store)
init {
getUserCountryUseCase.invoke()
.distinctUntilChanged()
.filterNotNull()
.onEach {
val userCountry = it.getOrNull() ?: UserCountry.Other(Locale.getDefault().country)
store.dispatchOnMain(HomeAction.UserCountryLoaded(userCountry))
}
.flowOn(dispatchers.io)
.launchIn(modelScope)
}
fun onCreateNewWalletScreen() {
router.push(AppRoute.CreateWalletSelection)
}
fun onAddExistingWalletScreen() {
router.push(AppRoute.AddExistingWallet)
}
fun onScanClick() {
analyticsEventHandler.send(IntroductionProcess.ButtonScanCard())
scanCard()
}
fun onShopClick() {
analyticsEventHandler.send(IntroductionProcess.ButtonBuyCards())
analyticsEventHandler.send(Shop.ScreenOpened())
Firebase.analytics.appInstanceId
.addOnSuccessListener { urlOpener.openUrl(url = "$NEW_BUY_WALLET_URL&app_instance_id=$it") }
.addOnFailureListener { urlOpener.openUrl(url = NEW_BUY_WALLET_URL) }
}
fun onSearchClick() {
analyticsEventHandler.send(IntroductionProcess.ButtonTokensList())
store.dispatch(TokensAction.SetArgs.ReadAccess)
store.dispatchNavigationAction { push(AppRoute.ManageTokens(Source.STORIES)) }
}
private fun scanCard() {
modelScope.launch {
cardSdkConfigRepository.isBiometricsRequestPolicy = settingsRepository.shouldSaveAccessCodes()
scanCardProcessor.scan(
analyticsSource = AnalyticsParam.ScreensSources.Intro,
onProgressStateChange = { showProgress ->
if (showProgress) {
store.dispatch(HomeAction.ScanInProgress(scanInProgress = true))
} else {
delay(HIDE_PROGRESS_DELAY)
store.dispatch(HomeAction.ScanInProgress(scanInProgress = false))
}
},
onFailure = {
tangemErrorHandler.onErrorReceived(error = it)
delay(HIDE_PROGRESS_DELAY)
store.dispatch(HomeAction.ScanInProgress(scanInProgress = false))
},
onSuccess = ::proceedWithScanResponse,
)
}
}
private suspend fun proceedWithScanResponse(scanResponse: ScanResponse) {
val userWallet = coldUserWalletBuilderFactory.create(scanResponse = scanResponse).build()
if (userWallet == null) {
Timber.e("User wallet not created")
return
}
saveWalletUseCase(userWallet).fold(
ifLeft = { Timber.e(it.toString(), "Unable to save user wallet") },
ifRight = {
sendSignedInCardAnalyticsEvent(scanResponse)
coroutineScope { store.onUserWalletSelected(userWallet = userWallet) }
},
)
store.dispatchWithMain(HomeAction.ScanInProgress(scanInProgress = false))
delay(HIDE_PROGRESS_DELAY)
store.dispatchNavigationAction { replaceAll(AppRoute.Wallet) }
}
private fun sendSignedInCardAnalyticsEvent(scanResponse: ScanResponse) {
val currency = ParamCardCurrencyConverter().convert(value = scanResponse.cardTypesResolver)
if (currency != null) {
Analytics.send(
event = Basic.SignedIn(
currency = currency,
batch = scanResponse.card.batchId,
signInType = Basic.SignedIn.SignInType.Card,
walletsCount = "1",
hasBackup = scanResponse.card.backupStatus?.isActive,
),
)
}
}
}

Some files were not shown because too many files have changed in this diff Show more