Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-14 14:47:48 +03:00
parent c68cf7165b
commit bea775e94b
46 changed files with 749 additions and 178 deletions

View file

@ -30,6 +30,7 @@ import com.tangem.tap.domain.userWalletList.utils.toUserWallets
import com.tangem.tap.domain.userWalletList.utils.updateWith
import com.tangem.utils.Provider
import com.tangem.utils.ProviderSuspend
import com.tangem.utils.extensions.indexOfFirstOrNull
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
@ -173,6 +174,8 @@ internal class DefaultUserWalletsListRepository(
userWalletEncryptionKeysRepository.delete(userWalletIds)
val userWalletsBeforeDelete = userWallets.value ?: return@either
userWallets.update { currentWallets ->
currentWallets?.filterNot { it.walletId in userWalletIds }
}
@ -181,7 +184,7 @@ internal class DefaultUserWalletsListRepository(
if (currentSelected == null) return@update null
userWallets.value?.findAvailableUserWallet(
userWallets.value?.indexOfFirst { it.walletId == currentSelected.walletId } ?: 0,
userWalletsBeforeDelete.indexOfFirstOrNull { it.walletId == currentSelected.walletId } ?: 0,
)
}
}
@ -199,7 +202,7 @@ internal class DefaultUserWalletsListRepository(
when (unlockMethod) {
UserWalletsListRepository.UnlockMethod.Biometric -> {
unlockAllWallets()
unlockAllWallets().bind()
select(userWalletId)
}
UserWalletsListRepository.UnlockMethod.AccessCode -> {
@ -225,7 +228,7 @@ internal class DefaultUserWalletsListRepository(
}
sensitiveInformationRepository.getAll(listOf(encryptionKey))
.doOnSuccess { userWallets.value?.updateWith(it) }
.doOnSuccess { sensitiveInfo -> userWallets.update { it?.updateWith(sensitiveInfo) } }
.doOnFailure { error ->
raise(UnlockWalletError.UnableToUnlock)
}
@ -255,6 +258,7 @@ internal class DefaultUserWalletsListRepository(
}
override suspend fun unlockAllWallets(): Either<UnlockWalletError, Unit> = either {
val userWalletIds = userWalletsSync().map { it.walletId }.toSet()
val biometricKeys = runCatching {
userWalletEncryptionKeysRepository.getAllBiometric()
}.getOrElse {
@ -264,8 +268,14 @@ internal class DefaultUserWalletsListRepository(
val unsecuredKeys = userWalletEncryptionKeysRepository.getAllUnsecured()
val allKeys = biometricKeys + unsecuredKeys
if (allKeys.all { it.walletId in userWalletIds }.not()) {
raise(UnlockWalletError.UnableToUnlock)
}
sensitiveInformationRepository.getAll(allKeys)
.doOnSuccess { userWallets.value?.updateWith(it) }
.doOnSuccess { sensitiveInfo -> userWallets.update { it?.updateWith(sensitiveInfo) } }
.doOnFailure { raise(UnlockWalletError.UnableToUnlock) }
}
override suspend fun lockAllWallets(): Either<LockWalletsError, Unit> = either {
@ -297,7 +307,7 @@ internal class DefaultUserWalletsListRepository(
biometryFallback: suspend () -> Either<UnlockWalletError, Unit>,
): Either<UnlockWalletError, UserWalletEncryptionKey?> {
val result = passwordRequester.requestPassword(
hasBiometry = tangemSdkManagerProvider.invoke().needEnrollBiometrics,
hasBiometry = tangemSdkManagerProvider.invoke().canUseBiometry,
)
return when (result) {
@ -312,6 +322,7 @@ internal class DefaultUserWalletsListRepository(
requestPasswordRecursive(block, biometryFallback)
} else {
passwordRequester.successfulAuthentication()
passwordRequester.dismiss()
decrypted.right()
}
}
@ -319,9 +330,9 @@ internal class DefaultUserWalletsListRepository(
biometryFallback()
.onRight {
passwordRequester.successfulAuthentication()
passwordRequester.dismiss()
}
passwordRequester.dismiss()
null.right()
.map { null }
}
}
}

View file

@ -62,15 +62,16 @@ internal class UserWalletEncryptionKeysRepository(
}
}
suspend fun getEncryptedWithPassword(userWalletId: UserWalletId, password: CharArray): UserWalletEncryptionKey? {
val encrypted = secureStorage.get(
account = StorageKey.UserWalletEncryptionKeyEncrypted(userWalletId).name,
) ?: return null
suspend fun getEncryptedWithPassword(userWalletId: UserWalletId, password: CharArray): UserWalletEncryptionKey? =
withContext(dispatchers.io) {
val encrypted = secureStorage.get(
account = StorageKey.UserWalletEncryptionKeyEncrypted(userWalletId).name,
) ?: return@withContext null
val decrypted = AESEncryptionProtocol.decryptWithPassword(password, encrypted)
return decrypted.decodeToKey()
}
withContext(dispatchers.default) {
AESEncryptionProtocol.decryptWithPassword(password, encrypted).decodeToKey()
}
}
suspend fun getAllBiometric(): List<UserWalletEncryptionKey> = withContext(dispatchers.io) {
val keys = getUserWalletsIds().map { userWalletId ->

View file

@ -11,7 +11,7 @@ import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper
import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper
@ -42,9 +42,9 @@ internal object WalletConnectInteractorModule {
wcSessionsRepository: WalletConnectSessionsRepository,
currenciesRepository: CurrenciesRepository,
walletManagersFacade: WalletManagersFacade,
userWalletsListManager: UserWalletsListManager,
walletConnectFeatureToggles: WalletConnectFeatureToggles,
coroutineDispatcherProvider: CoroutineDispatcherProvider,
getSelectedWalletUseCase: GetSelectedWalletUseCase,
): WalletConnectInteractor {
return WalletConnectInteractor(
handler = WalletConnectEventsHandlerImpl(),
@ -54,7 +54,7 @@ internal object WalletConnectInteractorModule {
blockchainHelper = TangemWcBlockchainHelper(),
currenciesRepository = currenciesRepository,
walletManagersFacade = walletManagersFacade,
userWalletsListManager = userWalletsListManager,
getSelectedWalletUseCase = getSelectedWalletUseCase,
dispatchers = coroutineDispatcherProvider,
walletConnectFeatureToggles = walletConnectFeatureToggles,
)

View file

@ -13,7 +13,6 @@ import com.tangem.domain.walletconnect.model.legacy.Account
import com.tangem.domain.walletconnect.model.legacy.Session
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.features.walletconnect.components.WalletConnectFeatureToggles
import com.tangem.tap.common.extensions.dispatchOnMain
@ -38,18 +37,14 @@ class WalletConnectInteractor(
private val dispatchers: CoroutineDispatcherProvider,
private val walletManagersFacade: WalletManagersFacade,
private val currenciesRepository: CurrenciesRepository,
private val userWalletsListManager: UserWalletsListManager,
private val walletConnectFeatureToggles: WalletConnectFeatureToggles,
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
val blockchainHelper: WcBlockchainHelper,
) {
private val isNewWc by lazy { walletConnectFeatureToggles.isRedesignedWalletConnectEnabled }
private var isWalletConnectReadyForDeepLinks = false
private val getSelectedWalletUseCase by lazy(LazyThreadSafetyMode.NONE) {
GetSelectedWalletUseCase(userWalletsListManager)
}
private val wcScope = CoroutineScope(
SupervisorJob() + dispatchers.io + CoroutineExceptionHandler { _, throwable ->
Timber.e("CoroutineException: from: LISTENER SCOPE, exception: $throwable")