Updated on 2026-08-14
This commit is contained in:
commit
3fba2ae059
872 changed files with 17188 additions and 8108 deletions
|
|
@ -42,8 +42,8 @@ configurations.all {
|
|||
|
||||
|
||||
dependencies {
|
||||
implementation(files("libs/walletconnect-1.5.6.aar"))
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.core)
|
||||
implementation(projects.domain.card)
|
||||
|
|
@ -67,6 +67,8 @@ dependencies {
|
|||
implementation(projects.domain.feedback)
|
||||
implementation(projects.domain.qrScanning)
|
||||
implementation(projects.domain.qrScanning.models)
|
||||
implementation(projects.domain.staking)
|
||||
implementation(projects.domain.walletConnect)
|
||||
|
||||
implementation(projects.common)
|
||||
implementation(projects.core.analytics)
|
||||
|
|
@ -77,9 +79,11 @@ dependencies {
|
|||
implementation(projects.core.ui)
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.deepLinks)
|
||||
implementation(projects.libs.crypto)
|
||||
implementation(projects.libs.auth)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
|
||||
implementation(projects.data.appCurrency)
|
||||
implementation(projects.data.appTheme)
|
||||
|
|
@ -87,7 +91,6 @@ dependencies {
|
|||
implementation(projects.data.card)
|
||||
implementation(projects.data.common)
|
||||
implementation(projects.data.settings)
|
||||
implementation(projects.data.source.preferences)
|
||||
implementation(projects.data.tokens)
|
||||
implementation(projects.data.txhistory)
|
||||
implementation(projects.data.wallets)
|
||||
|
|
@ -98,6 +101,8 @@ dependencies {
|
|||
implementation(projects.data.onboarding)
|
||||
implementation(projects.data.feedback)
|
||||
implementation(projects.data.qrScanning)
|
||||
implementation(projects.data.staking)
|
||||
implementation(projects.data.walletConnect)
|
||||
|
||||
/** Features */
|
||||
implementation(projects.features.onboarding)
|
||||
|
|
@ -121,6 +126,10 @@ dependencies {
|
|||
implementation(projects.features.send.impl)
|
||||
implementation(projects.features.qrScanning.api)
|
||||
implementation(projects.features.qrScanning.impl)
|
||||
implementation(projects.features.staking.api)
|
||||
implementation(projects.features.staking.impl)
|
||||
implementation(projects.features.details.api)
|
||||
implementation(projects.features.details.impl)
|
||||
|
||||
/** AndroidX libraries */
|
||||
implementation(deps.androidx.core.ktx)
|
||||
|
|
@ -183,7 +192,6 @@ dependencies {
|
|||
implementation(deps.reKotlin)
|
||||
implementation(deps.zxing.qrCore)
|
||||
implementation(deps.coil)
|
||||
implementation(deps.appsflyer)
|
||||
implementation(deps.amplitude)
|
||||
implementation(deps.kotsonGson)
|
||||
implementation(deps.spongecastle.core)
|
||||
|
|
@ -216,9 +224,12 @@ dependencies {
|
|||
androidTestImplementation(deps.test.kaspresso.compose)
|
||||
androidTestImplementation(deps.test.compose.junit)
|
||||
androidTestImplementation(deps.test.hamcrest)
|
||||
androidTestImplementation(deps.test.hilt)
|
||||
kaptAndroidTest(deps.test.hilt.compiler)
|
||||
|
||||
/** Chucker */
|
||||
debugImplementation(deps.chucker)
|
||||
mockedImplementation(deps.chuckerStub)
|
||||
externalImplementation(deps.chuckerStub)
|
||||
internalImplementation(deps.chuckerStub)
|
||||
releaseImplementation(deps.chuckerStub)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.common
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.tangem.tap.ApplicationEntryPoint
|
||||
import com.tangem.tap.TangemApplication
|
||||
import dagger.hilt.android.testing.OnComponentReadyRunner
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
|
||||
class ApplicationInjectionExecutionRule : TestRule {
|
||||
|
||||
private val tangemApplication: TangemApplication
|
||||
get() = ApplicationProvider.getApplicationContext()
|
||||
|
||||
override fun apply(base: Statement, description: Description): Statement {
|
||||
return object : Statement() {
|
||||
override fun evaluate() {
|
||||
OnComponentReadyRunner.addListener(
|
||||
tangemApplication, ApplicationEntryPoint::class.java
|
||||
) { _: ApplicationEntryPoint ->
|
||||
tangemApplication.init()
|
||||
}
|
||||
base.evaluate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt
Normal file
56
app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package com.tangem.common
|
||||
|
||||
import android.Manifest
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import com.kaspersky.components.composesupport.config.withComposeSupport
|
||||
import com.kaspersky.kaspresso.kaspresso.Kaspresso
|
||||
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
|
||||
import com.tangem.tap.MainActivity
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.RuleChain
|
||||
import org.junit.runner.RunWith
|
||||
import javax.inject.Inject
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
abstract class BaseTestCase : TestCase(
|
||||
kaspressoBuilder = Kaspresso.Builder.withComposeSupport()
|
||||
) {
|
||||
|
||||
@Inject
|
||||
lateinit var tangemSdkManager: TangemSdkManager
|
||||
|
||||
@get:Rule
|
||||
open val composeTestRule = createAndroidComposeRule<MainActivity>()
|
||||
|
||||
@get:Rule
|
||||
val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
|
||||
Manifest.permission.POST_NOTIFICATIONS,
|
||||
Manifest.permission.CAMERA
|
||||
)
|
||||
|
||||
private val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val ruleChain = RuleChain
|
||||
.outerRule(hiltRule)
|
||||
.around(ApplicationInjectionExecutionRule())
|
||||
|
||||
protected fun setupHooks(
|
||||
additionalBeforeSection: () -> Unit = {},
|
||||
additionalAfterSection: () -> Unit = {},
|
||||
) = before {
|
||||
hiltRule.inject()
|
||||
Intents.init()
|
||||
additionalBeforeSection()
|
||||
}.after {
|
||||
additionalAfterSection()
|
||||
Intents.release()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.common
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.test.runner.AndroidJUnitRunner
|
||||
import com.tangem.common.di.TangemMockedApplication_Application
|
||||
|
||||
class HiltTestRunner : AndroidJUnitRunner() {
|
||||
|
||||
override fun newApplication(
|
||||
cl: ClassLoader?,
|
||||
className: String?,
|
||||
context: Context?
|
||||
): Application {
|
||||
return super.newApplication(cl, TangemMockedApplication_Application::class.java.name, context)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.common
|
||||
|
||||
import com.tangem.tap.TangemApplication
|
||||
|
||||
open class TangemEmptyApplication : TangemApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
// super.onCreate() is not called intentionally
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.common.di
|
||||
|
||||
import com.tangem.common.TangemEmptyApplication
|
||||
import dagger.hilt.android.testing.CustomTestApplication
|
||||
|
||||
@CustomTestApplication(TangemEmptyApplication::class)
|
||||
internal class TangemMockedApplication
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.common.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.tap.di.TangemSdkManagerModule
|
||||
import com.tangem.tap.domain.sdk.impl.MockTangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.hilt.testing.TestInstallIn
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@TestInstallIn(
|
||||
components = [SingletonComponent::class],
|
||||
replaces = [TangemSdkManagerModule::class]
|
||||
)
|
||||
object TestModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTangemSdkManager(
|
||||
@ApplicationContext context: Context
|
||||
): TangemSdkManager {
|
||||
return MockTangemSdkManager(resources = context.resources)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package com.tangem.helpers.base
|
||||
|
||||
import android.Manifest
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import com.kaspersky.components.composesupport.config.withComposeSupport
|
||||
import com.kaspersky.kaspresso.kaspresso.Kaspresso
|
||||
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
|
||||
import com.tangem.tap.MainActivity
|
||||
import org.junit.Rule
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
open class BaseAutoTestCase : TestCase(
|
||||
kaspressoBuilder = Kaspresso.Builder.withComposeSupport()
|
||||
) {
|
||||
|
||||
@get:Rule
|
||||
open val composeTestRule = createAndroidComposeRule<MainActivity>()
|
||||
|
||||
@get: Rule
|
||||
val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
|
||||
Manifest.permission.POST_NOTIFICATIONS,
|
||||
Manifest.permission.CAMERA
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import com.kaspersky.kaspresso.screens.KScreen
|
||||
import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
|
||||
import com.tangem.wallet.R
|
||||
import io.github.kakaocup.kakao.text.KButton
|
||||
|
||||
object DisclaimerScreen : KScreen<DisclaimerScreen>(){
|
||||
|
||||
override val layoutId = R.layout.fragment_disclaimer
|
||||
|
||||
override val viewClass = DisclaimerFragment::class.java
|
||||
|
||||
val acceptButton: KButton = KButton {
|
||||
withId(R.id.btn_accept)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.tap.common.compose.resources.C
|
||||
import com.tangem.core.ui.test.TestTags
|
||||
import com.tangem.wallet.R
|
||||
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||
import io.github.kakaocup.compose.node.element.KNode
|
||||
|
|
@ -11,15 +11,15 @@ import io.github.kakaocup.kakao.text.KButton
|
|||
class StoriesScreen(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<StoriesScreen>(
|
||||
semanticsProvider = semanticsProvider,
|
||||
viewBuilderAction = { hasTestTag(C.Tag.STORIES_SCREEN) }
|
||||
viewBuilderAction = { hasTestTag(TestTags.STORIES_SCREEN) }
|
||||
) {
|
||||
|
||||
val scanButton: KNode = child {
|
||||
hasTestTag(C.Tag.STORIES_SCREEN_SCAN_BUTTON)
|
||||
hasTestTag(TestTags.STORIES_SCREEN_SCAN_BUTTON)
|
||||
}
|
||||
|
||||
val orderButton: KNode = child {
|
||||
hasTestTag(C.Tag.STORIES_SCREEN_ORDER_BUTTON)
|
||||
hasTestTag(TestTags.STORIES_SCREEN_ORDER_BUTTON)
|
||||
}
|
||||
|
||||
val enableNFCAlert: KView = KView {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.screens
|
||||
|
||||
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
|
||||
import com.tangem.core.ui.test.TestTags
|
||||
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||
|
||||
class WalletScreen(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
||||
ComposeScreen<WalletScreen>(
|
||||
semanticsProvider = semanticsProvider,
|
||||
viewBuilderAction = { hasTestTag(TestTags.WALLET_SCREEN) }
|
||||
)
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.tests
|
||||
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.screens.DisclaimerScreen
|
||||
import com.tangem.screens.StoriesScreen
|
||||
import com.tangem.screens.WalletScreen
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||
import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class MainScreenTest : BaseTestCase() {
|
||||
|
||||
@Test
|
||||
fun goToMain() =
|
||||
setupHooks().run {
|
||||
ComposeScreen.onComposeScreen<StoriesScreen>(composeTestRule) {
|
||||
step("Click on \"Scan\" button") {
|
||||
scanButton {
|
||||
assertIsDisplayed()
|
||||
performClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
DisclaimerScreen {
|
||||
step("Click on \"Accept\" button") {
|
||||
acceptButton {
|
||||
isVisible()
|
||||
click()
|
||||
}
|
||||
}
|
||||
}
|
||||
ComposeScreen.onComposeScreen<WalletScreen>(composeTestRule) {
|
||||
step("Make sure wallet screen is visible") {
|
||||
assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
app/src/androidTest/kotlin/com/tangem/tests/ScanErrorTest.kt
Normal file
48
app/src/androidTest/kotlin/com/tangem/tests/ScanErrorTest.kt
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package com.tangem.tests
|
||||
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.screens.DisclaimerScreen
|
||||
import com.tangem.screens.StoriesScreen
|
||||
import com.tangem.screens.WalletScreen
|
||||
import com.tangem.tap.domain.sdk.mocks.MockProvider
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||
import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class ScanErrorTest : BaseTestCase() {
|
||||
|
||||
@Test
|
||||
fun goToMain() =
|
||||
setupHooks().run {
|
||||
ComposeScreen.onComposeScreen<StoriesScreen>(composeTestRule) {
|
||||
step("Click on \"Scan\" button emulating scan error") {
|
||||
MockProvider.setEmulateError()
|
||||
scanButton {
|
||||
assertIsDisplayed()
|
||||
performClick()
|
||||
}
|
||||
}
|
||||
step("Click on \"Scan\" button again without emulating error") {
|
||||
MockProvider.resetEmulateError()
|
||||
scanButton {
|
||||
assertIsDisplayed()
|
||||
performClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
DisclaimerScreen {
|
||||
step("Click on \"Accept\" button") {
|
||||
acceptButton {
|
||||
isVisible()
|
||||
click()
|
||||
}
|
||||
}
|
||||
}
|
||||
ComposeScreen.onComposeScreen<WalletScreen>(composeTestRule) {
|
||||
step("Make sure wallet screen is visible") {
|
||||
assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +1,43 @@
|
|||
package com.tangem.tests
|
||||
|
||||
import android.content.Intent.ACTION_VIEW
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import com.tangem.helpers.base.BaseAutoTestCase
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.screens.StoriesScreen
|
||||
import com.tangem.tap.features.home.redux.HomeMiddleware.NEW_BUY_WALLET_URL
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.github.kakaocup.compose.node.element.ComposeScreen
|
||||
import io.github.kakaocup.kakao.intent.KIntent
|
||||
import org.junit.Test
|
||||
|
||||
class StoriesTest : BaseAutoTestCase() {
|
||||
@HiltAndroidTest
|
||||
class StoriesTest : BaseTestCase() {
|
||||
|
||||
@Test
|
||||
fun clickOnButtons() = before {
|
||||
Intents.init()
|
||||
}.after {
|
||||
Intents.release()
|
||||
}.run {
|
||||
ComposeScreen.onComposeScreen<StoriesScreen>(composeTestRule) {
|
||||
step("Click on \"Scan\" button") {
|
||||
scanButton {
|
||||
assertIsDisplayed()
|
||||
performClick()
|
||||
fun clickOnButtons() =
|
||||
setupHooks().run {
|
||||
ComposeScreen.onComposeScreen<StoriesScreen>(composeTestRule) {
|
||||
step("Click on \"Scan\" button") {
|
||||
scanButton {
|
||||
assertIsDisplayed()
|
||||
performClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
step("Assert: \"Scan card\" popup opened") {
|
||||
enableNFCAlert.isDisplayed()
|
||||
cancelButton.click()
|
||||
device.uiDevice.pressBack()
|
||||
}
|
||||
step("Click on \"Order\" button") {
|
||||
orderButton.performClick()
|
||||
}
|
||||
step("Assert: browser opened") {
|
||||
val expectedIntent = KIntent {
|
||||
hasAction(ACTION_VIEW)
|
||||
hasData(NEW_BUY_WALLET_URL)
|
||||
step("Assert: \"Scan card\" popup opened") {
|
||||
enableNFCAlert.isDisplayed()
|
||||
cancelButton.click()
|
||||
device.uiDevice.pressBack()
|
||||
}
|
||||
step("Click on \"Order\" button") {
|
||||
orderButton.performClick()
|
||||
}
|
||||
step("Assert: browser opened") {
|
||||
val expectedIntent = KIntent {
|
||||
hasAction(ACTION_VIEW)
|
||||
hasData(NEW_BUY_WALLET_URL)
|
||||
}
|
||||
expectedIntent.intended()
|
||||
device.uiDevice.pressBack()
|
||||
}
|
||||
expectedIntent.intended()
|
||||
device.uiDevice.pressBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@
|
|||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
|
|
@ -36,7 +35,7 @@
|
|||
</queries>
|
||||
|
||||
<application
|
||||
android:name="com.tangem.tap.TapApplication"
|
||||
android:name="com.tangem.tap.TangemHiltApplication"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:hardwareAccelerated="true"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 367aa6465ec6ee7fdccce6cc7d7abeed5aad1111
|
||||
Subproject commit 4424b32de5a80305cd3ca6eca219b09a16e940c5
|
||||
113
app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt
Normal file
113
app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
package com.tangem.tap
|
||||
|
||||
import com.tangem.TangemSdkLogger
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.datasource.local.token.UserTokensStore
|
||||
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
|
||||
import com.tangem.domain.apptheme.GetAppThemeModeUseCase
|
||||
import com.tangem.domain.apptheme.repository.AppThemeModeRepository
|
||||
import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
|
||||
import com.tangem.domain.card.ScanCardProcessor
|
||||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.feedback.FeedbackManagerFeatureToggles
|
||||
import com.tangem.domain.feedback.GetFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
|
||||
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
||||
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
|
||||
import com.tangem.features.details.DetailsEntryPoint
|
||||
import com.tangem.features.details.DetailsFeatureToggles
|
||||
import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles
|
||||
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
|
||||
import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository
|
||||
|
||||
@EntryPoint
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@Suppress("TooManyFunctions")
|
||||
interface ApplicationEntryPoint {
|
||||
|
||||
fun getConfigManager(): ConfigManager
|
||||
|
||||
fun getAppStateHolder(): AppStateHolder
|
||||
|
||||
fun getAssetLoader(): AssetLoader
|
||||
|
||||
fun getFeatureTogglesManager(): FeatureTogglesManager
|
||||
|
||||
fun getNetworkConnectionManager(): NetworkConnectionManager
|
||||
|
||||
fun getCustomTokenFeatureToggles(): CustomTokenFeatureToggles
|
||||
|
||||
fun getWalletConnect2Repository(): WalletConnect2Repository
|
||||
|
||||
fun getWalletConnectSessionsRepository(): WalletConnectSessionsRepository
|
||||
|
||||
fun getManageTokensFeatureToggles(): ManageTokensFeatureToggles
|
||||
|
||||
fun getScanCardProcessor(): ScanCardProcessor
|
||||
|
||||
fun getAppCurrencyRepository(): AppCurrencyRepository
|
||||
|
||||
fun getWalletManagersFacade(): WalletManagersFacade
|
||||
|
||||
fun getNetworksRepository(): NetworksRepository
|
||||
|
||||
fun getCurrenciesRepository(): CurrenciesRepository
|
||||
|
||||
fun getAppThemeModeRepository(): AppThemeModeRepository
|
||||
|
||||
fun getBalanceHidingRepository(): BalanceHidingRepository
|
||||
|
||||
fun getUserTokensStore(): UserTokensStore
|
||||
|
||||
fun getGetAppThemeModeUseCase(): GetAppThemeModeUseCase
|
||||
|
||||
fun getWalletsRepository(): WalletsRepository
|
||||
|
||||
fun getSendFeatureToggles(): SendFeatureToggles
|
||||
|
||||
fun getOneTimeEventFilter(): OneTimeEventFilter
|
||||
|
||||
fun getGeneralUserWalletsListManager(): UserWalletsListManager
|
||||
|
||||
fun getWasTwinsOnboardingShownUseCase(): WasTwinsOnboardingShownUseCase
|
||||
|
||||
fun getSaveTwinsOnboardingShownUseCase(): SaveTwinsOnboardingShownUseCase
|
||||
|
||||
fun getWalletNameGenerateUseCase(): GenerateWalletNameUseCase
|
||||
|
||||
fun getCardRepository(): CardRepository
|
||||
|
||||
fun getFeedbackManagerFeatureToggles(): FeedbackManagerFeatureToggles
|
||||
|
||||
fun getTangemSdkLogger(): TangemSdkLogger
|
||||
|
||||
fun getSettingsRepository(): SettingsRepository
|
||||
|
||||
fun getBlockchainSDKFactory(): BlockchainSDKFactory
|
||||
|
||||
fun getGetFeedbackEmailUseCase(): GetFeedbackEmailUseCase
|
||||
|
||||
fun getSaveBlockchainErrorUseCase(): SaveBlockchainErrorUseCase
|
||||
|
||||
fun getDetailsFeatureToggles(): DetailsFeatureToggles
|
||||
|
||||
fun getDetailsEntryPoint(): DetailsEntryPoint
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@ import androidx.lifecycle.LifecycleOwner
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.legacy.asLockable
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
|
|
@ -14,7 +16,9 @@ import kotlin.time.Duration
|
|||
|
||||
internal class LockUserWalletsTimer(
|
||||
owner: LifecycleOwner,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val duration: Duration = with(Duration) { 10.minutes },
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
) : LifecycleOwner by owner,
|
||||
DefaultLifecycleObserver {
|
||||
|
||||
|
|
@ -29,24 +33,35 @@ internal class LockUserWalletsTimer(
|
|||
}
|
||||
|
||||
override fun onResume(owner: LifecycleOwner) {
|
||||
Timber.d(
|
||||
"""
|
||||
owner.lifecycleScope.launch {
|
||||
val wasApplicationStopped = settingsRepository.wasApplicationStopped()
|
||||
val shouldOpenWelcomeScreenOnResume = settingsRepository.shouldOpenWelcomeScreenOnResume()
|
||||
|
||||
Timber.d(
|
||||
"""
|
||||
Owner resumed
|
||||
|- Was stopped: ${preferencesStorage.wasApplicationStopped}
|
||||
|- Need to open welcome screen: ${preferencesStorage.shouldOpenWelcomeScreenOnResume}
|
||||
""".trimIndent(),
|
||||
)
|
||||
preferencesStorage.wasApplicationStopped = false
|
||||
start()
|
||||
if (preferencesStorage.shouldOpenWelcomeScreenOnResume) {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome))
|
||||
preferencesStorage.shouldOpenWelcomeScreenOnResume = false
|
||||
|- Was stopped: $wasApplicationStopped
|
||||
|- Need to open welcome screen: $shouldOpenWelcomeScreenOnResume
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
settingsRepository.setWasApplicationStopped(value = false)
|
||||
|
||||
start()
|
||||
|
||||
if (shouldOpenWelcomeScreenOnResume) {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome))
|
||||
settingsRepository.setShouldOpenWelcomeScreenOnResume(value = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
Timber.d("Owner stopped")
|
||||
preferencesStorage.wasApplicationStopped = true
|
||||
|
||||
owner.lifecycleScope.launch {
|
||||
settingsRepository.setWasApplicationStopped(value = true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
|
|
@ -91,27 +106,30 @@ internal class LockUserWalletsTimer(
|
|||
|
||||
private fun createDelayJob(): Job = lifecycleScope.launch(Dispatchers.Default) {
|
||||
val startTime = System.currentTimeMillis()
|
||||
|
||||
delay(duration)
|
||||
|
||||
if (isActive) {
|
||||
val userWalletsListManager = userWalletsListManagerSafe?.asLockable()
|
||||
?: return@launch
|
||||
val userWalletsListManager = userWalletsListManager.asLockable() ?: return@launch
|
||||
|
||||
if (userWalletsListManager.hasUserWallets) {
|
||||
val currentTime = System.currentTimeMillis()
|
||||
val wasApplicationStopped = settingsRepository.wasApplicationStopped()
|
||||
|
||||
Timber.d(
|
||||
"""
|
||||
Finished
|
||||
|- App is stopped: ${preferencesStorage.wasApplicationStopped}
|
||||
|- App is stopped: $wasApplicationStopped
|
||||
|- Millis passed: ${currentTime - startTime}
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
if (preferencesStorage.wasApplicationStopped) {
|
||||
preferencesStorage.shouldOpenWelcomeScreenOnResume = true
|
||||
userWalletsListManager.lock()
|
||||
if (wasApplicationStopped) {
|
||||
settingsRepository.setShouldOpenWelcomeScreenOnResume(value = true)
|
||||
} else {
|
||||
store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Welcome))
|
||||
}
|
||||
|
||||
userWalletsListManager.lock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@ package com.tangem.tap
|
|||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.nfc.NfcAdapter
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
|
|
@ -30,6 +33,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.email.EmailSender
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
|
|
@ -37,11 +41,10 @@ import com.tangem.data.card.sdk.CardSdkLifecycleObserver
|
|||
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
|
||||
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManagerFeatureToggles
|
||||
import com.tangem.domain.wallets.legacy.asLockable
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.features.managetokens.navigation.ManageTokensUi
|
||||
|
|
@ -57,8 +60,7 @@ import com.tangem.tap.common.OnActivityResultCallback
|
|||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
|
||||
import com.tangem.tap.common.redux.NotificationsHandler
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.userWalletList.implementation.BiometricUserWalletsListManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
import com.tangem.tap.features.intentHandler.IntentProcessor
|
||||
import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler
|
||||
|
|
@ -94,12 +96,6 @@ private val mainCoroutineContext: CoroutineContext
|
|||
get() = Job() + Dispatchers.Main + FeatureCoroutineExceptionHandler.create("mainScope")
|
||||
val mainScope = CoroutineScope(mainCoroutineContext)
|
||||
|
||||
// TODO: Move to DI
|
||||
val userWalletsListManagerSafe: UserWalletsListManager?
|
||||
get() = store.state.globalState.userWalletsListManager
|
||||
val userWalletsListManager: UserWalletsListManager
|
||||
get() = userWalletsListManagerSafe!!
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbackHolder {
|
||||
|
|
@ -145,7 +141,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
lateinit var deepLinksRegistry: DeepLinksRegistry
|
||||
|
||||
@Inject
|
||||
lateinit var userWalletsListManagerFeatureToggles: UserWalletsListManagerFeatureToggles
|
||||
lateinit var settingsRepository: SettingsRepository
|
||||
|
||||
@Inject
|
||||
lateinit var getPolkadotCheckHasResetUseCase: GetPolkadotCheckHasResetUseCase
|
||||
|
|
@ -156,6 +152,12 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
@Inject
|
||||
lateinit var analyticsEventsHandler: AnalyticsEventHandler
|
||||
|
||||
@Inject
|
||||
lateinit var userWalletsListManager: UserWalletsListManager
|
||||
|
||||
@Inject
|
||||
lateinit var emailSender: EmailSender
|
||||
|
||||
internal val viewModel: MainViewModel by viewModels()
|
||||
|
||||
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode?>
|
||||
|
|
@ -170,11 +172,14 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
private val onActivityResultCallbacks = mutableListOf<OnActivityResultCallback>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
installSplashScreen()
|
||||
val splashScreen = installSplashScreen()
|
||||
|
||||
installAppTheme() // We need to call it before onCreate to prevent unnecessary activity recreation
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
splashScreen.setKeepOnScreenCondition { viewModel.isSplashScreenShown }
|
||||
|
||||
installActivityDependencies()
|
||||
observeAppThemeModeUpdates()
|
||||
|
||||
|
|
@ -217,7 +222,11 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
tangemSdkManager = injectedTangemSdkManager
|
||||
appStateHolder.tangemSdkManager = tangemSdkManager
|
||||
backupService = BackupService.init(cardSdkConfigRepository.sdk, this)
|
||||
lockUserWalletsTimer = LockUserWalletsTimer(owner = this)
|
||||
lockUserWalletsTimer = LockUserWalletsTimer(
|
||||
owner = this,
|
||||
settingsRepository = settingsRepository,
|
||||
userWalletsListManager = userWalletsListManager,
|
||||
)
|
||||
|
||||
initIntentHandlers()
|
||||
|
||||
|
|
@ -232,6 +241,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
cardSdkConfigRepository = cardSdkConfigRepository,
|
||||
sendRouter = sendRouter,
|
||||
qrScanningRouter = qrScanningRouter,
|
||||
emailSender = emailSender,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -264,9 +274,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
}
|
||||
|
||||
private fun createAppThemeModeFlow(): SharedFlow<AppThemeMode?> {
|
||||
val tapApplication = application as TapApplication
|
||||
val tangemApplication = application as TangemApplication
|
||||
|
||||
return tapApplication.getAppThemeModeUseCase()
|
||||
return tangemApplication.getAppThemeModeUseCase()
|
||||
.map { maybeMode ->
|
||||
maybeMode.getOrElse { AppThemeMode.DEFAULT }
|
||||
}
|
||||
|
|
@ -283,12 +293,31 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val nfcAdapter = NfcAdapter.getDefaultAdapter(this)
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
|
||||
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE,
|
||||
)
|
||||
val intentFilters = arrayOf(
|
||||
IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED),
|
||||
IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED),
|
||||
IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED),
|
||||
)
|
||||
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, null)
|
||||
// TODO: RESEARCH! NotificationsHandler is created in onResume and destroyed in onStop
|
||||
notificationsHandler = NotificationsHandler(binding.fragmentContainer)
|
||||
|
||||
navigateToInitialScreenIfNeeded(intent)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
val nfcAdapter = NfcAdapter.getDefaultAdapter(this)
|
||||
nfcAdapter.disableForegroundDispatch(this)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
notificationsHandler = null
|
||||
dialogManager.onStop()
|
||||
|
|
@ -303,7 +332,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
}
|
||||
|
||||
private fun initIntentHandlers() {
|
||||
val hasSavedWalletsProvider = { store.state.globalState.userWalletsListManager?.hasUserWallets == true }
|
||||
val hasSavedWalletsProvider = { userWalletsListManager.hasUserWallets }
|
||||
intentProcessor.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider, lifecycleScope))
|
||||
intentProcessor.addHandler(WalletConnectLinkIntentHandler())
|
||||
}
|
||||
|
|
@ -341,7 +370,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
cardSdkLifecycleObserver.onCreate(context = this)
|
||||
|
||||
lifecycleScope.launch {
|
||||
intentProcessor.handleIntent(intent)
|
||||
intentProcessor.handleIntent(intent, true)
|
||||
}
|
||||
|
||||
if (intent != null) {
|
||||
|
|
@ -437,15 +466,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
}
|
||||
|
||||
private fun navigateToInitialScreen(intentWhichStartedActivity: Intent?) {
|
||||
val canSaveWallets = if (userWalletsListManagerFeatureToggles.isGeneralManagerEnabled) {
|
||||
runCatching { userWalletsListManager.asLockable()?.isLockedSync }
|
||||
.fold(onSuccess = { true }, onFailure = { false })
|
||||
} else {
|
||||
userWalletsListManager is BiometricUserWalletsListManager
|
||||
}
|
||||
val hasSavedWallets = userWalletsListManager.hasUserWallets
|
||||
|
||||
if (canSaveWallets && hasSavedWallets) {
|
||||
if (userWalletsListManager.isLockable && userWalletsListManager.hasUserWallets) {
|
||||
store.dispatch(
|
||||
NavigationAction.NavigateTo(
|
||||
screen = AppScreen.Welcome,
|
||||
|
|
@ -457,7 +478,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
} else {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Home))
|
||||
lifecycleScope.launch {
|
||||
intentProcessor.handleIntent(intentWhichStartedActivity)
|
||||
intentProcessor.handleIntent(intentWhichStartedActivity, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,17 +11,14 @@ import com.orhanobut.logger.Logger
|
|||
import com.tangem.Log
|
||||
import com.tangem.LogFormat
|
||||
import com.tangem.TangemSdkLogger
|
||||
import com.tangem.blockchain.common.AccountCreator
|
||||
import com.tangem.blockchain.common.datastorage.BlockchainDataStorage
|
||||
import com.tangem.blockchain.common.logging.BlockchainSDKLogger
|
||||
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.data.source.preferences.PreferencesDataSource
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
|
||||
import com.tangem.datasource.asset.AssetReader
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.config.FeaturesLocalLoader
|
||||
import com.tangem.datasource.config.models.Config
|
||||
|
|
@ -35,20 +32,24 @@ import com.tangem.domain.card.ScanCardProcessor
|
|||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.common.LogConfig
|
||||
import com.tangem.domain.feedback.FeedbackManagerFeatureToggles
|
||||
import com.tangem.domain.feedback.GetFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
|
||||
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
||||
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManagerFeatureToggles
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
|
||||
import com.tangem.features.details.DetailsEntryPoint
|
||||
import com.tangem.features.details.DetailsFeatureToggles
|
||||
import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles
|
||||
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles
|
||||
import com.tangem.tap.common.analytics.AnalyticsFactory
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
||||
import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.chat.ChatManager
|
||||
import com.tangem.tap.common.feedback.AdditionalFeedbackInfo
|
||||
|
|
@ -61,134 +62,140 @@ import com.tangem.tap.common.redux.appReducer
|
|||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import com.tangem.tap.domain.tasks.product.DerivationsFinder
|
||||
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
|
||||
import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
|
||||
import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import dagger.hilt.EntryPoints
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.rekotlin.Store
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectRepository as WalletConnect2Repository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository
|
||||
|
||||
lateinit var store: Store<AppState>
|
||||
|
||||
lateinit var foregroundActivityObserver: ForegroundActivityObserver
|
||||
lateinit var activityResultCaller: ActivityResultCaller
|
||||
lateinit var preferencesStorage: PreferencesDataSource
|
||||
lateinit var walletConnectRepository: WalletConnectRepository
|
||||
internal lateinit var derivationsFinder: DerivationsFinder
|
||||
|
||||
@HiltAndroidApp
|
||||
internal class TapApplication : Application(), ImageLoaderFactory {
|
||||
abstract class TangemApplication : Application(), ImageLoaderFactory {
|
||||
|
||||
// region Injected
|
||||
@Inject
|
||||
lateinit var appStateHolder: AppStateHolder
|
||||
// region DI
|
||||
private val entryPoint: ApplicationEntryPoint
|
||||
get() = EntryPoints.get(this, ApplicationEntryPoint::class.java)
|
||||
|
||||
@Inject
|
||||
lateinit var configManager: ConfigManager
|
||||
private val appStateHolder: AppStateHolder
|
||||
get() = entryPoint.getAppStateHolder()
|
||||
|
||||
@Inject
|
||||
lateinit var assetReader: AssetReader
|
||||
private val configManager: ConfigManager
|
||||
get() = entryPoint.getConfigManager()
|
||||
|
||||
@Inject
|
||||
lateinit var featureTogglesManager: FeatureTogglesManager
|
||||
private val assetLoader: AssetLoader
|
||||
get() = entryPoint.getAssetLoader()
|
||||
|
||||
@Inject
|
||||
lateinit var networkConnectionManager: NetworkConnectionManager
|
||||
private val featureTogglesManager: FeatureTogglesManager
|
||||
get() = entryPoint.getFeatureTogglesManager()
|
||||
|
||||
@Inject
|
||||
lateinit var customTokenFeatureToggles: CustomTokenFeatureToggles
|
||||
private val networkConnectionManager: NetworkConnectionManager
|
||||
get() = entryPoint.getNetworkConnectionManager()
|
||||
|
||||
@Inject
|
||||
lateinit var preferencesDataSource: PreferencesDataSource
|
||||
private val customTokenFeatureToggles: CustomTokenFeatureToggles
|
||||
get() = entryPoint.getCustomTokenFeatureToggles()
|
||||
|
||||
@Inject
|
||||
lateinit var walletConnect2Repository: WalletConnect2Repository
|
||||
private val walletConnect2Repository: WalletConnect2Repository
|
||||
get() = entryPoint.getWalletConnect2Repository()
|
||||
|
||||
@Inject
|
||||
lateinit var walletConnectSessionsRepository: WalletConnectSessionsRepository
|
||||
private val walletConnectSessionsRepository: WalletConnectSessionsRepository
|
||||
get() = entryPoint.getWalletConnectSessionsRepository()
|
||||
|
||||
@Inject
|
||||
lateinit var manageTokensFeatureToggles: ManageTokensFeatureToggles
|
||||
private val manageTokensFeatureToggles: ManageTokensFeatureToggles
|
||||
get() = entryPoint.getManageTokensFeatureToggles()
|
||||
|
||||
@Inject
|
||||
lateinit var scanCardProcessor: ScanCardProcessor
|
||||
private val scanCardProcessor: ScanCardProcessor
|
||||
get() = entryPoint.getScanCardProcessor()
|
||||
|
||||
@Inject
|
||||
lateinit var appCurrencyRepository: AppCurrencyRepository
|
||||
private val appCurrencyRepository: AppCurrencyRepository
|
||||
get() = entryPoint.getAppCurrencyRepository()
|
||||
|
||||
@Inject
|
||||
lateinit var walletManagersFacade: WalletManagersFacade
|
||||
private val walletManagersFacade: WalletManagersFacade
|
||||
get() = entryPoint.getWalletManagersFacade()
|
||||
|
||||
@Inject
|
||||
lateinit var networksRepository: NetworksRepository
|
||||
private val networksRepository: NetworksRepository
|
||||
get() = entryPoint.getNetworksRepository()
|
||||
|
||||
@Inject
|
||||
lateinit var currenciesRepository: CurrenciesRepository
|
||||
private val currenciesRepository: CurrenciesRepository
|
||||
get() = entryPoint.getCurrenciesRepository()
|
||||
|
||||
@Inject
|
||||
lateinit var appThemeModeRepository: AppThemeModeRepository
|
||||
private val appThemeModeRepository: AppThemeModeRepository
|
||||
get() = entryPoint.getAppThemeModeRepository()
|
||||
|
||||
@Inject
|
||||
lateinit var balanceHidingRepository: BalanceHidingRepository
|
||||
private val balanceHidingRepository: BalanceHidingRepository
|
||||
get() = entryPoint.getBalanceHidingRepository()
|
||||
|
||||
@Inject
|
||||
lateinit var userTokensStore: UserTokensStore
|
||||
private val userTokensStore: UserTokensStore
|
||||
get() = entryPoint.getUserTokensStore()
|
||||
|
||||
@Inject
|
||||
lateinit var getAppThemeModeUseCase: GetAppThemeModeUseCase
|
||||
val getAppThemeModeUseCase: GetAppThemeModeUseCase
|
||||
get() = entryPoint.getGetAppThemeModeUseCase()
|
||||
|
||||
@Inject
|
||||
lateinit var walletsRepository: WalletsRepository
|
||||
private val walletsRepository: WalletsRepository
|
||||
get() = entryPoint.getWalletsRepository()
|
||||
|
||||
@Inject
|
||||
lateinit var sendFeatureToggles: SendFeatureToggles
|
||||
private val sendFeatureToggles: SendFeatureToggles
|
||||
get() = entryPoint.getSendFeatureToggles()
|
||||
|
||||
@Inject
|
||||
lateinit var oneTimeEventFilter: OneTimeEventFilter
|
||||
private val oneTimeEventFilter: OneTimeEventFilter
|
||||
get() = entryPoint.getOneTimeEventFilter()
|
||||
|
||||
@Inject
|
||||
lateinit var blockchainDataStorage: BlockchainDataStorage
|
||||
private val generalUserWalletsListManager: UserWalletsListManager
|
||||
get() = entryPoint.getGeneralUserWalletsListManager()
|
||||
|
||||
@Inject
|
||||
lateinit var accountCreator: AccountCreator
|
||||
private val wasTwinsOnboardingShownUseCase: WasTwinsOnboardingShownUseCase
|
||||
get() = entryPoint.getWasTwinsOnboardingShownUseCase()
|
||||
|
||||
@Inject
|
||||
lateinit var userWalletsListManagerFeatureToggles: UserWalletsListManagerFeatureToggles
|
||||
private val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase
|
||||
get() = entryPoint.getSaveTwinsOnboardingShownUseCase()
|
||||
|
||||
@Inject
|
||||
lateinit var generalUserWalletsListManager: UserWalletsListManager
|
||||
private val generateWalletNameUseCase: GenerateWalletNameUseCase
|
||||
get() = entryPoint.getWalletNameGenerateUseCase()
|
||||
|
||||
@Inject
|
||||
lateinit var wasTwinsOnboardingShownUseCase: WasTwinsOnboardingShownUseCase
|
||||
private val cardRepository: CardRepository
|
||||
get() = entryPoint.getCardRepository()
|
||||
|
||||
@Inject
|
||||
lateinit var saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase
|
||||
private val feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles
|
||||
get() = entryPoint.getFeedbackManagerFeatureToggles()
|
||||
|
||||
@Inject
|
||||
lateinit var cardRepository: CardRepository
|
||||
private val tangemSdkLogger: TangemSdkLogger
|
||||
get() = entryPoint.getTangemSdkLogger()
|
||||
|
||||
@Inject
|
||||
lateinit var feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles
|
||||
private val settingsRepository: SettingsRepository
|
||||
get() = entryPoint.getSettingsRepository()
|
||||
|
||||
@Inject
|
||||
lateinit var blockchainSDKLogger: BlockchainSDKLogger
|
||||
private val blockchainSDKFactory: BlockchainSDKFactory
|
||||
get() = entryPoint.getBlockchainSDKFactory()
|
||||
|
||||
@Inject
|
||||
lateinit var tangemSdkLogger: TangemSdkLogger
|
||||
// endregion Injected
|
||||
private val getFeedbackEmailUseCase: GetFeedbackEmailUseCase
|
||||
get() = entryPoint.getGetFeedbackEmailUseCase()
|
||||
|
||||
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase
|
||||
get() = entryPoint.getSaveBlockchainErrorUseCase()
|
||||
// endregion
|
||||
|
||||
private val detailsFeatureToggles: DetailsFeatureToggles
|
||||
get() = entryPoint.getDetailsFeatureToggles()
|
||||
|
||||
private val detailsEntryPoint: DetailsEntryPoint
|
||||
get() = entryPoint.getDetailsEntryPoint()
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
init()
|
||||
}
|
||||
|
||||
fun init() {
|
||||
store = createReduxStore()
|
||||
|
||||
if (BuildConfig.LOG_ENABLED) {
|
||||
|
|
@ -206,23 +213,16 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
activityResultCaller = foregroundActivityObserver
|
||||
registerActivityLifecycleCallbacks(foregroundActivityObserver.callbacks)
|
||||
|
||||
preferencesStorage = preferencesDataSource
|
||||
walletConnectRepository = WalletConnectRepository(this)
|
||||
|
||||
// TODO: Try to performance and user experience.
|
||||
// [REDACTED_JIRA]
|
||||
runBlocking {
|
||||
featureTogglesManager.init()
|
||||
|
||||
if (userWalletsListManagerFeatureToggles.isGeneralManagerEnabled) {
|
||||
store.dispatch(GlobalAction.UpdateUserWalletsListManager(generalUserWalletsListManager))
|
||||
} else {
|
||||
initUserWalletsListManager()
|
||||
}
|
||||
initConfigManager(
|
||||
loader = FeaturesLocalLoader(assetLoader, BuildConfig.ENVIRONMENT),
|
||||
onComplete = ::initWithConfigDependency,
|
||||
)
|
||||
}
|
||||
|
||||
val configLoader = FeaturesLocalLoader(assetReader, MoshiConverter.sdkMoshi, BuildConfig.ENVIRONMENT)
|
||||
initConfigManager(configLoader, ::initWithConfigDependency)
|
||||
initWarningMessagesManager()
|
||||
|
||||
loadNativeLibraries()
|
||||
|
|
@ -264,16 +264,18 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
balanceHidingRepository = balanceHidingRepository,
|
||||
walletsRepository = walletsRepository,
|
||||
sendFeatureToggles = sendFeatureToggles,
|
||||
blockchainDataStorage = blockchainDataStorage,
|
||||
accountCreator = accountCreator,
|
||||
userWalletsListManagerFeatureToggles = userWalletsListManagerFeatureToggles,
|
||||
generalUserWalletsListManager = generalUserWalletsListManager,
|
||||
wasTwinsOnboardingShownUseCase = wasTwinsOnboardingShownUseCase,
|
||||
saveTwinsOnboardingShownUseCase = saveTwinsOnboardingShownUseCase,
|
||||
generateWalletNameUseCase = generateWalletNameUseCase,
|
||||
cardRepository = cardRepository,
|
||||
feedbackManagerFeatureToggles = feedbackManagerFeatureToggles,
|
||||
tangemSdkLogger = tangemSdkLogger,
|
||||
blockchainSDKLogger = blockchainSDKLogger,
|
||||
settingsRepository = settingsRepository,
|
||||
blockchainSDKFactory = blockchainSDKFactory,
|
||||
saveBlockchainErrorUseCase = saveBlockchainErrorUseCase,
|
||||
detailsFeatureToggles = detailsFeatureToggles,
|
||||
detailsEntryPoint = detailsEntryPoint,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -290,7 +292,7 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
System.loadLibrary("TrustWalletCore")
|
||||
}
|
||||
|
||||
private fun initConfigManager(loader: FeaturesLocalLoader, onComplete: (Config) -> Unit) {
|
||||
private suspend fun initConfigManager(loader: FeaturesLocalLoader, onComplete: (Config) -> Unit) {
|
||||
configManager.load(loader) { config ->
|
||||
store.dispatch(GlobalAction.SetConfigManager(configManager))
|
||||
onComplete(config)
|
||||
|
|
@ -305,7 +307,6 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
private fun initAnalytics(application: Application, config: Config) {
|
||||
val factory = AnalyticsFactory()
|
||||
factory.addHandlerBuilder(AmplitudeAnalyticsHandler.Builder())
|
||||
factory.addHandlerBuilder(AppsFlyerAnalyticsHandler.Builder())
|
||||
factory.addHandlerBuilder(FirebaseAnalyticsHandler.Builder())
|
||||
|
||||
factory.addFilter(oneTimeEventFilter)
|
||||
|
|
@ -366,6 +367,8 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
infoHolder = additionalFeedbackInfo,
|
||||
logCollector = tangemLogCollector,
|
||||
chatManager = ChatManager(foregroundActivityObserver),
|
||||
feedbackManagerFeatureToggles = feedbackManagerFeatureToggles,
|
||||
getFeedbackEmailUseCase = getFeedbackEmailUseCase,
|
||||
)
|
||||
store.dispatch(GlobalAction.SetFeedbackManager(feedbackManager))
|
||||
}
|
||||
|
|
@ -373,14 +376,4 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
private fun initWarningMessagesManager() {
|
||||
store.dispatch(GlobalAction.SetWarningManager(WarningMessagesManager()))
|
||||
}
|
||||
|
||||
private suspend fun initUserWalletsListManager() {
|
||||
val manager = if (walletsRepository.shouldSaveUserWalletsSync()) {
|
||||
UserWalletsListManager.provideBiometricImplementation(applicationContext)
|
||||
} else {
|
||||
UserWalletsListManager.provideRuntimeImplementation()
|
||||
}
|
||||
|
||||
store.dispatch(GlobalAction.UpdateUserWalletsListManager(manager))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.tap
|
||||
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
@HiltAndroidApp
|
||||
class TangemHiltApplication : TangemApplication()
|
||||
|
|
@ -86,10 +86,6 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
context = context,
|
||||
)
|
||||
}
|
||||
is WalletConnectDialog.ApproveWcSession ->
|
||||
ApproveWcSessionDialog.create(state.dialog.session, state.dialog.networks, context)
|
||||
is WalletConnectDialog.ChooseNetwork ->
|
||||
ChooseNetworkDialog.create(state.dialog.session, state.dialog.networks, context)
|
||||
is WalletConnectDialog.ClipboardOrScanQr ->
|
||||
ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context)
|
||||
is WalletConnectDialog.RequestTransaction -> TransactionDialog.create(state.dialog.data, context)
|
||||
|
|
|
|||
|
|
@ -13,20 +13,20 @@ sealed class AnalyticsParam {
|
|||
}
|
||||
|
||||
sealed class CardBalanceState(val value: String) {
|
||||
object Empty : CardBalanceState("Empty")
|
||||
object Full : CardBalanceState("Full")
|
||||
data object Empty : CardBalanceState("Empty")
|
||||
data object Full : CardBalanceState("Full")
|
||||
companion object
|
||||
}
|
||||
|
||||
sealed class RateApp(val value: String) {
|
||||
object Liked : RateApp("Liked")
|
||||
object Closed : RateApp("Close")
|
||||
data object Liked : RateApp("Liked")
|
||||
data object Closed : RateApp("Close")
|
||||
}
|
||||
|
||||
sealed class OnOffState(val value: String) {
|
||||
|
||||
object On : OnOffState("On")
|
||||
object Off : OnOffState("Off")
|
||||
data object On : OnOffState("On")
|
||||
data object Off : OnOffState("Off")
|
||||
|
||||
companion object {
|
||||
|
||||
|
|
@ -35,13 +35,13 @@ sealed class AnalyticsParam {
|
|||
}
|
||||
|
||||
sealed class UserCode(val value: String) {
|
||||
object AccessCode : UserCode("Access Code")
|
||||
data object AccessCode : UserCode("Access Code")
|
||||
}
|
||||
|
||||
sealed class SecurityMode(val value: String) {
|
||||
object AccessCode : SecurityMode("Access Code")
|
||||
object Passcode : SecurityMode("Passcode")
|
||||
object LongTap : SecurityMode("Long Tap")
|
||||
data object AccessCode : SecurityMode("Access Code")
|
||||
data object Passcode : SecurityMode("Passcode")
|
||||
data object LongTap : SecurityMode("Long Tap")
|
||||
|
||||
companion object {
|
||||
fun from(option: SecurityOption): SecurityMode = when (option) {
|
||||
|
|
@ -56,8 +56,8 @@ sealed class AnalyticsParam {
|
|||
|
||||
val key: String = "Status"
|
||||
|
||||
object Enabled : AccessCodeRecoveryStatus("Enabled")
|
||||
object Disabled : AccessCodeRecoveryStatus("Disabled")
|
||||
data object Enabled : AccessCodeRecoveryStatus("Enabled")
|
||||
data object Disabled : AccessCodeRecoveryStatus("Disabled")
|
||||
|
||||
companion object {
|
||||
fun from(enabled: Boolean): AccessCodeRecoveryStatus {
|
||||
|
|
@ -67,21 +67,21 @@ sealed class AnalyticsParam {
|
|||
}
|
||||
|
||||
sealed class Error(val value: String) {
|
||||
object App : Error("App Error")
|
||||
object CardSdk : Error("Card Sdk Error")
|
||||
object BlockchainSdk : Error("Blockchain Sdk Error")
|
||||
data object App : Error("App Error")
|
||||
data object CardSdk : Error("Card Sdk Error")
|
||||
data object BlockchainSdk : Error("Blockchain Sdk Error")
|
||||
}
|
||||
|
||||
sealed class WalletCreationType(val value: String) {
|
||||
object PrivateKey : WalletCreationType(value = "Private Key")
|
||||
object NewSeed : WalletCreationType(value = "New Seed")
|
||||
object SeedImport : WalletCreationType(value = "Seed Import")
|
||||
data object PrivateKey : WalletCreationType(value = "Private Key")
|
||||
data object NewSeed : WalletCreationType(value = "New Seed")
|
||||
data object SeedImport : WalletCreationType(value = "Seed Import")
|
||||
}
|
||||
|
||||
sealed class AppTheme(val value: String) {
|
||||
object System : AppTheme("System")
|
||||
object Dark : AppTheme("Dark")
|
||||
object Light : AppTheme("Light")
|
||||
data object System : AppTheme("System")
|
||||
data object Dark : AppTheme("Dark")
|
||||
data object Light : AppTheme("Light")
|
||||
|
||||
companion object {
|
||||
fun fromAppThemeMode(mode: AppThemeMode): AppTheme {
|
||||
|
|
@ -104,6 +104,7 @@ sealed class AnalyticsParam {
|
|||
const val PERMISSION_TYPE = "Permission Type"
|
||||
const val PRODUCT_TYPE = "Product Type"
|
||||
const val FIRMWARE = "Firmware"
|
||||
const val USER_WALLET_ID = "User Wallet ID"
|
||||
const val CURRENCY = "Currency"
|
||||
const val ERROR_DESCRIPTION = "Error Description"
|
||||
const val ERROR_CODE = "Error Code"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.features.addCustomToken.CustomCurrency
|
||||
import com.tangem.tap.common.extensions.filterNotNull
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.handlers.appsFlyer
|
||||
|
||||
import com.appsflyer.AFInAppEventType
|
||||
import com.tangem.core.analytics.api.AnalyticsHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
||||
import com.tangem.tap.common.analytics.events.Shop
|
||||
|
||||
class AppsFlyerAnalyticsHandler(
|
||||
private val client: AppsFlyerAnalyticsClient,
|
||||
) : AnalyticsHandler {
|
||||
|
||||
override fun id(): String = ID
|
||||
|
||||
override fun send(eventId: String, params: Map<String, String>) {
|
||||
client.logEvent(eventId, params)
|
||||
}
|
||||
|
||||
override fun send(event: AnalyticsEvent) {
|
||||
if (event is Shop.Purchased) {
|
||||
send(AFInAppEventType.PURCHASE, event.params)
|
||||
} else {
|
||||
super.send(event)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ID = "AppsFlyer"
|
||||
}
|
||||
|
||||
class Builder : AnalyticsHandlerBuilder {
|
||||
override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsHandler? = when {
|
||||
!data.isDebug -> AppsFlyerClient(data.application, data.config.appsFlyerDevKey)
|
||||
data.isDebug && data.logConfig.appsFlyer -> AppsFlyerLogClient(data.jsonConverter)
|
||||
else -> null
|
||||
}?.let { AppsFlyerAnalyticsHandler(it) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.handlers.appsFlyer
|
||||
|
||||
import android.content.Context
|
||||
import com.appsflyer.AppsFlyerLib
|
||||
import com.tangem.core.analytics.api.EventLogger
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface AppsFlyerAnalyticsClient : EventLogger
|
||||
|
||||
internal class AppsFlyerClient(
|
||||
private val context: Context,
|
||||
key: String,
|
||||
) : AppsFlyerAnalyticsClient {
|
||||
|
||||
private val appsFlyerLib: AppsFlyerLib = AppsFlyerLib.getInstance()
|
||||
|
||||
init {
|
||||
appsFlyerLib.init(key, null, context)
|
||||
appsFlyerLib.start(context)
|
||||
}
|
||||
|
||||
override fun logEvent(event: String, params: Map<String, String>) {
|
||||
appsFlyerLib.logEvent(context, event, params)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.handlers.appsFlyer
|
||||
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.tap.common.analytics.AnalyticsEventsLogger
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class AppsFlyerLogClient(
|
||||
jsonConverter: MoshiJsonConverter,
|
||||
) : AppsFlyerAnalyticsClient {
|
||||
|
||||
private val logger = AnalyticsEventsLogger(AppsFlyerAnalyticsHandler.ID, jsonConverter)
|
||||
|
||||
override fun logEvent(event: String, params: Map<String, String>) {
|
||||
logger.logEvent(event, params)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
|
|||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.IntroductionProcess
|
||||
|
|
@ -18,6 +19,8 @@ class CardContextInterceptor(
|
|||
private val scanResponse: ScanResponse,
|
||||
) : ParamsInterceptor {
|
||||
|
||||
private val userWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build()
|
||||
|
||||
override fun id(): String = CardContextInterceptor.id()
|
||||
|
||||
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean {
|
||||
|
|
@ -32,6 +35,9 @@ class CardContextInterceptor(
|
|||
params[AnalyticsParam.BATCH] = card.batchId
|
||||
params[AnalyticsParam.PRODUCT_TYPE] = getProductType(scanResponse)
|
||||
params[AnalyticsParam.FIRMWARE] = card.firmwareVersion.stringValue
|
||||
if (userWalletId != null) {
|
||||
params[AnalyticsParam.USER_WALLET_ID] = userWalletId.stringValue
|
||||
}
|
||||
|
||||
ParamCardCurrencyConverter().convert(scanResponse.cardTypesResolver)?.let {
|
||||
params[AnalyticsParam.CURRENCY] = it.value
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.tap.common.clipboard
|
||||
|
||||
import android.content.ClipData
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import android.content.ClipboardManager as AndroidClipboardManager
|
||||
|
||||
internal class DefaultClipboardManager(private val clipboardManager: AndroidClipboardManager) : ClipboardManager {
|
||||
|
||||
override fun setText(label: String, text: String) {
|
||||
clipboardManager.setPrimaryClip(ClipData.newPlainText(label, text))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.tap.common.clipboard
|
||||
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import timber.log.Timber
|
||||
|
||||
internal object MockClipboardManager : ClipboardManager {
|
||||
override fun setText(label: String, text: String) {
|
||||
/** Intentionnaly do nothing */
|
||||
Timber.w("Clipboard Manager not available")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package com.tangem.tap.common.compose.resources
|
||||
|
||||
object C {
|
||||
object Tag {
|
||||
const val STORIES_SCREEN = "STORIES_SCREEN_CONTAINER"
|
||||
const val STORIES_SCREEN_SCAN_BUTTON = "STORIES_SCREEN_SCAN_BUTTON"
|
||||
const val STORIES_SCREEN_ORDER_BUTTON = "STORIES_SCREEN_ORDER_BUTTON"
|
||||
}
|
||||
}
|
||||
|
|
@ -137,7 +137,15 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
SendFragment()
|
||||
}
|
||||
}
|
||||
AppScreen.Details -> DetailsFragment()
|
||||
AppScreen.Details -> {
|
||||
val featureToggles = store.inject(getDependency = DaggerGraphState::detailsFeatureToggles)
|
||||
|
||||
if (featureToggles.isRedesignEnabled) {
|
||||
store.inject(DaggerGraphState::detailsEntryPoint).entryFragment()
|
||||
} else {
|
||||
DetailsFragment()
|
||||
}
|
||||
}
|
||||
AppScreen.DetailsSecurity -> SecurityModeFragment()
|
||||
AppScreen.CardSettings -> CardSettingsFragment()
|
||||
AppScreen.AppSettings -> AppSettingsFragment()
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import com.tangem.blockchain.common.BlockchainSdkError
|
|||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.blockchainsdk.utils.amountToCreateAccount
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.common.extensions.amountToCreateAccount
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.tap.common.TestActions
|
||||
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.tangem.blockchain.common.address.Address
|
|||
import com.tangem.crypto.NetworkType
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.userwallets.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
package com.tangem.tap.common.feedback
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.core.navigation.email.EmailSender
|
||||
import com.tangem.datasource.config.models.ChatConfig
|
||||
import com.tangem.domain.common.TapWorkarounds
|
||||
import com.tangem.domain.feedback.FeedbackManagerFeatureToggles
|
||||
import com.tangem.domain.feedback.GetFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.tap.common.chat.ChatManager
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.extensions.sendEmail
|
||||
import com.tangem.tap.common.log.TangemLogCollector
|
||||
import com.tangem.tap.foregroundActivityObserver
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.withForegroundActivity
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
|
|
@ -20,21 +29,46 @@ class FeedbackManager(
|
|||
val infoHolder: AdditionalFeedbackInfo,
|
||||
private val logCollector: TangemLogCollector,
|
||||
private val chatManager: ChatManager,
|
||||
private val feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles,
|
||||
private val getFeedbackEmailUseCase: GetFeedbackEmailUseCase,
|
||||
) {
|
||||
|
||||
private var sessionFeedbackFile: File? = null
|
||||
private var sessionLogsFile: File? = null
|
||||
|
||||
fun sendEmail(feedbackData: FeedbackData, onFail: ((Exception) -> Unit)? = null) {
|
||||
feedbackData.prepare(infoHolder)
|
||||
foregroundActivityObserver.withForegroundActivity { activity ->
|
||||
activity.sendEmail(
|
||||
email = getSupportEmail(),
|
||||
subject = activity.getString(feedbackData.subjectResId),
|
||||
message = feedbackData.joinTogether(activity, infoHolder),
|
||||
file = getLogFile(activity),
|
||||
onFail = onFail,
|
||||
)
|
||||
if (feedbackManagerFeatureToggles.isLocalLogsEnabled) {
|
||||
mainScope.launch {
|
||||
val email = getFeedbackEmailUseCase(
|
||||
when (feedbackData) {
|
||||
is FeedbackEmail -> FeedbackEmailType.DirectUserRequest
|
||||
is RateCanBeBetterEmail -> FeedbackEmailType.RateCanBeBetter
|
||||
is ScanFailsEmail -> FeedbackEmailType.ScanningProblem
|
||||
is SendTransactionFailedEmail -> FeedbackEmailType.TransactionSendingProblem
|
||||
else -> FeedbackEmailType.DirectUserRequest
|
||||
},
|
||||
)
|
||||
|
||||
store.inject(DaggerGraphState::emailSender).send(
|
||||
email = EmailSender.Email(
|
||||
address = email.address,
|
||||
subject = email.subject,
|
||||
message = email.message,
|
||||
attachment = email.file,
|
||||
),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
feedbackData.prepare(infoHolder)
|
||||
foregroundActivityObserver.withForegroundActivity { activity ->
|
||||
activity.sendEmail(
|
||||
email = getSupportEmail(),
|
||||
subject = activity.getString(feedbackData.subjectResId),
|
||||
message = feedbackData.joinTogether(activity, infoHolder),
|
||||
file = getLogFile(activity),
|
||||
onFail = onFail,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.tap.common.haptic
|
||||
|
||||
import android.os.Build
|
||||
import android.os.VibrationEffect
|
||||
import android.os.Vibrator
|
||||
import com.tangem.core.ui.haptic.HapticManager
|
||||
|
||||
class DefaultHapticManager(private val vibrator: Vibrator) : HapticManager {
|
||||
|
||||
override fun vibrateShort() {
|
||||
vibrate(VIBRATION_SHORT_DURATION)
|
||||
}
|
||||
|
||||
override fun vibrateMeduim() {
|
||||
vibrate(VIBRATION_MEDIUM_LOW_DURATION)
|
||||
}
|
||||
|
||||
override fun vibrateLong() {
|
||||
vibrate(VIBRATION_MEDIUM_DURATION)
|
||||
}
|
||||
|
||||
private fun vibrate(durationMs: Long) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
vibrator.vibrate(VibrationEffect.createOneShot(durationMs, VibrationEffect.DEFAULT_AMPLITUDE))
|
||||
} else {
|
||||
vibrator.vibrate(durationMs)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val VIBRATION_SHORT_DURATION = 50L
|
||||
const val VIBRATION_MEDIUM_LOW_DURATION = 75L
|
||||
const val VIBRATION_MEDIUM_DURATION = 100L
|
||||
const val VIBRATION_LONG_DURATION = 200L
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,10 @@ package com.tangem.tap.common.redux
|
|||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
class AccessCodeRequestPolicyMiddleware {
|
||||
|
|
@ -21,8 +22,12 @@ class AccessCodeRequestPolicyMiddleware {
|
|||
}
|
||||
|
||||
private fun updateAccessCodeRequestPolicy(scanResponse: ScanResponse) {
|
||||
store.inject(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
|
||||
isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes && scanResponse.card.isAccessCodeSet,
|
||||
)
|
||||
mainScope.launch {
|
||||
val shouldSaveAccessCodes = store.inject(DaggerGraphState::settingsRepository).shouldSaveAccessCodes()
|
||||
|
||||
store.inject(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
|
||||
isBiometricsRequestPolicy = shouldSaveAccessCodes && scanResponse.card.isAccessCodeSet,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
|
|
@ -9,7 +8,6 @@ import com.tangem.datasource.config.models.ChatConfig
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.tap.common.feedback.FeedbackData
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
import com.tangem.tap.common.redux.DebugErrorAction
|
||||
|
|
@ -85,7 +83,6 @@ sealed class GlobalAction : Action {
|
|||
|
||||
data class SendEmail(val feedbackData: FeedbackData) : GlobalAction()
|
||||
data class OpenChat(val feedbackData: FeedbackData, val chatConfig: ChatConfig? = null) : GlobalAction()
|
||||
data class UpdateFeedbackInfo(val walletManagers: List<WalletManager>) : GlobalAction()
|
||||
|
||||
object ExchangeManager : GlobalAction() {
|
||||
object Init : GlobalAction() {
|
||||
|
|
@ -101,6 +98,5 @@ sealed class GlobalAction : Action {
|
|||
data class Success(val countryCode: String) : GlobalAction()
|
||||
}
|
||||
|
||||
data class UpdateUserWalletsListManager(val manager: UserWalletsListManager) : GlobalAction()
|
||||
data class ChangeAppThemeMode(val appThemeMode: AppThemeMode) : GlobalAction()
|
||||
}
|
||||
|
|
@ -3,9 +3,7 @@ package com.tangem.tap.common.redux.global
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.navigation.StateDialog
|
||||
import com.tangem.datasource.config.models.Config
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
|
|
@ -15,7 +13,6 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.network.exchangeServices.BuyExchangeService
|
||||
import com.tangem.tap.network.exchangeServices.CardExchangeRules
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
|
|
@ -26,13 +23,10 @@ import com.tangem.tap.network.exchangeServices.moonpay.MoonPayService
|
|||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
|
||||
object GlobalMiddleware {
|
||||
|
|
@ -97,16 +91,13 @@ private fun handleAction(action: Action, appState: () -> AppState?) {
|
|||
}
|
||||
feedbackManager.openChat(chatConfig, action.feedbackData)
|
||||
}
|
||||
is GlobalAction.UpdateFeedbackInfo -> {
|
||||
store.state.globalState.feedbackManager?.infoHolder
|
||||
?.setWalletsInfo(action.walletManagers)
|
||||
}
|
||||
is GlobalAction.ExchangeManager.Init -> {
|
||||
val appStateSafe = appState() ?: return
|
||||
val config = appStateSafe.globalState.configManager?.config ?: return
|
||||
|
||||
scope.launch {
|
||||
val scanResponseProvider: () -> ScanResponse? = {
|
||||
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
|
||||
userWalletsListManager.selectedUserWalletSync?.scanResponse
|
||||
}
|
||||
val cardProvider: () -> CardDTO? = { scanResponseProvider.invoke()?.card }
|
||||
|
|
@ -148,33 +139,6 @@ private fun handleAction(action: Action, appState: () -> AppState?) {
|
|||
}
|
||||
}
|
||||
}
|
||||
is GlobalAction.UpdateUserWalletsListManager -> {
|
||||
val walletManagersFacade = store.inject(DaggerGraphState::walletManagersFacade)
|
||||
|
||||
/*
|
||||
* If implementation of the UserWalletsListManager is changed,
|
||||
* then all observers of selectedUserWallet become irrelevant.
|
||||
*/
|
||||
action.manager.selectedUserWallet
|
||||
.distinctUntilChanged()
|
||||
.onEach { userWallet ->
|
||||
Analytics.setContext(userWallet.scanResponse)
|
||||
Analytics.send(Basic.WalletOpened())
|
||||
|
||||
store.state.globalState.feedbackManager?.infoHolder?.let { infoHolder ->
|
||||
infoHolder.setCardInfo(userWallet.scanResponse)
|
||||
|
||||
walletManagersFacade
|
||||
.getAll(userWallet.walletId)
|
||||
.distinctUntilChanged()
|
||||
.onEach(infoHolder::setWalletsInfo)
|
||||
.catch { Timber.e(it) }
|
||||
.launchIn(mainScope)
|
||||
}
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
.launchIn(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,10 @@ package com.tangem.tap.common.redux.global
|
|||
import com.tangem.domain.redux.domainStore
|
||||
import com.tangem.domain.redux.global.DomainGlobalAction
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.onboarding.OnboardingManager
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.utils.extensions.replaceBy
|
||||
import org.rekotlin.Action
|
||||
|
|
@ -94,19 +92,6 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde
|
|||
userCountryCode = action.countryCode,
|
||||
)
|
||||
}
|
||||
is GlobalAction.UpdateUserWalletsListManager -> {
|
||||
val featureToggles = store.inject(DaggerGraphState::userWalletsListManagerFeatureToggles)
|
||||
|
||||
if (featureToggles.isGeneralManagerEnabled) {
|
||||
val generalUserWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
|
||||
|
||||
appStateHolder.userWalletsListManager = generalUserWalletsListManager
|
||||
globalState.copy(userWalletsListManager = generalUserWalletsListManager)
|
||||
} else {
|
||||
appStateHolder.userWalletsListManager = action.manager
|
||||
globalState.copy(userWalletsListManager = action.manager)
|
||||
}
|
||||
}
|
||||
is GlobalAction.ChangeAppThemeMode -> globalState.copy(
|
||||
appThemeMode = action.appThemeMode,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.tangem.datasource.config.ConfigManager
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
import com.tangem.tap.domain.TapWalletManager
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
|
|
@ -27,7 +26,6 @@ data class GlobalState(
|
|||
val dialog: StateDialog? = null,
|
||||
val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(),
|
||||
val userCountryCode: String? = null,
|
||||
val userWalletsListManager: UserWalletsListManager? = null,
|
||||
val appThemeMode: AppThemeMode = AppThemeMode.DEFAULT,
|
||||
) : StateType
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.data
|
||||
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
|
|
@ -9,26 +9,24 @@ import kotlinx.coroutines.flow.firstOrNull
|
|||
// FIXME: Workaround, remove it once the normal UserWalletsStore has been implemented
|
||||
// [REDACTED_JIRA]
|
||||
internal class RuntimeUserWalletsStore(
|
||||
private val walletsStateHolder: WalletsStateHolder,
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
) : UserWalletsStore {
|
||||
|
||||
override val selectedUserWalletOrNull: UserWallet?
|
||||
get() = walletsStateHolder.userWalletsListManager?.selectedUserWalletSync
|
||||
get() = userWalletsListManager.selectedUserWalletSync
|
||||
|
||||
override suspend fun getSyncOrNull(key: UserWalletId): UserWallet? {
|
||||
return walletsStateHolder.userWalletsListManager
|
||||
?.userWallets
|
||||
?.firstOrNull()
|
||||
return userWalletsListManager
|
||||
.userWallets
|
||||
.firstOrNull()
|
||||
?.singleOrNull { it.walletId == key }
|
||||
}
|
||||
|
||||
override suspend fun getAllSyncOrNull(): List<UserWallet>? {
|
||||
return walletsStateHolder.userWalletsListManager
|
||||
?.userWallets
|
||||
?.firstOrNull()
|
||||
return userWalletsListManager.userWallets.firstOrNull()
|
||||
}
|
||||
|
||||
override suspend fun update(userWalletId: UserWalletId, update: suspend (UserWallet) -> UserWallet) {
|
||||
walletsStateHolder.userWalletsListManager?.update(userWalletId, update)
|
||||
userWalletsListManager.update(userWalletId, update)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,20 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
|
||||
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
|
||||
import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.feature.tester.ActivityClassWrapper
|
||||
import com.tangem.tap.MainActivity
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository
|
||||
import com.tangem.tap.network.exchangeServices.DefaultRampManager
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -25,15 +25,6 @@ import javax.inject.Singleton
|
|||
@InstallIn(SingletonComponent::class)
|
||||
internal object ActivityModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTangemSdkManager(
|
||||
@ApplicationContext context: Context,
|
||||
cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
): TangemSdkManager {
|
||||
return TangemSdkManager(cardSdkConfigRepository = cardSdkConfigRepository, resources = context.resources)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideScanCardUseCase(
|
||||
|
|
@ -76,4 +67,10 @@ internal object ActivityModule {
|
|||
): GetPolkadotCheckHasImmortalUseCase {
|
||||
return GetPolkadotCheckHasImmortalUseCase(polkadotAccountHealthCheckRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideActivityClassWrapper(): ActivityClassWrapper {
|
||||
return ActivityClassWrapper(MainActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.di
|
|||
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -14,10 +13,6 @@ import javax.inject.Singleton
|
|||
@InstallIn(SingletonComponent::class)
|
||||
internal interface AppStateHolderModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindsWalletsStateHolder(appStateHolder: AppStateHolder): WalletsStateHolder
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindsNavigationStateHolder(appStateHolder: AppStateHolder): ReduxNavController
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.tap.common.clipboard.MockClipboardManager
|
||||
import com.tangem.tap.common.clipboard.DefaultClipboardManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
import android.content.ClipboardManager as AndroidClipboardManager
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal class ClipboardManagerModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideClipboardManager(@ApplicationContext context: Context): ClipboardManager {
|
||||
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as? AndroidClipboardManager
|
||||
|
||||
return if (clipboardManager != null) {
|
||||
DefaultClipboardManager(clipboardManager = clipboardManager)
|
||||
} else {
|
||||
MockClipboardManager
|
||||
}
|
||||
}
|
||||
}
|
||||
37
app/src/main/java/com/tangem/tap/di/HapticModule.kt
Normal file
37
app/src/main/java/com/tangem/tap/di/HapticModule.kt
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Vibrator
|
||||
import android.os.VibratorManager
|
||||
import com.tangem.tap.common.haptic.DefaultHapticManager
|
||||
import com.tangem.core.ui.haptic.HapticManager
|
||||
import com.tangem.core.ui.haptic.MockHapticManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class HapticModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideHapticManager(@ApplicationContext context: Context): HapticManager {
|
||||
val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||
vibratorManager.defaultVibrator
|
||||
} else {
|
||||
context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
}
|
||||
|
||||
return if (vibrator.hasVibrator()) {
|
||||
DefaultHapticManager(vibrator = vibrator)
|
||||
} else {
|
||||
MockHapticManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.arkivanov.decompose.defaultComponentContext
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.DefaultAppComponentContext
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.RootAppComponentContext
|
||||
import com.tangem.core.decompose.ui.UiMessage
|
||||
import com.tangem.core.decompose.ui.UiMessageHandler
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ActivityComponent
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import timber.log.Timber
|
||||
|
||||
@Module
|
||||
@InstallIn(ActivityComponent::class)
|
||||
internal object RootAppComponentContextModule {
|
||||
|
||||
@Provides
|
||||
@ActivityScoped
|
||||
@RootAppComponentContext
|
||||
fun provideRootAppComponentContext(
|
||||
@ActivityContext context: Context,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
componentBuilder: DecomposeComponent.Builder,
|
||||
): AppComponentContext {
|
||||
// TODO: Implement message handler
|
||||
val dummyMessageHandler = object : UiMessageHandler {
|
||||
override fun handleMessage(message: UiMessage) {
|
||||
Timber.w("Unable to handle message: $message")
|
||||
}
|
||||
}
|
||||
|
||||
return DefaultAppComponentContext(
|
||||
componentContext = (context as AppCompatActivity).defaultComponentContext(),
|
||||
messageHandler = dummyMessageHandler,
|
||||
dispatchers = dispatchers,
|
||||
hiltComponentBuilder = componentBuilder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.domain.card.BuildConfig
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.tap.domain.sdk.impl.DefaultTangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.impl.MockTangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class TangemSdkManagerModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTangemSdkManager(
|
||||
@ApplicationContext context: Context,
|
||||
cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
): TangemSdkManager {
|
||||
return if (BuildConfig.MOCK_DATA_SOURCE) {
|
||||
MockTangemSdkManager(resources = context.resources)
|
||||
} else {
|
||||
DefaultTangemSdkManager(cardSdkConfigRepository = cardSdkConfigRepository, resources = context.resources)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,13 +5,15 @@ import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
|
|||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ActivityComponent
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(ActivityComponent::class)
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object ThemeModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppThemeModeHolder(): AppThemeModeHolder {
|
||||
return MutableAppThemeModeHolder
|
||||
}
|
||||
|
|
|
|||
24
app/src/main/java/com/tangem/tap/di/UiDependenciesModule.kt
Normal file
24
app/src/main/java/com/tangem/tap/di/UiDependenciesModule.kt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.haptic.HapticManager
|
||||
import com.tangem.core.ui.theme.AppThemeModeHolder
|
||||
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 UiDependenciesModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUiDependencies(hapticManager: HapticManager, appThemeModeHolder: AppThemeModeHolder): UiDependencies {
|
||||
return object : UiDependencies {
|
||||
override val hapticManager = hapticManager
|
||||
override val appThemeModeHolder = appThemeModeHolder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package com.tangem.tap.di.data
|
|||
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.card.repository.DerivationsRepository
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.card.DefaultDerivationsRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.di.data
|
||||
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.tap.data.RuntimeUserWalletsStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -15,7 +15,7 @@ internal object UserWalletsStoreModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUserWalletsStore(walletsStateHolder: WalletsStateHolder): UserWalletsStore {
|
||||
return RuntimeUserWalletsStore(walletsStateHolder)
|
||||
fun provideUserWalletsStore(userWalletsListManager: UserWalletsListManager): UserWalletsStore {
|
||||
return RuntimeUserWalletsStore(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,14 +8,15 @@ import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
|
|||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ViewModelComponent
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(ViewModelComponent::class)
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object AppCurrencyDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetSelectedAppCurrencyUseCase(
|
||||
appCurrencyRepository: AppCurrencyRepository,
|
||||
): GetSelectedAppCurrencyUseCase {
|
||||
|
|
@ -23,11 +24,13 @@ internal object AppCurrencyDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSelectAppCurrencyUseCase(appCurrencyRepository: AppCurrencyRepository): SelectAppCurrencyUseCase {
|
||||
return SelectAppCurrencyUseCase(appCurrencyRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetAvailableCurrenciesUseCase(
|
||||
appCurrencyRepository: AppCurrencyRepository,
|
||||
): GetAvailableCurrenciesUseCase {
|
||||
|
|
@ -35,7 +38,7 @@ internal object AppCurrencyDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideFetchAppCurrenciesUseCase(appCurrencyRepository: AppCurrencyRepository): FetchAppCurrenciesUseCase {
|
||||
return FetchAppCurrenciesUseCase(appCurrencyRepository)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ import com.tangem.domain.card.repository.CardSdkConfigRepository
|
|||
import com.tangem.domain.card.repository.DerivationsRepository
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.card.DefaultDeleteSavedAccessCodesUseCase
|
||||
import com.tangem.tap.domain.card.DefaultResetCardUseCase
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -36,14 +37,6 @@ internal object CardDomainModule {
|
|||
return SetAccessCodeRequestPolicyUseCase(cardSdkConfigRepository = cardSdkConfigRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideGetAccessCodeSavingStatusUseCase(
|
||||
cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
): GetAccessCodeSavingStatusUseCase {
|
||||
return GetAccessCodeSavingStatusUseCase(cardSdkConfigRepository = cardSdkConfigRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideWasWalletAlreadySignedHashesConfirmedUseCase(cardRepository: CardRepository): WasCardScannedUseCase {
|
||||
|
|
@ -68,8 +61,8 @@ internal object CardDomainModule {
|
|||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideIsNeedToBackupUseCase(walletStateHolder: WalletsStateHolder): IsNeedToBackupUseCase {
|
||||
return IsNeedToBackupUseCase(walletStateHolder)
|
||||
fun provideIsNeedToBackupUseCase(userWalletsListManager: UserWalletsListManager): IsNeedToBackupUseCase {
|
||||
return IsNeedToBackupUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -85,4 +78,10 @@ internal object CardDomainModule {
|
|||
fun provideDeleteSavedAccessCodesUseCase(tangemSdkManager: TangemSdkManager): DeleteSavedAccessCodesUseCase {
|
||||
return DefaultDeleteSavedAccessCodesUseCase(tangemSdkManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideResetCardUseCase(tangemSdkManager: TangemSdkManager): ResetCardUseCase {
|
||||
return DefaultResetCardUseCase(tangemSdkManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.card.*
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
|
||||
import com.tangem.tap.domain.scanCard.DefaultScanCardProcessor
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -14,5 +16,11 @@ internal object CardLegacyDomainModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideScanCardUseCase(): ScanCardProcessor = DefaultScanCardProcessor()
|
||||
fun provideScanCardProcessor(): ScanCardProcessor = DefaultScanCardProcessor()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesWalletNameGenerateUseCase(userWalletsListManager: UserWalletsListManager): GenerateWalletNameUseCase {
|
||||
return GenerateWalletNameUseCase(userWalletsListManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.domain.feedback.GetSupportFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.GetFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
|
||||
import com.tangem.domain.feedback.repository.FeedbackRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -16,10 +17,16 @@ internal object FeedbackDomainModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetFeedbackToSupportUseCase(
|
||||
fun provideGetFeedbackEmailUseCase(
|
||||
feedbackRepository: FeedbackRepository,
|
||||
@ApplicationContext context: Context,
|
||||
): GetSupportFeedbackEmailUseCase {
|
||||
return GetSupportFeedbackEmailUseCase(feedbackRepository = feedbackRepository, resources = context.resources)
|
||||
): GetFeedbackEmailUseCase {
|
||||
return GetFeedbackEmailUseCase(feedbackRepository = feedbackRepository, resources = context.resources)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSaveBlockchainErrorUseCase(feedbackRepository: FeedbackRepository): SaveBlockchainErrorUseCase {
|
||||
return SaveBlockchainErrorUseCase(feedbackRepository = feedbackRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,38 +9,38 @@ import com.tangem.domain.settings.*
|
|||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import com.tangem.domain.settings.repositories.PromoSettingsRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.settings.DefaultLegacySettingsRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ViewModelComponent
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(ViewModelComponent::class)
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object SettingsDomainModule {
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesIsReadyToShowRatingUseCase(appRatingRepository: AppRatingRepository): IsReadyToShowRateAppUseCase {
|
||||
return IsReadyToShowRateAppUseCase(appRatingRepository = appRatingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesRemindToRateAppLaterUseCase(appRatingRepository: AppRatingRepository): RemindToRateAppLaterUseCase {
|
||||
return RemindToRateAppLaterUseCase(appRatingRepository = appRatingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesNeverToSuggestRateAppUseCase(appRatingRepository: AppRatingRepository): NeverToSuggestRateAppUseCase {
|
||||
return NeverToSuggestRateAppUseCase(appRatingRepository = appRatingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesSetWalletWithFundsFoundUseCase(
|
||||
appRatingRepository: AppRatingRepository,
|
||||
): SetWalletWithFundsFoundUseCase {
|
||||
|
|
@ -48,7 +48,7 @@ internal object SettingsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesShouldShowSaveWalletScreenUseCase(
|
||||
settingsRepository: SettingsRepository,
|
||||
): ShouldShowSaveWalletScreenUseCase {
|
||||
|
|
@ -56,7 +56,7 @@ internal object SettingsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesCanUseBiometryUseCase(tangemSdkManager: TangemSdkManager): CanUseBiometryUseCase {
|
||||
return CanUseBiometryUseCase(
|
||||
legacySettingsRepository = DefaultLegacySettingsRepository(tangemSdkManager = tangemSdkManager),
|
||||
|
|
@ -64,7 +64,7 @@ internal object SettingsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesGetBalanceHidingSettingsUseCase(
|
||||
balanceHidingRepository: BalanceHidingRepository,
|
||||
): GetBalanceHidingSettingsUseCase {
|
||||
|
|
@ -74,7 +74,7 @@ internal object SettingsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesListenUseCase(
|
||||
flipDetector: DeviceFlipDetector,
|
||||
balanceHidingRepository: BalanceHidingRepository,
|
||||
|
|
@ -86,7 +86,7 @@ internal object SettingsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideUpdateHideBalancesSettingsUseCase(
|
||||
balanceHidingRepository: BalanceHidingRepository,
|
||||
): UpdateBalanceHidingSettingsUseCase {
|
||||
|
|
@ -94,19 +94,19 @@ internal object SettingsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideSetWalletsScrollPreviewIsShown(settingsRepository: SettingsRepository): NeverToShowWalletsScrollPreview {
|
||||
return NeverToShowWalletsScrollPreview(settingsRepository = settingsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideIsWalletsScrollPreviewEnabled(settingsRepository: SettingsRepository): IsWalletsScrollPreviewEnabled {
|
||||
return IsWalletsScrollPreviewEnabled(settingsRepository = settingsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideShouldShowSwapPromoWalletUseCase(
|
||||
promoSettingsRepository: PromoSettingsRepository,
|
||||
): ShouldShowSwapPromoWalletUseCase {
|
||||
|
|
@ -114,7 +114,7 @@ internal object SettingsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideShouldShowTravalaPromoWalletUseCase(
|
||||
promoSettingsRepository: PromoSettingsRepository,
|
||||
): ShouldShowTravalaPromoWalletUseCase {
|
||||
|
|
@ -122,7 +122,7 @@ internal object SettingsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideShouldShowSwapPromoTokenUseCase(
|
||||
promoSettingsRepository: PromoSettingsRepository,
|
||||
): ShouldShowSwapPromoTokenUseCase {
|
||||
|
|
@ -130,13 +130,13 @@ internal object SettingsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideDeleteDeprecatedLogsUseCase(settingsRepository: SettingsRepository): DeleteDeprecatedLogsUseCase {
|
||||
return DeleteDeprecatedLogsUseCase(settingsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideIsSendTapHelpPreviewEnabledUseCase(
|
||||
settingsRepository: SettingsRepository,
|
||||
): IsSendTapHelpEnabledUseCase {
|
||||
|
|
@ -144,8 +144,24 @@ internal object SettingsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideNeverShowTapHelpUseCase(settingsRepository: SettingsRepository): NeverShowTapHelpUseCase {
|
||||
return NeverShowTapHelpUseCase(settingsRepository = settingsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSetSaveWalletScreenShownUseCase(
|
||||
settingsRepository: SettingsRepository,
|
||||
): SetSaveWalletScreenShownUseCase {
|
||||
return SetSaveWalletScreenShownUseCase(settingsRepository = settingsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideIncrementAppLaunchCounterUseCase(
|
||||
settingsRepository: SettingsRepository,
|
||||
): IncrementAppLaunchCounterUseCase {
|
||||
return IncrementAppLaunchCounterUseCase(settingsRepository = settingsRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
|
||||
import com.tangem.domain.staking.GetStakingEntryInfoUseCase
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
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 StakingDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingEntryInfoUseCase(stakingRepository: StakingRepository): GetStakingEntryInfoUseCase {
|
||||
return GetStakingEntryInfoUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingAvailabilityUseCase(stakingRepository: StakingRepository): GetStakingAvailabilityUseCase {
|
||||
return GetStakingAvailabilityUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,21 +6,20 @@ import com.tangem.domain.tokens.*
|
|||
import com.tangem.domain.tokens.repository.*
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.feature.swap.domain.api.SwapRepository
|
||||
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ViewModelComponent
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(ViewModelComponent::class)
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
internal object TokensDomainModule {
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideAddCryptoCurrenciesUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
|
|
@ -32,7 +31,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideFetchTokenListUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
|
|
@ -42,7 +41,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideFetchPendingTransactionsUseCase(
|
||||
networksRepository: NetworksRepository,
|
||||
): FetchPendingTransactionsUseCase {
|
||||
|
|
@ -50,29 +49,27 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetTokenListUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetTokenListUseCase {
|
||||
return GetTokenListUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
|
||||
return GetTokenListUseCase(currenciesRepository, quotesRepository, networksRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetCardTokensListUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetCardTokensListUseCase {
|
||||
return GetCardTokensListUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
|
||||
return GetCardTokensListUseCase(currenciesRepository, quotesRepository, networksRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideRemoveCurrencyUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
|
|
@ -81,7 +78,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetCurrencyUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
|
|
@ -92,7 +89,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetCurrencyWarningsUseCase(
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
|
|
@ -120,7 +117,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetPrimaryCurrencyUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
|
|
@ -136,7 +133,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideFetchCurrencyStatusUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
|
|
@ -146,7 +143,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideFetchCardTokenListUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
|
|
@ -156,13 +153,13 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetCryptoCurrencyUseCase(currenciesRepository: CurrenciesRepository): GetCryptoCurrencyUseCase {
|
||||
return GetCryptoCurrencyUseCase(currenciesRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideToggleTokenListGroupingUseCase(
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): ToggleTokenListGroupingUseCase {
|
||||
|
|
@ -170,13 +167,13 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideToggleTokenListSortingUseCase(dispatchers: CoroutineDispatcherProvider): ToggleTokenListSortingUseCase {
|
||||
return ToggleTokenListSortingUseCase(dispatchers)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideApplyTokenListSortingUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -185,29 +182,29 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetCryptoCurrencyActionsUseCase(
|
||||
rampStateManager: RampStateManager,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
marketCryptoCurrencyRepository: MarketCryptoCurrencyRepository,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
sendFeatureToggles: SendFeatureToggles,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetCryptoCurrencyActionsUseCase {
|
||||
return GetCryptoCurrencyActionsUseCase(
|
||||
rampManager = rampStateManager,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
marketCryptoCurrencyRepository = marketCryptoCurrencyRepository,
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
sendFeatureToggles = sendFeatureToggles,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetCurrencyStatusByNetworkUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
|
|
@ -223,7 +220,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
|
|
@ -239,13 +236,13 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetCurrenciesUseCase(currenciesRepository: CurrenciesRepository): GetCryptoCurrenciesUseCase {
|
||||
return GetCryptoCurrenciesUseCase(currenciesRepository = currenciesRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideIsCryptoCurrencyCoinCouldHideUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
): IsCryptoCurrencyCoinCouldHideUseCase {
|
||||
|
|
@ -255,7 +252,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideUpdateDelayedCurrencyStatusUseCase(
|
||||
networksRepository: NetworksRepository,
|
||||
): UpdateDelayedNetworkStatusUseCase {
|
||||
|
|
@ -265,7 +262,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideHasMissedAddressesCryptoCurrenciesUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
): GetMissedAddressesCryptoCurrenciesUseCase {
|
||||
|
|
@ -273,13 +270,13 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetGlobalTokenListUseCase(tokensListRepository: TokensListRepository): GetGlobalTokenListUseCase {
|
||||
return GetGlobalTokenListUseCase(repository = tokensListRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideCheckTokenCompatibilityUseCase(
|
||||
networksCompatibilityRepository: NetworksCompatibilityRepository,
|
||||
): CheckCurrencyCompatibilityUseCase {
|
||||
|
|
@ -287,7 +284,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideNeedHardenedDerivationUseCase(
|
||||
networksCompatibilityRepository: NetworksCompatibilityRepository,
|
||||
): RequiresHardenedDerivationOnlyUseCase {
|
||||
|
|
@ -295,7 +292,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideFindTokenByContractAddressUseCase(
|
||||
tokensListRepository: TokensListRepository,
|
||||
): FindTokenByContractAddressUseCase {
|
||||
|
|
@ -303,7 +300,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideValidateContractAddressUseCase(
|
||||
tokensListRepository: TokensListRepository,
|
||||
): ValidateContractAddressUseCase {
|
||||
|
|
@ -311,7 +308,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideAreTokensSupportedByNetworkUseCase(
|
||||
repository: NetworksCompatibilityRepository,
|
||||
): AreTokensSupportedByNetworkUseCase {
|
||||
|
|
@ -319,7 +316,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetNetworksSupportedByWallet(
|
||||
repository: NetworksCompatibilityRepository,
|
||||
): GetNetworksSupportedByWallet {
|
||||
|
|
@ -327,7 +324,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetBalanceNotEnoughForFeeWarningUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -336,7 +333,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideIsAmountSubtractAvailableUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -345,7 +342,7 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideRunPolkadotAccountHealthCheckUseCase(
|
||||
repository: PolkadotAccountHealthCheckRepository,
|
||||
): RunPolkadotAccountHealthCheckUseCase {
|
||||
|
|
@ -353,10 +350,24 @@ internal object TokensDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetNetworkStatusesUseCase(networksRepository: NetworksRepository): GetNetworkAddressesUseCase {
|
||||
return GetNetworkAddressesUseCase(
|
||||
networksRepository = networksRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetWalletTotalBalanceUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
): GetWalletTotalBalanceUseCase {
|
||||
return GetWalletTotalBalanceUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,25 +2,24 @@ package com.tangem.tap.di.domain
|
|||
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.transaction.FeeRepository
|
||||
import com.tangem.domain.transaction.TransactionRepository
|
||||
import com.tangem.domain.transaction.usecase.CreateTransactionUseCase
|
||||
import com.tangem.domain.transaction.usecase.GetFeeUseCase
|
||||
import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase
|
||||
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
|
||||
import com.tangem.domain.transaction.usecase.*
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ViewModelComponent
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(ViewModelComponent::class)
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object TransactionDomainModule {
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideGetFeeUseCase(walletManagersFacade: WalletManagersFacade): GetFeeUseCase {
|
||||
return GetFeeUseCase(
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
|
|
@ -29,7 +28,7 @@ internal object TransactionDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideSendTransactionUseCase(
|
||||
cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
transactionRepository: TransactionRepository,
|
||||
|
|
@ -44,14 +43,44 @@ internal object TransactionDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideAssociateAssetUseCase(
|
||||
cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
): AssociateAssetUseCase {
|
||||
return AssociateAssetUseCase(
|
||||
cardSdkConfigRepository = cardSdkConfigRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
currenciesRepository = currenciesRepository,
|
||||
networksRepository = networksRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCreateTransactionUseCase(transactionRepository: TransactionRepository): CreateTransactionUseCase {
|
||||
return CreateTransactionUseCase(transactionRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun provideIsFeeApproximateUseCase(feeRepository: FeeRepository): IsFeeApproximateUseCase {
|
||||
return IsFeeApproximateUseCase(feeRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideValidateTransactionUseCase(transactionRepository: TransactionRepository): ValidateTransactionUseCase {
|
||||
return ValidateTransactionUseCase(transactionRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideIsUtxoConsolidationAvailableUseCase(
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
): IsUtxoConsolidationAvailableUseCase {
|
||||
return IsUtxoConsolidationAvailableUseCase(walletManagersFacade)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.walletconnect.CheckIsWalletConnectAvailableUseCase
|
||||
import com.tangem.domain.walletconnect.repository.WalletConnectRepository
|
||||
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 WalletConnectDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesCheckIsWalletConnectAvailableUseCase(
|
||||
walletConnectRepository: WalletConnectRepository,
|
||||
): CheckIsWalletConnectAvailableUseCase {
|
||||
return CheckIsWalletConnectAvailableUseCase(walletConnectRepository = walletConnectRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,12 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.common.AccountCreator
|
||||
import com.tangem.blockchain.common.datastorage.BlockchainDataStorage
|
||||
import com.tangem.blockchain.common.logging.BlockchainSDKLogger
|
||||
import com.tangem.datasource.asset.AssetReader
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.di.SdkMoshi
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.datasource.asset.loader.AssetLoader
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||
import com.tangem.domain.feedback.FeedbackManagerFeatureToggles
|
||||
import com.tangem.domain.walletmanager.DefaultWalletManagersFacade
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.feature.onboarding.data.MnemonicRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -28,26 +22,16 @@ internal object WalletManagersFacadeModule {
|
|||
fun provideWalletManagersFacade(
|
||||
walletManagersStore: WalletManagersStore,
|
||||
userWalletsStore: UserWalletsStore,
|
||||
configManager: ConfigManager,
|
||||
blockchainDataStorage: BlockchainDataStorage,
|
||||
accountCreator: AccountCreator,
|
||||
mnemonicRepository: MnemonicRepository,
|
||||
assetReader: AssetReader,
|
||||
@SdkMoshi moshi: Moshi,
|
||||
blockchainSDKLogger: BlockchainSDKLogger,
|
||||
feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles,
|
||||
assetLoader: AssetLoader,
|
||||
blockchainSDKFactory: BlockchainSDKFactory,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): WalletManagersFacade {
|
||||
return DefaultWalletManagersFacade(
|
||||
walletManagersStore = walletManagersStore,
|
||||
userWalletsStore = userWalletsStore,
|
||||
configManager = configManager,
|
||||
blockchainDataStorage = blockchainDataStorage,
|
||||
assetReader = assetReader,
|
||||
moshi = moshi,
|
||||
mnemonic = mnemonicRepository.generateDefaultMnemonic(),
|
||||
accountCreator = accountCreator,
|
||||
blockchainSDKLogger = blockchainSDKLogger,
|
||||
feedbackManagerFeatureToggles = feedbackManagerFeatureToggles,
|
||||
assetLoader = assetLoader,
|
||||
dispatchers = dispatchers,
|
||||
blockchainSDKFactory = blockchainSDKFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,86 +2,117 @@ package com.tangem.tap.di.domain
|
|||
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.domain.wallets.repository.WalletAddressServiceRepository
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.transaction.WalletAddressServiceRepository
|
||||
import com.tangem.domain.transaction.usecase.ParseSharedAddressUseCase
|
||||
import com.tangem.domain.transaction.usecase.ValidateWalletAddressUseCase
|
||||
import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase
|
||||
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ViewModelComponent
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(ViewModelComponent::class)
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object WalletsDomainModule {
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesGetWalletsUseCase(walletsStateHolder: WalletsStateHolder): GetWalletsUseCase {
|
||||
return GetWalletsUseCase(walletsStateHolder = walletsStateHolder)
|
||||
@Singleton
|
||||
fun providesGetWalletsUseCase(userWalletsListManager: UserWalletsListManager): GetWalletsUseCase {
|
||||
return GetWalletsUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesGetUserWalletUseCase(walletsStateHolder: WalletsStateHolder): GetUserWalletUseCase {
|
||||
return GetUserWalletUseCase(walletsStateHolder = walletsStateHolder)
|
||||
@Singleton
|
||||
fun providesWalletNameMigrationUseCase(
|
||||
userWalletsListManager: UserWalletsListManager,
|
||||
walletNamesMigrationRepository: WalletNamesMigrationRepository,
|
||||
): WalletNameMigrationUseCase {
|
||||
return WalletNameMigrationUseCase(
|
||||
userWalletsListManager = userWalletsListManager,
|
||||
walletNamesMigrationRepository = walletNamesMigrationRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesGetSelectedWalletSyncUseCase(walletsStateHolder: WalletsStateHolder): GetSelectedWalletSyncUseCase {
|
||||
return GetSelectedWalletSyncUseCase(walletsStateHolder = walletsStateHolder)
|
||||
@Singleton
|
||||
fun providesGetUserWalletUseCase(userWalletsListManager: UserWalletsListManager): GetUserWalletUseCase {
|
||||
return GetUserWalletUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesGetSelectedWalletUseCase(walletsStateHolder: WalletsStateHolder): GetSelectedWalletUseCase {
|
||||
return GetSelectedWalletUseCase(walletsStateHolder = walletsStateHolder)
|
||||
@Singleton
|
||||
fun providesGetSelectedWalletSyncUseCase(
|
||||
userWalletsListManager: UserWalletsListManager,
|
||||
): GetSelectedWalletSyncUseCase {
|
||||
return GetSelectedWalletSyncUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesSaveWalletUseCase(walletsStateHolder: WalletsStateHolder): SaveWalletUseCase {
|
||||
return SaveWalletUseCase(walletsStateHolder = walletsStateHolder)
|
||||
@Singleton
|
||||
fun providesGetSelectedWalletUseCase(userWalletsListManager: UserWalletsListManager): GetSelectedWalletUseCase {
|
||||
return GetSelectedWalletUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesSaveWalletUseCase(userWalletsListManager: UserWalletsListManager): SaveWalletUseCase {
|
||||
return SaveWalletUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesGetExploreUrlUseCase(walletsManagersFacade: WalletManagersFacade): GetExploreUrlUseCase {
|
||||
return GetExploreUrlUseCase(walletsManagersFacade = walletsManagersFacade)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesUnlockWalletUseCase(walletsStateHolder: WalletsStateHolder): UnlockWalletsUseCase {
|
||||
return UnlockWalletsUseCase(walletsStateHolder = walletsStateHolder)
|
||||
@Singleton
|
||||
fun providesUnlockWalletUseCase(userWalletsListManager: UserWalletsListManager): UnlockWalletsUseCase {
|
||||
return UnlockWalletsUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesSelectWalletUseCase(
|
||||
walletsStateHolder: WalletsStateHolder,
|
||||
userWalletsListManager: UserWalletsListManager,
|
||||
reduxStateHolder: ReduxStateHolder,
|
||||
): SelectWalletUseCase {
|
||||
return SelectWalletUseCase(walletsStateHolder, reduxStateHolder)
|
||||
return SelectWalletUseCase(userWalletsListManager = userWalletsListManager, reduxStateHolder = reduxStateHolder)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesUpdateWalletUseCase(walletsStateHolder: WalletsStateHolder): UpdateWalletUseCase {
|
||||
return UpdateWalletUseCase(walletsStateHolder = walletsStateHolder)
|
||||
@Singleton
|
||||
fun providesUpdateWalletUseCase(userWalletsListManager: UserWalletsListManager): UpdateWalletUseCase {
|
||||
return UpdateWalletUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun providesDeleteWalletUseCase(walletsStateHolder: WalletsStateHolder): DeleteWalletUseCase {
|
||||
return DeleteWalletUseCase(walletsStateHolder = walletsStateHolder)
|
||||
@Singleton
|
||||
fun providesRenameWalletUseCase(userWalletsListManager: UserWalletsListManager): RenameWalletUseCase {
|
||||
return RenameWalletUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesGetWalletsSyncUseCase(userWalletsListManager: UserWalletsListManager): GetWalletNamesUseCase {
|
||||
return GetWalletNamesUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesDeleteWalletUseCase(userWalletsListManager: UserWalletsListManager): DeleteWalletUseCase {
|
||||
return DeleteWalletUseCase(userWalletsListManager = userWalletsListManager)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesShouldSaveUserWalletsSyncUseCase(
|
||||
walletsRepository: WalletsRepository,
|
||||
): ShouldSaveUserWalletsSyncUseCase {
|
||||
|
|
@ -89,25 +120,25 @@ internal object WalletsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesShouldSaveUserWalletsUseCase(walletsRepository: WalletsRepository): ShouldSaveUserWalletsUseCase {
|
||||
return ShouldSaveUserWalletsUseCase(walletsRepository = walletsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesValidateWalletAddressUseCase(
|
||||
walletAddressServiceRepository: WalletAddressServiceRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
): ValidateWalletAddressUseCase {
|
||||
return ValidateWalletAddressUseCase(
|
||||
walletAddressServiceRepository = walletAddressServiceRepository,
|
||||
dispatchers = dispatchers,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesValidateWalletMemoUseCase(
|
||||
walletAddressServiceRepository: WalletAddressServiceRepository,
|
||||
): ValidateWalletMemoUseCase {
|
||||
|
|
@ -115,7 +146,7 @@ internal object WalletsDomainModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
@Singleton
|
||||
fun providesParseSharedAddressUseCase(
|
||||
walletAddressServiceRepository: WalletAddressServiceRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
|
|
@ -15,7 +13,6 @@ import com.tangem.operations.attestation.Attestation
|
|||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.disclaimer.createDisclaimer
|
||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
|
|
@ -32,29 +29,12 @@ class TapWalletManager(
|
|||
private val dispatchers: CoroutineDispatcherProvider = AppCoroutineDispatcherProvider(),
|
||||
) {
|
||||
|
||||
private val blockchainSdkConfig by lazy {
|
||||
store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig()
|
||||
}
|
||||
|
||||
private var loadUserWalletDataJob: Job? = null
|
||||
set(value) {
|
||||
field?.cancel()
|
||||
field = value
|
||||
}
|
||||
|
||||
val walletManagerFactory: WalletManagerFactory by lazy {
|
||||
WalletManagerFactory(
|
||||
config = blockchainSdkConfig,
|
||||
accountCreator = store.inject(DaggerGraphState::accountCreator),
|
||||
blockchainDataStorage = store.inject(DaggerGraphState::blockchainDataStorage),
|
||||
loggers = if (store.inject(DaggerGraphState::feedbackManagerFeatureToggles).isLocalLogsEnabled) {
|
||||
listOf(store.inject(DaggerGraphState::blockchainSDKLogger))
|
||||
} else {
|
||||
emptyList()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun onWalletSelected(userWallet: UserWallet, sendAnalyticsEvent: Boolean) {
|
||||
// If a previous job was running, it gets cancelled before the new one starts,
|
||||
// ensuring that only one job is active at any given time.
|
||||
|
|
@ -73,15 +53,18 @@ class TapWalletManager(
|
|||
val attestationFailed = card.attestation.status == Attestation.Status.Failed
|
||||
|
||||
tangemSdkManager.changeDisplayedCardIdNumbersCount(scanResponse)
|
||||
store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(scanResponse)
|
||||
|
||||
val featureToggles = store.inject(DaggerGraphState::feedbackManagerFeatureToggles)
|
||||
if (!featureToggles.isLocalLogsEnabled) {
|
||||
store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(scanResponse)
|
||||
}
|
||||
|
||||
updateConfigManager(scanResponse)
|
||||
withMainContext {
|
||||
// Order is important
|
||||
store.dispatch(DisclaimerAction.SetDisclaimer(card.createDisclaimer()))
|
||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||
store.dispatch(WalletConnectAction.ResetState)
|
||||
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
|
||||
store.dispatch(WalletConnectAction.RestoreSessions(scanResponse))
|
||||
store.dispatch(GlobalAction.SetIfCardVerifiedOnline(!attestationFailed))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import arrow.core.right
|
|||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.domain.card.DeleteSavedAccessCodesUseCase
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
|
||||
internal class DefaultDeleteSavedAccessCodesUseCase(
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
|||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.card.repository.DerivationsRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.userwallets.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.runCatching
|
||||
import timber.log.Timber
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
package com.tangem.tap.domain.card
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.right
|
||||
import com.tangem.common.*
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.core.UserCodeRequestPolicy
|
||||
import com.tangem.domain.card.ResetCardUseCase
|
||||
import com.tangem.domain.card.models.ResetCardError
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
|
||||
internal class DefaultResetCardUseCase(
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
) : ResetCardUseCase {
|
||||
|
||||
override suspend fun invoke(card: CardDTO): Either<ResetCardError, Unit> = either {
|
||||
enterRequiredAccessCode(card) {
|
||||
tangemSdkManager.resetToFactorySettings(
|
||||
cardId = card.cardId,
|
||||
allowsRequestAccessCodeFromRepository = true,
|
||||
)
|
||||
.doOnSuccess { Unit.right() }
|
||||
.doOnFailure { raise(it.mapToDomainError()) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun invoke(
|
||||
cardNumber: Int,
|
||||
card: CardDTO,
|
||||
userWalletId: UserWalletId,
|
||||
): Either<ResetCardError, Unit> = either {
|
||||
enterRequiredAccessCode(card) {
|
||||
tangemSdkManager.resetBackupCard(cardNumber, userWalletId)
|
||||
.doOnSuccess { Unit.right() }
|
||||
.doOnFailure { raise(it.mapToDomainError()) }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun enterRequiredAccessCode(
|
||||
card: CardDTO,
|
||||
task: suspend TangemSdkManager.() -> CompletionResult<*>,
|
||||
) {
|
||||
val policyBeforeReset = tangemSdkManager.userCodeRequestPolicy
|
||||
requestMandatoryAccessCodeEntry(card)
|
||||
|
||||
tangemSdkManager.task()
|
||||
.doOnResult { tangemSdkManager.setUserCodeRequestPolicy(policyBeforeReset) }
|
||||
}
|
||||
|
||||
private fun requestMandatoryAccessCodeEntry(card: CardDTO) {
|
||||
val type = if (card.isAccessCodeSet) {
|
||||
UserCodeType.AccessCode
|
||||
} else if (card.isPasscodeSet == true) {
|
||||
UserCodeType.Passcode
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
type?.let {
|
||||
tangemSdkManager.setUserCodeRequestPolicy(policy = UserCodeRequestPolicy.Always(type))
|
||||
}
|
||||
}
|
||||
|
||||
private fun TangemError.mapToDomainError(): ResetCardError {
|
||||
return if (this is TangemSdkError.UserCancelled) ResetCardError.UserCanceled else ResetCardError.AnotherSdkError
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import com.tangem.common.services.Result
|
|||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.domain.common.getTwinCardNumber
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.userwallets.Artwork
|
||||
import com.tangem.domain.wallets.models.Artwork
|
||||
import com.tangem.operations.attestation.CardVerifyAndGetInfo
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
|
||||
|
|
@ -20,8 +20,8 @@ suspend fun CardDTO.getOrLoadCardArtworkUrl(cardInfo: Result<CardVerifyAndGetInf
|
|||
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
|
||||
else -> {
|
||||
when (getTwinCardNumber()) {
|
||||
TwinCardNumber.First -> Artwork.TWIN_CARD_1
|
||||
TwinCardNumber.Second -> Artwork.TWIN_CARD_2
|
||||
TwinCardNumber.First -> Artwork.TWIN_CARD_1_URL
|
||||
TwinCardNumber.Second -> Artwork.TWIN_CARD_2_URL
|
||||
else -> Artwork.DEFAULT_IMG_URL
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.tap.domain.scanCard.repository
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.domain.card.repository.ScanCardRepository
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.scanCard.utils.ScanCardExceptionConverter
|
||||
|
||||
// TODO: Move to the :data:card module
|
||||
|
|
|
|||
134
app/src/main/java/com/tangem/tap/domain/sdk/TangemSdkManager.kt
Normal file
134
app/src/main/java/com/tangem/tap/domain/sdk/TangemSdkManager.kt
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
package com.tangem.tap.domain.sdk
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.Message
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.KeyPair
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.authentication.keystore.KeystoreManager
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.UserCodeRequestPolicy
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
interface TangemSdkManager {
|
||||
|
||||
val canUseBiometry: Boolean
|
||||
|
||||
val needEnrollBiometrics: Boolean
|
||||
|
||||
val keystoreManager: KeystoreManager
|
||||
|
||||
val secureStorage: SecureStorage
|
||||
|
||||
val userCodeRequestPolicy: UserCodeRequestPolicy
|
||||
|
||||
suspend fun scanProduct(
|
||||
cardId: String? = null,
|
||||
messageRes: Int? = null,
|
||||
allowsRequestAccessCodeFromRepository: Boolean = false,
|
||||
): CompletionResult<ScanResponse>
|
||||
|
||||
suspend fun createProductWallet(
|
||||
scanResponse: ScanResponse,
|
||||
shouldReset: Boolean = false,
|
||||
): CompletionResult<CreateProductWalletTaskResponse>
|
||||
|
||||
// Wallet2 specific
|
||||
suspend fun importWallet(
|
||||
scanResponse: ScanResponse,
|
||||
mnemonic: String,
|
||||
passphrase: String?,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse>
|
||||
|
||||
suspend fun derivePublicKeys(
|
||||
cardId: String?,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): CompletionResult<DerivationTaskResponse>
|
||||
|
||||
suspend fun deriveExtendedPublicKey(
|
||||
cardId: String?,
|
||||
walletPublicKey: ByteArray,
|
||||
derivation: DerivationPath,
|
||||
): CompletionResult<ExtendedPublicKey>
|
||||
|
||||
suspend fun resetToFactorySettings(
|
||||
cardId: String,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO>
|
||||
|
||||
suspend fun resetBackupCard(cardNumber: Int, userWalletId: UserWalletId): CompletionResult<Unit>
|
||||
|
||||
suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit>
|
||||
|
||||
suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit>
|
||||
|
||||
suspend fun clearSavedUserCodes(): CompletionResult<Unit>
|
||||
|
||||
suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun setAccessCodeRecoveryEnabled(cardId: String?, enabled: Boolean): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun scanCard(
|
||||
cardId: String? = null,
|
||||
allowRequestAccessCodeFromRepository: Boolean = false,
|
||||
): CompletionResult<CardDTO>
|
||||
|
||||
@Deprecated(
|
||||
"TangemSdkManager shouldn't run custom tasks. " +
|
||||
"All of them should be specified in TangemSdkManager certain methods.",
|
||||
)
|
||||
suspend fun <T> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>,
|
||||
cardId: String? = null,
|
||||
initialMessage: Message? = null,
|
||||
accessCode: String? = null,
|
||||
@DrawableRes iconScanRes: Int? = null,
|
||||
): CompletionResult<T>
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?)
|
||||
|
||||
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
||||
fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String
|
||||
|
||||
fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy)
|
||||
|
||||
// region Twin-specific
|
||||
|
||||
suspend fun finalizeTwin(
|
||||
secondCardPublicKey: ByteArray,
|
||||
issuerKeyPair: KeyPair,
|
||||
cardId: String,
|
||||
initialMessage: Message,
|
||||
): CompletionResult<ScanResponse>
|
||||
|
||||
suspend fun createFirstTwinWallet(cardId: String, initialMessage: Message): CompletionResult<CreateWalletResponse>
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun createSecondTwinWallet(
|
||||
firstPublicKey: String,
|
||||
firstCardId: String,
|
||||
issuerKeys: KeyPair,
|
||||
preparingMessage: Message,
|
||||
creatingWalletMessage: Message,
|
||||
initialMessage: Message,
|
||||
): CompletionResult<CreateWalletResponse>
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.tap.domain
|
||||
package com.tangem.tap.domain.sdk.impl
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.DrawableRes
|
||||
|
|
@ -16,23 +16,26 @@ import com.tangem.core.analytics.Analytics
|
|||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.crypto.bip39.DefaultMnemonic
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
|
||||
import com.tangem.operations.pins.SetUserCodeCommand
|
||||
import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.derivationsFinder
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTask
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanProductTask
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.tasks.product.*
|
||||
import com.tangem.tap.domain.twins.CreateFirstTwinWalletTask
|
||||
import com.tangem.tap.domain.twins.CreateSecondTwinWalletTask
|
||||
import com.tangem.tap.domain.twins.FinalizeTwinTask
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
|
|
@ -40,10 +43,10 @@ import kotlinx.coroutines.withContext
|
|||
import kotlin.coroutines.resume
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class TangemSdkManager(
|
||||
class DefaultTangemSdkManager(
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val resources: Resources,
|
||||
) {
|
||||
) : TangemSdkManager {
|
||||
|
||||
private val tangemSdk: TangemSdk
|
||||
get() = cardSdkConfigRepository.sdk
|
||||
|
|
@ -55,25 +58,25 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
val canUseBiometry: Boolean
|
||||
override val canUseBiometry: Boolean
|
||||
get() = tangemSdk.authenticationManager.canAuthenticate || needEnrollBiometrics
|
||||
|
||||
val needEnrollBiometrics: Boolean
|
||||
override val needEnrollBiometrics: Boolean
|
||||
get() = tangemSdk.authenticationManager.needEnrollBiometrics
|
||||
|
||||
val keystoreManager: KeystoreManager
|
||||
override val keystoreManager: KeystoreManager
|
||||
get() = tangemSdk.keystoreManager
|
||||
|
||||
val secureStorage: SecureStorage
|
||||
override val secureStorage: SecureStorage
|
||||
get() = tangemSdk.secureStorage
|
||||
|
||||
val userCodeRequestPolicy: UserCodeRequestPolicy
|
||||
override val userCodeRequestPolicy: UserCodeRequestPolicy
|
||||
get() = tangemSdk.config.userCodeRequestPolicy
|
||||
|
||||
suspend fun scanProduct(
|
||||
cardId: String? = null,
|
||||
messageRes: Int? = null,
|
||||
allowsRequestAccessCodeFromRepository: Boolean = false,
|
||||
override suspend fun scanProduct(
|
||||
cardId: String?,
|
||||
messageRes: Int?,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<ScanResponse> {
|
||||
val message = Message(resources.getString(messageRes ?: R.string.initial_message_scan_header))
|
||||
return runTaskAsyncReturnOnMain(
|
||||
|
|
@ -87,9 +90,9 @@ class TangemSdkManager(
|
|||
).also { sendScanResultsToAnalytics(it) }
|
||||
}
|
||||
|
||||
suspend fun createProductWallet(
|
||||
override suspend fun createProductWallet(
|
||||
scanResponse: ScanResponse,
|
||||
shouldReset: Boolean = false,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
return runTaskAsync(
|
||||
runnable = CreateProductWalletTask(
|
||||
|
|
@ -103,7 +106,7 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun importWallet(
|
||||
override suspend fun importWallet(
|
||||
scanResponse: ScanResponse,
|
||||
mnemonic: String,
|
||||
passphrase: String?,
|
||||
|
|
@ -135,14 +138,14 @@ class TangemSdkManager(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun derivePublicKeys(
|
||||
override suspend fun derivePublicKeys(
|
||||
cardId: String?,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): CompletionResult<DerivationTaskResponse> {
|
||||
return runTaskAsyncReturnOnMain(DeriveMultipleWalletPublicKeysTask(derivations), cardId)
|
||||
}
|
||||
|
||||
suspend fun deriveExtendedPublicKey(
|
||||
override suspend fun deriveExtendedPublicKey(
|
||||
cardId: String?,
|
||||
walletPublicKey: ByteArray,
|
||||
derivation: DerivationPath,
|
||||
|
|
@ -153,7 +156,7 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun resetToFactorySettings(
|
||||
override suspend fun resetToFactorySettings(
|
||||
cardId: String,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
|
|
@ -167,7 +170,19 @@ class TangemSdkManager(
|
|||
.map { CardDTO(it) }
|
||||
}
|
||||
|
||||
suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
override suspend fun resetBackupCard(cardNumber: Int, userWalletId: UserWalletId): CompletionResult<Unit> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = ResetBackupCardTask(userWalletId),
|
||||
initialMessage = Message(
|
||||
resources.getString(
|
||||
R.string.initial_message_reset_backup_card_header,
|
||||
cardNumber.toString(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
return userCodeRepository.save(
|
||||
cardsIds = cardsIds,
|
||||
userCode = UserCode(
|
||||
|
|
@ -177,15 +192,15 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
override suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
return userCodeRepository.delete(cardsIds.toSet())
|
||||
}
|
||||
|
||||
suspend fun clearSavedUserCodes(): CompletionResult<Unit> {
|
||||
override suspend fun clearSavedUserCodes(): CompletionResult<Unit> {
|
||||
return userCodeRepository.clear()
|
||||
}
|
||||
|
||||
suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
override suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.changePasscode(null),
|
||||
cardId,
|
||||
|
|
@ -193,7 +208,7 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
override suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.changeAccessCode(null),
|
||||
cardId,
|
||||
|
|
@ -201,7 +216,7 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
override suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.resetUserCodes(),
|
||||
cardId,
|
||||
|
|
@ -209,7 +224,10 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun setAccessCodeRecoveryEnabled(cardId: String?, enabled: Boolean): CompletionResult<SuccessResponse> {
|
||||
override suspend fun setAccessCodeRecoveryEnabled(
|
||||
cardId: String?,
|
||||
enabled: Boolean,
|
||||
): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeRecoveryAllowedTask(enabled),
|
||||
cardId,
|
||||
|
|
@ -217,9 +235,9 @@ class TangemSdkManager(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun scanCard(
|
||||
cardId: String? = null,
|
||||
allowRequestAccessCodeFromRepository: Boolean = false,
|
||||
override suspend fun scanCard(
|
||||
cardId: String?,
|
||||
allowRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = ScanTask(allowRequestAccessCodeFromRepository),
|
||||
|
|
@ -229,12 +247,12 @@ class TangemSdkManager(
|
|||
.map { CardDTO(it) }
|
||||
}
|
||||
|
||||
suspend fun <T> runTaskAsync(
|
||||
override suspend fun <T> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>,
|
||||
cardId: String? = null,
|
||||
initialMessage: Message? = null,
|
||||
accessCode: String? = null,
|
||||
@DrawableRes iconScanRes: Int? = null,
|
||||
cardId: String?,
|
||||
initialMessage: Message?,
|
||||
accessCode: String?,
|
||||
@DrawableRes iconScanRes: Int?,
|
||||
): CompletionResult<T> = withContext(Dispatchers.Main) {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage, accessCode, iconScanRes) { result ->
|
||||
|
|
@ -253,7 +271,7 @@ class TangemSdkManager(
|
|||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
|
||||
override fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
|
||||
tangemSdk.config.cardIdDisplayFormat = when {
|
||||
scanResponse == null -> CardIdDisplayFormat.Full
|
||||
scanResponse.cardTypesResolver.isTangemTwins() -> CardIdDisplayFormat.LastLuhn(4)
|
||||
|
|
@ -262,14 +280,60 @@ class TangemSdkManager(
|
|||
}
|
||||
|
||||
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
||||
fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
|
||||
override fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
|
||||
return resources.getString(stringResId, *formatArgs)
|
||||
}
|
||||
|
||||
fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {
|
||||
override fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {
|
||||
tangemSdk.config.userCodeRequestPolicy = policy
|
||||
}
|
||||
|
||||
// region Twin-specific
|
||||
|
||||
override suspend fun createFirstTwinWallet(
|
||||
cardId: String,
|
||||
initialMessage: Message,
|
||||
): CompletionResult<CreateWalletResponse> {
|
||||
return runTaskAsync(
|
||||
runnable = CreateFirstTwinWalletTask(cardId),
|
||||
cardId = cardId,
|
||||
initialMessage = initialMessage,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun createSecondTwinWallet(
|
||||
firstPublicKey: String,
|
||||
firstCardId: String,
|
||||
issuerKeys: KeyPair,
|
||||
preparingMessage: Message,
|
||||
creatingWalletMessage: Message,
|
||||
initialMessage: Message,
|
||||
): CompletionResult<CreateWalletResponse> {
|
||||
val task = CreateSecondTwinWalletTask(
|
||||
firstPublicKey = firstPublicKey,
|
||||
firstCardId = firstCardId,
|
||||
issuerKeys = issuerKeys,
|
||||
preparingMessage = preparingMessage,
|
||||
creatingWalletMessage = creatingWalletMessage,
|
||||
)
|
||||
return runTaskAsync(task, null, initialMessage)
|
||||
}
|
||||
|
||||
override suspend fun finalizeTwin(
|
||||
secondCardPublicKey: ByteArray,
|
||||
issuerKeyPair: KeyPair,
|
||||
cardId: String,
|
||||
initialMessage: Message,
|
||||
): CompletionResult<ScanResponse> {
|
||||
return runTaskAsync(
|
||||
runnable = FinalizeTwinTask(secondCardPublicKey, issuerKeyPair),
|
||||
cardId = cardId,
|
||||
initialMessage = initialMessage,
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
companion object {
|
||||
@Deprecated("Use [DefaultCardSdkProvider] instead")
|
||||
val config = Config(
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
package com.tangem.tap.domain.sdk.impl
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.Message
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.KeyPair
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.authentication.keystore.DummyKeystoreManager
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.UserCodeRequestPolicy
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.services.InMemoryStorage
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.mocks.MockProvider
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class MockTangemSdkManager(
|
||||
private val resources: Resources,
|
||||
) : TangemSdkManager {
|
||||
|
||||
override val canUseBiometry = false
|
||||
|
||||
override val needEnrollBiometrics = false
|
||||
|
||||
override val keystoreManager = DummyKeystoreManager()
|
||||
|
||||
override val secureStorage = InMemoryStorage()
|
||||
|
||||
override val userCodeRequestPolicy: UserCodeRequestPolicy
|
||||
get() = userCodeRequestPolicyInternal
|
||||
|
||||
private var userCodeRequestPolicyInternal: UserCodeRequestPolicy = UserCodeRequestPolicy.Default
|
||||
|
||||
override suspend fun scanProduct(
|
||||
cardId: String?,
|
||||
messageRes: Int?,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<ScanResponse> {
|
||||
return MockProvider.getScanResponse()
|
||||
}
|
||||
|
||||
override suspend fun createProductWallet(
|
||||
scanResponse: ScanResponse,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
return MockProvider.getCreateProductWalletResponse()
|
||||
}
|
||||
|
||||
override suspend fun importWallet(
|
||||
scanResponse: ScanResponse,
|
||||
mnemonic: String,
|
||||
passphrase: String?,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
return MockProvider.getImportWalletResponse()
|
||||
}
|
||||
|
||||
override suspend fun derivePublicKeys(
|
||||
cardId: String?,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): CompletionResult<DerivationTaskResponse> {
|
||||
return MockProvider.getDerivationTaskResponse()
|
||||
}
|
||||
|
||||
override suspend fun deriveExtendedPublicKey(
|
||||
cardId: String?,
|
||||
walletPublicKey: ByteArray,
|
||||
derivation: DerivationPath,
|
||||
): CompletionResult<ExtendedPublicKey> {
|
||||
return MockProvider.getExtendedPublicKey()
|
||||
}
|
||||
|
||||
override suspend fun resetToFactorySettings(
|
||||
cardId: String,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
return MockProvider.getCardDto()
|
||||
}
|
||||
|
||||
override suspend fun resetBackupCard(cardNumber: Int, userWalletId: UserWalletId): CompletionResult<Unit> {
|
||||
return CompletionResult.Success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
return CompletionResult.Success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
return CompletionResult.Success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun clearSavedUserCodes(): CompletionResult<Unit> {
|
||||
return CompletionResult.Success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return MockProvider.getSuccessResponse()
|
||||
}
|
||||
|
||||
override suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return MockProvider.getSuccessResponse()
|
||||
}
|
||||
|
||||
override suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return MockProvider.getSuccessResponse()
|
||||
}
|
||||
|
||||
override suspend fun setAccessCodeRecoveryEnabled(
|
||||
cardId: String?,
|
||||
enabled: Boolean,
|
||||
): CompletionResult<SuccessResponse> {
|
||||
return MockProvider.getSuccessResponse()
|
||||
}
|
||||
|
||||
override suspend fun scanCard(
|
||||
cardId: String?,
|
||||
allowRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
return MockProvider.getCardDto()
|
||||
}
|
||||
|
||||
override suspend fun <T> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>,
|
||||
cardId: String?,
|
||||
initialMessage: Message?,
|
||||
accessCode: String?,
|
||||
@DrawableRes iconScanRes: Int?,
|
||||
): CompletionResult<T> = error("This method is deprecated")
|
||||
|
||||
override fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
|
||||
// intentionally do nothing
|
||||
}
|
||||
|
||||
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
||||
override fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
|
||||
return resources.getString(stringResId, *formatArgs)
|
||||
}
|
||||
|
||||
override fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {
|
||||
userCodeRequestPolicyInternal = policy
|
||||
}
|
||||
|
||||
// region Twin-specific
|
||||
|
||||
override suspend fun createFirstTwinWallet(
|
||||
cardId: String,
|
||||
initialMessage: Message,
|
||||
): CompletionResult<CreateWalletResponse> {
|
||||
return MockProvider.createFirstTwinWallet()
|
||||
}
|
||||
|
||||
override suspend fun createSecondTwinWallet(
|
||||
firstPublicKey: String,
|
||||
firstCardId: String,
|
||||
issuerKeys: KeyPair,
|
||||
preparingMessage: Message,
|
||||
creatingWalletMessage: Message,
|
||||
initialMessage: Message,
|
||||
): CompletionResult<CreateWalletResponse> {
|
||||
return MockProvider.createSecondTwinWallet()
|
||||
}
|
||||
|
||||
override suspend fun finalizeTwin(
|
||||
secondCardPublicKey: ByteArray,
|
||||
issuerKeyPair: KeyPair,
|
||||
cardId: String,
|
||||
initialMessage: Message,
|
||||
): CompletionResult<ScanResponse> {
|
||||
return MockProvider.finalizeTwin()
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.tap.domain.sdk.mocks
|
||||
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
|
||||
interface MockContent {
|
||||
|
||||
val successResponse: SuccessResponse
|
||||
|
||||
val scanResponse: ScanResponse
|
||||
|
||||
val derivationTaskResponse: DerivationTaskResponse
|
||||
|
||||
val cardDto: CardDTO
|
||||
|
||||
val extendedPublicKey: ExtendedPublicKey
|
||||
|
||||
val createProductWalletTaskResponse: CreateProductWalletTaskResponse
|
||||
|
||||
// Wallet2-specific
|
||||
val importWalletResponse: CreateProductWalletTaskResponse
|
||||
|
||||
// Twin-specific
|
||||
val finalizeTwinResponse: ScanResponse
|
||||
|
||||
// Twin-specific
|
||||
val createFirstTwinResponse: CreateWalletResponse
|
||||
|
||||
// Twin-specific
|
||||
val createSecondTwinResponse: CreateWalletResponse
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package com.tangem.tap.domain.sdk.mocks
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.tap.domain.sdk.mocks.content.WalletMockContent
|
||||
import com.tangem.tap.domain.sdk.mocks.content.Wallet2MockContent
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
|
||||
object MockProvider {
|
||||
|
||||
private var content: MockContent = getMockContent(ProductType.Wallet)
|
||||
|
||||
private var emulateError: Boolean = false
|
||||
|
||||
private var emulatedError: TangemError = TangemSdkError.TagLost()
|
||||
|
||||
fun setEmulateError(error: TangemError? = null) {
|
||||
emulateError = true
|
||||
error?.let {
|
||||
emulatedError = it
|
||||
}
|
||||
}
|
||||
|
||||
fun resetEmulateError() {
|
||||
emulateError = false
|
||||
}
|
||||
|
||||
fun setMocks(productType: ProductType) {
|
||||
content = getMockContent(productType)
|
||||
}
|
||||
|
||||
fun setMocks(mockContent: MockContent) {
|
||||
content = mockContent
|
||||
}
|
||||
|
||||
fun getSuccessResponse() = CompletionResult.Success(content.successResponse).orFailure()
|
||||
|
||||
fun getScanResponse() = CompletionResult.Success(content.scanResponse).orFailure()
|
||||
|
||||
fun getDerivationTaskResponse() = CompletionResult.Success(content.derivationTaskResponse).orFailure()
|
||||
|
||||
fun getCardDto() = CompletionResult.Success(content.cardDto).orFailure()
|
||||
|
||||
fun getExtendedPublicKey() = CompletionResult.Success(content.extendedPublicKey).orFailure()
|
||||
|
||||
fun getCreateProductWalletResponse(): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
return CompletionResult.Success(content.createProductWalletTaskResponse).orFailure()
|
||||
}
|
||||
|
||||
fun getImportWalletResponse(): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
return CompletionResult.Success(content.importWalletResponse).orFailure()
|
||||
}
|
||||
|
||||
// region Twin-specific
|
||||
|
||||
fun finalizeTwin() = CompletionResult.Success(content.finalizeTwinResponse).orFailure()
|
||||
|
||||
fun createFirstTwinWallet() = CompletionResult.Success(content.createFirstTwinResponse).orFailure()
|
||||
|
||||
fun createSecondTwinWallet() = CompletionResult.Success(content.createSecondTwinResponse).orFailure()
|
||||
|
||||
// endregion
|
||||
|
||||
private fun getMockContent(productType: ProductType): MockContent {
|
||||
return when (productType) {
|
||||
ProductType.Wallet -> WalletMockContent
|
||||
ProductType.Wallet2 -> Wallet2MockContent
|
||||
else -> TODO()
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> CompletionResult.Success<T>.orFailure(): CompletionResult<T> {
|
||||
return if (emulateError) {
|
||||
CompletionResult.Failure(emulatedError)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.content
|
||||
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
|
||||
object Wallet2MockContent : MockContent {
|
||||
|
||||
override val scanResponse: ScanResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val cardDto: CardDTO
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val derivationTaskResponse: DerivationTaskResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val extendedPublicKey: ExtendedPublicKey
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val successResponse: SuccessResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val createProductWalletTaskResponse: CreateProductWalletTaskResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val importWalletResponse: CreateProductWalletTaskResponse
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val createFirstTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val createSecondTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val finalizeTwinResponse: ScanResponse
|
||||
get() = error("Available only for Twin")
|
||||
}
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
package com.tangem.tap.domain.sdk.mocks.content
|
||||
|
||||
import com.tangem.common.SuccessResponse
|
||||
import com.tangem.common.card.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.operations.backup.PrimaryCard
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.ExtendedPublicKeysMap
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.domain.sdk.mocks.MockContent
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
import java.util.Date
|
||||
|
||||
object WalletMockContent : MockContent {
|
||||
|
||||
private val primaryCard = PrimaryCard(
|
||||
cardId = "AC05000000086747",
|
||||
batchId = "AC05",
|
||||
cardPublicKey = byteArrayOf(2, -120, -3, -32, -122, -127, -120, -104, 59, 72, 76, 114, 94, 75, -37, -55, 55, 99, 66, 123, 85, -87, 80, 106, 105, -116, 87, -83, -12, 70, 108, -68, -39),
|
||||
linkingKey = byteArrayOf(
|
||||
2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121,
|
||||
-57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91,
|
||||
),
|
||||
existingWalletsCount = 3, isHDWalletAllowed = true,
|
||||
issuer = Card.Issuer(
|
||||
name = "TANGEM AG",
|
||||
publicKey = byteArrayOf(3, 86, -25, -61, 55, 99, 41, -33, -82, 115, -120, -33, 22, -107, 103, 3, -122, 16, 60, -110, 72, 106, -121, 100, 79, -87, -27, 18, -55, -49, 78, -110, -2),
|
||||
),
|
||||
walletCurves = listOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519, EllipticCurve.Bls12381G2Aug),
|
||||
firmwareVersion = FirmwareVersion(
|
||||
major = 4,
|
||||
minor = 52,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
isKeysImportAllowed = false,
|
||||
certificate = null,
|
||||
)
|
||||
|
||||
override val cardDto = CardDTO(
|
||||
cardId = "AC05000000086747",
|
||||
batchId = "AC05",
|
||||
cardPublicKey = byteArrayOf(2, -120, -3, -32, -122, -127, -120, -104, 59, 72, 76, 114, 94, 75, -37, -55, 55, 99, 66, 123, 85, -87, 80, 106, 105, -116, 87, -83, -12, 70, 108, -68, -39),
|
||||
firmwareVersion = CardDTO.FirmwareVersion(
|
||||
major = 4,
|
||||
minor = 52,
|
||||
patch = 0,
|
||||
type = FirmwareVersion.FirmwareType.Release,
|
||||
),
|
||||
manufacturer = CardDTO.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1649635200000),
|
||||
signature = byteArrayOf(-20, 98, -101, 94, -23, 73, 122, -21, 74, 76, 79, -55, -102, -62, -30, 44, -38, 118, 75, 121, -36, 118, -62, 60, -38, -63, 33, -14, -98, -69, 112, 22, 48, -43, 47, 65, -61, -56, 38, -94, -45, 44, 95, -22, 6, -31, -40, -25, 42, 38, 94, -99, 71, 98, -33, 27, -102, 30, 52, -7, 51, -27, 84, 66),
|
||||
),
|
||||
issuer = CardDTO.Issuer(
|
||||
name = "TANGEM AG",
|
||||
publicKey = byteArrayOf(3, 86, -25, -61, 55, 99, 41, -33, -82, 115, -120, -33, 22, -107, 103, 3, -122, 16, 60, -110, 72, 106, -121, 100, 79, -87, -27, 18, -55, -49, 78, -110, -2),
|
||||
),
|
||||
settings = CardDTO.Settings(
|
||||
securityDelay = 15000,
|
||||
maxWalletsCount = 20,
|
||||
isSettingAccessCodeAllowed = false,
|
||||
isSettingPasscodeAllowed = false,
|
||||
isResettingUserCodesAllowed = true,
|
||||
isLinkedTerminalEnabled = true,
|
||||
isBackupAllowed = true,
|
||||
supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None),
|
||||
isFilesAllowed = true,
|
||||
isHDWalletAllowed = true,
|
||||
isKeysImportAllowed = false,
|
||||
),
|
||||
userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true),
|
||||
linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None,
|
||||
isAccessCodeSet = false,
|
||||
isPasscodeSet = false,
|
||||
supportedCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
EllipticCurve.Secp256r1,
|
||||
EllipticCurve.Bls12381G2,
|
||||
EllipticCurve.Bls12381G2Aug,
|
||||
EllipticCurve.Bls12381G2Pop,
|
||||
EllipticCurve.Bip0340,
|
||||
),
|
||||
wallets = listOf(
|
||||
CardDTO.Wallet(
|
||||
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),
|
||||
chainCode = byteArrayOf(80, 13, -8, -108, -35, 116, -92, 125, -65, -28, 85, 72, -113, 59, 83, 13, 5, -83, -102, 123, 124, -22, 94, 108, -71, 95, 65, -2, 38, 38, -108, 14),
|
||||
curve = EllipticCurve.Secp256k1,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 0,
|
||||
hasBackup = false,
|
||||
derivedKeys = mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
||||
),
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey(
|
||||
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'/1'/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),
|
||||
),
|
||||
),
|
||||
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),
|
||||
chainCode = byteArrayOf(80, 13, -8, -108, -35, 116, -92, 125, -65, -28, 85, 72, -113, 59, 83, 13, 5, -83, -102, 123, 124, -22, 94, 108, -71, 95, 65, -2, 38, 38, -108, 14),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
CardDTO.Wallet(
|
||||
publicKey = byteArrayOf(-65, -53, -62, -12, -57, -32, -38, -9, -128, -52, -83, -61, 73, 39, 41, 15, -74, -97, 38, 52, -101, 63, 74, -56, -20, 15, 57, -127, 114, -93, -17, -109),
|
||||
chainCode = byteArrayOf(-81, 15, 125, 28, -115, -22, 87, 81, 87, -123, -25, -74, 86, 2, 1, 110, -115, 65, -110, 63, -64, 83, -93, -97, -104, 123, 12, -26, 94, 27, 84, -6),
|
||||
curve = EllipticCurve.Ed25519,
|
||||
settings = CardWallet.Settings(isPermanent = false),
|
||||
totalSignedHashes = 0,
|
||||
remainingSignatures = null,
|
||||
index = 1,
|
||||
hasBackup = false,
|
||||
derivedKeys = emptyMap(),
|
||||
extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(-65, -53, -62, -12, -57, -32, -38, -9, -128, -52, -83, -61, 73, 39, 41, 15, -74, -97, 38, 52, -101, 63, 74, -56, -20, 15, 57, -127, 114, -93, -17, -109),
|
||||
chainCode = byteArrayOf(-81, 15, 125, 28, -115, -22, 87, 81, 87, -123, -25, -74, 86, 2, 1, 110, -115, 65, -110, 63, -64, 83, -93, -97, -104, 123, 12, -26, 94, 27, 84, -6),
|
||||
),
|
||||
isImported = false,
|
||||
),
|
||||
),
|
||||
attestation = Attestation(
|
||||
cardKeyAttestation = Attestation.Status.Verified,
|
||||
walletKeysAttestation = Attestation.Status.Skipped,
|
||||
firmwareAttestation = Attestation.Status.Skipped,
|
||||
cardUniquenessAttestation = Attestation.Status.Skipped,
|
||||
),
|
||||
backupStatus = CardDTO.BackupStatus.NoBackup,
|
||||
)
|
||||
|
||||
override val scanResponse = ScanResponse(
|
||||
card = cardDto,
|
||||
productType = ProductType.Wallet,
|
||||
walletData = null,
|
||||
secondTwinPublicKey = null,
|
||||
derivedKeys = emptyMap(),
|
||||
primaryCard = null,
|
||||
)
|
||||
|
||||
override val derivationTaskResponse = DerivationTaskResponse(
|
||||
entries = mapOf(
|
||||
ByteArrayKey(
|
||||
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),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch
|
||||
publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121),
|
||||
chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge
|
||||
publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124),
|
||||
chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
override val extendedPublicKey = ExtendedPublicKey(
|
||||
publicKey = byteArrayOf(3, 2, 95, 53, 40, -87, -60, 11, -8, -47, 41, 37, 100, 15, -69, 1, -122, 127, -20, -81, -32, -20, -24, 5, -28, 113, 106, -90, -59, -30, -27, -110, -110),
|
||||
chainCode = byteArrayOf(-95, -87, -95, -25, 27, 96, -57, -92, -69, -106, -45, 10, 85, 4, -92, -68, 49, -24, -28, -50, -49, -77, -20, 118, -50, -27, 104, -93, 115, -50, -46, -34),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
)
|
||||
|
||||
override val successResponse = SuccessResponse(cardId = "AC05000000086747")
|
||||
|
||||
override val createProductWalletTaskResponse = CreateProductWalletTaskResponse(
|
||||
card = cardDto,
|
||||
derivedKeys = mapOf(
|
||||
ByteArrayKey(
|
||||
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),
|
||||
)
|
||||
to
|
||||
ExtendedPublicKeysMap(
|
||||
mapOf(
|
||||
DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc
|
||||
publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52),
|
||||
chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth
|
||||
publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67),
|
||||
chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109),
|
||||
depth = 0,
|
||||
parentFingerprint = byteArrayOf(0, 0, 0, 0),
|
||||
childNumber = 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
primaryCard = primaryCard,
|
||||
)
|
||||
|
||||
override val importWalletResponse: CreateProductWalletTaskResponse
|
||||
get() = error("Available only for Wallet 2")
|
||||
|
||||
override val createFirstTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val createSecondTwinResponse: CreateWalletResponse
|
||||
get() = error("Available only for Twin")
|
||||
|
||||
override val finalizeTwinResponse: ScanResponse
|
||||
get() = error("Available only for Twin")
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.domain.settings
|
||||
|
||||
import com.tangem.domain.settings.repositories.LegacySettingsRepository
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||
|
||||
internal class DefaultLegacySettingsRepository(
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.tap.domain.tasks
|
||||
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.SessionEnvironment
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.operations.preflightread.PreflightReadFilter
|
||||
|
||||
/**
|
||||
* [PreflightReadFilter] for checking if card has expected user wallet id
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class UserWalletIdPreflightReadFilter(private val expectedUserWalletId: UserWalletId) : PreflightReadFilter {
|
||||
|
||||
override fun onCardRead(card: Card, environment: SessionEnvironment) = Unit
|
||||
|
||||
override fun onFullCardRead(card: Card, environment: SessionEnvironment) {
|
||||
val actualUserWalletId = UserWalletIdBuilder.card(card = CardDTO(card)).build()
|
||||
|
||||
if (expectedUserWalletId != actualUserWalletId) throw TangemSdkError.WalletNotFound()
|
||||
}
|
||||
}
|
||||
|
|
@ -3,13 +3,13 @@ package com.tangem.tap.domain.tasks.product
|
|||
import com.tangem.blockchain.blockchains.cardano.CardanoUtils
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.datasource.local.token.UserTokensStore
|
||||
import com.tangem.domain.common.DerivationStyleProvider
|
||||
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.userwallets.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.tap.domain.tasks.product
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.CompletionCallback
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.operations.PreflightReadMode
|
||||
import com.tangem.operations.PreflightReadTask
|
||||
import com.tangem.tap.domain.tasks.UserWalletIdPreflightReadFilter
|
||||
|
||||
/**
|
||||
* Task for resetting backup card.
|
||||
*
|
||||
* 1. Read card and check if card is corresponding to expected user wallet id.
|
||||
* 2. Reset card.
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class ResetBackupCardTask(
|
||||
private val userWalletId: UserWalletId,
|
||||
) : CardSessionRunnable<Unit> {
|
||||
|
||||
override val allowsRequestAccessCodeFromRepository: Boolean = false
|
||||
|
||||
override fun run(session: CardSession, callback: CompletionCallback<Unit>) {
|
||||
PreflightReadTask(
|
||||
readMode = PreflightReadMode.FullCardRead,
|
||||
filter = UserWalletIdPreflightReadFilter(expectedUserWalletId = userWalletId),
|
||||
).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> resetCard(session, callback)
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetCard(session: CardSession, callback: CompletionCallback<Unit>) {
|
||||
ResetToFactorySettingsTask(allowsRequestAccessCodeFromRepository).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> callback(CompletionResult.Success(Unit))
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +1,36 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.Message
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.KeyPair
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.datasource.asset.AssetReader
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class TwinCardsManager(
|
||||
card: CardDTO,
|
||||
assetReader: AssetReader,
|
||||
) {
|
||||
class TwinCardsManager(card: CardDTO) {
|
||||
private val firstCardId: String = card.cardId
|
||||
private val publicKey: String = card.issuer.publicKey.toHexString()
|
||||
|
||||
private var currentCardPublicKey: String? = null
|
||||
private var secondCardPublicKey: String? = null
|
||||
|
||||
private val issuerKeyPair: KeyPair = getIssuerKeys(assetReader, card.issuer.publicKey.toHexString())
|
||||
private val issuerKeyPairFlow = MutableStateFlow<KeyPair?>(value = null)
|
||||
|
||||
suspend fun createFirstWallet(message: Message): CompletionResult<CreateWalletResponse> {
|
||||
val response = tangemSdkManager.runTaskAsync(
|
||||
runnable = CreateFirstTwinWalletTask(firstCardId),
|
||||
cardId = firstCardId,
|
||||
initialMessage = message,
|
||||
)
|
||||
when (response) {
|
||||
is CompletionResult.Success -> currentCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
is CompletionResult.Failure -> {}
|
||||
val response = tangemSdkManager.createFirstTwinWallet(cardId = firstCardId, initialMessage = message)
|
||||
|
||||
if (response is CompletionResult.Success) {
|
||||
currentCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
|
@ -44,58 +39,54 @@ class TwinCardsManager(
|
|||
preparingMessage: Message,
|
||||
creatingWalletMessage: Message,
|
||||
): CompletionResult<CreateWalletResponse> {
|
||||
val task = CreateSecondTwinWalletTask(
|
||||
val response = tangemSdkManager.createSecondTwinWallet(
|
||||
firstPublicKey = currentCardPublicKey!!,
|
||||
firstCardId = firstCardId,
|
||||
issuerKeys = issuerKeyPair,
|
||||
issuerKeys = getIssuerKeys(),
|
||||
preparingMessage = preparingMessage,
|
||||
creatingWalletMessage = creatingWalletMessage,
|
||||
initialMessage = initialMessage,
|
||||
)
|
||||
val response = tangemSdkManager.runTaskAsync(task, null, initialMessage)
|
||||
when (response) {
|
||||
is CompletionResult.Success -> {
|
||||
secondCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
}
|
||||
is CompletionResult.Failure -> {}
|
||||
|
||||
if (response is CompletionResult.Success) {
|
||||
secondCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
suspend fun complete(message: Message): Result<ScanResponse> {
|
||||
val response = tangemSdkManager.runTaskAsync(
|
||||
runnable = FinalizeTwinTask(secondCardPublicKey!!.hexToBytes(), issuerKeyPair),
|
||||
val response = tangemSdkManager.finalizeTwin(
|
||||
secondCardPublicKey = secondCardPublicKey!!.hexToBytes(),
|
||||
issuerKeyPair = getIssuerKeys(),
|
||||
cardId = firstCardId,
|
||||
initialMessage = message,
|
||||
)
|
||||
|
||||
return when (response) {
|
||||
is CompletionResult.Success -> Result.Success(response.data)
|
||||
is CompletionResult.Failure -> Result.fromTangemSdkError(response.error)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun getIssuerKeys(reader: AssetReader, publicKey: String): KeyPair {
|
||||
val issuer = getIssuers(reader).first { it.publicKey == publicKey }
|
||||
return KeyPair(
|
||||
publicKey = issuer.publicKey.hexToBytes(),
|
||||
privateKey = issuer.privateKey.hexToBytes(),
|
||||
)
|
||||
}
|
||||
private suspend fun getIssuerKeys(): KeyPair {
|
||||
issuerKeyPairFlow.value?.let { return it }
|
||||
|
||||
private fun getAdapter(): JsonAdapter<List<Issuer>> {
|
||||
return MoshiConverter.sdkMoshi.adapter(
|
||||
Types.newParameterizedType(List::class.java, Issuer::class.java),
|
||||
)
|
||||
}
|
||||
val assetLoader = store.inject(DaggerGraphState::assetLoader)
|
||||
val issuer = assetLoader.loadList<Issuer>(fileName = ISSUERS_FILE_NAME)
|
||||
.first { it.publicKey == publicKey }
|
||||
|
||||
private fun getIssuers(reader: AssetReader): List<Issuer> {
|
||||
val file = reader.readJson(fileName = "tangem-app-config/issuers")
|
||||
return getAdapter().fromJson(file)!!
|
||||
return KeyPair(
|
||||
publicKey = issuer.publicKey.hexToBytes(),
|
||||
privateKey = issuer.privateKey.hexToBytes(),
|
||||
).also {
|
||||
issuerKeyPairFlow.value = it
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val ISSUERS_FILE_NAME = "tangem-app-config/issuers"
|
||||
}
|
||||
}
|
||||
|
||||
private class Issuer(
|
||||
val privateKey: String,
|
||||
val publicKey: String,
|
||||
)
|
||||
private class Issuer(val privateKey: String, val publicKey: String)
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.tangem.tap.domain.userWalletList
|
||||
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManagerFeatureToggles
|
||||
|
||||
internal class DefaultUserWalletsListManagerFeatureToggles(
|
||||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
) : UserWalletsListManagerFeatureToggles {
|
||||
|
||||
override val isGeneralManagerEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled(name = "GENERAL_USER_WALLETS_LIST_MANAGER_ENABLED")
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package com.tangem.tap.domain.userWalletList.di
|
||||
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManagerFeatureToggles
|
||||
import com.tangem.tap.domain.userWalletList.DefaultUserWalletsListManagerFeatureToggles
|
||||
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 UserWalletsListManagerFeatureTogglesModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUserWalletsListManagerFeatureToggles(
|
||||
featureTogglesManager: FeatureTogglesManager,
|
||||
): UserWalletsListManagerFeatureToggles {
|
||||
return DefaultUserWalletsListManagerFeatureToggles(featureTogglesManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ internal object UserWalletsListManagerModule {
|
|||
val secureStorage = AndroidSecureStorage(
|
||||
preferences = SecureStorage.createEncryptedSharedPreferences(
|
||||
context = applicationContext,
|
||||
storageName = USER_WALLETS_STORAGE_NAME,
|
||||
storageName = "user_wallets_storage",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
package com.tangem.tap.domain.userWalletList.di
|
||||
|
||||
import android.content.Context
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.common.authentication.storage.AuthenticatedStorage
|
||||
import com.tangem.common.json.TangemSdkAdapter
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.sdk.storage.AndroidSecureStorage
|
||||
import com.tangem.sdk.storage.createEncryptedSharedPreferences
|
||||
import com.tangem.tap.domain.userWalletList.implementation.BiometricUserWalletsListManager
|
||||
import com.tangem.tap.domain.userWalletList.implementation.RuntimeUserWalletsListManager
|
||||
import com.tangem.tap.domain.userWalletList.repository.DelegatedKeystoreManager
|
||||
import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysStoreDecorator
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.BiometricUserWalletsKeysRepository
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultSelectedUserWalletRepository
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsPublicInformationRepository
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsSensitiveInformationRepository
|
||||
import com.tangem.tap.domain.userWalletList.utils.json.*
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.utils.Provider
|
||||
|
||||
internal const val USER_WALLETS_STORAGE_NAME = "user_wallets_storage"
|
||||
|
||||
fun UserWalletsListManager.Companion.provideBiometricImplementation(
|
||||
applicationContext: Context,
|
||||
): UserWalletsListManager {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(WalletDerivedKeysMapAdapter())
|
||||
.add(ScanResponseDerivedKeysMapAdapter())
|
||||
.add(ByteArrayKeyAdapter())
|
||||
.add(ExtendedPublicKeysMapAdapter())
|
||||
.add(CardBackupStatusAdapter())
|
||||
.add(DerivationPathAdapterWithMigration())
|
||||
.add(TangemSdkAdapter.DateAdapter())
|
||||
.add(TangemSdkAdapter.DerivationNodeAdapter())
|
||||
.add(TangemSdkAdapter.FirmwareVersionAdapter()) // For PrimaryCard model
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
|
||||
val secureStorage = AndroidSecureStorage(
|
||||
preferences = SecureStorage.createEncryptedSharedPreferences(
|
||||
context = applicationContext,
|
||||
storageName = USER_WALLETS_STORAGE_NAME,
|
||||
),
|
||||
)
|
||||
|
||||
val authenticatedStorage = AuthenticatedStorage(
|
||||
secureStorage = UserWalletsKeysStoreDecorator(
|
||||
featureStorage = secureStorage,
|
||||
cardSdkStorageProvider = Provider { tangemSdkManager.secureStorage },
|
||||
),
|
||||
keystoreManager = DelegatedKeystoreManager(
|
||||
keystoreManagerProvider = Provider { tangemSdkManager.keystoreManager },
|
||||
),
|
||||
)
|
||||
|
||||
val keysRepository = BiometricUserWalletsKeysRepository(
|
||||
moshi = moshi,
|
||||
secureStorage = secureStorage,
|
||||
authenticatedStorage = authenticatedStorage,
|
||||
)
|
||||
val publicInformationRepository = DefaultUserWalletsPublicInformationRepository(
|
||||
moshi = moshi,
|
||||
secureStorage = secureStorage,
|
||||
)
|
||||
val sensitiveInformationRepository = DefaultUserWalletsSensitiveInformationRepository(
|
||||
moshi = moshi,
|
||||
secureStorage = secureStorage,
|
||||
)
|
||||
val selectedUserWalletRepository = DefaultSelectedUserWalletRepository(
|
||||
secureStorage = secureStorage,
|
||||
)
|
||||
|
||||
return BiometricUserWalletsListManager(
|
||||
keysRepository = keysRepository,
|
||||
publicInformationRepository = publicInformationRepository,
|
||||
sensitiveInformationRepository = sensitiveInformationRepository,
|
||||
selectedUserWalletRepository = selectedUserWalletRepository,
|
||||
)
|
||||
}
|
||||
|
||||
fun UserWalletsListManager.Companion.provideRuntimeImplementation(): UserWalletsListManager {
|
||||
return RuntimeUserWalletsListManager()
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository
|
|||
import com.tangem.tap.domain.userWalletList.repository.UserWalletsPublicInformationRepository
|
||||
import com.tangem.tap.domain.userWalletList.repository.UserWalletsSensitiveInformationRepository
|
||||
import com.tangem.tap.domain.userWalletList.utils.encryptionKey
|
||||
import com.tangem.tap.domain.userWalletList.utils.lockAll
|
||||
import com.tangem.tap.domain.userWalletList.utils.toUserWallets
|
||||
import com.tangem.tap.domain.userWalletList.utils.updateWith
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
|
@ -27,11 +28,16 @@ internal class BiometricUserWalletsListManager(
|
|||
) : UserWalletsListManager.Lockable {
|
||||
private val state = MutableStateFlow(State())
|
||||
|
||||
override val isLockable: Boolean = true
|
||||
|
||||
override val userWallets: Flow<List<UserWallet>>
|
||||
get() = state
|
||||
.mapLatest { it.userWallets }
|
||||
.distinctUntilChanged()
|
||||
|
||||
override val userWalletsSync: List<UserWallet>
|
||||
get() = state.value.userWallets
|
||||
|
||||
override val selectedUserWallet: Flow<UserWallet>
|
||||
get() = state
|
||||
.mapLatest { state ->
|
||||
|
|
@ -78,11 +84,13 @@ internal class BiometricUserWalletsListManager(
|
|||
}
|
||||
|
||||
override fun lock() {
|
||||
state.update { State() }
|
||||
}
|
||||
|
||||
override fun isLockable(): Boolean {
|
||||
return true
|
||||
state.update { prevState ->
|
||||
prevState.copy(
|
||||
encryptionKeys = emptyList(),
|
||||
userWallets = prevState.userWallets.lockAll(),
|
||||
isLocked = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> = catching {
|
||||
|
|
|
|||
|
|
@ -33,31 +33,52 @@ internal class GeneralUserWalletsListManager(
|
|||
) : UserWalletsListManager.Lockable {
|
||||
|
||||
private val applicationScope = CoroutineScope(dispatchers.io)
|
||||
private val implementation = MutableStateFlow(runtimeUserWalletsListManager)
|
||||
private val implementation: MutableStateFlow<UserWalletsListManager?> = MutableStateFlow(value = null)
|
||||
|
||||
private val requireImplementation: UserWalletsListManager
|
||||
get() = requireNotNull(implementation.value) {
|
||||
"UserWalletsListManager is not initialized"
|
||||
}
|
||||
|
||||
init {
|
||||
subscribeOnCurrentManager()
|
||||
}
|
||||
|
||||
override val isLockable: Boolean
|
||||
get() = requireImplementation.isLockable
|
||||
|
||||
override val userWallets: Flow<List<UserWallet>>
|
||||
get() = implementation.flatMapLatest { it.userWallets }
|
||||
get() = implementation.transformLatest { impl ->
|
||||
if (impl != null && impl.hasUserWallets) {
|
||||
emitAll(impl.userWallets)
|
||||
}
|
||||
}
|
||||
|
||||
override val userWalletsSync: List<UserWallet>
|
||||
get() = requireImplementation.userWalletsSync
|
||||
|
||||
override val selectedUserWallet: Flow<UserWallet>
|
||||
get() = implementation.flatMapLatest { it.selectedUserWallet }
|
||||
get() = implementation.transformLatest { impl ->
|
||||
if (impl != null && impl.hasUserWallets) {
|
||||
emitAll(impl.selectedUserWallet)
|
||||
}
|
||||
}
|
||||
|
||||
override val selectedUserWalletSync: UserWallet?
|
||||
get() = implementation.value.selectedUserWalletSync
|
||||
get() = requireImplementation.selectedUserWalletSync
|
||||
|
||||
override val hasUserWallets: Boolean
|
||||
get() = implementation.value.hasUserWallets
|
||||
get() = requireImplementation.hasUserWallets
|
||||
|
||||
override val walletsCount: Int
|
||||
get() = implementation.value.walletsCount
|
||||
get() = requireImplementation.walletsCount
|
||||
|
||||
override val isLocked: Flow<Boolean>
|
||||
get() = implementation.flatMapLatest {
|
||||
if (it is UserWalletsListManager.Lockable) {
|
||||
it.isLocked
|
||||
get() = implementation.transformLatest { impl ->
|
||||
if (impl == null) return@transformLatest
|
||||
|
||||
if (impl is UserWalletsListManager.Lockable) {
|
||||
emitAll(impl.isLocked)
|
||||
} else {
|
||||
error("RuntimeUserWalletsListManager is not lockable")
|
||||
}
|
||||
|
|
@ -65,43 +86,45 @@ internal class GeneralUserWalletsListManager(
|
|||
|
||||
override val isLockedSync: Boolean
|
||||
get() {
|
||||
val implementation = implementation.value
|
||||
return if (implementation is UserWalletsListManager.Lockable) {
|
||||
implementation.isLockedSync
|
||||
val impl = requireImplementation
|
||||
|
||||
return if (impl is UserWalletsListManager.Lockable) {
|
||||
impl.isLockedSync
|
||||
} else {
|
||||
error("RuntimeUserWalletsListManager is not lockable")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> {
|
||||
return implementation.value.select(userWalletId)
|
||||
return requireImplementation.select(userWalletId)
|
||||
}
|
||||
|
||||
override suspend fun save(userWallet: UserWallet, canOverride: Boolean): CompletionResult<Unit> {
|
||||
return implementation.value.save(userWallet, canOverride)
|
||||
return requireImplementation.save(userWallet, canOverride)
|
||||
}
|
||||
|
||||
override suspend fun update(
|
||||
userWalletId: UserWalletId,
|
||||
update: suspend (UserWallet) -> UserWallet,
|
||||
): CompletionResult<UserWallet> {
|
||||
return implementation.value.update(userWalletId, update)
|
||||
return requireImplementation.update(userWalletId, update)
|
||||
}
|
||||
|
||||
override suspend fun delete(userWalletIds: List<UserWalletId>): CompletionResult<Unit> {
|
||||
return implementation.value.delete(userWalletIds)
|
||||
return requireImplementation.delete(userWalletIds)
|
||||
}
|
||||
|
||||
override suspend fun clear(): CompletionResult<Unit> {
|
||||
return implementation.value.clear()
|
||||
return requireImplementation.clear()
|
||||
}
|
||||
|
||||
override suspend fun get(userWalletId: UserWalletId): CompletionResult<UserWallet> {
|
||||
return implementation.value.get(userWalletId)
|
||||
return requireImplementation.get(userWalletId)
|
||||
}
|
||||
|
||||
override suspend fun unlock(type: UserWalletsListManager.Lockable.UnlockType): CompletionResult<UserWallet> {
|
||||
val implementation = implementation.value
|
||||
val implementation = requireImplementation
|
||||
|
||||
return if (implementation is UserWalletsListManager.Lockable) {
|
||||
implementation.unlock(type)
|
||||
} else {
|
||||
|
|
@ -110,7 +133,8 @@ internal class GeneralUserWalletsListManager(
|
|||
}
|
||||
|
||||
override fun lock() {
|
||||
val implementation = implementation.value
|
||||
val implementation = requireImplementation
|
||||
|
||||
return if (implementation is UserWalletsListManager.Lockable) {
|
||||
implementation.lock()
|
||||
} else {
|
||||
|
|
@ -118,10 +142,6 @@ internal class GeneralUserWalletsListManager(
|
|||
}
|
||||
}
|
||||
|
||||
override fun isLockable(): Boolean {
|
||||
return implementation.value.isLockable()
|
||||
}
|
||||
|
||||
private fun subscribeOnCurrentManager() {
|
||||
appPreferencesStore.get(key = PreferencesKeys.SAVE_USER_WALLETS_KEY, default = false)
|
||||
.distinctUntilChanged()
|
||||
|
|
@ -144,17 +164,17 @@ internal class GeneralUserWalletsListManager(
|
|||
destinationManager = possibleManager,
|
||||
)
|
||||
|
||||
previousManager.clear()
|
||||
previousManager?.clear()
|
||||
}
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(applicationScope)
|
||||
}
|
||||
|
||||
private suspend fun copySelectedUserWallet(
|
||||
sourceManager: UserWalletsListManager,
|
||||
sourceManager: UserWalletsListManager?,
|
||||
destinationManager: UserWalletsListManager,
|
||||
): UserWalletsListManager {
|
||||
sourceManager.selectedUserWalletSync?.let { selectedWallet ->
|
||||
sourceManager?.selectedUserWalletSync?.let { selectedWallet ->
|
||||
destinationManager.save(selectedWallet, canOverride = true)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import kotlinx.coroutines.flow.*
|
|||
internal class RuntimeUserWalletsListManager : UserWalletsListManager {
|
||||
private val state = MutableStateFlow(State())
|
||||
|
||||
override val isLockable: Boolean = false
|
||||
|
||||
override val userWallets: Flow<List<UserWallet>>
|
||||
get() = state
|
||||
.mapLatest { listOfNotNull(it.userWallet) }
|
||||
|
|
@ -24,6 +26,9 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
|
|||
.filterNotNull()
|
||||
.distinctUntilChanged()
|
||||
|
||||
override val userWalletsSync: List<UserWallet>
|
||||
get() = listOfNotNull(state.value.userWallet)
|
||||
|
||||
override val selectedUserWalletSync: UserWallet?
|
||||
get() = state.value.userWallet
|
||||
|
||||
|
|
@ -34,7 +39,7 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
|
|||
* only 1 wallet stored in runtime implementation
|
||||
*/
|
||||
override val walletsCount: Int
|
||||
get() = 1
|
||||
get() = if (hasUserWallets) 1 else 0
|
||||
|
||||
override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> = catching {
|
||||
state.value.userWallet
|
||||
|
|
@ -85,10 +90,6 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
|
|||
state.value.userWallet ?: walletNotFound()
|
||||
}
|
||||
|
||||
override fun isLockable(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun saveInternal(userWallet: UserWallet): CompletionResult<Unit> = catching {
|
||||
state.update { prevState ->
|
||||
prevState.copy(
|
||||
|
|
|
|||
|
|
@ -61,4 +61,14 @@ internal fun List<UserWallet>.updateWith(
|
|||
?: wallet
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun List<UserWallet>.lockAll(): List<UserWallet> = map(UserWallet::lock)
|
||||
|
||||
internal fun UserWallet.lock(): UserWallet = copy(
|
||||
scanResponse = scanResponse.copy(
|
||||
card = scanResponse.card.copy(
|
||||
wallets = emptyList(),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -10,13 +10,11 @@ import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTrans
|
|||
import com.tangem.tap.domain.walletconnect2.domain.models.binance.tradeOrderSerializer
|
||||
import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData
|
||||
import com.tangem.tap.features.details.redux.walletconnect.TradeData
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
|
||||
import timber.log.Timber
|
||||
|
||||
internal object BnbHelper {
|
||||
|
||||
fun createMessageData(order: WCBinanceTransferOrder): BinanceMessageData.Transfer {
|
||||
fun createMessageData(order: WcBinanceTransferOrder): BinanceMessageData.Transfer {
|
||||
val input = order.msgs.first().inputs.first()
|
||||
val output = order.msgs.first().inputs.first()
|
||||
|
||||
|
|
@ -42,51 +40,7 @@ internal object BnbHelper {
|
|||
)
|
||||
}
|
||||
|
||||
fun WcBinanceTradeOrder.toWCBinanceTradeOrder(): WCBinanceTradeOrder {
|
||||
return WCBinanceTradeOrder(
|
||||
account_number = accountNumber,
|
||||
chain_id = chainId,
|
||||
data = data,
|
||||
memo = memo,
|
||||
sequence = sequence,
|
||||
source = source,
|
||||
msgs = msgs.map { it.toWCBinanceTradeOrderMessage() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun WcBinanceTradeOrder.Message.toWCBinanceTradeOrderMessage(): WCBinanceTradeOrder.Message {
|
||||
return WCBinanceTradeOrder.Message(id, orderType, price, quantity, sender, side, symbol, timeInforce)
|
||||
}
|
||||
|
||||
fun WcBinanceTransferOrder.toWCBinanceTransferOrder(): WCBinanceTransferOrder {
|
||||
return WCBinanceTransferOrder(
|
||||
account_number = accountNumber,
|
||||
chain_id = chainId,
|
||||
data = data,
|
||||
memo = memo,
|
||||
sequence = sequence,
|
||||
source = source,
|
||||
msgs = msgs.map { it.toWCBinanceTransferOrderMessage() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun WcBinanceTransferOrder.Message.toWCBinanceTransferOrderMessage(): WCBinanceTransferOrder.Message {
|
||||
return WCBinanceTransferOrder.Message(
|
||||
inputs.map { it.toWCBinanceItem() },
|
||||
outputs.map { it.toWCBinanceItem() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun WcBinanceTransferOrder.Message.Item.toWCBinanceItem(): WCBinanceTransferOrder.Message.Item {
|
||||
return WCBinanceTransferOrder.Message.Item(
|
||||
address,
|
||||
coins.map {
|
||||
WCBinanceTransferOrder.Message.Item.Coin(it.amount, it.denom)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun createMessageData(order: WCBinanceTradeOrder): BinanceMessageData.Trade {
|
||||
fun createMessageData(order: WcBinanceTradeOrder): BinanceMessageData.Trade {
|
||||
val address = order.msgs.first().sender
|
||||
|
||||
val tradeData = order.msgs.map {
|
||||
|
|
|
|||
|
|
@ -1,562 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.walletconnect.extensions.isDappSupported
|
||||
import com.tangem.tap.domain.walletconnect.extensions.toWcEthereumSignMessage
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcEthereumTransaction
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcPreparedRequest
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.EthTransactionData
|
||||
import com.tangem.tap.features.details.redux.walletconnect.*
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.walletConnectRepository
|
||||
import com.trustwallet.walletconnect.WCClient
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceCancelOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTradeOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
|
||||
import com.trustwallet.walletconnect.models.binance.WCBinanceTxConfirmParam
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
|
||||
import com.trustwallet.walletconnect.models.session.WCAddNetwork
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
import com.trustwallet.walletconnect.models.session.WCSessionUpdate
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.collections.set
|
||||
|
||||
@Suppress("LargeClass")
|
||||
internal class WalletConnectManager {
|
||||
|
||||
private var cardId: String? = null
|
||||
|
||||
private val okHttpClient: OkHttpClient by lazy {
|
||||
OkHttpClient.Builder()
|
||||
.connectTimeout(20, TimeUnit.SECONDS)
|
||||
.readTimeout(20, TimeUnit.SECONDS)
|
||||
.writeTimeout(20, TimeUnit.SECONDS)
|
||||
.addInterceptor(interceptor)
|
||||
.addInterceptor(RetryInterceptor())
|
||||
.build()
|
||||
}
|
||||
|
||||
private val interceptor by lazy {
|
||||
createNetworkLoggingInterceptor()
|
||||
}
|
||||
|
||||
private var sessions: MutableMap<Topic, WalletConnectActiveData> = mutableMapOf()
|
||||
|
||||
fun connect(wcUri: String) {
|
||||
val session = WCSession.from(wcUri).guard {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = null,
|
||||
error = TapError.WalletConnect.UnsupportedLink,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
if (sessions[session.topic] != null) {
|
||||
store.dispatchOnMain(WalletConnectAction.RefuseOpeningSession)
|
||||
return
|
||||
}
|
||||
val client = WCClient(httpClient = okHttpClient)
|
||||
setListeners(client)
|
||||
val peerId = UUID.randomUUID().toString()
|
||||
|
||||
try {
|
||||
client.connect(session, tangemPeerMeta, peerId)
|
||||
} catch (exception: IllegalArgumentException) {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = null,
|
||||
error = TapError.WalletConnect.UnsupportedLink,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
sessions[session.topic] = WalletConnectActiveData(
|
||||
peerId = peerId,
|
||||
remotePeerId = null,
|
||||
session = session,
|
||||
client = client,
|
||||
wallet = WalletForSession(),
|
||||
)
|
||||
setupConnectionTimeoutCheck(session)
|
||||
}
|
||||
|
||||
fun updateSession(session: WalletConnectSession) {
|
||||
val updatedSession = sessions[session.session.topic]?.copy(
|
||||
wallet = session.wallet,
|
||||
)
|
||||
if (updatedSession != null) {
|
||||
sessions[session.session.topic] = updatedSession
|
||||
}
|
||||
}
|
||||
|
||||
fun updateBlockchain(session: WalletConnectSession) {
|
||||
sessions[session.session.topic]?.client?.updateSession(
|
||||
accounts = listOfNotNull(session.getAddress()),
|
||||
chainId = session.wallet.blockchain?.getChainId(),
|
||||
approved = true,
|
||||
)
|
||||
|
||||
val updatedSession = sessions[session.session.topic]?.copy(
|
||||
wallet = session.wallet,
|
||||
)
|
||||
if (updatedSession != null) {
|
||||
sessions[session.session.topic] = updatedSession
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun setupConnectionTimeoutCheck(session: WCSession) {
|
||||
scope.launch {
|
||||
delay(20_000)
|
||||
val data = sessions[session.topic]
|
||||
if (data != null && data.peerMeta == null) {
|
||||
disconnect(session)
|
||||
store.dispatchOnMain(WalletConnectAction.OpeningSessionTimeout(session))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun restoreSessions(scanResponse: ScanResponse) {
|
||||
val walletPublicKey = scanResponse.card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }?.publicKey
|
||||
?: return
|
||||
if (scanResponse.card.backupStatus?.isActive != true) cardId = scanResponse.card.cardId
|
||||
val sessions = walletConnectRepository.loadSavedSessions()
|
||||
// filter sessions for this particular card
|
||||
.filter { it.wallet.walletPublicKey.contentEquals(walletPublicKey) }
|
||||
this.sessions = sessions
|
||||
.map { session ->
|
||||
WalletConnectActiveData(
|
||||
peerId = session.peerId,
|
||||
remotePeerId = session.remotePeerId,
|
||||
client = WCClient(httpClient = okHttpClient),
|
||||
session = session.session,
|
||||
peerMeta = session.peerMeta,
|
||||
wallet = session.wallet,
|
||||
)
|
||||
.also {
|
||||
setListeners(it.client)
|
||||
it.client.connect(it.session, tangemPeerMeta, it.peerId, it.remotePeerId)
|
||||
}
|
||||
}.associateBy { it.session.topic }.toMutableMap()
|
||||
|
||||
store.dispatchOnMain(WalletConnectAction.SetSessionsRestored(sessions))
|
||||
}
|
||||
|
||||
fun approve(session: WCSession) {
|
||||
val activeData = sessions[session.topic] ?: return
|
||||
removeSimilarSessions(activeData)
|
||||
|
||||
val key = activeData.wallet.derivedPublicKey ?: activeData.wallet.walletPublicKey ?: return
|
||||
val blockchain = activeData.wallet.getBlockchainForSession()
|
||||
val accounts = listOf(blockchain.makeAddresses(key).first().value)
|
||||
val approved = activeData.client.approveSession(
|
||||
accounts = accounts,
|
||||
chainId = blockchain.getChainId() ?: Blockchain.Ethereum.getChainId()!!,
|
||||
)
|
||||
if (approved) {
|
||||
val walletConnectSession = WalletConnectSession(
|
||||
peerId = activeData.peerId,
|
||||
remotePeerId = activeData.remotePeerId,
|
||||
wallet = activeData.wallet,
|
||||
session = session,
|
||||
peerMeta = activeData.peerMeta!!,
|
||||
)
|
||||
walletConnectRepository.saveSession(walletConnectSession)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.ApproveSession.Success(
|
||||
walletConnectSession,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeSimilarSessions(activeData: WalletConnectActiveData) {
|
||||
val sessionsToRemove = sessions.filter {
|
||||
it.value.wallet.walletPublicKey?.equals(activeData.wallet.walletPublicKey) == true &&
|
||||
it.value.peerMeta?.url == activeData.peerMeta?.url &&
|
||||
it.value.session != activeData.session
|
||||
}
|
||||
Timber.d("RemoveSimilarSessions: ${sessionsToRemove.values.map { it.client.session }}")
|
||||
sessionsToRemove.forEach { disconnect(it.value.session) }
|
||||
}
|
||||
|
||||
fun rejectRequest(topic: String, id: Long) {
|
||||
val activeData = sessions[topic] ?: return
|
||||
activeData.client.rejectRequest(id)
|
||||
}
|
||||
|
||||
private fun acceptRequest(topic: String, id: Long, data: String) {
|
||||
val activeData = sessions[topic] ?: return
|
||||
activeData.client.approveRequest(id, data)
|
||||
}
|
||||
|
||||
fun disconnect(session: WCSession) {
|
||||
val activeData = sessions[session.topic] ?: return
|
||||
val disconnected = if (activeData.client.isConnected) {
|
||||
activeData.client.killSession()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
if (disconnected) {
|
||||
onSessionClosed(session)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSessionClosed(session: WCSession) {
|
||||
sessions.remove(session.topic)
|
||||
walletConnectRepository.removeSession(session)
|
||||
store.dispatchOnMain(WalletConnectAction.RemoveSession(session))
|
||||
}
|
||||
|
||||
fun handleTransactionRequest(
|
||||
transaction: WcEthereumTransaction,
|
||||
session: WalletConnectSession,
|
||||
id: Long,
|
||||
type: WcEthTransactionType,
|
||||
) {
|
||||
val activeData = sessions[session.session.topic] ?: return
|
||||
scope.launch {
|
||||
val data = WalletConnectSdkHelper().prepareTransactionData(
|
||||
EthTransactionData(
|
||||
transaction = transaction,
|
||||
networkId = session.wallet.blockchain?.toNetworkId() ?: "",
|
||||
rawDerivationPath = session.wallet.derivationPath?.rawPath,
|
||||
id = id,
|
||||
topic = session.session.topic,
|
||||
type = type,
|
||||
metaName = session.peerMeta.name,
|
||||
metaUrl = session.peerMeta.url,
|
||||
),
|
||||
).guard {
|
||||
sessions[session.session.topic] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
session.session.topic,
|
||||
id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
sessions[session.session.topic] = activeData.copy(transactionData = data)
|
||||
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.RequestTransaction(
|
||||
WcPreparedRequest.EthTransaction(
|
||||
preparedRequestData = data,
|
||||
topic = session.session.topic,
|
||||
requestId = id,
|
||||
derivationPath = data.walletManager.wallet.publicKey.derivationPath?.rawPath,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun completeTransaction(topic: Topic) {
|
||||
val activeData = sessions[topic]
|
||||
val data = activeData?.transactionData ?: return
|
||||
scope.launch {
|
||||
val hash = WalletConnectSdkHelper().completeTransaction(data, cardId).guard {
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
topic,
|
||||
data.id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
acceptRequest(topic, data.id, hash)
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
}
|
||||
}
|
||||
|
||||
fun signBnb(id: Long, data: ByteArray, topic: Topic) {
|
||||
val activeData = sessions[topic] ?: return
|
||||
scope.launch {
|
||||
val hash = WalletConnectSdkHelper().signBnbTransaction(
|
||||
data = data,
|
||||
networkId = activeData.wallet.blockchain?.toNetworkId() ?: "",
|
||||
derivationPath = activeData.wallet.derivationPath?.rawPath,
|
||||
cardId = cardId,
|
||||
).guard {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
topic,
|
||||
id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
acceptRequest(topic, id, hash)
|
||||
}
|
||||
}
|
||||
|
||||
fun handlePersonalSignRequest(message: WCEthereumSignMessage, session: WalletConnectSession, id: Long) {
|
||||
val activeData = sessions[session.session.topic] ?: return
|
||||
scope.launch {
|
||||
val data = WalletConnectSdkHelper().prepareDataForPersonalSign(
|
||||
message = message.toWcEthereumSignMessage(),
|
||||
topic = session.session.topic,
|
||||
id = id,
|
||||
metaName = session.peerMeta.name,
|
||||
)
|
||||
sessions[session.session.topic] = activeData.copy(personalSignData = data)
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.PersonalSign(
|
||||
WcPreparedRequest.EthSign(
|
||||
preparedRequestData = data,
|
||||
topic = session.session.topic,
|
||||
requestId = id,
|
||||
derivationPath = session.wallet.derivationPath?.rawPath,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendSignedMessage(topic: Topic) {
|
||||
val activeData = sessions[topic]
|
||||
val data = activeData?.personalSignData ?: return
|
||||
scope.launch {
|
||||
val hash = WalletConnectSdkHelper().signPersonalMessage(
|
||||
hashToSign = data.hash,
|
||||
networkId = activeData.wallet.blockchain?.toNetworkId() ?: "",
|
||||
type = data.type,
|
||||
derivationPath = activeData.wallet.derivationPath?.rawPath,
|
||||
cardId = cardId,
|
||||
)
|
||||
.guard {
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.RejectRequest(
|
||||
topic,
|
||||
data.id,
|
||||
),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
sessions[topic] = activeData.copy(personalSignData = data)
|
||||
acceptRequest(topic, data.id, hash)
|
||||
sessions[topic] = activeData.copy(transactionData = null)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "ComplexMethod")
|
||||
private fun setListeners(client: WCClient) {
|
||||
client.onSessionRequest = { id: Long, peer: WCPeerMeta ->
|
||||
Timber.d("OnSessionRequest: $peer")
|
||||
val session = client.session
|
||||
val data = sessions[session?.topic]?.copy(peerMeta = peer, remotePeerId = client.remotePeerId)
|
||||
if (data != null && session != null) {
|
||||
if (!peer.isDappSupported()) {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.FailureEstablishingSession(
|
||||
session = session,
|
||||
error = TapError.WalletConnect.UnsupportedDapp,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
sessions[session.topic] = data
|
||||
val sessionData = data.toWalletConnectSession()
|
||||
sessionData?.let {
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.ScanCard(
|
||||
session = sessionData,
|
||||
chainId = client.chainId?.toIntOrNull(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
client.onSessionUpdate = { id: Long, update: WCSessionUpdate ->
|
||||
Timber.d("onSessionUpdate: $update")
|
||||
val session = client.session
|
||||
if (session != null && !update.approved) onSessionClosed(session)
|
||||
}
|
||||
client.onEthSendTransaction = { id: Long, transaction: WCEthereumTransaction ->
|
||||
Timber.d("onEthSendTransaction: $transaction")
|
||||
// Analytics.logWcEvent(
|
||||
// AnalyticsAnOld.WcAnalyticsEvent.Action(
|
||||
// AnalyticsAnOld.WcAction.SendTransaction
|
||||
// )
|
||||
// )
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandleTransactionRequest(
|
||||
transaction = transaction,
|
||||
session = sessionData,
|
||||
id = id,
|
||||
type = WcEthTransactionType.EthSendTransaction,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction ->
|
||||
Timber.d("onEthSignTransaction: $transaction")
|
||||
// Analytics.logWcEvent(
|
||||
// AnalyticsAnOld.WcAnalyticsEvent.Action(
|
||||
// AnalyticsAnOld.WcAction.SignTransaction
|
||||
// )
|
||||
// )
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandleTransactionRequest(
|
||||
transaction = transaction,
|
||||
session = sessionData,
|
||||
id = id,
|
||||
type = WcEthTransactionType.EthSignTransaction,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onEthSign = { id: Long, message: WCEthereumSignMessage ->
|
||||
Timber.d("onEthSign: $message")
|
||||
// Analytics.logWcEvent(
|
||||
// AnalyticsAnOld.WcAnalyticsEvent.Action(
|
||||
// AnalyticsAnOld.WcAction.PersonalSign
|
||||
// )
|
||||
// )
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.HandlePersonalSignRequest(
|
||||
message,
|
||||
sessionData,
|
||||
id,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
client.onBnbCancel = { id: Long, order: WCBinanceCancelOrder ->
|
||||
}
|
||||
client.onBnbTrade = { id: Long, order: WCBinanceTradeOrder ->
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.BinanceTransaction.Trade(
|
||||
id = id,
|
||||
order = order,
|
||||
sessionData = sessionData,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onBnbTransfer = { id: Long, order: WCBinanceTransferOrder ->
|
||||
sessions[client.session?.topic]?.toWalletConnectSession()?.let { sessionData ->
|
||||
store.dispatchOnMain(
|
||||
WalletConnectAction.BinanceTransaction.Transfer(
|
||||
id = id,
|
||||
order = order,
|
||||
sessionData = sessionData,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
client.onBnbTxConfirm = { id: Long, order: WCBinanceTxConfirmParam ->
|
||||
// send empty approve request if status is OK
|
||||
if (order.ok) client.approveRequest(id, "")
|
||||
}
|
||||
client.onDisconnect = { code: Int, reason: String ->
|
||||
val session = client.session
|
||||
if (session != null) {
|
||||
onSessionClosed(session)
|
||||
}
|
||||
}
|
||||
client.onWalletChangeNetwork = { id: Long, chainId: Int ->
|
||||
switchChain(chainId, client)
|
||||
}
|
||||
client.onWalletAddNetwork = { id: Long, network: WCAddNetwork ->
|
||||
// TODO
|
||||
// In fact this method is used to add a EVM network. It provides RPC url and chain ID.
|
||||
// Here now it just tries to switch to a EVM network with a provided chain ID.
|
||||
try {
|
||||
val chainId = Integer.decode(network.chainIdHex)
|
||||
switchChain(chainId, client)
|
||||
} catch (exception: Exception) {
|
||||
Timber.d("WC add network error: chain could not be parsed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchChain(chainId: Int, client: WCClient) {
|
||||
val blockchain = Blockchain.fromChainId(chainId)
|
||||
Timber.d("WC switch chainID\nNew Blockchain: $blockchain")
|
||||
val session = sessions[client.session?.topic]?.toWalletConnectSession()
|
||||
if (session != null) {
|
||||
store.dispatchOnMain(WalletConnectAction.SwitchBlockchain(blockchain, session))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val WC_SCHEME = "wc"
|
||||
|
||||
private val tangemPeerMeta = WCPeerMeta(name = "Tangem Wallet", url = "https://tangem.com")
|
||||
|
||||
fun isCorrectWcUri(string: String): Boolean = WCSession.from(string) != null
|
||||
}
|
||||
}
|
||||
|
||||
internal typealias Topic = String
|
||||
|
||||
internal data class WalletConnectActiveData(
|
||||
val peerId: String,
|
||||
val remotePeerId: String?,
|
||||
val client: WCClient,
|
||||
val session: WCSession,
|
||||
val peerMeta: WCPeerMeta? = null,
|
||||
val wallet: WalletForSession,
|
||||
val transactionData: WcTransactionData? = null,
|
||||
val personalSignData: WcPersonalSignData? = null,
|
||||
) {
|
||||
fun toWalletConnectSession(): WalletConnectSession? {
|
||||
if (peerMeta == null) return null
|
||||
return WalletConnectSession(
|
||||
peerId = peerId,
|
||||
remotePeerId = remotePeerId,
|
||||
wallet = wallet,
|
||||
session = session,
|
||||
peerMeta = peerMeta,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
internal class RetryInterceptor : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request: Request = chain.request()
|
||||
val response = chain.proceed(request)
|
||||
when (response.code) {
|
||||
502 -> {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
|
||||
object WalletConnectNetworkUtils {
|
||||
fun parseBlockchain(chainId: Int?, peer: WCPeerMeta): Blockchain? {
|
||||
return when {
|
||||
peer.url.contains("pancakeswap.finance") -> {
|
||||
Blockchain.BSC
|
||||
}
|
||||
peer.url.contains("optimism") -> {
|
||||
Blockchain.Optimism
|
||||
}
|
||||
chainId != null -> {
|
||||
Blockchain.fromChainId(chainId)
|
||||
}
|
||||
peer.url.contains("matic.network") || peer.name == "Polygon" -> {
|
||||
Blockchain.Polygon
|
||||
}
|
||||
peer.url.contains("binance.org") || peer.name.contains("Binance") -> {
|
||||
if (peer.icons.firstOrNull()?.contains("testnet") == true) {
|
||||
Blockchain.BinanceTestnet
|
||||
} else {
|
||||
Blockchain.Binance
|
||||
}
|
||||
}
|
||||
peer.name.contains("BSC") -> {
|
||||
Blockchain.BSC
|
||||
}
|
||||
peer.url.contains("honeyswap.1hive.eth.limo") -> {
|
||||
// Check if something's changed after this bug report:
|
||||
// https://github.com/1Hive/honeyswap-interface/issues/83
|
||||
Blockchain.Gnosis
|
||||
}
|
||||
else -> {
|
||||
Blockchain.Ethereum
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletForSession
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.session.WCSession
|
||||
import timber.log.Timber
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class WalletConnectRepository(val context: Application) {
|
||||
private val walletConnectAdapter: JsonAdapter<List<SessionDao>> = MoshiConverter.sdkMoshi.adapter(
|
||||
Types.newParameterizedType(List::class.java, SessionDao::class.java),
|
||||
)
|
||||
|
||||
fun saveSession(session: WalletConnectSession) {
|
||||
val sessions = loadSavedSessions() + session
|
||||
saveSessions(sessions)
|
||||
}
|
||||
|
||||
fun removeSession(session: WCSession) {
|
||||
val sessions = loadSavedSessions().filterNot { it.session == session }
|
||||
saveSessions(sessions)
|
||||
}
|
||||
|
||||
fun loadSavedSessions(): List<WalletConnectSession> {
|
||||
return try {
|
||||
val json = context.readFileText(FILE_NAME_PREFIX_SESSIONS)
|
||||
.hexToUtf8()
|
||||
walletConnectAdapter.fromJson(json)!!.map { it.toSession() }
|
||||
} catch (e: FileNotFoundException) {
|
||||
emptyList()
|
||||
} catch (exception: Exception) {
|
||||
Timber.w(exception)
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveSessions(sessions: List<WalletConnectSession>) {
|
||||
val json = walletConnectAdapter.toJson(sessions.map { SessionDao.fromSession(it) })
|
||||
.utf8ToHex() // convert to hex to solve problems with saving text with emojis
|
||||
Timber.e("WC sessions, saving following json: $json")
|
||||
context.rewriteFile(json, FILE_NAME_PREFIX_SESSIONS)
|
||||
}
|
||||
|
||||
private fun String.utf8ToHex(): String {
|
||||
return this.toByteArray().toHexString()
|
||||
}
|
||||
|
||||
private fun String.hexToUtf8(): String {
|
||||
return this.hexToBytes().toString(Charset.defaultCharset())
|
||||
}
|
||||
|
||||
private fun Context.readFileText(fileName: String): String =
|
||||
this.openFileInput(fileName).bufferedReader().readText()
|
||||
|
||||
private fun Context.rewriteFile(content: String, fileName: String) {
|
||||
this.openFileOutput(fileName, Context.MODE_PRIVATE).use {
|
||||
it.write(content.toByteArray(), 0, content.length)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FILE_NAME_PREFIX_SESSIONS = "wc_sessions"
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SessionDao(
|
||||
val peerId: String,
|
||||
val remotePeerId: String?,
|
||||
val wallet: WalletForSession,
|
||||
val session: WCSession,
|
||||
val peerMeta: WCPeerMeta,
|
||||
) {
|
||||
fun toSession(): WalletConnectSession {
|
||||
return WalletConnectSession(
|
||||
peerId = peerId,
|
||||
remotePeerId = remotePeerId,
|
||||
wallet = wallet,
|
||||
session = session,
|
||||
peerMeta = peerMeta,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromSession(session: WalletConnectSession): SessionDao {
|
||||
return SessionDao(
|
||||
peerId = session.peerId,
|
||||
remotePeerId = session.remotePeerId,
|
||||
wallet = session.wallet,
|
||||
session = session.session,
|
||||
peerMeta = session.peerMeta,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import com.tangem.blockchain.blockchains.ethereum.EthereumUtils.toKeccak
|
|||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.extensions.*
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toDecompressedPublicKey
|
||||
|
|
@ -15,13 +16,10 @@ import com.tangem.common.extensions.toHexString
|
|||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.models.Basic.TransactionSent.MemoType
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.operations.sign.SignHashCommand
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
import com.tangem.tap.domain.walletconnect.BnbHelper.toWCBinanceTradeOrder
|
||||
import com.tangem.tap.domain.walletconnect.BnbHelper.toWCBinanceTransferOrder
|
||||
import com.tangem.tap.domain.walletconnect2.domain.TransactionType
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcEthereumTransaction
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcSignMessage
|
||||
|
|
@ -38,7 +36,6 @@ import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionReque
|
|||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import com.tangem.core.analytics.models.AnalyticsParam as CoreAnalyticsParam
|
||||
|
|
@ -46,20 +43,35 @@ import com.tangem.core.analytics.models.AnalyticsParam as CoreAnalyticsParam
|
|||
@Suppress("LargeClass")
|
||||
class WalletConnectSdkHelper {
|
||||
|
||||
private val userWalletsListManager by lazy {
|
||||
store.inject(DaggerGraphState::generalUserWalletsListManager)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
suspend fun prepareTransactionData(data: EthTransactionData): WcTransactionData? {
|
||||
suspend fun prepareTransactionData(data: EthTransactionData): WcTransactionData {
|
||||
val transaction = data.transaction
|
||||
val blockchain = Blockchain.fromNetworkId(data.networkId) ?: return null
|
||||
val walletManager = getWalletManager(blockchain, data.rawDerivationPath) ?: return null
|
||||
val blockchain = requireNotNull(Blockchain.fromNetworkId(data.networkId)) {
|
||||
"Blockchain not found"
|
||||
}
|
||||
val walletManager = requireNotNull(getWalletManager(blockchain, data.rawDerivationPath)) {
|
||||
"WalletManager not found"
|
||||
}
|
||||
|
||||
walletManager.safeUpdate(isDemoCard())
|
||||
val wallet = walletManager.wallet
|
||||
val balance = wallet.amounts[AmountType.Coin]?.value ?: return null
|
||||
val balance = requireNotNull(wallet.amounts[AmountType.Coin]?.value) {
|
||||
"Coin balance not found"
|
||||
}
|
||||
|
||||
val decimals = wallet.blockchain.decimals()
|
||||
|
||||
val value = (transaction.value ?: "0").hexToBigDecimal()
|
||||
.movePointLeft(decimals) ?: return null
|
||||
val value = (transaction.value ?: "0")
|
||||
.hexToBigDecimal()
|
||||
.movePointLeft(decimals)
|
||||
|
||||
requireNotNull(value) {
|
||||
"Transaction amount is null"
|
||||
}
|
||||
|
||||
val gasLimit = getGasLimitFromTx(value, walletManager, transaction)
|
||||
|
||||
|
|
@ -68,20 +80,23 @@ class WalletConnectSdkHelper {
|
|||
is Result.Success -> result.data.toBigDecimal()
|
||||
is Result.Failure -> {
|
||||
(result.error as? Throwable)?.let { Timber.e(it, "getGasPrice failed") }
|
||||
return null
|
||||
|
||||
error("Unable to get gas price: ${result.error}")
|
||||
}
|
||||
null -> return null
|
||||
null -> error("Gas price is null")
|
||||
}
|
||||
|
||||
val fee = (gasLimit * gasPrice).movePointLeft(decimals)
|
||||
val total = value + fee
|
||||
|
||||
val destinationAddress = requireNotNull(transaction.to) { "Destination address is null" }
|
||||
|
||||
val transactionData = TransactionData(
|
||||
amount = Amount(value, wallet.blockchain),
|
||||
// TODO refactoring
|
||||
fee = Fee.Common(Amount(fee, wallet.blockchain)),
|
||||
sourceAddress = transaction.from,
|
||||
destinationAddress = transaction.to!!,
|
||||
destinationAddress = destinationAddress,
|
||||
extras = EthereumTransactionExtras(
|
||||
data = transaction.data.removePrefix(HEX_PREFIX).hexToBytes(),
|
||||
gasLimit = gasLimit.toBigInteger(),
|
||||
|
|
@ -101,6 +116,7 @@ class WalletConnectSdkHelper {
|
|||
id = data.id,
|
||||
type = data.type,
|
||||
)
|
||||
|
||||
return WcTransactionData(
|
||||
type = data.type,
|
||||
transaction = transactionData,
|
||||
|
|
@ -176,17 +192,17 @@ class WalletConnectSdkHelper {
|
|||
),
|
||||
)
|
||||
return when (result) {
|
||||
SimpleResult.Success -> {
|
||||
is Result.Success -> {
|
||||
val sentFrom = CoreAnalyticsParam.TxSentFrom.WalletConnect
|
||||
Analytics.send(Basic.TransactionSent(sentFrom = sentFrom, memoType = MemoType.Null))
|
||||
val hash = data.walletManager.wallet.recentTransactions.last().hash
|
||||
if (hash?.startsWith(HEX_PREFIX) == true) {
|
||||
val hash = result.data.hash
|
||||
if (hash.startsWith(HEX_PREFIX)) {
|
||||
hash
|
||||
} else {
|
||||
HEX_PREFIX + hash
|
||||
}
|
||||
}
|
||||
is SimpleResult.Failure -> {
|
||||
is Result.Failure -> {
|
||||
Timber.e(result.error as BlockchainSdkError)
|
||||
null
|
||||
}
|
||||
|
|
@ -227,11 +243,11 @@ class WalletConnectSdkHelper {
|
|||
}
|
||||
|
||||
fun prepareBnbTradeOrder(data: WcBinanceTradeOrder): BinanceMessageData.Trade {
|
||||
return BnbHelper.createMessageData(data.toWCBinanceTradeOrder())
|
||||
return BnbHelper.createMessageData(data)
|
||||
}
|
||||
|
||||
fun prepareBnbTransferOrder(data: WcBinanceTransferOrder): BinanceMessageData.Transfer {
|
||||
return BnbHelper.createMessageData(data.toWCBinanceTransferOrder())
|
||||
return BnbHelper.createMessageData(data)
|
||||
}
|
||||
|
||||
suspend fun signBnbTransaction(
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
package com.tangem.tap.domain.walletconnect.extensions
|
||||
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcEthereumTransaction
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcSignMessage
|
||||
import com.trustwallet.walletconnect.models.WCPeerMeta
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
|
||||
|
||||
internal fun WCPeerMeta.isDappSupported(): Boolean {
|
||||
return !unsupportedDappsList.any { this.url.contains(it) }
|
||||
}
|
||||
|
||||
private val unsupportedDappsList: List<String> = listOf("dydx.exchange")
|
||||
|
||||
internal fun WCEthereumTransaction.toWcEthTransaction(): WcEthereumTransaction {
|
||||
return WcEthereumTransaction(
|
||||
from = from,
|
||||
to = to,
|
||||
nonce = nonce,
|
||||
gasPrice = gasPrice,
|
||||
maxFeePerGas = maxFeePerGas,
|
||||
maxPriorityFeePerGas = maxPriorityFeePerGas,
|
||||
gas = gas,
|
||||
gasLimit = gasLimit,
|
||||
value = value,
|
||||
data = data,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun WCEthereumSignMessage.toWcEthereumSignMessage(): WcSignMessage {
|
||||
return WcSignMessage(
|
||||
raw = raw,
|
||||
type = when (type) {
|
||||
WCEthereumSignMessage.WCSignType.MESSAGE -> WcSignMessage.WCSignType.MESSAGE
|
||||
WCEthereumSignMessage.WCSignType.PERSONAL_MESSAGE -> WcSignMessage.WCSignType.PERSONAL_MESSAGE
|
||||
WCEthereumSignMessage.WCSignType.TYPED_MESSAGE -> WcSignMessage.WCSignType.TYPED_MESSAGE
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
package com.tangem.tap.domain.walletconnect2.app
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcBlockchainHelper
|
||||
import com.tangem.tap.domain.walletconnect2.toggles.WalletConnectFeatureToggles
|
||||
|
||||
internal class TangemWcBlockchainHelper(
|
||||
private val featureToggles: WalletConnectFeatureToggles,
|
||||
featureToggles: WalletConnectFeatureToggles,
|
||||
) : WcBlockchainHelper {
|
||||
|
||||
private val supportedNonEvmBlockchains = if (featureToggles.isSolanaTxSignEnabled) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import android.app.Application
|
|||
import arrow.core.flatten
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.tap.common.analytics.events.WalletConnect
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcJrpcMethods
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcJrpcRequestsDeserializer
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcRequest
|
||||
|
|
@ -19,11 +19,11 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultWalletConnectRepository(
|
||||
internal class DefaultLegacyWalletConnectRepository(
|
||||
private val application: Application,
|
||||
private val wcRequestDeserializer: WcJrpcRequestsDeserializer,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
) : WalletConnectRepository {
|
||||
) : LegacyWalletConnectRepository {
|
||||
|
||||
private var sessionProposal: Wallet.Model.SessionProposal? = null
|
||||
private var userNamespaces: Map<NetworkNamespace, List<Account>>? = null
|
||||
|
|
@ -94,7 +94,7 @@ internal class DefaultWalletConnectRepository(
|
|||
) {
|
||||
// Triggered when wallet receives the session proposal sent by a Dapp
|
||||
Timber.d("sessionProposal: $sessionProposal")
|
||||
this@DefaultWalletConnectRepository.sessionProposal = sessionProposal
|
||||
this@DefaultLegacyWalletConnectRepository.sessionProposal = sessionProposal
|
||||
|
||||
if (sessionProposal.name in unsupportedDApps) {
|
||||
Timber.w("Unsupported DApp")
|
||||
|
|
@ -110,7 +110,7 @@ internal class DefaultWalletConnectRepository(
|
|||
|
||||
val missingNetworks = findMissingNetworks(
|
||||
namespaces = sessionProposal.requiredNamespaces,
|
||||
userNamespaces = this@DefaultWalletConnectRepository.userNamespaces ?: emptyMap(),
|
||||
userNamespaces = this@DefaultLegacyWalletConnectRepository.userNamespaces ?: emptyMap(),
|
||||
)
|
||||
|
||||
if (missingNetworks.isNotEmpty()) {
|
||||
|
|
@ -127,7 +127,7 @@ internal class DefaultWalletConnectRepository(
|
|||
|
||||
val optionalWithoutMissingNetworks = removeMissingNetworks(
|
||||
namespaces = sessionProposal.optionalNamespaces,
|
||||
userNamespaces = this@DefaultWalletConnectRepository.userNamespaces ?: emptyMap(),
|
||||
userNamespaces = this@DefaultLegacyWalletConnectRepository.userNamespaces ?: emptyMap(),
|
||||
)
|
||||
|
||||
scope.launch {
|
||||
|
|
@ -375,6 +375,7 @@ internal class DefaultWalletConnectRepository(
|
|||
|
||||
override fun rejectRequest(requestData: RequestData, error: WalletConnectError) {
|
||||
val session = currentSessions.find { it.topic == requestData.topic }
|
||||
|
||||
analyticsHandler.send(
|
||||
WalletConnect.RequestHandled(
|
||||
WalletConnect.RequestHandledParams(
|
||||
|
|
@ -386,17 +387,18 @@ internal class DefaultWalletConnectRepository(
|
|||
),
|
||||
),
|
||||
)
|
||||
cancelRequest(requestData.topic, requestData.requestId)
|
||||
|
||||
cancelRequest(requestData.topic, requestData.requestId, error.error)
|
||||
}
|
||||
|
||||
override fun cancelRequest(topic: String, id: Long) {
|
||||
override fun cancelRequest(topic: String, id: Long, message: String) {
|
||||
Web3Wallet.respondSessionRequest(
|
||||
params = Wallet.Params.SessionRequestResponse(
|
||||
sessionTopic = topic,
|
||||
jsonRpcResponse = Wallet.Model.JsonRpcResponse.JsonRpcError(
|
||||
id = id,
|
||||
code = 0,
|
||||
message = "",
|
||||
message = message,
|
||||
),
|
||||
),
|
||||
onSuccess = {},
|
||||
|
|
@ -407,8 +409,8 @@ internal class DefaultWalletConnectRepository(
|
|||
override fun reject() {
|
||||
Web3Wallet.rejectSession(
|
||||
params = Wallet.Params.SessionReject(
|
||||
sessionProposal?.proposerPublicKey ?: "",
|
||||
"",
|
||||
proposerPublicKey = sessionProposal?.proposerPublicKey ?: "",
|
||||
reason = "",
|
||||
),
|
||||
onSuccess = {
|
||||
Timber.d("Rejected successfully: $it")
|
||||
|
|
@ -6,13 +6,16 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.datasource.di.SdkMoshi
|
||||
import com.tangem.datasource.files.FileReader
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper
|
||||
import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper
|
||||
import com.tangem.tap.domain.walletconnect2.app.WalletConnectEventsHandlerImpl
|
||||
import com.tangem.tap.domain.walletconnect2.data.DefaultWalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.data.DefaultLegacyWalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.data.DefaultWalletConnectSessionsRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcJrpcRequestsDeserializer
|
||||
import com.tangem.tap.domain.walletconnect2.toggles.WalletConnectFeatureToggles
|
||||
|
|
@ -32,9 +35,12 @@ internal object WalletConnectInteractorModule {
|
|||
@Provides
|
||||
@ActivityScoped
|
||||
fun provideWalletConnectInteractor(
|
||||
wcRepository: WalletConnectRepository,
|
||||
wcRepository: LegacyWalletConnectRepository,
|
||||
wcSessionsRepository: WalletConnectSessionsRepository,
|
||||
walletConnectFeatureToggles: WalletConnectFeatureToggles,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
userWalletsListManager: UserWalletsListManager,
|
||||
): WalletConnectInteractor {
|
||||
return WalletConnectInteractor(
|
||||
handler = WalletConnectEventsHandlerImpl(),
|
||||
|
|
@ -42,7 +48,10 @@ internal object WalletConnectInteractorModule {
|
|||
sessionsRepository = wcSessionsRepository,
|
||||
sdkHelper = WalletConnectSdkHelper(),
|
||||
blockchainHelper = TangemWcBlockchainHelper(walletConnectFeatureToggles),
|
||||
dispatcher = AppCoroutineDispatcherProvider(),
|
||||
currenciesRepository = currenciesRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
userWalletsListManager = userWalletsListManager,
|
||||
dispatchers = AppCoroutineDispatcherProvider(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -63,8 +72,8 @@ internal object WalletConnectModule {
|
|||
application: Application,
|
||||
wcRequestDeserializer: WcJrpcRequestsDeserializer,
|
||||
analyticsHandler: AnalyticsEventHandler,
|
||||
): WalletConnectRepository {
|
||||
return DefaultWalletConnectRepository(
|
||||
): LegacyWalletConnectRepository {
|
||||
return DefaultLegacyWalletConnectRepository(
|
||||
application = application,
|
||||
wcRequestDeserializer = wcRequestDeserializer,
|
||||
analyticsHandler = analyticsHandler,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.tap.domain.walletconnect2.domain
|
|||
import com.tangem.tap.domain.walletconnect2.domain.models.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface WalletConnectRepository {
|
||||
interface LegacyWalletConnectRepository {
|
||||
|
||||
val events: Flow<WalletConnectEvents>
|
||||
|
||||
|
|
@ -27,5 +27,5 @@ interface WalletConnectRepository {
|
|||
|
||||
fun rejectRequest(requestData: RequestData, error: WalletConnectError)
|
||||
|
||||
fun cancelRequest(topic: String, id: Long)
|
||||
fun cancelRequest(topic: String, id: Long, message: String = "")
|
||||
}
|
||||
|
|
@ -1,26 +1,52 @@
|
|||
package com.tangem.tap.domain.walletconnect2.domain
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
import com.tangem.tap.common.extensions.filterNotNull
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.*
|
||||
import com.tangem.tap.features.details.ui.walletconnect.WcSessionForScreen
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList")
|
||||
class WalletConnectInteractor(
|
||||
private val handler: WalletConnectEventsHandler,
|
||||
private val walletConnectRepository: WalletConnectRepository,
|
||||
private val walletConnectRepository: LegacyWalletConnectRepository,
|
||||
private val sessionsRepository: WalletConnectSessionsRepository,
|
||||
private val sdkHelper: WalletConnectSdkHelper,
|
||||
private val dispatcher: CoroutineDispatcherProvider,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
val blockchainHelper: WcBlockchainHelper,
|
||||
) {
|
||||
|
||||
private val getSelectedWalletUseCase by lazy(LazyThreadSafetyMode.NONE) {
|
||||
GetSelectedWalletUseCase(userWalletsListManager)
|
||||
}
|
||||
|
||||
private val wcScope = CoroutineScope(
|
||||
Job() + dispatchers.io + FeatureCoroutineExceptionHandler.create("wcScope"),
|
||||
)
|
||||
|
||||
private val listenerScope = CoroutineScope(
|
||||
Job() + dispatchers.io + FeatureCoroutineExceptionHandler.create("listenScope"),
|
||||
)
|
||||
|
||||
private val events = walletConnectRepository.events
|
||||
private val sessions = walletConnectRepository.activeSessions
|
||||
|
||||
|
|
@ -34,19 +60,56 @@ class WalletConnectInteractor(
|
|||
sdkHelper = sdkHelper,
|
||||
)
|
||||
|
||||
suspend fun startListening(userWalletId: String, cardId: String?) {
|
||||
init {
|
||||
getSelectedWalletUseCase().onRight { userWalletFlow ->
|
||||
userWalletFlow
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach(::initWithWallet)
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(wcScope)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun initWithWallet(userWallet: UserWallet) {
|
||||
if (userWallet.isMultiCurrency) {
|
||||
Timber.d("WalletConnect: initialize and setup networks for ${userWallet.walletId}")
|
||||
startListeningWc(userWallet.walletId.stringValue, getCardId(userWallet))
|
||||
subscribeOnCurrenciesUpdates(userWallet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnCurrenciesUpdates(userWallet: UserWallet) {
|
||||
currenciesRepository.getMultiCurrencyWalletCurrenciesUpdates(userWallet.walletId)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach { currencies ->
|
||||
setupUserChains(userWallet, currencies)
|
||||
}
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(wcScope)
|
||||
}
|
||||
|
||||
private suspend fun setupUserChains(userWallet: UserWallet, currencies: List<CryptoCurrency>) {
|
||||
val accounts = getAccountsForWc(
|
||||
userWallet = userWallet,
|
||||
networks = currencies.map { it.network },
|
||||
)
|
||||
setUserChains(accounts)
|
||||
}
|
||||
|
||||
private suspend fun startListeningWc(userWalletId: String, cardId: String?) {
|
||||
this.userWalletId = userWalletId
|
||||
this.cardId = cardId
|
||||
|
||||
coroutineScope {
|
||||
listenerScope.coroutineContext.cancelChildren()
|
||||
listenerScope.launch {
|
||||
launch { subscribeToEvents() }
|
||||
launch { subscribeToSessions() }
|
||||
|
||||
walletConnectRepository.updateSessions()
|
||||
}
|
||||
}
|
||||
|
||||
fun setUserChains(accounts: List<Account>) {
|
||||
private fun setUserChains(accounts: List<Account>) {
|
||||
val userNamespaces: Map<NetworkNamespace, List<Account>> = accounts
|
||||
.groupBy { account ->
|
||||
blockchainHelper.getNamespaceFromFullChainIdOrNull(account.chainId)
|
||||
|
|
@ -106,7 +169,7 @@ class WalletConnectInteractor(
|
|||
}
|
||||
}
|
||||
}
|
||||
.flowOn(dispatcher.io)
|
||||
.flowOn(dispatchers.io)
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +180,7 @@ class WalletConnectInteractor(
|
|||
val filteredSessions = filterSessionsForUserWallet(listOfSessions, relevantTopics)
|
||||
handler.onListOfSessionsUpdated(filteredSessions)
|
||||
}
|
||||
.flowOn(dispatcher.io)
|
||||
.flowOn(dispatchers.io)
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
@ -197,10 +260,18 @@ class WalletConnectInteractor(
|
|||
)
|
||||
else -> {
|
||||
currentRequest = sessionRequest
|
||||
val data = prepareRequestData(sessionRequest)
|
||||
if (data != null) {
|
||||
handler.onSessionRequest(data)
|
||||
|
||||
val data = prepareRequestData(sessionRequest).getOrElse { e ->
|
||||
val wrappedError = e as? WalletConnectError ?: WalletConnectError.UnknownError(
|
||||
message = e.localizedMessage ?: "Unknown error",
|
||||
)
|
||||
|
||||
walletConnectRepository.rejectRequest(requestData, wrappedError)
|
||||
handler.onSessionRejected(wrappedError)
|
||||
return
|
||||
}
|
||||
|
||||
handler.onSessionRequest(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -258,7 +329,41 @@ class WalletConnectInteractor(
|
|||
return uri.lowercase().startsWith(WC_SCHEME)
|
||||
}
|
||||
|
||||
private suspend fun prepareRequestData(sessionRequest: WalletConnectEvents.SessionRequest): WcPreparedRequest? {
|
||||
private fun getCardId(userWallet: UserWallet): String? {
|
||||
return if (userWallet.scanResponse.card.backupStatus?.isActive != true) {
|
||||
userWallet.cardId
|
||||
} else { // if wallet has backup, any card from wallet can be used to sign
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getAccountsForWc(userWallet: UserWallet, networks: List<Network>): List<Account> {
|
||||
val walletManagers = networks.mapNotNull {
|
||||
val blockchain = Blockchain.fromId(it.id.value)
|
||||
walletManagersFacade.getOrCreateWalletManager(
|
||||
userWalletId = userWallet.walletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = it.derivationPath.value,
|
||||
)
|
||||
}
|
||||
return walletManagers.mapNotNull {
|
||||
val wallet = it.wallet
|
||||
val chainId = blockchainHelper.networkIdToChainIdOrNull(
|
||||
wallet.blockchain.toNetworkId(),
|
||||
)
|
||||
chainId?.let {
|
||||
Account(
|
||||
chainId,
|
||||
wallet.address,
|
||||
wallet.publicKey.derivationPath?.rawPath,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun prepareRequestData(
|
||||
sessionRequest: WalletConnectEvents.SessionRequest,
|
||||
): Result<WcPreparedRequest> {
|
||||
return sessionRequestConverter.prepareRequest(sessionRequest, userWalletId)
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue