Updated on 2026-08-14
This commit is contained in:
commit
c306b16a7b
1308 changed files with 41564 additions and 8590 deletions
|
|
@ -5,7 +5,9 @@ import com.tangem.TangemSdkLogger
|
|||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.abtests.manager.ABTestsManager
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||
|
|
@ -151,4 +153,8 @@ interface ApplicationEntryPoint {
|
|||
fun getHotWalletFeatureToggles(): HotWalletFeatureToggles
|
||||
|
||||
fun getWcInitializeUseCase(): WcInitializeUseCase
|
||||
|
||||
fun getTrackingContextProxy(): TrackingContextProxy
|
||||
|
||||
fun getABTestsManager(): ABTestsManager
|
||||
}
|
||||
|
|
@ -302,7 +302,9 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
|
|||
}
|
||||
|
||||
private fun createAppThemeModeFlow(): SharedFlow<AppThemeMode> {
|
||||
val tangemApplication = application as TangemApplication
|
||||
val tangemApplication = requireNotNull(application as? TangemApplication) {
|
||||
"Application is null"
|
||||
}
|
||||
|
||||
return tangemApplication.getAppThemeModeUseCase()
|
||||
.filterNotNull()
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
|
|||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.abtests.manager.ABTestsManager
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.api.ParamsInterceptor
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
|
|
@ -240,6 +241,12 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
|
|||
private val wcInitializeUseCase
|
||||
get() = entryPoint.getWcInitializeUseCase()
|
||||
|
||||
private val trackingContextProxy
|
||||
get() = entryPoint.getTrackingContextProxy()
|
||||
|
||||
private val abTestsManager: ABTestsManager
|
||||
get() = entryPoint.getABTestsManager()
|
||||
|
||||
// endregion
|
||||
|
||||
private val appScope = MainScope()
|
||||
|
|
@ -310,6 +317,8 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
|
|||
initWithConfigDependency(environmentConfig = environmentConfigStorage.initialize())
|
||||
}
|
||||
|
||||
abTestsManager.init()
|
||||
|
||||
appScope.launch {
|
||||
launch(Dispatchers.IO) {
|
||||
loadNativeLibraries()
|
||||
|
|
@ -385,6 +394,7 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
|
|||
userWalletsListRepository = userWalletsListRepository,
|
||||
tangemHotSdk = tangemHotSdk,
|
||||
hotWalletFeatureToggles = hotWalletFeatureToggles,
|
||||
trackingContextProxy = trackingContextProxy,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.utils.AnalyticsContextProxy
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.common.extensions.addContext
|
||||
import com.tangem.tap.common.extensions.eraseContext
|
||||
import com.tangem.tap.common.extensions.removeContext
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultAnalyticsContextProxy : AnalyticsContextProxy {
|
||||
|
||||
override fun setContext(scanResponse: ScanResponse) {
|
||||
Analytics.setContext(scanResponse)
|
||||
}
|
||||
|
||||
override fun eraseContext() {
|
||||
Analytics.eraseContext()
|
||||
}
|
||||
|
||||
override fun addContext(scanResponse: ScanResponse) {
|
||||
Analytics.addContext(scanResponse)
|
||||
}
|
||||
|
||||
override fun removeContext() {
|
||||
Analytics.removeContext()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.analytics.ChangeCardAnalyticsContextUseCase
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
|
||||
internal class DefaultChangeCardAnalyticsContextUseCase : ChangeCardAnalyticsContextUseCase {
|
||||
|
||||
override fun invoke(scanResponse: ScanResponse) {
|
||||
Analytics.setContext(scanResponse)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
import com.tangem.core.abtests.manager.ABTestsManager
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
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.extensions.addContext
|
||||
import com.tangem.tap.common.extensions.addHotWalletContext
|
||||
import com.tangem.tap.common.extensions.eraseContext
|
||||
import com.tangem.tap.common.extensions.removeContext
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
import com.tangem.tap.common.extensions.setHotWalletContext
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultTrackingContextProxy(private val abTestsManager: ABTestsManager) : TrackingContextProxy {
|
||||
|
||||
override fun setContext(scanResponse: ScanResponse) {
|
||||
val userWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build()
|
||||
|
||||
Analytics.setContext(userWalletId, scanResponse)
|
||||
|
||||
abTestsManager.setUserProperties(
|
||||
userId = calculateUserIdHash(userWalletId),
|
||||
batch = scanResponse.card.batchId,
|
||||
productType = scanResponse.productType.name,
|
||||
firmware = scanResponse.card.firmwareVersion.stringValue,
|
||||
)
|
||||
}
|
||||
|
||||
override fun setContext(userWallet: UserWallet) {
|
||||
Analytics.setContext(userWallet)
|
||||
|
||||
when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
setColdWalletUserProperties(userWallet)
|
||||
}
|
||||
is UserWallet.Hot -> {
|
||||
setHotWalletUserProperties(userWallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun addContext(userWallet: UserWallet) {
|
||||
Analytics.addContext(userWallet)
|
||||
}
|
||||
|
||||
override fun setHotWalletContext() {
|
||||
Analytics.setHotWalletContext()
|
||||
}
|
||||
|
||||
override fun eraseContext() {
|
||||
Analytics.eraseContext()
|
||||
abTestsManager.removeUserProperties()
|
||||
}
|
||||
|
||||
override fun addContext(scanResponse: ScanResponse) {
|
||||
Analytics.addContext(scanResponse)
|
||||
}
|
||||
|
||||
override fun addHotWalletContext() {
|
||||
Analytics.addHotWalletContext()
|
||||
}
|
||||
|
||||
override fun removeContext() {
|
||||
Analytics.removeContext()
|
||||
}
|
||||
|
||||
private fun calculateUserIdHash(userWalletId: UserWalletId?): String? {
|
||||
return userWalletId?.value
|
||||
?.calculateSha256()
|
||||
?.toHexString()
|
||||
}
|
||||
|
||||
private fun setColdWalletUserProperties(userWallet: UserWallet.Cold) {
|
||||
abTestsManager.setUserProperties(
|
||||
userId = calculateUserIdHash(userWallet.walletId),
|
||||
batch = userWallet.scanResponse.card.batchId,
|
||||
productType = userWallet.scanResponse.productType.name,
|
||||
firmware = userWallet.scanResponse.card.firmwareVersion.stringValue,
|
||||
)
|
||||
}
|
||||
|
||||
private fun setHotWalletUserProperties(userWallet: UserWallet.Hot) {
|
||||
abTestsManager.setUserProperties(
|
||||
userId = calculateUserIdHash(userWallet.walletId),
|
||||
batch = null,
|
||||
productType = "Mobile Wallet",
|
||||
firmware = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ class BlockchainApiExceptionEvent(
|
|||
selectedHost: String,
|
||||
exceptionHost: String,
|
||||
error: String,
|
||||
blockchain: String,
|
||||
) : AnalyticsEvent(
|
||||
category = "BlockchainSdk",
|
||||
event = "Exception",
|
||||
|
|
@ -13,5 +14,6 @@ class BlockchainApiExceptionEvent(
|
|||
AnalyticsParam.BLOCKCHAIN_SELECTED_HOST to selectedHost,
|
||||
AnalyticsParam.BLOCKCHAIN_EXCEPTION_HOST to exceptionHost,
|
||||
AnalyticsParam.ERROR_DESCRIPTION to error,
|
||||
AnalyticsParam.BLOCKCHAIN to blockchain,
|
||||
),
|
||||
)
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.common.analytics.handlers
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.ExceptionHandlerOutput
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.core.analytics.api.AnalyticsErrorHandler
|
||||
import com.tangem.tap.common.analytics.events.BlockchainApiExceptionEvent
|
||||
import javax.inject.Inject
|
||||
|
|
@ -8,12 +10,13 @@ import javax.inject.Inject
|
|||
class BlockchainExceptionHandler @Inject constructor(
|
||||
private val analyticsErrorHandler: AnalyticsErrorHandler,
|
||||
) : ExceptionHandlerOutput {
|
||||
override fun handleApiSwitch(currentHost: String, nextHost: String, message: String) {
|
||||
override fun handleApiSwitch(currentHost: String, nextHost: String, message: String, blockchain: Blockchain) {
|
||||
analyticsErrorHandler.sendErrorEvent(
|
||||
BlockchainApiExceptionEvent(
|
||||
selectedHost = nextHost,
|
||||
exceptionHost = currentHost,
|
||||
error = message,
|
||||
blockchain = blockchain.toNetworkId(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.tap.common.analytics.paramsInterceptor
|
||||
|
||||
import com.tangem.core.analytics.api.ParamsInterceptor
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
|
||||
class HotWalletContextInterceptor(
|
||||
val parent: ParamsInterceptor? = null,
|
||||
) : ParamsInterceptor {
|
||||
|
||||
override fun id(): String = HotWalletContextInterceptor.id()
|
||||
|
||||
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = true
|
||||
|
||||
override fun intercept(params: MutableMap<String, String>) {
|
||||
params[AnalyticsParam.PRODUCT_TYPE] = "Mobile Wallet"
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun id(): String = HotWalletContextInterceptor::class.java.simpleName
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
*/
|
||||
class LinkedCardContextInterceptor(
|
||||
scanResponse: ScanResponse,
|
||||
val parent: LinkedCardContextInterceptor? = null,
|
||||
val parent: ParamsInterceptor? = null,
|
||||
) : ParamsInterceptor {
|
||||
|
||||
private val contextInterceptor = CardContextInterceptor(scanResponse)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ 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.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.tap.common.analytics.paramsInterceptor.HotWalletContextInterceptor
|
||||
import com.tangem.tap.common.analytics.paramsInterceptor.LinkedCardContextInterceptor
|
||||
|
||||
/**
|
||||
|
|
@ -13,8 +15,7 @@ import com.tangem.tap.common.analytics.paramsInterceptor.LinkedCardContextInterc
|
|||
/**
|
||||
* Sets the new context
|
||||
*/
|
||||
fun Analytics.setContext(scanResponse: ScanResponse) {
|
||||
val userWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build()
|
||||
fun Analytics.setContext(userWalletId: UserWalletId?, scanResponse: ScanResponse) {
|
||||
if (userWalletId != null) {
|
||||
setUserId(userWalletId.stringValue)
|
||||
}
|
||||
|
|
@ -24,19 +25,46 @@ fun Analytics.setContext(scanResponse: 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))
|
||||
when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
removeParamsInterceptor(HotWalletContextInterceptor.id())
|
||||
addParamsInterceptor(LinkedCardContextInterceptor(userWallet.scanResponse))
|
||||
}
|
||||
is UserWallet.Hot -> {
|
||||
removeParamsInterceptor(LinkedCardContextInterceptor.id())
|
||||
addParamsInterceptor(HotWalletContextInterceptor())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Analytics.setHotWalletContext() {
|
||||
addParamsInterceptor(HotWalletContextInterceptor())
|
||||
}
|
||||
|
||||
/**
|
||||
* Erases the context
|
||||
*/
|
||||
fun Analytics.eraseContext() {
|
||||
clearUserId()
|
||||
removeParamsInterceptor(LinkedCardContextInterceptor.id())
|
||||
removeParamsInterceptor(HotWalletContextInterceptor.id())
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new context and keeps a previous context as the parent of the new one
|
||||
*/
|
||||
fun Analytics.addContext(userWallet: UserWallet) {
|
||||
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id())
|
||||
?: removeParamsInterceptor(HotWalletContextInterceptor.id())
|
||||
|
||||
val newContext = when (userWallet) {
|
||||
is UserWallet.Cold -> LinkedCardContextInterceptor(userWallet.scanResponse, parent = currentContext)
|
||||
is UserWallet.Hot -> HotWalletContextInterceptor(parent = currentContext)
|
||||
}
|
||||
|
||||
setUserId(userId = userWallet.walletId.stringValue)
|
||||
addParamsInterceptor(newContext)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -48,18 +76,32 @@ fun Analytics.addContext(scanResponse: ScanResponse) {
|
|||
setUserId(userWalletId.stringValue)
|
||||
}
|
||||
|
||||
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id()) as? LinkedCardContextInterceptor
|
||||
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id())
|
||||
?: removeParamsInterceptor(HotWalletContextInterceptor.id())
|
||||
val newContext = LinkedCardContextInterceptor(scanResponse, parent = currentContext)
|
||||
|
||||
addParamsInterceptor(newContext)
|
||||
}
|
||||
|
||||
fun Analytics.addHotWalletContext() {
|
||||
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id()) as? LinkedCardContextInterceptor
|
||||
val newContext = HotWalletContextInterceptor(currentContext)
|
||||
|
||||
addParamsInterceptor(newContext)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the current context and restores the previous one if it was present.
|
||||
*/
|
||||
fun Analytics.removeContext() {
|
||||
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id()) as? LinkedCardContextInterceptor
|
||||
val previousContext = currentContext?.parent ?: return
|
||||
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id())
|
||||
?: removeParamsInterceptor(HotWalletContextInterceptor.id())
|
||||
|
||||
val previousContext = when (currentContext) {
|
||||
is LinkedCardContextInterceptor -> currentContext.parent
|
||||
is HotWalletContextInterceptor -> currentContext.parent
|
||||
else -> null
|
||||
} ?: return
|
||||
|
||||
addParamsInterceptor(previousContext)
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
import java.util.Locale
|
||||
|
||||
// TODO: move extensions to utils
|
||||
fun BigDecimal.toFormattedString(
|
||||
decimals: Int,
|
||||
roundingMode: RoundingMode = RoundingMode.DOWN,
|
||||
locale: Locale = Locale.US,
|
||||
): String {
|
||||
val symbols = DecimalFormatSymbols(locale)
|
||||
val df = DecimalFormat()
|
||||
df.decimalFormatSymbols = symbols
|
||||
df.maximumFractionDigits = decimals
|
||||
df.minimumFractionDigits = 0
|
||||
df.isGroupingUsed = true
|
||||
df.roundingMode = roundingMode
|
||||
return df.format(this)
|
||||
}
|
||||
|
|
@ -54,8 +54,8 @@ private class CoilTimberLogger : Logger {
|
|||
|
||||
override fun log(tag: String, priority: Int, message: String?, throwable: Throwable?) {
|
||||
with(Timber.tag(COIL_LOG_TAG)) {
|
||||
throwable?.let { e -> e(e, message) }
|
||||
message?.let { msg -> d(msg) }
|
||||
if (throwable != null) e(throwable, message)
|
||||
if (message != null) d(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -54,8 +54,8 @@ class PushNotificationDelegate(private val context: Context) {
|
|||
.setContentIntent(pendingIntent)
|
||||
.setVibrate(vibratePattern)
|
||||
.apply {
|
||||
imageUrl?.let { uri ->
|
||||
val bitmap = getBitmapImageFromUrl(uri)
|
||||
if (imageUrl != null) {
|
||||
val bitmap = getBitmapImageFromUrl(imageUrl)
|
||||
setStyle(
|
||||
NotificationCompat
|
||||
.BigPictureStyle()
|
||||
|
|
@ -64,7 +64,10 @@ class PushNotificationDelegate(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val service = context.getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
val notificationManager = requireNotNull(service as? NotificationManager) {
|
||||
"NotificationManager not available"
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val notificationChannel = NotificationChannel(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.tap.features.welcome.redux.WelcomeReducer
|
|||
import com.tangem.tap.proxy.redux.DaggerGraphReducer
|
||||
import org.rekotlin.Action
|
||||
|
||||
@Suppress("CanBeNonNullable")
|
||||
fun appReducer(action: Action, state: AppState?): AppState {
|
||||
requireNotNull(state)
|
||||
if (action is AppAction.RestoreState) return action.state
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.flow.*
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
@Suppress("MemberNameEqualsClassName")
|
||||
internal object LegacyMiddleware {
|
||||
private val prepareDetailsScreenJobHolder = JobHolder()
|
||||
|
||||
|
|
@ -29,7 +30,14 @@ internal object LegacyMiddleware {
|
|||
val walletsRepository = store.inject(DaggerGraphState::walletsRepository)
|
||||
|
||||
selectedUserWallet()
|
||||
.distinctUntilChanged()
|
||||
.distinctUntilChanged { old, new ->
|
||||
if (old is UserWallet.Cold && new is UserWallet.Cold) {
|
||||
old.walletId == new.walletId &&
|
||||
old.scanResponse == new.scanResponse
|
||||
} else {
|
||||
old.walletId == new.walletId
|
||||
}
|
||||
}
|
||||
.onEach { selectedUserWallet ->
|
||||
val initializedAppSettingsStateContent = initializeAppSettingsState(
|
||||
shouldSaveUserWallets = walletsRepository.shouldSaveUserWalletsSync(),
|
||||
|
|
@ -78,6 +86,7 @@ internal object LegacyMiddleware {
|
|||
isHidingEnabled = store.inject(DaggerGraphState::balanceHidingRepository)
|
||||
.getBalanceHidingSettings().isHidingEnabledInSettings,
|
||||
needEnrollBiometrics = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull() == true,
|
||||
hasSecuredWallets = store.inject(DaggerGraphState::userWalletsListRepository).hasSecuredWallets(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -41,8 +41,8 @@ object SimpleCancelableAlertDialog {
|
|||
context: Context,
|
||||
): AlertDialog {
|
||||
return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply {
|
||||
setTitle(titleRes?.let { context.getString(it) } ?: title)
|
||||
setMessage(messageRes?.let { context.getString(it) } ?: message)
|
||||
setTitle(if (titleRes != null) context.getString(titleRes) else title)
|
||||
setMessage(if (messageRes != null) context.getString(messageRes) else message)
|
||||
setPositiveButton(context.getText(primaryButtonRes)) { _, _ -> primaryButtonAction() }
|
||||
if (secondaryButtonRes != null) {
|
||||
setNegativeButton(context.getText(secondaryButtonRes)) { _, _ -> secondaryButtonAction() }
|
||||
|
|
|
|||
|
|
@ -2,8 +2,13 @@ package com.tangem.tap.data
|
|||
|
||||
import android.content.Context
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getSyncOrNull
|
||||
import com.tangem.datasource.local.preferences.utils.store
|
||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.visa.model.VisaAuthTokens
|
||||
import com.tangem.sdk.storage.AndroidSecureStorageV2
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -11,7 +16,6 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.text.encodeToByteArray
|
||||
|
||||
private const val DEFAULT_KEY = "tangem_pay_default_key"
|
||||
private const val ORDER_ID_KEY = "tangem_pay_order_id_key"
|
||||
|
|
@ -19,7 +23,9 @@ private const val ORDER_ID_KEY = "tangem_pay_order_id_key"
|
|||
@Singleton
|
||||
internal class DefaultTangemPayStorage @Inject constructor(
|
||||
@ApplicationContext applicationContext: Context,
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
private val dispatcherProvider: CoroutineDispatcherProvider,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
) : TangemPayStorage {
|
||||
|
||||
private val secureStorage by lazy {
|
||||
|
|
@ -29,14 +35,21 @@ internal class DefaultTangemPayStorage @Inject constructor(
|
|||
name = "tangem_pay_storage",
|
||||
)
|
||||
}
|
||||
private val moshi by lazy {
|
||||
Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
|
||||
private val tokensAdapter by lazy { moshi.adapter(VisaAuthTokens::class.java) }
|
||||
|
||||
override suspend fun storeCustomerWalletAddress(userWalletId: UserWalletId, customerWalletAddress: String) {
|
||||
withContext(dispatcherProvider.io) {
|
||||
secureStorage.store(key = createCustomerAddressKey(userWalletId), value = customerWalletAddress)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getCustomerWalletAddress(userWalletId: UserWalletId): String? {
|
||||
return withContext(dispatcherProvider.io) {
|
||||
secureStorage.getAsString(createCustomerAddressKey(userWalletId))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun storeAuthTokens(customerWalletAddress: String, tokens: VisaAuthTokens) =
|
||||
withContext(dispatcherProvider.io) {
|
||||
val json = tokensAdapter.toJson(tokens)
|
||||
|
|
@ -67,14 +80,33 @@ internal class DefaultTangemPayStorage @Inject constructor(
|
|||
secureStorage.get(createOrderIdKey(customerWalletAddress))?.decodeToString(throwOnInvalidSequence = true)
|
||||
}
|
||||
|
||||
override suspend fun getAddToWalletDone(customerWalletAddress: String): Boolean {
|
||||
return withContext(dispatcherProvider.io) {
|
||||
appPreferencesStore.getSyncOrNull(
|
||||
key = PreferencesKeys.getTangemPayAddToWalletKey(customerWalletAddress),
|
||||
) == true
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun storeAddToWalletDone(customerWalletAddress: String, isDone: Boolean) {
|
||||
withContext(dispatcherProvider.io) {
|
||||
appPreferencesStore.store(PreferencesKeys.getTangemPayAddToWalletKey(customerWalletAddress), isDone)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun clearOrderId(customerWalletAddress: String) = withContext(dispatcherProvider.io) {
|
||||
secureStorage.delete(createOrderIdKey(customerWalletAddress))
|
||||
}
|
||||
|
||||
override suspend fun clearAll(customerWalletAddress: String) = withContext(dispatcherProvider.io) {
|
||||
secureStorage.delete(createKey(customerWalletAddress))
|
||||
secureStorage.delete(createOrderIdKey(customerWalletAddress))
|
||||
}
|
||||
override suspend fun clearAll(userWalletId: UserWalletId, customerWalletAddress: String) =
|
||||
withContext(dispatcherProvider.io) {
|
||||
secureStorage.delete(createCustomerAddressKey(userWalletId))
|
||||
secureStorage.delete(createKey(customerWalletAddress))
|
||||
secureStorage.delete(createOrderIdKey(customerWalletAddress))
|
||||
appPreferencesStore.store(PreferencesKeys.getTangemPayAddToWalletKey(customerWalletAddress), false)
|
||||
}
|
||||
|
||||
private fun createCustomerAddressKey(userWalletId: UserWalletId): String = userWalletId.stringValue
|
||||
|
||||
private fun createKey(address: String): String = "${DEFAULT_KEY}_$address"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,18 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||
import com.tangem.datasource.api.moonpay.MoonPayApi
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository
|
||||
import com.tangem.tap.network.exchangeServices.DefaultRampManager
|
||||
import com.tangem.tap.network.exchangeServices.SellService
|
||||
import com.tangem.tap.network.exchangeServices.moonpay.MoonPayService
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -43,17 +47,15 @@ internal object ActivityModule {
|
|||
@Singleton
|
||||
fun provideDefaultRampManager(
|
||||
appStateHolder: AppStateHolder,
|
||||
expressServiceLoader: ExpressServiceLoader,
|
||||
expressServiceFetcher: ExpressServiceFetcher,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): RampStateManager {
|
||||
return DefaultRampManager(
|
||||
sellService = Provider { requireNotNull(appStateHolder.sellService) },
|
||||
expressServiceLoader = expressServiceLoader,
|
||||
expressServiceFetcher = expressServiceFetcher,
|
||||
currenciesRepository = currenciesRepository,
|
||||
dispatchers = dispatchers,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -63,4 +65,19 @@ internal object ActivityModule {
|
|||
fun provideActivityDelayedWorkCoroutineScope(): CoroutineScope {
|
||||
return CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideExchangeService(
|
||||
environmentConfigStorage: EnvironmentConfigStorage,
|
||||
getSelectedWalletUseCase: GetSelectedWalletUseCase,
|
||||
moonPayApi: MoonPayApi,
|
||||
): SellService {
|
||||
return MoonPayService(
|
||||
api = moonPayApi,
|
||||
apiKeyProvider = Provider { environmentConfigStorage.getConfigSync().moonPayApiKey },
|
||||
secretKeyProvider = Provider { environmentConfigStorage.getConfigSync().moonPayApiSecretKey },
|
||||
userWalletProvider = { getSelectedWalletUseCase.sync().getOrNull() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,9 @@ import android.content.Context
|
|||
import android.os.Build
|
||||
import android.os.Vibrator
|
||||
import android.os.VibratorManager
|
||||
import com.tangem.tap.common.haptic.DefaultVibratorHapticManager
|
||||
import com.tangem.core.ui.haptic.TangemHapticEffect
|
||||
import com.tangem.core.ui.haptic.VibratorHapticManager
|
||||
import com.tangem.tap.common.haptic.DefaultVibratorHapticManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -22,10 +22,14 @@ class HapticModule {
|
|||
@Singleton
|
||||
fun provideHapticManager(@ApplicationContext context: Context): VibratorHapticManager {
|
||||
val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||
val service = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE)
|
||||
val vibratorManager = requireNotNull(service as? VibratorManager) {
|
||||
"VibratorManager not available"
|
||||
}
|
||||
vibratorManager.defaultVibrator
|
||||
} else {
|
||||
context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
val service = context.getSystemService(Context.VIBRATOR_SERVICE)
|
||||
requireNotNull(service as? Vibrator) { "Vibrator service not available" }
|
||||
}
|
||||
|
||||
return if (vibrator.hasVibrator()) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
|
|||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.impl.DefaultTangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.impl.MockTangemSdkManager
|
||||
import com.tangem.tap.domain.tasks.visa.TangemPayGenerateAddressAndSignChallengeTask
|
||||
import com.tangem.tap.domain.tasks.visa.VisaCardActivationTask
|
||||
import com.tangem.tap.domain.visa.VisaCardScanHandler
|
||||
import dagger.Module
|
||||
|
|
@ -27,6 +28,7 @@ internal class TangemSdkManagerModule {
|
|||
cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
visaCardScanHandler: VisaCardScanHandler,
|
||||
visaCardActivationTaskFactory: VisaCardActivationTask.Factory,
|
||||
tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory,
|
||||
onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
|
||||
): TangemSdkManager {
|
||||
return if (BuildConfig.MOCK_DATA_SOURCE) {
|
||||
|
|
@ -37,6 +39,7 @@ internal class TangemSdkManagerModule {
|
|||
resources = context.resources,
|
||||
visaCardScanHandler = visaCardScanHandler,
|
||||
visaCardActivationTaskFactory = visaCardActivationTaskFactory,
|
||||
tangemPayChallengeTaskFactory = tangemPayChallengeTaskFactory,
|
||||
onboardingV2FeatureToggles = onboardingV2FeatureToggles,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
package com.tangem.tap.di.analytics
|
||||
|
||||
import com.tangem.core.abtests.manager.ABTestsManager
|
||||
import com.tangem.core.analytics.AppInstanceIdProvider
|
||||
import com.tangem.core.analytics.utils.AnalyticsContextProxy
|
||||
import com.tangem.domain.analytics.ChangeCardAnalyticsContextUseCase
|
||||
import com.tangem.tap.common.analytics.DefaultAnalyticsContextProxy
|
||||
import com.tangem.tap.common.analytics.DefaultChangeCardAnalyticsContextUseCase
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.tap.common.analytics.DefaultTrackingContextProxy
|
||||
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAppInstanceIdProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -18,13 +17,8 @@ internal object AnalyticsModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideChangeCardAnalyticsContextUseCase(): ChangeCardAnalyticsContextUseCase {
|
||||
return DefaultChangeCardAnalyticsContextUseCase()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAnalyticsContextProxy(): AnalyticsContextProxy = DefaultAnalyticsContextProxy()
|
||||
fun provideAnalyticsContextProxy(abtestsManager: ABTestsManager): TrackingContextProxy =
|
||||
DefaultTrackingContextProxy(abtestsManager)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
|
|
|||
|
|
@ -3,8 +3,14 @@ package com.tangem.tap.di.domain
|
|||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.fetcher.SingleAccountListFetcher
|
||||
import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.account.status.usecase.ArchiveCryptoPortfolioUseCase
|
||||
import com.tangem.domain.account.status.usecase.RecoverCryptoPortfolioUseCase
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||
import com.tangem.domain.account.tokens.MainAccountTokensMigration
|
||||
import com.tangem.domain.account.usecase.*
|
||||
import com.tangem.feature.referral.data.ExternalReferralRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -32,17 +38,27 @@ internal object AccountDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideUpdateCryptoPortfolioUseCase(
|
||||
singleAccountListFetcher: SingleAccountListFetcher,
|
||||
accountsCRUDRepository: AccountsCRUDRepository,
|
||||
): UpdateCryptoPortfolioUseCase {
|
||||
return UpdateCryptoPortfolioUseCase(crudRepository = accountsCRUDRepository)
|
||||
return UpdateCryptoPortfolioUseCase(
|
||||
singleAccountListFetcher = singleAccountListFetcher,
|
||||
crudRepository = accountsCRUDRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideArchiveCryptoPortfolioUseCase(
|
||||
singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
accountsCRUDRepository: AccountsCRUDRepository,
|
||||
referralRepository: ExternalReferralRepository,
|
||||
): ArchiveCryptoPortfolioUseCase {
|
||||
return ArchiveCryptoPortfolioUseCase(crudRepository = accountsCRUDRepository)
|
||||
return ArchiveCryptoPortfolioUseCase(
|
||||
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
|
||||
crudRepository = accountsCRUDRepository,
|
||||
referralRepository = referralRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -50,10 +66,12 @@ internal object AccountDomainModule {
|
|||
fun provideRecoverCryptoPortfolioUseCase(
|
||||
accountsCRUDRepository: AccountsCRUDRepository,
|
||||
mainAccountTokensMigration: MainAccountTokensMigration,
|
||||
cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
||||
): RecoverCryptoPortfolioUseCase {
|
||||
return RecoverCryptoPortfolioUseCase(
|
||||
crudRepository = accountsCRUDRepository,
|
||||
mainAccountTokensMigration = mainAccountTokensMigration,
|
||||
cryptoCurrencyBalanceFetcher = cryptoCurrencyBalanceFetcher,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -82,4 +100,16 @@ internal object AccountDomainModule {
|
|||
accountsFeatureToggles = accountsFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideApplyAccountListSortingUseCase(
|
||||
accountsCRUDRepository: AccountsCRUDRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): ApplyAccountListSortingUseCase {
|
||||
return ApplyAccountListSortingUseCase(
|
||||
accountsCRUDRepository = accountsCRUDRepository,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ internal object CardDomainModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideIsDemoCardUseCase(): IsDemoCardUseCase = IsDemoCardUseCase(config = DemoConfig())
|
||||
fun provideIsDemoCardUseCase(): IsDemoCardUseCase = IsDemoCardUseCase(config = DemoConfig)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.hotwallet.GetAccessCodeSkippedUseCase
|
||||
import com.tangem.domain.hotwallet.SetAccessCodeSkippedUseCase
|
||||
import com.tangem.domain.hotwallet.repository.HotWalletRepository
|
||||
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 HotWalletDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetAccessCodeSkippedUseCase(hotWalletRepository: HotWalletRepository): GetAccessCodeSkippedUseCase {
|
||||
return GetAccessCodeSkippedUseCase(hotWalletRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSetAccessCodeSkippedUseCase(hotWalletRepository: HotWalletRepository): SetAccessCodeSkippedUseCase {
|
||||
return SetAccessCodeSkippedUseCase(hotWalletRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
|
||||
import com.tangem.domain.nft.*
|
||||
import com.tangem.domain.nft.repository.NFTRepository
|
||||
|
|
@ -24,9 +27,13 @@ internal object NFTDomainModule {
|
|||
fun providesGetNFTCollectionsUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
nftRepository: NFTRepository,
|
||||
singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
accountsFeatureToggles: AccountsFeatureToggles,
|
||||
): GetNFTCollectionsUseCase = GetNFTCollectionsUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
nftRepository = nftRepository,
|
||||
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
|
||||
accountsFeatureToggles = accountsFeatureToggles,
|
||||
)
|
||||
|
||||
@Provides
|
||||
|
|
@ -60,10 +67,12 @@ internal object NFTDomainModule {
|
|||
@Singleton
|
||||
fun providesGetNFTAvailableNetworksUseCase(
|
||||
nftRepository: NFTRepository,
|
||||
singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
): GetNFTNetworksUseCase = GetNFTNetworksUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
nftRepository = nftRepository,
|
||||
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
|
||||
)
|
||||
|
||||
@Provides
|
||||
|
|
@ -138,8 +147,15 @@ internal object NFTDomainModule {
|
|||
fun provideClearNFTCacheUseCase(
|
||||
nftRepository: NFTRepository,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
accountsFeatureToggles: AccountsFeatureToggles,
|
||||
singleAccountListSupplier: SingleAccountListSupplier,
|
||||
): ObserveAndClearNFTCacheIfNeedUseCase {
|
||||
return ObserveAndClearNFTCacheIfNeedUseCase(nftRepository, currenciesRepository)
|
||||
return ObserveAndClearNFTCacheIfNeedUseCase(
|
||||
nftRepository = nftRepository,
|
||||
currenciesRepository = currenciesRepository,
|
||||
accountsFeatureToggles = accountsFeatureToggles,
|
||||
singleAccountListSupplier = singleAccountListSupplier,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.news.repository.NewsRepository
|
||||
import com.tangem.domain.news.usecase.GetNewsCategoriesUseCase
|
||||
import com.tangem.domain.news.usecase.GetNewsListBatchFlowUseCase
|
||||
import com.tangem.domain.news.usecase.ObserveNewsDetailsUseCase
|
||||
import com.tangem.domain.news.usecase.ManageTrendingNewsUseCase
|
||||
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 NewsDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetNewsCategoriesUseCase(repository: NewsRepository): GetNewsCategoriesUseCase {
|
||||
return GetNewsCategoriesUseCase(repository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideObserveNewsDetailsUseCase(repository: NewsRepository): ObserveNewsDetailsUseCase {
|
||||
return ObserveNewsDetailsUseCase(repository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideObserveTrendingNewsUseCase(repository: NewsRepository): ManageTrendingNewsUseCase {
|
||||
return ManageTrendingNewsUseCase(repository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetNewsListBatchFlowUseCase(repository: NewsRepository): GetNewsListBatchFlowUseCase {
|
||||
return GetNewsListBatchFlowUseCase(repository)
|
||||
}
|
||||
}
|
||||
|
|
@ -179,20 +179,6 @@ internal object OnrampDomainModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetOnrampV2QuotesUseCase(
|
||||
settingsRepository: SettingsRepository,
|
||||
onrampRepository: OnrampRepository,
|
||||
onrampErrorResolver: OnrampErrorResolver,
|
||||
): GetOnrampV2QuotesUseCase {
|
||||
return GetOnrampV2QuotesUseCase(
|
||||
settingsRepository = settingsRepository,
|
||||
repository = onrampRepository,
|
||||
errorResolver = onrampErrorResolver,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetOnrampProviderWithQuoteUseCase(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.staking.*
|
||||
import com.tangem.domain.staking.repositories.StakingActionRepository
|
||||
import com.tangem.domain.staking.repositories.P2PEthPoolRepository
|
||||
import com.tangem.domain.staking.repositories.StakeKitActionRepository
|
||||
import com.tangem.domain.staking.repositories.StakingErrorResolver
|
||||
import com.tangem.domain.staking.repositories.StakeKitRepository
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
|
||||
import com.tangem.domain.staking.repositories.StakeKitTransactionHashRepository
|
||||
import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
|
||||
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
||||
import com.tangem.domain.staking.usecase.StakingApyFlowUseCase
|
||||
|
|
@ -17,6 +19,7 @@ import javax.inject.Singleton
|
|||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@Suppress("TooManyFunctions")
|
||||
internal object StakingDomainModule {
|
||||
|
||||
@Provides
|
||||
|
|
@ -34,11 +37,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingEntryInfoUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetStakingEntryInfoUseCase {
|
||||
return GetStakingEntryInfoUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -46,11 +49,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetYieldUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetYieldUseCase {
|
||||
return GetYieldUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -58,13 +61,13 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideFetchActionsUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakingActionRepository: StakingActionRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakeKitActionRepository: StakeKitActionRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): FetchActionsUseCase {
|
||||
return FetchActionsUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakingActionRepository = stakingActionRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakeKitActionRepository = stakeKitActionRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -72,11 +75,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetActionsUseCase(
|
||||
stakingActionRepository: StakingActionRepository,
|
||||
stakeKitActionRepository: StakeKitActionRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetActionsUseCase {
|
||||
return GetActionsUseCase(
|
||||
stakingActionRepository = stakingActionRepository,
|
||||
stakeKitActionRepository = stakeKitActionRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -84,11 +87,25 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingTokensUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): FetchStakingTokensUseCase {
|
||||
return FetchStakingTokensUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFetchStakingOptionsUseCase(
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
p2pRepository: P2PEthPoolRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): FetchStakingOptionsUseCase {
|
||||
return FetchStakingOptionsUseCase(
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
p2pRepository = p2pRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -108,11 +125,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingTransactionsUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetStakingTransactionsUseCase {
|
||||
return GetStakingTransactionsUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -120,11 +137,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGasEstimateUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): EstimateGasUseCase {
|
||||
return EstimateGasUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -132,11 +149,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideSubmitHashUseCase(
|
||||
stakingTransactionHashRepository: StakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): SubmitHashUseCase {
|
||||
return SubmitHashUseCase(
|
||||
stakingTransactionHashRepository = stakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository = stakeKitTransactionHashRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -144,11 +161,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideSaveUnsubmittedHashUseCase(
|
||||
stakingTransactionHashRepository: StakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): SaveUnsubmittedHashUseCase {
|
||||
return SaveUnsubmittedHashUseCase(
|
||||
stakingTransactionHashRepository = stakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository = stakeKitTransactionHashRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -166,11 +183,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideSendUnsubmittedHashesUseCase(
|
||||
stakingTransactionHashRepository: StakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository: StakeKitTransactionHashRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): SendUnsubmittedHashesUseCase {
|
||||
return SendUnsubmittedHashesUseCase(
|
||||
stakingTransactionHashRepository = stakingTransactionHashRepository,
|
||||
stakeKitTransactionHashRepository = stakeKitTransactionHashRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -178,11 +195,11 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGetConstructedStakingTransactionUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingErrorResolver: StakingErrorResolver,
|
||||
): GetConstructedStakingTransactionUseCase {
|
||||
return GetConstructedStakingTransactionUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
stakeKitRepository = stakeKitRepository,
|
||||
stakingErrorResolver = stakingErrorResolver,
|
||||
)
|
||||
}
|
||||
|
|
@ -222,9 +239,9 @@ internal object StakingDomainModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingApyFlowUseCase(
|
||||
stakingRepository: StakingRepository,
|
||||
stakeKitRepository: StakeKitRepository,
|
||||
stakingFeatureToggles: StakingFeatureToggles,
|
||||
): StakingApyFlowUseCase {
|
||||
return StakingApyFlowUseCase(stakingRepository, stakingFeatureToggles)
|
||||
return StakingApyFlowUseCase(stakeKitRepository, stakingFeatureToggles)
|
||||
}
|
||||
}
|
||||
|
|
@ -20,10 +20,7 @@ import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
|
|||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
|
||||
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.TokenReceiveWarningsViewedRepository
|
||||
import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository
|
||||
import com.tangem.domain.tokens.repository.*
|
||||
import com.tangem.domain.tokens.wallet.WalletBalanceFetcher
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.tap.domain.tokens.DefaultTokensFeatureToggles
|
||||
|
|
@ -59,24 +56,6 @@ internal object TokensDomainModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFetchTokenListUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
stakingIdFactory: StakingIdFactory,
|
||||
): FetchTokenListUseCase {
|
||||
return FetchTokenListUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
|
||||
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFetchPendingTransactionsUseCase(
|
||||
|
|
@ -183,24 +162,6 @@ internal object TokensDomainModule {
|
|||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFetchCardTokenListUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
stakingIdFactory: StakingIdFactory,
|
||||
): FetchCardTokenListUseCase {
|
||||
return FetchCardTokenListUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
|
||||
multiYieldBalanceFetcher = multiYieldBalanceFetcher,
|
||||
stakingIdFactory = stakingIdFactory,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetCryptoCurrencyUseCase(
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ internal object TransactionDomainModule {
|
|||
fun provideGetFeeUseCase(walletManagersFacade: WalletManagersFacade): GetFeeUseCase {
|
||||
return GetFeeUseCase(
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
demoConfig = DemoConfig(),
|
||||
demoConfig = DemoConfig,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ internal object TransactionDomainModule {
|
|||
dispatchers: CoroutineDispatcherProvider,
|
||||
): SendTransactionUseCase {
|
||||
return SendTransactionUseCase(
|
||||
demoConfig = DemoConfig(),
|
||||
demoConfig = DemoConfig,
|
||||
cardSdkConfigRepository = cardSdkConfigRepository,
|
||||
transactionRepository = transactionRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
|
|
@ -131,7 +131,7 @@ internal object TransactionDomainModule {
|
|||
fun provideEstimateFeeUseCase(walletManagersFacade: WalletManagersFacade): EstimateFeeUseCase {
|
||||
return EstimateFeeUseCase(
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
demoConfig = DemoConfig(),
|
||||
demoConfig = DemoConfig,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -166,10 +166,34 @@ internal object WalletsDomainModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesUnlockWalletUseCase(userWalletsListManager: UserWalletsListManager): UnlockWalletsUseCase {
|
||||
fun providesUnlockWalletsUseCase(userWalletsListManager: UserWalletsListManager): UnlockWalletsUseCase {
|
||||
return UnlockWalletsUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesUnlockWalletUseCase(
|
||||
nonBiometricUnlockWalletUseCase: NonBiometricUnlockWalletUseCase,
|
||||
userWalletsListRepository: UserWalletsListRepository,
|
||||
): UnlockWalletUseCase {
|
||||
return UnlockWalletUseCase(
|
||||
nonBiometricUnlockWalletUseCase = nonBiometricUnlockWalletUseCase,
|
||||
userWalletsListRepository = userWalletsListRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesNonBiometricUnlockWalletUseCase(
|
||||
userWalletsListRepository: UserWalletsListRepository,
|
||||
walletsRepository: WalletsRepository,
|
||||
): NonBiometricUnlockWalletUseCase {
|
||||
return NonBiometricUnlockWalletUseCase(
|
||||
userWalletsListRepository = userWalletsListRepository,
|
||||
walletsRepository = walletsRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesSelectWalletUseCase(
|
||||
|
|
@ -483,4 +507,18 @@ internal object WalletsDomainModule {
|
|||
dispatchers = dispatcherProvider,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideHasSecuredWalletsUseCase(
|
||||
userWalletsListRepository: UserWalletsListRepository,
|
||||
): HasSecuredWalletsUseCase {
|
||||
return HasSecuredWalletsUseCase(userWalletsListRepository = userWalletsListRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSyncWalletWithRemoteUseCase(walletsRepository: WalletsRepository): SyncWalletWithRemoteUseCase {
|
||||
return SyncWalletWithRemoteUseCase(walletsRepository = walletsRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,11 +2,11 @@ package com.tangem.tap.domain
|
|||
|
||||
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.models.wallet.UserWallet
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
|
||||
|
|
@ -30,11 +30,12 @@ class TapWalletManager(
|
|||
// ensuring that only one job is active at any given time.
|
||||
loadUserWalletDataJob = CoroutineScope(dispatchers.io)
|
||||
.launch { loadUserWalletData(userWallet) }
|
||||
.also { it.join() }
|
||||
.apply { join() }
|
||||
}
|
||||
|
||||
private suspend fun loadUserWalletData(userWallet: UserWallet) {
|
||||
Analytics.setContext(userWallet)
|
||||
val trackingContextProxy = store.inject(DaggerGraphState::trackingContextProxy)
|
||||
trackingContextProxy.setContext(userWallet)
|
||||
|
||||
if (userWallet is UserWallet.Cold) {
|
||||
val scanResponse = userWallet.scanResponse
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
|
|||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.R
|
||||
|
|
@ -47,6 +48,7 @@ import javax.inject.Singleton
|
|||
internal class LegacyScanProcessor @Inject constructor(
|
||||
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val trackingContextProxy: TrackingContextProxy,
|
||||
) {
|
||||
|
||||
suspend fun scan(
|
||||
|
|
@ -118,16 +120,14 @@ internal class LegacyScanProcessor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun sendAnalytics(analyticsEvent: AnalyticsEvent?, scanResponse: ScanResponse) {
|
||||
analyticsEvent?.let { event ->
|
||||
// this workaround needed to send CardWasScannedEvent without adding a context
|
||||
val interceptor = CardContextInterceptor(scanResponse)
|
||||
val params = event.params.toMutableMap()
|
||||
interceptor.intercept(params)
|
||||
event.params = params.toMap()
|
||||
private fun sendAnalytics(analyticsEvent: AnalyticsEvent, scanResponse: ScanResponse) {
|
||||
// this workaround needed to send CardWasScannedEvent without adding a context
|
||||
val interceptor = CardContextInterceptor(scanResponse)
|
||||
val params = analyticsEvent.params.toMutableMap()
|
||||
interceptor.intercept(params)
|
||||
analyticsEvent.params = params.toMap()
|
||||
|
||||
Analytics.send(event)
|
||||
}
|
||||
Analytics.send(analyticsEvent)
|
||||
}
|
||||
|
||||
// TODO: [REDACTED_JIRA]
|
||||
|
|
@ -211,7 +211,7 @@ internal class LegacyScanProcessor @Inject constructor(
|
|||
},
|
||||
) {
|
||||
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
|
||||
Analytics.addContext(scanResponse)
|
||||
trackingContextProxy.addContext(scanResponse)
|
||||
onWalletNotCreated()
|
||||
navigateTo(
|
||||
AppRoute.Onboarding(
|
||||
|
|
@ -220,7 +220,7 @@ internal class LegacyScanProcessor @Inject constructor(
|
|||
),
|
||||
) { onProgressStateChange(it) }
|
||||
} else {
|
||||
Analytics.setContext(scanResponse)
|
||||
trackingContextProxy.setContext(scanResponse)
|
||||
|
||||
val wasTwinsOnboardingShown =
|
||||
store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase).invokeSync()
|
||||
|
|
|
|||
|
|
@ -3,15 +3,12 @@ package com.tangem.tap.domain.scanCard.chains
|
|||
import arrow.core.left
|
||||
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.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
|
||||
import com.tangem.tap.common.extensions.addContext
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
|
|
@ -34,9 +31,11 @@ class CheckForOnboardingChain(
|
|||
) : ResultChain<ScanCardException, ScanResponse>() {
|
||||
|
||||
override suspend fun launch(previousChainResult: ScanResponse): ScanChainResult {
|
||||
val trackingContextProxy = store.inject(DaggerGraphState::trackingContextProxy)
|
||||
|
||||
return when {
|
||||
OnboardingHelper.isOnboardingCase(previousChainResult) -> {
|
||||
Analytics.addContext(previousChainResult)
|
||||
trackingContextProxy.addContext(previousChainResult)
|
||||
ScanChainException.OnboardingNeeded(
|
||||
AppRoute.Onboarding(
|
||||
scanResponse = previousChainResult,
|
||||
|
|
@ -45,7 +44,7 @@ class CheckForOnboardingChain(
|
|||
).left()
|
||||
}
|
||||
else -> {
|
||||
Analytics.setContext(previousChainResult)
|
||||
trackingContextProxy.setContext(previousChainResult)
|
||||
|
||||
val wasTwinsOnboardingShown = store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase)
|
||||
.invokeSync()
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@ import com.tangem.core.res.getStringSafe
|
|||
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.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
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.*
|
||||
import com.tangem.domain.wallets.derivations.derivationStyleProvider
|
||||
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
|
||||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
|
|
@ -44,6 +44,7 @@ import com.tangem.tap.domain.tasks.product.CreateProductWalletTask
|
|||
import com.tangem.tap.domain.tasks.product.ResetBackupCardTask
|
||||
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanProductTask
|
||||
import com.tangem.tap.domain.tasks.visa.TangemPayGenerateAddressAndSignChallengeTask
|
||||
import com.tangem.tap.domain.tasks.visa.VisaCardActivationTask
|
||||
import com.tangem.tap.domain.tasks.visa.VisaCustomerWalletApproveTask
|
||||
import com.tangem.tap.domain.twins.CreateFirstTwinWalletTask
|
||||
|
|
@ -62,6 +63,7 @@ internal class DefaultTangemSdkManager(
|
|||
private val resources: Resources,
|
||||
private val visaCardScanHandler: VisaCardScanHandler,
|
||||
private val visaCardActivationTaskFactory: VisaCardActivationTask.Factory,
|
||||
private val tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory,
|
||||
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
|
||||
) : TangemSdkManager {
|
||||
|
||||
|
|
@ -76,13 +78,12 @@ internal class DefaultTangemSdkManager(
|
|||
secureStorage = tangemSdk.secureStorage,
|
||||
)
|
||||
}
|
||||
override val needEnrollBiometrics: Boolean
|
||||
get() = tangemSdk.authenticationManager.needEnrollBiometrics
|
||||
|
||||
override val canUseBiometry: Boolean
|
||||
get() = tangemSdk.authenticationManager.canAuthenticate || needEnrollBiometrics
|
||||
|
||||
override val needEnrollBiometrics: Boolean
|
||||
get() = tangemSdk.authenticationManager.needEnrollBiometrics
|
||||
|
||||
override val keystoreManager: KeystoreManager
|
||||
get() = tangemSdk.keystoreManager
|
||||
|
||||
|
|
@ -511,6 +512,18 @@ internal class DefaultTangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun tangemPayProduceInitialCredentials(
|
||||
cardId: String,
|
||||
): CompletionResult<TangemPayInitialCredentials> {
|
||||
return coroutineScope {
|
||||
runTaskAsyncReturnOnMain(
|
||||
runnable = tangemPayChallengeTaskFactory.create(coroutineScope = this),
|
||||
cardId = cardId,
|
||||
initialMessage = Message(resources.getStringSafe(R.string.initial_message_tap_header)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -213,5 +213,11 @@ class MockTangemSdkManager(
|
|||
error("Not implemented")
|
||||
}
|
||||
|
||||
override suspend fun tangemPayProduceInitialCredentials(
|
||||
cardId: String,
|
||||
): CompletionResult<TangemPayInitialCredentials> {
|
||||
error("Not implemented")
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.content
|
||||
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.card.*
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.CardWallet
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.card.EncryptionMode
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
|
|
@ -125,10 +129,18 @@ object WalletMockContent : MockContent {
|
|||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
),
|
||||
DerivationPath("m/1852'/1815'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
),
|
||||
DerivationPath("m/44'/144'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
),
|
||||
DerivationPath("m/44'/501'/0'") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
),
|
||||
),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5),
|
||||
|
|
@ -235,6 +247,20 @@ object WalletMockContent : MockContent {
|
|||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/1729'/0'/0'") to ExtendedPublicKey( // Tezos
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/111111'/0'/0/0") to ExtendedPublicKey( // Kaspa
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
ByteArrayKey(
|
||||
|
|
@ -244,14 +270,49 @@ object WalletMockContent : MockContent {
|
|||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/1852'/1815'/0'/0/0") to ExtendedPublicKey( // cardano
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/1852'/1815'/0'/2/0") to ExtendedPublicKey( // cardano extended
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/501'/0'") to ExtendedPublicKey( // Solana
|
||||
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),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/354'/0'/0'/0'") to ExtendedPublicKey( // Polkadot
|
||||
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),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/434'/0'/0'/0'") to ExtendedPublicKey( // Kusama
|
||||
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),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/643'/0'/0'/0'") to ExtendedPublicKey( // Azero
|
||||
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),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/148'/0'") to ExtendedPublicKey( // Stellar
|
||||
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),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
|
|
@ -296,9 +357,16 @@ object WalletMockContent : MockContent {
|
|||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // xrp
|
||||
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),
|
||||
DerivationPath("m/44'/1729'/0'/0'") to ExtendedPublicKey( // Tezos
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/111111'/0'/0/0") to ExtendedPublicKey( // Kaspa
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
|
|
@ -312,19 +380,54 @@ object WalletMockContent : MockContent {
|
|||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/1852'/1815'/0'/0/0") to ExtendedPublicKey( // cardano
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/1852'/1815'/0'/2/0") to ExtendedPublicKey( // cardano extended
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/501'/0'") to ExtendedPublicKey( // Solana
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/354'/0'/0'/0'") to ExtendedPublicKey( // Polkadot
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/434'/0'/0'/0'") to ExtendedPublicKey( // Kusama
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/643'/0'/0'/0'") to ExtendedPublicKey( // Azero
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/148'/0'") to ExtendedPublicKey( // Stellar
|
||||
publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62),
|
||||
chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -14,10 +14,11 @@ import com.tangem.common.map
|
|||
import com.tangem.crypto.bip39.Mnemonic
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
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.demo.models.DemoConfig
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.wallets.derivations.DerivationStyleProvider
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.backup.StartPrimaryCardLinkingCommand
|
||||
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
|
||||
|
|
@ -60,7 +61,13 @@ class CreateProductWalletTask(
|
|||
val cardDto = CardDTO(card)
|
||||
|
||||
val commandProcessor = when {
|
||||
cardTypesResolver.isTangemNote() -> CreateWalletTangemNote(cardTypesResolver)
|
||||
/**
|
||||
* @workaround isDemoNoteAsMultiwallet
|
||||
* There were produced 20k Note demo cards that should work like multiwallet (except Onboarding)
|
||||
* for that reasons we've just added some specific checks for their BatchId
|
||||
*/
|
||||
DemoConfig.isDemoNoteAsMultiwallet(card.cardId) || cardTypesResolver.isTangemNote() ->
|
||||
CreateWalletTangemNote(cardTypesResolver)
|
||||
cardTypesResolver.isTangemTwins() ->
|
||||
throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet")
|
||||
|
||||
|
|
@ -374,7 +381,7 @@ private class CreateWalletTangemWallet(
|
|||
|
||||
private fun getBlockchains(cardId: String, card: CardDTO): List<Blockchain> {
|
||||
return when {
|
||||
DemoHelper.isDemoCardId(cardId) -> DemoHelper.config.demoBlockchains.toList()
|
||||
DemoHelper.isDemoCardId(cardId) -> DemoHelper.config.getDemoBlockchains(cardId).toList()
|
||||
card.isTestCard -> listOf(Blockchain.BitcoinTestnet, Blockchain.EthereumTestnet)
|
||||
else -> listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ internal class DerivationsFinder(
|
|||
getBlockchains(userWalletId)
|
||||
}.ifEmpty {
|
||||
if (DemoHelper.isDemoCardId(card.cardId)) {
|
||||
getDemoBlockchains(derivationStyle)
|
||||
getDemoBlockchains(derivationStyle, card.cardId)
|
||||
} else {
|
||||
getDefaultBlockchains(derivationStyle)
|
||||
}
|
||||
|
|
@ -77,8 +77,8 @@ internal class DerivationsFinder(
|
|||
}
|
||||
|
||||
// TODO: Move to user wallet config
|
||||
private fun getDemoBlockchains(derivationStyle: DerivationStyle?): MutableSet<BlockchainToDerive> {
|
||||
return DemoHelper.config.demoBlockchains.mapToBlockchainsWithDerivations(derivationStyle)
|
||||
private fun getDemoBlockchains(derivationStyle: DerivationStyle?, cardId: String): MutableSet<BlockchainToDerive> {
|
||||
return DemoHelper.config.getDemoBlockchains(cardId).mapToBlockchainsWithDerivations(derivationStyle)
|
||||
}
|
||||
|
||||
// TODO: Move to user wallet config
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import com.tangem.tap.domain.tasks.UserWalletIdPreflightReadFilter
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
// TODO remove it after test after resolve [REDACTED_JIRA]
|
||||
internal class ResetBackupCardTask(
|
||||
private val userWalletId: UserWalletId,
|
||||
) : CardSessionRunnable<Boolean> {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
package com.tangem.tap.domain.tasks.visa
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.CardWallet
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.CompletionCallback
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.error.ext.tangemError
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.card.common.visa.VisaUtilities
|
||||
import com.tangem.domain.visa.datasource.VisaAuthRemoteDataSource
|
||||
import com.tangem.domain.visa.error.VisaActivationError
|
||||
import com.tangem.domain.visa.model.TangemPayInitialCredentials
|
||||
import com.tangem.domain.visa.model.VisaDataToSignByCustomerWallet
|
||||
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
|
||||
import com.tangem.domain.visa.model.sign
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class TangemPayGenerateAddressAndSignChallengeTask @AssistedInject constructor(
|
||||
@Assisted private val coroutineScope: CoroutineScope,
|
||||
private val dispatchersProvider: CoroutineDispatcherProvider,
|
||||
private val visaAuthRemoteDataSource: VisaAuthRemoteDataSource,
|
||||
) : CardSessionRunnable<TangemPayInitialCredentials> {
|
||||
|
||||
override fun run(session: CardSession, callback: CompletionCallback<TangemPayInitialCredentials>) {
|
||||
coroutineScope.launch {
|
||||
callback(runSuspend(session = session))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun runSuspend(session: CardSession): CompletionResult<TangemPayInitialCredentials> {
|
||||
val card = session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
|
||||
val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }
|
||||
?: return CompletionResult.Failure(VisaActivationError.MissingWallet.tangemError)
|
||||
|
||||
val derivationResult = runDerivationTask(session, wallet)
|
||||
val address = when (derivationResult) {
|
||||
is CompletionResult.Failure<*> -> return CompletionResult.Failure(derivationResult.error)
|
||||
is CompletionResult.Success<ExtendedPublicKey> -> generateAddressFromExtendedKey(derivationResult.data)
|
||||
}
|
||||
|
||||
val challenge = withContext(dispatchersProvider.io) {
|
||||
visaAuthRemoteDataSource.getCustomerWalletAuthChallenge(address)
|
||||
}.getOrElse { return CompletionResult.Failure(it.tangemError) }
|
||||
|
||||
val dataToSign = VisaDataToSignByCustomerWallet(hashToSign = challenge.challenge)
|
||||
val approveResult = runVisaCustomerWalletApproveTask(
|
||||
session = session,
|
||||
cardId = card.cardId,
|
||||
targetAddress = address,
|
||||
dataToSign = dataToSign,
|
||||
)
|
||||
val signedData = when (approveResult) {
|
||||
is CompletionResult.Failure<*> -> return CompletionResult.Failure(approveResult.error)
|
||||
is CompletionResult.Success<VisaSignedDataByCustomerWallet> -> approveResult.data
|
||||
}
|
||||
|
||||
val authTokens = withContext(dispatchersProvider.io) {
|
||||
visaAuthRemoteDataSource.getTokenWithCustomerWallet(
|
||||
sessionId = challenge.session.sessionId,
|
||||
signature = signedData.signature,
|
||||
nonce = signedData.dataToSign.hashToSign,
|
||||
)
|
||||
}.getOrNull() ?: return CompletionResult.Failure(VisaActivationError.FailedRemoteState.tangemError)
|
||||
|
||||
return CompletionResult.Success(
|
||||
data = TangemPayInitialCredentials(
|
||||
customerWalletAddress = address,
|
||||
authTokens = authTokens,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun runDerivationTask(
|
||||
session: CardSession,
|
||||
wallet: CardWallet,
|
||||
): CompletionResult<ExtendedPublicKey> {
|
||||
val deferred = CompletableDeferred<CompletionResult<ExtendedPublicKey>>()
|
||||
val derivationTask = DeriveWalletPublicKeyTask(
|
||||
walletPublicKey = wallet.publicKey,
|
||||
derivationPath = VisaUtilities.customDerivationPath,
|
||||
)
|
||||
|
||||
derivationTask.run(session = session, callback = deferred::complete)
|
||||
return deferred.await()
|
||||
}
|
||||
|
||||
private suspend fun runVisaCustomerWalletApproveTask(
|
||||
session: CardSession,
|
||||
cardId: String,
|
||||
targetAddress: String,
|
||||
dataToSign: VisaDataToSignByCustomerWallet,
|
||||
): CompletionResult<VisaSignedDataByCustomerWallet> {
|
||||
val deferred = CompletableDeferred<CompletionResult<VisaSignedDataByCustomerWallet>>()
|
||||
val task = VisaCustomerWalletApproveTask(
|
||||
visaDataForApprove = VisaCustomerWalletApproveTask.Input(
|
||||
cardId = cardId,
|
||||
targetAddress = targetAddress,
|
||||
hashToSign = dataToSign.hashToSign,
|
||||
sign = dataToSign::sign,
|
||||
),
|
||||
)
|
||||
task.run(session = session, callback = deferred::complete)
|
||||
return deferred.await()
|
||||
}
|
||||
|
||||
private fun generateAddressFromExtendedKey(extendedPublicKey: ExtendedPublicKey): String {
|
||||
val derivationData = VisaUtilities.visaBlockchain.makeAddressesFromExtendedPublicKey(
|
||||
extendedPublicKey = extendedPublicKey,
|
||||
cachedIndex = null,
|
||||
)
|
||||
return derivationData.address
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(coroutineScope: CoroutineScope): TangemPayGenerateAddressAndSignChallengeTask
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +177,7 @@ class VisaCardActivationTask @AssistedInject constructor(
|
|||
.getOrElse { raise(it.tangemError) }
|
||||
|
||||
if (remoteState !is VisaActivationRemoteState.CardWalletSignatureRequired) {
|
||||
return raise(VisaActivationError.WrongRemoteState.tangemError)
|
||||
raise(VisaActivationError.WrongRemoteState.tangemError)
|
||||
}
|
||||
|
||||
visaActivationRepository.getCardWalletAcceptanceData(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ 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.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
|
||||
|
|
@ -59,21 +58,7 @@ class VisaCustomerWalletApproveTask(
|
|||
session: CardSession,
|
||||
callback: CompletionCallback<VisaSignedDataByCustomerWallet>,
|
||||
) {
|
||||
val cardDTO = CardDTO(card)
|
||||
|
||||
val derivationStyle = cardDTO.derivationStyleProvider.getDerivationStyle() ?: run {
|
||||
proceedApproveWithLegacyCard(
|
||||
card = card,
|
||||
session = session,
|
||||
callback = callback,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val derivationPath = VisaUtilities.visaDefaultDerivationPath(derivationStyle) ?: run {
|
||||
callback(CompletionResult.Failure(VisaActivationError.FailedToCreateAddress.tangemError))
|
||||
return
|
||||
}
|
||||
val derivationPath = VisaUtilities.customDerivationPath
|
||||
|
||||
val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: run {
|
||||
callback(CompletionResult.Failure(VisaActivationError.MissingWallet.tangemError))
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.tangem.domain.visa.model.VisaCardActivationStatus
|
|||
import com.tangem.domain.wallets.hot.HotWalletAccessCodeAttemptsRepository
|
||||
import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.hot.sdk.TangemHotSdk
|
||||
import com.tangem.sdk.storage.AndroidSecureStorage
|
||||
import com.tangem.sdk.storage.AndroidSecureStorageV2
|
||||
import com.tangem.sdk.storage.createEncryptedSharedPreferences
|
||||
|
|
@ -122,6 +123,7 @@ internal object UserWalletsListManagerModule {
|
|||
passwordRequester: HotWalletPasswordRequester,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
hotWalletAccessCodeAttemptsRepository: HotWalletAccessCodeAttemptsRepository,
|
||||
tangemHotSdk: TangemHotSdk,
|
||||
): UserWalletsListRepository {
|
||||
val moshi = buildMoshi()
|
||||
val secureStorage = buildSecureStorage(applicationContext = applicationContext)
|
||||
|
|
@ -168,6 +170,7 @@ internal object UserWalletsListManagerModule {
|
|||
appPreferencesStore = appPreferencesStore,
|
||||
savePersistentInformation = ProviderSuspend { true }, // Always save persistent information for now
|
||||
hotWalletAccessCodeAttemptsRepository = hotWalletAccessCodeAttemptsRepository,
|
||||
tangemHotSdk = tangemHotSdk,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,11 +36,6 @@ internal class BiometricUserWalletsListManager(
|
|||
.mapLatest { it.userWallets }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override val savedWalletsCount: Flow<Int>
|
||||
get() = state
|
||||
.mapLatest { walletsCount }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override val userWalletsSync: List<UserWallet>
|
||||
get() = state.value.userWallets
|
||||
|
||||
|
|
@ -71,6 +66,11 @@ internal class BiometricUserWalletsListManager(
|
|||
override val walletsCount: Int
|
||||
get() = state.value.userWallets.size
|
||||
|
||||
override val savedWalletsCount: Flow<Int>
|
||||
get() = state
|
||||
.mapLatest { walletsCount }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override suspend fun unlock(type: UnlockType): CompletionResult<UserWallet> {
|
||||
return unlockAndSetSelectedUserWallet(type)
|
||||
.mapFailure { error ->
|
||||
|
|
|
|||
|
|
@ -20,11 +20,6 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
|
|||
.mapLatest { listOfNotNull(it.userWallet) }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override val savedWalletsCount: Flow<Int>
|
||||
get() = state
|
||||
.mapLatest { walletsCount }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override val selectedUserWallet: Flow<UserWallet>
|
||||
get() = state
|
||||
.mapLatest { it.userWallet }
|
||||
|
|
@ -46,6 +41,11 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
|
|||
override val walletsCount: Int
|
||||
get() = if (hasUserWallets) 1 else 0
|
||||
|
||||
override val savedWalletsCount: Flow<Int>
|
||||
get() = state
|
||||
.mapLatest { walletsCount }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> = catching {
|
||||
state.value.userWallet
|
||||
?.takeIf { it.walletId == userWalletId }
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import com.tangem.domain.wallets.R
|
|||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.hot.HotWalletAccessCodeAttemptsRepository
|
||||
import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
|
||||
import com.tangem.hot.sdk.TangemHotSdk
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey
|
||||
|
|
@ -27,6 +28,8 @@ import com.tangem.tap.domain.userWalletList.utils.toUserWallets
|
|||
import com.tangem.tap.domain.userWalletList.utils.updateWith
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.ProviderSuspend
|
||||
import com.tangem.utils.coroutines.runSuspendCatching
|
||||
import com.tangem.utils.extensions.addOrReplace
|
||||
import com.tangem.utils.extensions.indexOfFirstOrNull
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
|
@ -44,6 +47,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
private val savePersistentInformation: ProviderSuspend<Boolean>,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val hotWalletAccessCodeAttemptsRepository: HotWalletAccessCodeAttemptsRepository,
|
||||
private val tangemHotSdk: TangemHotSdk,
|
||||
) : UserWalletsListRepository {
|
||||
|
||||
override val userWallets = MutableStateFlow<List<UserWallet>?>(null)
|
||||
|
|
@ -119,14 +123,19 @@ internal class DefaultUserWalletsListRepository(
|
|||
}
|
||||
}
|
||||
|
||||
val oldUserWallet = userWalletsSync().find { it.walletId == userWallet.walletId }
|
||||
|
||||
if (oldUserWallet != null) {
|
||||
checkForUpgradeAndDeleteHotWalletIfNeeded(
|
||||
newUserWallet = userWallet,
|
||||
oldUserWallet = oldUserWallet,
|
||||
)
|
||||
}
|
||||
|
||||
// update the userWallets state and add if it doesn't exist
|
||||
updateWallets { currentWallets ->
|
||||
val wallets = currentWallets.orEmpty()
|
||||
if (wallets.any { it.walletId == userWallet.walletId }) {
|
||||
wallets.map { if (it.walletId == userWallet.walletId) userWallet else it }
|
||||
} else {
|
||||
wallets + userWallet
|
||||
}
|
||||
wallets.addOrReplace(userWallet) { it.walletId == userWallet.walletId }
|
||||
}
|
||||
|
||||
// update the selectedUserWallet state if it is the only wallet
|
||||
|
|
@ -149,7 +158,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
val encryptionKey = userWallet.encryptionKey
|
||||
?: raise(SetLockError.UserWalletLocked)
|
||||
|
||||
runCatching {
|
||||
runSuspendCatching {
|
||||
userWalletEncryptionKeysRepository.save(
|
||||
encryptionKey = UserWalletEncryptionKey(
|
||||
walletId = userWalletId,
|
||||
|
|
@ -193,6 +202,8 @@ internal class DefaultUserWalletsListRepository(
|
|||
|
||||
userWalletEncryptionKeysRepository.delete(userWalletIds)
|
||||
|
||||
removeHotWalletsFromSDK(userWalletIds)
|
||||
|
||||
userWallets.update { currentWallets ->
|
||||
val updatedWallets = currentWallets?.filter { userWalletIds.contains(it.walletId).not() }
|
||||
selectedUserWallet.update { currentSelected ->
|
||||
|
|
@ -232,7 +243,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
val encryptionKey = requestPasswordRecursive(
|
||||
hotWalletId = userWallet.hotWalletId,
|
||||
block = { password ->
|
||||
runCatching {
|
||||
runSuspendCatching {
|
||||
userWalletEncryptionKeysRepository.getEncryptedWithPassword(userWalletId, password)
|
||||
}.onFailure {
|
||||
raise(UnlockWalletError.UnableToUnlock)
|
||||
|
|
@ -287,9 +298,13 @@ internal class DefaultUserWalletsListRepository(
|
|||
}
|
||||
|
||||
override suspend fun unlockAllWallets(): Either<UnlockWalletError, Unit> = either {
|
||||
val userWallets = userWalletsSync()
|
||||
val userWalletIds = userWallets.map { it.walletId }.toSet()
|
||||
val biometricKeys = runCatching {
|
||||
val userWallets = userWalletsSync().filter { it.isLocked }
|
||||
|
||||
if (userWallets.isEmpty()) {
|
||||
return@either
|
||||
}
|
||||
|
||||
val biometricKeys = runSuspendCatching {
|
||||
userWalletEncryptionKeysRepository.getAllBiometric()
|
||||
}.getOrElse {
|
||||
// TODO handle error properly [REDACTED_TASK_KEY]
|
||||
|
|
@ -309,14 +324,15 @@ internal class DefaultUserWalletsListRepository(
|
|||
removePasswordAttempts(it)
|
||||
}
|
||||
|
||||
// if we cant unlock all wallets
|
||||
if (userWalletIds.all { it in unlockedWalletsIds }.not()) {
|
||||
// if we cant unlock any of the locked wallets, return error
|
||||
// (isLocked remains `true` here because we haven't updated the wallets yet)
|
||||
if (unlockedWallets.any { it.isLocked }.not()) {
|
||||
raise(UnlockWalletError.UnableToUnlock)
|
||||
}
|
||||
|
||||
sensitiveInformationRepository.getAll(allKeys)
|
||||
.doOnSuccess { sensitiveInfo ->
|
||||
updateWallets { userWallets.updateWith(sensitiveInfo) }
|
||||
updateWallets { wallets -> wallets?.updateWith(sensitiveInfo) }
|
||||
}
|
||||
.doOnFailure { raise(UnlockWalletError.UnableToUnlock) }
|
||||
}
|
||||
|
|
@ -345,6 +361,33 @@ internal class DefaultUserWalletsListRepository(
|
|||
userWalletEncryptionKeysRepository.clear()
|
||||
}
|
||||
|
||||
override suspend fun hasSecuredWallets(): Boolean {
|
||||
val userWallets = userWalletsSync()
|
||||
val unsecuredWalletIds = userWalletEncryptionKeysRepository.getAllUnsecured().map { it.walletId }.toSet()
|
||||
return userWallets.any { it.walletId !in unsecuredWalletIds }
|
||||
}
|
||||
|
||||
private suspend fun checkForUpgradeAndDeleteHotWalletIfNeeded(
|
||||
newUserWallet: UserWallet,
|
||||
oldUserWallet: UserWallet,
|
||||
) {
|
||||
if (newUserWallet.walletId == oldUserWallet.walletId &&
|
||||
oldUserWallet is UserWallet.Hot && newUserWallet is UserWallet.Cold
|
||||
) {
|
||||
removeHotWalletsFromSDK(walletIds = listOf(oldUserWallet.walletId))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun removeHotWalletsFromSDK(walletIds: List<UserWalletId>) {
|
||||
val hotWalletsToDelete = userWalletsSync()
|
||||
.filterIsInstance<UserWallet.Hot>()
|
||||
.filter { walletIds.contains(it.walletId) }
|
||||
|
||||
hotWalletsToDelete.forEach {
|
||||
tangemHotSdk.delete(it.hotWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun requestPasswordRecursive(
|
||||
hotWalletId: HotWalletId,
|
||||
block: suspend (CharArray) -> UserWalletEncryptionKey?,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import com.tangem.tap.common.redux.AppState
|
||||
|
||||
object DemoHelper {
|
||||
val config = DemoConfig()
|
||||
val config = DemoConfig
|
||||
|
||||
fun isDemoCard(scanResponse: ScanResponse): Boolean = isDemoCardId(scanResponse.card.cardId)
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import org.rekotlin.Action
|
|||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("MemberNameEqualsClassName")
|
||||
class DetailsMiddleware {
|
||||
private val appSettingsMiddleware = AppSettingsMiddleware()
|
||||
val detailsMiddleware: Middleware<AppState> = { _, stateProvider ->
|
||||
|
|
@ -66,14 +67,8 @@ class DetailsMiddleware {
|
|||
when (action.setting) {
|
||||
AppSetting.SaveWallets -> toggleSaveWallets(state, enable = action.enable)
|
||||
AppSetting.SaveAccessCode -> toggleSaveAccessCodes(state, enable = action.enable)
|
||||
AppSetting.RequireAccessCode -> toggleRequireAccessCode(
|
||||
state = state,
|
||||
enable = action.enable,
|
||||
)
|
||||
AppSetting.BiometricAuthentication -> toggleBiometricsAuthentication(
|
||||
state = state,
|
||||
enable = action.enable,
|
||||
)
|
||||
AppSetting.RequireAccessCode -> toggleRequireAccessCode(enable = action.enable)
|
||||
AppSetting.BiometricAuthentication -> toggleBiometricsAuthentication(enable = action.enable)
|
||||
}
|
||||
}
|
||||
is DetailsAction.AppSettings.CheckBiometricsStatus -> {
|
||||
|
|
@ -100,7 +95,7 @@ class DetailsMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun toggleBiometricsAuthentication(state: DetailsState, enable: Boolean) {
|
||||
private fun toggleBiometricsAuthentication(enable: Boolean) {
|
||||
scope.launch {
|
||||
val walletsRepository = store.inject(DaggerGraphState::walletsRepository)
|
||||
|
||||
|
|
@ -110,16 +105,12 @@ class DetailsMiddleware {
|
|||
return@launch
|
||||
}
|
||||
|
||||
toggleRequireAccessCode(
|
||||
state = state,
|
||||
enable = true,
|
||||
)
|
||||
|
||||
if (enable) {
|
||||
setBiometricLockForAllWallets()
|
||||
} else {
|
||||
// Remove all biometric-related data
|
||||
removeAllBiometricData()
|
||||
walletsRepository.setRequireAccessCode(value = true)
|
||||
}
|
||||
|
||||
walletsRepository.setUseBiometricAuthentication(value = enable)
|
||||
|
|
@ -127,7 +118,7 @@ class DetailsMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun toggleRequireAccessCode(state: DetailsState, enable: Boolean) {
|
||||
private fun toggleRequireAccessCode(enable: Boolean) {
|
||||
scope.launch {
|
||||
val walletsRepository = store.inject(DaggerGraphState::walletsRepository)
|
||||
|
||||
|
|
@ -138,11 +129,8 @@ class DetailsMiddleware {
|
|||
}
|
||||
|
||||
if (enable) {
|
||||
// Remove all biometric sign data
|
||||
// Remove all saved access codes
|
||||
removeAllBiometricSingData()
|
||||
toggleSaveAccessCodes(state, enable = false)
|
||||
} else {
|
||||
toggleSaveAccessCodes(state, enable = true)
|
||||
}
|
||||
|
||||
walletsRepository.setRequireAccessCode(value = enable)
|
||||
|
|
|
|||
|
|
@ -111,6 +111,9 @@ private fun handlePrivacyAction(action: DetailsAction.AppSettings, state: Detail
|
|||
isHidingEnabled = action.state.isHidingEnabled,
|
||||
selectedAppCurrency = action.state.selectedAppCurrency,
|
||||
selectedThemeMode = action.state.selectedThemeMode,
|
||||
useBiometricAuthentication = action.state.useBiometricAuthentication,
|
||||
requireAccessCode = action.state.requireAccessCode,
|
||||
hasSecuredWallets = action.state.hasSecuredWallets,
|
||||
),
|
||||
)
|
||||
is DetailsAction.AppSettings.EnrollBiometrics,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ data class AppSettingsState(
|
|||
val requireAccessCode: Boolean = false,
|
||||
val useBiometricAuthentication: Boolean = false,
|
||||
val needEnrollBiometrics: Boolean = false,
|
||||
val hasSecuredWallets: Boolean = false,
|
||||
val isHidingEnabled: Boolean = false,
|
||||
val isInProgress: Boolean = false,
|
||||
val selectedAppCurrency: AppCurrency = AppCurrency.Default,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
|
|||
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||
import com.tangem.domain.apptheme.repository.AppThemeModeRepository
|
||||
import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.settings.CanUseBiometryUseCase
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
|
|
@ -48,6 +49,7 @@ internal class AppSettingsModel @Inject constructor(
|
|||
private val appCurrencyRepository: AppCurrencyRepository,
|
||||
private val walletsRepository: WalletsRepository,
|
||||
private val canUseBiometryUseCase: CanUseBiometryUseCase,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val balanceHidingRepository: BalanceHidingRepository,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val appThemeModeRepository: AppThemeModeRepository,
|
||||
|
|
@ -114,7 +116,8 @@ internal class AppSettingsModel @Inject constructor(
|
|||
)
|
||||
|
||||
if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||
val canUseBiometrics = !state.needEnrollBiometrics && !state.isInProgress
|
||||
val canUseBiometrics =
|
||||
!state.needEnrollBiometrics && !state.isInProgress && state.hasSecuredWallets
|
||||
|
||||
add(
|
||||
itemsFactory.createUseBiometricsSwitch(
|
||||
|
|
@ -126,7 +129,7 @@ internal class AppSettingsModel @Inject constructor(
|
|||
|
||||
add(
|
||||
itemsFactory.createRequireAccessCodeSwitch(
|
||||
isChecked = state.requireAccessCode,
|
||||
isChecked = state.requireAccessCode || !state.useBiometricAuthentication,
|
||||
isEnabled = canUseBiometrics && state.useBiometricAuthentication,
|
||||
onCheckedChange = ::onRequireAccessCodeToggled,
|
||||
),
|
||||
|
|
@ -206,14 +209,12 @@ internal class AppSettingsModel @Inject constructor(
|
|||
// analyticsEventHandler.send(Settings.AppSettings.BiometricAuthenticationChanged(param))
|
||||
if (isChecked) {
|
||||
onSettingsToggled(AppSetting.BiometricAuthentication, enable = true)
|
||||
onSettingsToggled(AppSetting.RequireAccessCode, enable = true)
|
||||
} else {
|
||||
updateContentState {
|
||||
copy(
|
||||
dialog = dialogsFactory.createDisableBiometricAuthenticationAlert(
|
||||
onDisable = {
|
||||
onSettingsToggled(AppSetting.BiometricAuthentication, enable = false)
|
||||
onSettingsToggled(AppSetting.RequireAccessCode, enable = true)
|
||||
dismissDialog()
|
||||
},
|
||||
onDismiss = ::dismissDialog,
|
||||
|
|
@ -323,6 +324,7 @@ internal class AppSettingsModel @Inject constructor(
|
|||
isHidingEnabled = balanceHidingRepository.getBalanceHidingSettings().isHidingEnabledInSettings,
|
||||
selectedAppCurrency = appCurrencyRepository.getSelectedAppCurrency().firstOrNull() ?: AppCurrency.Default,
|
||||
selectedThemeMode = appThemeModeRepository.getAppThemeMode().firstOrNull() ?: AppThemeMode.DEFAULT,
|
||||
hasSecuredWallets = userWalletsListRepository.hasSecuredWallets(),
|
||||
)
|
||||
|
||||
store.dispatchWithMain(DetailsAction.AppSettings.Prepare(state))
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.common.keyboard.KeyboardValidator
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.event.TechAnalyticsEvent
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.R
|
||||
|
|
@ -16,14 +16,11 @@ import com.tangem.core.ui.message.BottomSheetMessage
|
|||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfig
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import com.tangem.domain.appcurrency.FetchAppCurrenciesUseCase
|
||||
import com.tangem.domain.balancehiding.BalanceHidingSettings
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
|
||||
import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.common.LogConfig
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.notifications.GetApplicationIdUseCase
|
||||
import com.tangem.domain.notifications.SendPushTokenUseCase
|
||||
|
|
@ -35,15 +32,13 @@ import com.tangem.domain.quotes.multi.MultiQuoteUpdater
|
|||
import com.tangem.domain.settings.DeleteDeprecatedLogsUseCase
|
||||
import com.tangem.domain.settings.IncrementAppLaunchCounterUseCase
|
||||
import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase
|
||||
import com.tangem.domain.staking.FetchStakingTokensUseCase
|
||||
import com.tangem.domain.staking.FetchStakingOptionsUseCase
|
||||
import com.tangem.domain.wallets.usecase.AssociateWalletsWithApplicationIdUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetSavedWalletsCountUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.UpdateRemoteWalletsInfoUseCase
|
||||
import com.tangem.feature.swap.analytics.StoriesEvents
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
import com.tangem.tap.network.exchangeServices.ExchangeService
|
||||
import com.tangem.tap.network.exchangeServices.moonpay.MoonPayService
|
||||
import com.tangem.tap.network.exchangeServices.SellService
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.tap.routing.configurator.AppRouterConfig
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -67,7 +62,7 @@ internal class MainViewModel @Inject constructor(
|
|||
private val incrementAppLaunchCounterUseCase: IncrementAppLaunchCounterUseCase,
|
||||
private val blockchainSDKFactory: BlockchainSDKFactory,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val fetchStakingTokensUseCase: FetchStakingTokensUseCase,
|
||||
private val fetchStakingOptionsUseCase: FetchStakingOptionsUseCase,
|
||||
private val fetchUserCountryUseCase: FetchUserCountryUseCase,
|
||||
@GlobalUiMessageSender private val messageSender: UiMessageSender,
|
||||
private val keyboardValidator: KeyboardValidator,
|
||||
|
|
@ -83,9 +78,10 @@ internal class MainViewModel @Inject constructor(
|
|||
private val apiConfigsManager: ApiConfigsManager,
|
||||
private val multiQuoteUpdater: MultiQuoteUpdater,
|
||||
private val appStateHolder: AppStateHolder,
|
||||
private val environmentConfigStorage: EnvironmentConfigStorage,
|
||||
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
|
||||
private val appRouterConfig: AppRouterConfig,
|
||||
private val sellService: SellService,
|
||||
private val trackingContextProxy: TrackingContextProxy,
|
||||
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
) : ViewModel() {
|
||||
|
||||
|
|
@ -108,7 +104,7 @@ internal class MainViewModel @Inject constructor(
|
|||
|
||||
launch { fetchAppCurrenciesUseCase() }
|
||||
|
||||
launch { fetchStakingTokens() }
|
||||
launch { fetchStakingOptions() }
|
||||
|
||||
launch { initPushNotifications() }
|
||||
}
|
||||
|
|
@ -181,36 +177,26 @@ internal class MainViewModel @Inject constructor(
|
|||
.mapLeft { emptyFlow<UserWallet>() }
|
||||
.onRight { wallet ->
|
||||
wallet.distinctUntilChanged()
|
||||
.onEach { Analytics.setContext(it) }
|
||||
.onEach { trackingContextProxy.setContext(it) }
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchStakingTokens() {
|
||||
fetchStakingTokensUseCase()
|
||||
.onLeft { Timber.e(it.toString(), "Unable to fetch the staking tokens list") }
|
||||
.onRight { Timber.d("Staking token list was fetched successfully") }
|
||||
private suspend fun fetchStakingOptions() {
|
||||
fetchStakingOptionsUseCase()
|
||||
.onLeft { Timber.e(it.toString(), "Unable to fetch staking options") }
|
||||
.onRight { Timber.d("Staking options were fetched successfully") }
|
||||
}
|
||||
|
||||
private fun initializeOffRamp() {
|
||||
viewModelScope.launch {
|
||||
val sellService = makeSellExchangeService(environmentConfig = environmentConfigStorage.getConfigSync())
|
||||
appStateHolder.sellService = sellService
|
||||
|
||||
sellService.update()
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeSellExchangeService(environmentConfig: EnvironmentConfig): ExchangeService {
|
||||
return MoonPayService(
|
||||
apiKey = environmentConfig.moonPayApiKey,
|
||||
secretKey = environmentConfig.moonPayApiSecretKey,
|
||||
isLogEnabled = LogConfig.network.moonPayService,
|
||||
userWalletProvider = { getSelectedWalletUseCase.sync().getOrNull() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun observeFlips() {
|
||||
listenToFlipsUseCase().launchIn(viewModelScope)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.tangem.tap.store
|
|||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
@Suppress("MemberNameEqualsClassName")
|
||||
class BackupMiddleware {
|
||||
val backupMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
|
|
|
|||
|
|
@ -77,9 +77,13 @@ internal class WelcomeModel @Inject constructor(
|
|||
warning = warning,
|
||||
error = state.error
|
||||
?.takeIf { !it.silent && warning == null }
|
||||
?.let { e ->
|
||||
e.messageResId?.let { TextReference.Res(it) }
|
||||
?: TextReference.Str(e.customMessage)
|
||||
?.let { error ->
|
||||
val messageResId = error.messageResId
|
||||
if (messageResId != null) {
|
||||
TextReference.Res(messageResId)
|
||||
} else {
|
||||
TextReference.Str(error.customMessage)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,7 +100,10 @@ internal class WelcomeMiddleware {
|
|||
val currency = ParamCardCurrencyConverter().convert(
|
||||
value = scanResponse.cardTypesResolver,
|
||||
)
|
||||
Analytics.addContext(scanResponse)
|
||||
|
||||
val trackingContextProxy = store.inject(DaggerGraphState::trackingContextProxy)
|
||||
trackingContextProxy.addContext(scanResponse)
|
||||
|
||||
if (currency != null) {
|
||||
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.tap.network.auth
|
||||
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import com.tangem.lib.auth.P2PEthPoolAuthProvider
|
||||
|
||||
internal class DefaultP2PEthPoolAuthProvider(
|
||||
private val environmentConfigStorage: EnvironmentConfigStorage,
|
||||
) : P2PEthPoolAuthProvider {
|
||||
|
||||
override fun getApiKey(): String {
|
||||
val keys = environmentConfigStorage.getConfigSync().p2pApiKey
|
||||
?: error("No P2P api keys provided")
|
||||
|
||||
return keys.mainnet
|
||||
}
|
||||
}
|
||||
|
|
@ -6,10 +6,12 @@ import com.tangem.domain.common.wallets.UserWalletsListRepository
|
|||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.features.hotwallet.HotWalletFeatureToggles
|
||||
import com.tangem.lib.auth.ExpressAuthProvider
|
||||
import com.tangem.lib.auth.P2PEthPoolAuthProvider
|
||||
import com.tangem.lib.auth.StakeKitAuthProvider
|
||||
import com.tangem.tap.network.auth.DefaultAppVersionProvider
|
||||
import com.tangem.tap.network.auth.DefaultAuthProvider
|
||||
import com.tangem.tap.network.auth.DefaultExpressAuthProvider
|
||||
import com.tangem.tap.network.auth.DefaultP2PEthPoolAuthProvider
|
||||
import com.tangem.tap.network.auth.DefaultStakeKitAuthProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
import dagger.Module
|
||||
|
|
@ -50,6 +52,12 @@ internal class AuthModule {
|
|||
return DefaultStakeKitAuthProvider(environmentConfigStorage)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideP2PEthPoolAuthProvider(environmentConfigStorage: EnvironmentConfigStorage): P2PEthPoolAuthProvider {
|
||||
return DefaultP2PEthPoolAuthProvider(environmentConfigStorage)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppVersionProvider(): AppVersionProvider {
|
||||
|
|
|
|||
|
|
@ -2,44 +2,14 @@ package com.tangem.tap.network.exchangeServices
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class CryptoCurrencyConverter(
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
) : TwoWayConverter<Currency, CryptoCurrency> {
|
||||
internal object CryptoCurrencyConverter : Converter<CryptoCurrency, Currency> {
|
||||
|
||||
private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory(excludedBlockchains) }
|
||||
|
||||
override fun convert(value: Currency): CryptoCurrency {
|
||||
return when (value) {
|
||||
is Currency.Blockchain -> requireNotNull(
|
||||
cryptoCurrencyFactory.createCoin(
|
||||
blockchain = value.blockchain,
|
||||
extraDerivationPath = value.derivationPath,
|
||||
userWallet = getSelectedWallet(),
|
||||
),
|
||||
)
|
||||
is Currency.Token -> requireNotNull(
|
||||
cryptoCurrencyFactory.createToken(
|
||||
sdkToken = value.token,
|
||||
blockchain = value.blockchain,
|
||||
extraDerivationPath = value.derivationPath,
|
||||
userWallet = getSelectedWallet(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun convertBack(value: CryptoCurrency): Currency {
|
||||
override fun convert(value: CryptoCurrency): Currency {
|
||||
val blockchain = value.network.toBlockchain()
|
||||
if (blockchain == Blockchain.Unknown) error("CryptoCurrencyConverter convertBack Unknown blockchain")
|
||||
return when (value) {
|
||||
|
|
@ -60,15 +30,4 @@ internal class CryptoCurrencyConverter(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getSelectedWallet(): UserWallet {
|
||||
val userWalletListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
|
||||
val userWalletsListRepository = store.inject(DaggerGraphState::userWalletsListRepository)
|
||||
val hotWalletFeatureToggles = store.inject(DaggerGraphState::hotWalletFeatureToggles)
|
||||
return if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||
requireNotNull(userWalletsListRepository.selectedUserWallet.value)
|
||||
} else {
|
||||
requireNotNull(userWalletListManager.selectedUserWalletSync)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,13 +5,11 @@ import arrow.core.raise.catch
|
|||
import arrow.core.raise.either
|
||||
import arrow.core.raise.ensure
|
||||
import arrow.core.right
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
||||
import com.tangem.datasource.api.express.models.response.Asset
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.exchange.ExpressAvailabilityState
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.express.ExpressServiceFetcher
|
||||
import com.tangem.domain.express.models.ExpressAsset
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -22,26 +20,23 @@ import com.tangem.domain.transaction.models.AssetRequirementsCondition
|
|||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.runCatching
|
||||
import com.tangem.utils.coroutines.runSuspendCatching
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultRampManager(
|
||||
private val sellService: Provider<ExchangeService>,
|
||||
private val expressServiceLoader: ExpressServiceLoader,
|
||||
private val sellService: Provider<SellService>,
|
||||
private val expressServiceFetcher: ExpressServiceFetcher,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
) : RampStateManager {
|
||||
|
||||
private val cryptoCurrencyConverter = CryptoCurrencyConverter(excludedBlockchains)
|
||||
|
||||
override suspend fun availableForBuy(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): ScenarioUnavailabilityReason {
|
||||
val availabilityState = runCatching { getOnrampAvailableState(userWallet.walletId, cryptoCurrency) }
|
||||
val availabilityState = runSuspendCatching { getOnrampAvailableState(userWallet.walletId, cryptoCurrency) }
|
||||
.getOrNull()
|
||||
?: ExpressAvailabilityState.Error
|
||||
|
||||
|
|
@ -56,7 +51,7 @@ internal class DefaultRampManager(
|
|||
return either {
|
||||
val isSellSupportedByService = catch(
|
||||
block = {
|
||||
val serviceCurrency = cryptoCurrencyConverter.convertBack(status.currency)
|
||||
val serviceCurrency = CryptoCurrencyConverter.convert(status.currency)
|
||||
|
||||
sellService().availableForSell(currency = serviceCurrency)
|
||||
},
|
||||
|
|
@ -90,13 +85,13 @@ internal class DefaultRampManager(
|
|||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): ScenarioUnavailabilityReason {
|
||||
val availabilityState = runCatching {
|
||||
val availabilityState = runSuspendCatching {
|
||||
getExchangeableState(userWalletId, cryptoCurrency)
|
||||
}.getOrNull() ?: ExpressAvailabilityState.Error
|
||||
return availabilityState.toReason(cryptoCurrency.name)
|
||||
}
|
||||
|
||||
override fun getSellInitializationStatus(): Flow<ExchangeServiceInitializationStatus> {
|
||||
override fun getSellInitializationStatus(): Flow<SellServiceInitializationStatus> {
|
||||
return sellService.invoke().initializationStatus
|
||||
}
|
||||
|
||||
|
|
@ -106,8 +101,8 @@ internal class DefaultRampManager(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getExpressInitializationStatus(userWalletId: UserWalletId): Flow<ExchangeServiceInitializationStatus> {
|
||||
return expressServiceLoader.getInitializationStatus(userWalletId)
|
||||
override fun getExpressInitializationStatus(userWalletId: UserWalletId): Flow<SellServiceInitializationStatus> {
|
||||
return expressServiceFetcher.getInitializationStatus(userWalletId)
|
||||
}
|
||||
|
||||
override suspend fun getSendUnavailabilityReason(
|
||||
|
|
@ -151,14 +146,14 @@ internal class DefaultRampManager(
|
|||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): ExpressAvailabilityState {
|
||||
val asset = expressServiceLoader.getInitializationStatus(userWalletId).firstOrNull()
|
||||
val asset = expressServiceFetcher.getInitializationStatus(userWalletId).firstOrNull()
|
||||
?: return ExpressAvailabilityState.Loading
|
||||
return when (asset) {
|
||||
is Lce.Error -> ExpressAvailabilityState.Error
|
||||
is Lce.Loading -> ExpressAvailabilityState.Loading
|
||||
is Lce.Content -> {
|
||||
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(it) }
|
||||
foundAsset?.exchangeAvailable?.toSwapAvailabilityState()
|
||||
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(assetId = it.id) }
|
||||
foundAsset?.isExchangeAvailable?.toSwapAvailabilityState()
|
||||
?: ExpressAvailabilityState.AssetNotFound
|
||||
}
|
||||
}
|
||||
|
|
@ -168,15 +163,15 @@ internal class DefaultRampManager(
|
|||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): ExpressAvailabilityState {
|
||||
val asset = expressServiceLoader.getInitializationStatus(userWalletId).firstOrNull()
|
||||
val asset = expressServiceFetcher.getInitializationStatus(userWalletId).firstOrNull()
|
||||
?: return ExpressAvailabilityState.Loading
|
||||
|
||||
return when (asset) {
|
||||
is Lce.Error -> ExpressAvailabilityState.Error
|
||||
is Lce.Loading -> ExpressAvailabilityState.Loading
|
||||
is Lce.Content -> {
|
||||
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(it) }
|
||||
foundAsset?.onrampAvailable?.toOnrampAvailabilityState()
|
||||
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(assetId = it.id) }
|
||||
foundAsset?.isOnrampAvailable?.toOnrampAvailabilityState()
|
||||
?: ExpressAvailabilityState.AssetNotFound
|
||||
}
|
||||
}
|
||||
|
|
@ -211,8 +206,13 @@ internal class DefaultRampManager(
|
|||
}
|
||||
}
|
||||
|
||||
private fun CryptoCurrency.findAssetPredicate(asset: Asset): Boolean {
|
||||
val contractAddress = (this as? CryptoCurrency.Token)?.contractAddress ?: EMPTY_CONTRACT_ADDRESS_VALUE
|
||||
return asset.network == network.backendId && asset.contractAddress.equals(contractAddress, ignoreCase = true)
|
||||
private fun CryptoCurrency.findAssetPredicate(assetId: ExpressAsset.ID): Boolean {
|
||||
val currencyAssedId = ExpressAsset.ID(
|
||||
networkId = this.network.backendId,
|
||||
contractAddress = (this as? CryptoCurrency.Token)?.contractAddress,
|
||||
)
|
||||
|
||||
return assetId.networkId == currencyAssedId.networkId &&
|
||||
assetId.contractAddress.equals(currencyAssedId.contractAddress, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,11 +5,11 @@ import com.tangem.domain.models.currency.CryptoCurrency
|
|||
import com.tangem.tap.domain.model.Currency
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
typealias ExchangeServiceInitializationStatus = Lce<Throwable, Any>
|
||||
typealias SellServiceInitializationStatus = Lce<Throwable, Any>
|
||||
|
||||
interface ExchangeService {
|
||||
interface SellService {
|
||||
|
||||
val initializationStatus: StateFlow<ExchangeServiceInitializationStatus>
|
||||
val initializationStatus: StateFlow<SellServiceInitializationStatus>
|
||||
|
||||
suspend fun update()
|
||||
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
package com.tangem.tap.network.exchangeServices.moonpay
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface MoonPayApi {
|
||||
|
||||
@GET(MOOONPAY_IP_ADDRESS_REQUEST_URL)
|
||||
suspend fun getUserStatus(@Query("apiKey") moonPayApiKey: String): MoonPayUserStatus
|
||||
|
||||
@GET(MOOONPAY_CURRENCIES_REQUEST_URL)
|
||||
suspend fun getCurrencies(@Query("apiKey") moonPayApiKey: String): List<MoonPayCurrencies>
|
||||
|
||||
companion object {
|
||||
const val MOOONPAY_BASE_URL = "https://api.moonpay.com/"
|
||||
const val MOOONPAY_IP_ADDRESS_REQUEST_URL = "v4/ip_address/"
|
||||
const val MOOONPAY_CURRENCIES_REQUEST_URL = "v3/currencies/"
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MoonPayUserStatus(
|
||||
@Json(name = "isBuyAllowed")
|
||||
val isBuyAllowed: Boolean,
|
||||
@Json(name = "isSellAllowed")
|
||||
val isSellAllowed: Boolean,
|
||||
@Json(name = "isAllowed")
|
||||
val isMoonpayAllowed: Boolean,
|
||||
@Json(name = "alpha3")
|
||||
val countryCode: String,
|
||||
@Json(name = "state")
|
||||
val stateCode: String,
|
||||
)
|
||||
|
||||
@Suppress("BooleanPropertyNaming")
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MoonPayCurrencies(
|
||||
@Json(name = "type") val type: String,
|
||||
@Json(name = "code") val code: String,
|
||||
@Json(name = "supportsLiveMode") val supportsLiveMode: Boolean = false,
|
||||
@Json(name = "isSuspended") val isSuspended: Boolean = true,
|
||||
@Json(name = "isSupportedInUS") val isSupportedInUS: Boolean = false,
|
||||
@Json(name = "isSellSupported") val isSellSupported: Boolean = false,
|
||||
@Json(name = "notAllowedUSStates") val notAllowedUSStates: List<String> = emptyList(),
|
||||
@Json(name = "metadata") val metadata: MoonPayCurrenciesMetadata? = null,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MoonPayCurrenciesMetadata(
|
||||
@Json(name = "contractAddress") val contractAddress: String?,
|
||||
@Json(name = "networkCode") val networkCode: String?,
|
||||
)
|
||||
|
|
@ -5,7 +5,9 @@ import android.util.Base64
|
|||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.datasource.api.common.createRetrofitInstance
|
||||
import com.tangem.datasource.api.moonpay.MoonPayApi
|
||||
import com.tangem.datasource.api.moonpay.MoonPayCurrencies
|
||||
import com.tangem.datasource.api.moonpay.MoonPayUserStatus
|
||||
import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.common.extensions.withIOContext
|
||||
import com.tangem.domain.core.utils.lceContent
|
||||
|
|
@ -14,9 +16,10 @@ import com.tangem.domain.core.utils.lceLoading
|
|||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.network.exchangeServices.ExchangeService
|
||||
import com.tangem.tap.network.exchangeServices.ExchangeServiceInitializationStatus
|
||||
import com.tangem.tap.network.exchangeServices.SellService
|
||||
import com.tangem.tap.network.exchangeServices.SellServiceInitializationStatus
|
||||
import com.tangem.tap.network.exchangeServices.moonpay.models.MoonPayAvailableCurrency
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import timber.log.Timber
|
||||
|
|
@ -24,24 +27,17 @@ import javax.crypto.Mac
|
|||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class MoonPayService(
|
||||
private val apiKey: String,
|
||||
private val secretKey: String,
|
||||
private val isLogEnabled: Boolean,
|
||||
private val api: MoonPayApi,
|
||||
private val apiKeyProvider: Provider<String>,
|
||||
private val secretKeyProvider: Provider<String>,
|
||||
private val userWalletProvider: () -> UserWallet?,
|
||||
) : ExchangeService {
|
||||
) : SellService {
|
||||
|
||||
override val initializationStatus: StateFlow<ExchangeServiceInitializationStatus>
|
||||
get() = _initializationStatus
|
||||
|
||||
private val _initializationStatus: MutableStateFlow<ExchangeServiceInitializationStatus> =
|
||||
private val _initializationStatus: MutableStateFlow<SellServiceInitializationStatus> =
|
||||
MutableStateFlow(value = lceLoading())
|
||||
|
||||
private val api: MoonPayApi by lazy {
|
||||
createRetrofitInstance(
|
||||
baseUrl = MoonPayApi.MOOONPAY_BASE_URL,
|
||||
logEnabled = isLogEnabled,
|
||||
).create(MoonPayApi::class.java)
|
||||
}
|
||||
override val initializationStatus: StateFlow<SellServiceInitializationStatus>
|
||||
get() = _initializationStatus
|
||||
|
||||
private var status: MoonPayStatus? = null
|
||||
|
||||
|
|
@ -51,7 +47,7 @@ class MoonPayService(
|
|||
_initializationStatus.value = lceLoading()
|
||||
|
||||
performRequest {
|
||||
val userStatus = when (val result = performRequest { api.getUserStatus(apiKey) }) {
|
||||
val userStatus = when (val result = performRequest { api.getUserStatus(apiKeyProvider()) }) {
|
||||
is Result.Failure -> {
|
||||
Timber.e("Failed to load user status", result.error)
|
||||
_initializationStatus.value = result.error.lceError()
|
||||
|
|
@ -60,7 +56,7 @@ class MoonPayService(
|
|||
is Result.Success -> result.data
|
||||
}
|
||||
|
||||
val currencies = when (val result = performRequest { api.getCurrencies(apiKey) }) {
|
||||
val currencies = when (val result = performRequest { api.getCurrencies(apiKeyProvider()) }) {
|
||||
is Result.Failure -> {
|
||||
Timber.e("Failed to load currencies", result.error)
|
||||
_initializationStatus.value = result.error.lceError()
|
||||
|
|
@ -78,7 +74,7 @@ class MoonPayService(
|
|||
MoonPayAvailableCurrency(
|
||||
currencyCode = currency.code,
|
||||
networkCode = currency.metadata?.networkCode ?: return@mapNotNull null,
|
||||
contractAddress = currency.metadata.contractAddress,
|
||||
contractAddress = currency.metadata?.contractAddress,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +163,7 @@ class MoonPayService(
|
|||
val uri = Uri.Builder()
|
||||
.scheme(SCHEME)
|
||||
.authority(URL_SELL)
|
||||
.appendQueryParameter("apiKey", apiKey)
|
||||
.appendQueryParameter("apiKey", apiKeyProvider())
|
||||
.appendQueryParameter("baseCurrencyCode", moonpayCurrency.currencyCode.uppercase())
|
||||
.appendQueryParameter("refundWalletAddress", walletAddress)
|
||||
.appendQueryParameter("redirectURL", "tangem://redirect_sell?currency_id=${cryptoCurrency.id.value}")
|
||||
|
|
@ -191,7 +187,7 @@ class MoonPayService(
|
|||
|
||||
private fun createSignature(data: String): String {
|
||||
val sha256Hmac = Mac.getInstance("HmacSHA256")
|
||||
val secretKey = SecretKeySpec(secretKey.toByteArray(), "HmacSHA256")
|
||||
val secretKey = SecretKeySpec(secretKeyProvider().toByteArray(), "HmacSHA256")
|
||||
sha256Hmac.init(secretKey)
|
||||
val sha256encoded = sha256Hmac.doFinal("?$data".toByteArray())
|
||||
return Base64.encodeToString(sha256encoded, Base64.NO_WRAP)
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@ internal val Blockchain.moonPaySupportedCurrency: MoonPaySupportedCurrency?
|
|||
Pepecoin, PepecoinTestnet -> null
|
||||
Hyperliquid, HyperliquidTestnet -> null
|
||||
Quai, QuaiTestnet -> null
|
||||
// Linea, LineaTestnet -> null
|
||||
// ArbitrumNova -> null
|
||||
Linea, LineaTestnet -> null
|
||||
ArbitrumNova -> null
|
||||
Plasma, PlasmaTestnet -> null
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import com.tangem.tap.common.extensions.dispatchDialogShow
|
|||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.network.exchangeServices.ExchangeService
|
||||
import com.tangem.tap.network.exchangeServices.SellService
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Store
|
||||
import javax.inject.Inject
|
||||
|
|
@ -19,7 +19,7 @@ import javax.inject.Inject
|
|||
class AppStateHolder @Inject constructor() : ReduxStateHolder {
|
||||
|
||||
var mainStore: Store<AppState>? = null
|
||||
var sellService: ExchangeService? = null
|
||||
var sellService: SellService? = null
|
||||
|
||||
override fun dispatch(action: Action) {
|
||||
mainStore?.dispatch(action)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.proxy.redux
|
|||
import com.tangem.tap.common.redux.AppState
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
@Suppress("MemberNameEqualsClassName")
|
||||
object DaggerGraphMiddleware {
|
||||
val daggerGraphMiddleware: Middleware<AppState> = { _, _ ->
|
||||
{ next ->
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.proxy.redux
|
|||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.settings.SettingsManager
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
|
|
@ -79,4 +80,5 @@ data class DaggerGraphState(
|
|||
val userWalletsListRepository: UserWalletsListRepository? = null,
|
||||
val hotWalletFeatureToggles: HotWalletFeatureToggles? = null,
|
||||
val tangemHotSdk: TangemHotSdk? = null,
|
||||
val trackingContextProxy: TrackingContextProxy? = null,
|
||||
) : StateType
|
||||
|
|
@ -40,6 +40,12 @@ internal class ProxyAppRouter(
|
|||
}
|
||||
}
|
||||
|
||||
override fun replaceCurrent(route: AppRoute, onComplete: (Boolean) -> Unit) {
|
||||
safeNavigate(onComplete, message = "Replace a current route with $route") {
|
||||
innerRouter.replaceCurrent(route, onComplete)
|
||||
}
|
||||
}
|
||||
|
||||
override fun replaceAll(vararg routes: AppRoute, onComplete: (isSuccess: Boolean) -> Unit) {
|
||||
safeNavigate(onComplete, message = "Replace all routes with $routes") {
|
||||
runCatching {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import android.os.Bundle
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -83,6 +84,7 @@ internal fun RootContent(
|
|||
TangemSnackbarHost(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.imePadding()
|
||||
.navigationBarsPadding()
|
||||
.padding(all = 16.dp),
|
||||
hostState = snackbarHostState,
|
||||
|
|
|
|||
|
|
@ -36,9 +36,10 @@ import com.tangem.features.send.v2.api.SendComponent
|
|||
import com.tangem.features.send.v2.api.SendEntryPointComponent
|
||||
import com.tangem.features.staking.api.StakingComponent
|
||||
import com.tangem.features.swap.SwapComponent
|
||||
import com.tangem.features.tangempay.components.TangemPayDetailsComponent
|
||||
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
|
||||
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent
|
||||
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent.Params.*
|
||||
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent.Params.ContinueOnboarding
|
||||
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent.Params.Deeplink
|
||||
import com.tangem.features.tokendetails.TokenDetailsComponent
|
||||
import com.tangem.features.wallet.WalletEntryComponent
|
||||
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent
|
||||
|
|
@ -62,6 +63,7 @@ internal class ChildFactory @Inject constructor(
|
|||
private val detailsComponentFactory: DetailsComponent.Factory,
|
||||
private val walletSettingsComponentFactory: WalletSettingsComponent.Factory,
|
||||
private val walletBackupComponentFactory: WalletBackupComponent.Factory,
|
||||
private val walletHardwareBackupComponentFactory: WalletHardwareBackupComponent.Factory,
|
||||
private val disclaimerComponentFactory: DisclaimerComponent.Factory,
|
||||
private val manageTokensComponentFactory: ManageTokensComponent.Factory,
|
||||
private val marketsTokenDetailsComponentFactory: MarketsTokenDetailsComponent.Factory,
|
||||
|
|
@ -100,6 +102,7 @@ internal class ChildFactory @Inject constructor(
|
|||
private val chooseManagedTokensComponentFactory: ChooseManagedTokensComponent.Factory,
|
||||
private val createWalletSelectionComponentFactory: CreateWalletSelectionComponent.Factory,
|
||||
private val createWalletStartComponentFactory: CreateWalletStartComponent.Factory,
|
||||
private val createHardwareWalletComponentFactory: CreateHardwareWalletComponent.Factory,
|
||||
private val createMobileWalletComponentFactory: CreateMobileWalletComponent.Factory,
|
||||
private val upgradeWalletComponentFactory: UpgradeWalletComponent.Factory,
|
||||
private val addExistingWalletComponentFactory: AddExistingWalletComponent.Factory,
|
||||
|
|
@ -107,8 +110,9 @@ internal class ChildFactory @Inject constructor(
|
|||
private val createWalletBackupComponentFactory: CreateWalletBackupComponent.Factory,
|
||||
private val updateAccessCodeComponentFactory: UpdateAccessCodeComponent.Factory,
|
||||
private val viewPhraseComponentFactory: ViewPhraseComponent.Factory,
|
||||
private val forgetWalletComponentFactory: ForgetWalletComponent.Factory,
|
||||
private val sendEntryPointComponentFactory: SendEntryPointComponent.Factory,
|
||||
private val tangemPayDetailsComponentFactory: TangemPayDetailsComponent.Factory,
|
||||
private val tangemPayDetailsContainerComponentFactory: TangemPayDetailsContainerComponent.Factory,
|
||||
private val tangemPayOnboardingComponentFactory: TangemPayOnboardingComponent.Factory,
|
||||
private val kycComponentFactory: KycComponent.Factory,
|
||||
private val yieldSupplyPromoComponentFactory: YieldSupplyPromoComponent.Factory,
|
||||
|
|
@ -138,7 +142,6 @@ internal class ChildFactory @Inject constructor(
|
|||
is AppRoute.ManageTokens -> {
|
||||
val source = when (route.source) {
|
||||
AppRoute.ManageTokens.Source.SETTINGS -> ManageTokensSource.SETTINGS
|
||||
AppRoute.ManageTokens.Source.ONBOARDING -> ManageTokensSource.ONBOARDING
|
||||
AppRoute.ManageTokens.Source.STORIES -> ManageTokensSource.STORIES
|
||||
}
|
||||
|
||||
|
|
@ -187,6 +190,15 @@ internal class ChildFactory @Inject constructor(
|
|||
componentFactory = walletBackupComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.WalletHardwareBackup -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = WalletHardwareBackupComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
),
|
||||
componentFactory = walletHardwareBackupComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.MarketsTokenDetails -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
|
|
@ -294,7 +306,7 @@ internal class ChildFactory @Inject constructor(
|
|||
context = context,
|
||||
params = StakingComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
cryptoCurrencyId = route.cryptoCurrencyId,
|
||||
cryptoCurrency = route.cryptoCurrency,
|
||||
yieldId = route.yieldId,
|
||||
),
|
||||
componentFactory = stakingComponentFactory,
|
||||
|
|
@ -309,6 +321,13 @@ internal class ChildFactory @Inject constructor(
|
|||
userWalletId = route.userWalletId,
|
||||
isInitialReverseOrder = route.isInitialReverseOrder,
|
||||
screenSource = route.screenSource,
|
||||
tangemPayInput = route.tangemPayInput?.let { tangemPayInput ->
|
||||
SwapComponent.Params.TangemPayInput(
|
||||
cryptoAmount = tangemPayInput.cryptoAmount,
|
||||
fiatAmount = tangemPayInput.fiatAmount,
|
||||
depositAddress = tangemPayInput.depositAddress,
|
||||
)
|
||||
},
|
||||
),
|
||||
componentFactory = swapComponentFactory,
|
||||
)
|
||||
|
|
@ -497,6 +516,13 @@ internal class ChildFactory @Inject constructor(
|
|||
componentFactory = createWalletSelectionComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.CreateHardwareWallet -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = Unit,
|
||||
componentFactory = createHardwareWalletComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.CreateMobileWallet -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
|
|
@ -525,6 +551,7 @@ internal class ChildFactory @Inject constructor(
|
|||
context = context,
|
||||
params = WalletActivationComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
isBackupExists = route.isBackupExists,
|
||||
),
|
||||
componentFactory = walletActivationComponentFactory,
|
||||
)
|
||||
|
|
@ -534,6 +561,8 @@ internal class ChildFactory @Inject constructor(
|
|||
context = context,
|
||||
params = CreateWalletBackupComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
isUpgradeFlow = route.isUpgradeFlow,
|
||||
shouldSetAccessCode = route.setAccessCode,
|
||||
),
|
||||
componentFactory = createWalletBackupComponentFactory,
|
||||
)
|
||||
|
|
@ -556,6 +585,15 @@ internal class ChildFactory @Inject constructor(
|
|||
componentFactory = viewPhraseComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.ForgetWallet -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = ForgetWalletComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
),
|
||||
componentFactory = forgetWalletComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.SendEntryPoint -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
|
|
@ -605,8 +643,11 @@ internal class ChildFactory @Inject constructor(
|
|||
is AppRoute.TangemPayDetails -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = TangemPayDetailsComponent.Params(config = route.config),
|
||||
componentFactory = tangemPayDetailsComponentFactory,
|
||||
params = TangemPayDetailsContainerComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
config = route.config,
|
||||
),
|
||||
componentFactory = tangemPayDetailsContainerComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.TangemPayOnboarding -> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue