Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-08 15:09:07 +04:00
parent 8568277f95
commit ca2804b0e0
9 changed files with 172 additions and 48 deletions

View file

@ -5,22 +5,22 @@ import arrow.core.left
import arrow.core.right
import com.tangem.data.wallets.converters.UserWalletRemoteInfoConverter
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.common.response.ApiResponseError.HttpException
import com.tangem.datasource.api.common.response.fold
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.common.response.isNetworkError
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.converters.WalletIdBodyConverter
import com.tangem.datasource.api.tangemTech.models.PromocodeActivationBody
import com.tangem.datasource.api.tangemTech.models.SeedPhraseNotificationDTO
import com.tangem.datasource.api.tangemTech.models.*
import com.tangem.datasource.api.tangemTech.models.SeedPhraseNotificationDTO.Status
import com.tangem.datasource.api.tangemTech.models.WalletBody
import com.tangem.datasource.api.tangemTech.models.WalletType
import com.tangem.datasource.local.datastore.RuntimeStateStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.PreferencesKeys.SEED_FIRST_NOTIFICATION_SHOW_TIME
import com.tangem.datasource.local.preferences.utils.*
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.models.SeedPhraseNotificationsStatus
@ -30,13 +30,15 @@ import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.utils.WEEK_MILLIS
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.runCatching
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
typealias SeedPhraseNotificationsStatuses = Map<UserWalletId, SeedPhraseNotificationsStatus>
@Suppress("TooManyFunctions", "LargeClass")
@Suppress("TooManyFunctions", "LargeClass", "LongParameterList")
internal class DefaultWalletsRepository(
private val appPreferencesStore: AppPreferencesStore,
private val tangemTechApi: TangemTechApi,
@ -44,6 +46,8 @@ internal class DefaultWalletsRepository(
private val seedPhraseNotificationVisibilityStore: RuntimeStateStore<SeedPhraseNotificationsStatuses>,
private val dispatchers: CoroutineDispatcherProvider,
private val authProvider: AuthProvider,
private val accountsFeatureToggles: AccountsFeatureToggles,
private val moshi: com.squareup.moshi.Moshi,
) : WalletsRepository {
private val upgradeWalletNotificationDisabled: MutableStateFlow<Set<UserWalletId>> =
@ -388,24 +392,58 @@ internal class DefaultWalletsRepository(
override suspend fun associateWallets(applicationId: String, wallets: List<UserWallet>) =
withContext(dispatchers.io) {
val publicKeys = authProvider.getCardsPublicKeys()
val walletsBody = wallets.map { userWallet ->
WalletIdBodyConverter.convert(
userWallet = userWallet,
publicKeys = if (userWallet is UserWallet.Cold) {
publicKeys.filterKeys {
userWallet.cardsInWallet.contains(it)
}
} else {
emptyMap()
},
)
}
if (accountsFeatureToggles.isFeatureEnabled) {
val associateApplicationIdWithWallets: suspend () -> ApiResponse<Unit> = {
tangemTechApi.associateApplicationIdWithWalletsV2(
applicationId = applicationId,
body = AssociateApplicationIdWithWalletsBody(
walletIds = wallets.map { it.walletId.stringValue }.distinct(),
),
)
}
tangemTechApi.associateApplicationIdWithWallets(
applicationId = applicationId,
body = walletsBody,
).getOrThrow()
val apiResponse = associateApplicationIdWithWallets()
if (apiResponse is ApiResponse.Success) return@withContext
if (apiResponse is ApiResponse.Error &&
apiResponse.cause.isNetworkError(HttpException.Code.BAD_REQUEST)
) {
val errorBody = (apiResponse.cause as? HttpException)?.errorBody
?: error("Bad Request must have error body")
val adapter = moshi.adapter(AssociateAppWithWalletsErrorResponse::class.java)
val errorResponse = adapter.fromJson(errorBody)
?: error("Cannot parse error body: $errorBody")
errorResponse.missingWalletIds
.map {
async { createWallet(userWalletId = UserWalletId(it)) }
}
.awaitAll()
associateApplicationIdWithWallets().getOrThrow()
}
} else {
val publicKeys = authProvider.getCardsPublicKeys()
val walletsBody = wallets.map { userWallet ->
WalletIdBodyConverter.convert(
userWallet = userWallet,
publicKeys = if (userWallet is UserWallet.Cold) {
publicKeys.filterKeys {
userWallet.cardsInWallet.contains(it)
}
} else {
emptyMap()
},
)
}
tangemTechApi.associateApplicationIdWithWallets(
applicationId = applicationId,
body = walletsBody,
).getOrThrow()
}
}
override suspend fun activatePromoCode(

View file

@ -1,5 +1,6 @@
package com.tangem.data.wallets.di
import com.squareup.moshi.Moshi
import com.tangem.data.wallets.DefaultWalletNamesMigrationRepository
import com.tangem.data.wallets.DefaultWalletsRepository
import com.tangem.data.wallets.cold.DefaultColdMapDerivationsRepository
@ -8,9 +9,11 @@ import com.tangem.data.wallets.hot.DefaultHotMapDerivationsRepository
import com.tangem.data.wallets.hot.DefaultHotWalletAccessCodeAttemptsRepository
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.datastore.RuntimeStateStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.wallets.derivations.ColdMapDerivationsRepository
import com.tangem.domain.wallets.derivations.DerivationsRepository
import com.tangem.domain.wallets.derivations.HotMapDerivationsRepository
@ -37,6 +40,8 @@ internal object WalletsDataModule {
userWalletsStore: UserWalletsStore,
dispatchers: CoroutineDispatcherProvider,
authProvider: AuthProvider,
accountsFeatureToggles: AccountsFeatureToggles,
@NetworkMoshi moshi: Moshi,
): WalletsRepository {
return DefaultWalletsRepository(
appPreferencesStore = appPreferencesStore,
@ -45,6 +50,8 @@ internal object WalletsDataModule {
seedPhraseNotificationVisibilityStore = RuntimeStateStore(defaultValue = emptyMap()),
dispatchers = dispatchers,
authProvider = authProvider,
accountsFeatureToggles = accountsFeatureToggles,
moshi = moshi,
)
}

View file

@ -13,6 +13,7 @@ import com.tangem.datasource.api.tangemTech.models.PromocodeActivationResponse
import com.tangem.datasource.api.tangemTech.models.WalletResponse
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.models.errors.ActivatePromoCodeError
@ -51,6 +52,8 @@ class DefaultWalletsRepositoryTest {
seedPhraseNotificationVisibilityStore = mockk(),
dispatchers = dispatchers,
authProvider = mockk(),
accountsFeatureToggles = mockk(),
moshi = mockk(),
)
}
@ -204,6 +207,10 @@ class DefaultWalletsRepositoryTest {
coEvery { getCardsPublicKeys() } returns publicKeys
}
val accountsFeatureToggles = mockk<AccountsFeatureToggles> {
every { isFeatureEnabled } returns false
}
repository = DefaultWalletsRepository(
appPreferencesStore = appPreferenceStore,
tangemTechApi = tangemTechApi,
@ -211,6 +218,8 @@ class DefaultWalletsRepositoryTest {
seedPhraseNotificationVisibilityStore = mockk(),
dispatchers = dispatchers,
authProvider = authProvider,
accountsFeatureToggles = accountsFeatureToggles,
moshi = mockk(),
)
coEvery {