From 6fab6d1ce354e0fbaa9b961d6ae2e77bc2c1abc8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 8 Apr 2025 15:05:06 +0700 Subject: [PATCH] Updated on 2026-08-14 --- .../di/WalletConnectDataModule.kt | 3 + .../pair/CaipNamespaceDelegate.kt | 6 +- .../pair/DefaultWcPairUseCase.kt | 12 ++-- .../sessions/DefaultWcSessionsManager.kt | 61 +++++++++++-------- .../domain/walletconnect/model/WcSession.kt | 4 +- .../walletconnect/model/WcSessionApprove.kt | 4 +- .../repository/WcSessionsManager.kt | 8 +-- .../usecase/WcSessionsUseCase.kt | 6 +- .../usecase/disconnect/WcDisconnectUseCase.kt | 2 +- 9 files changed, 59 insertions(+), 47 deletions(-) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt index e36fcd3b18..ee70b52018 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt @@ -29,6 +29,7 @@ import com.tangem.domain.walletconnect.request.WcRequestService import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase import com.tangem.domain.walletconnect.usecase.pair.WcPairUseCase import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module @@ -88,6 +89,7 @@ internal object WalletConnectDataModule { store: WalletConnectStore, dispatchers: CoroutineDispatcherProvider, legacyStore: WalletConnectSessionsRepository, + getUserWallet: GetUserWalletUseCase, getWallets: GetWalletsUseCase, ): DefaultWcSessionsManager { val scope = CoroutineScope(SupervisorJob() + dispatchers.io) @@ -96,6 +98,7 @@ internal object WalletConnectDataModule { dispatchers = dispatchers, legacyStore = legacyStore, getWallets = getWallets, + getUserWallet = getUserWallet, scope = scope, ) } diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt index 98e5e6be32..2361baa676 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt @@ -7,7 +7,7 @@ import com.tangem.data.walletconnect.model.NamespaceKey import com.tangem.data.walletconnect.utils.WcNamespaceConverter import com.tangem.domain.tokens.model.Network import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.wallets.models.UserWallet internal class CaipNamespaceDelegate constructor( private val namespaceConverters: Map, @@ -16,7 +16,7 @@ internal class CaipNamespaceDelegate constructor( suspend fun associate( sessionProposal: Wallet.Model.SessionProposal, - userWalletId: UserWalletId, + userWallet: UserWallet, networks: List, ): Map { val converters = namespaceConverters.values @@ -25,7 +25,7 @@ internal class CaipNamespaceDelegate constructor( networks.map { network -> val blockchain = Blockchain.fromId(network.id.value) - val address = walletManagersFacade.getDefaultAddress(userWalletId, network) + val address = walletManagersFacade.getDefaultAddress(userWallet.walletId, network) val chainId = converters.firstOrNull { it.toCAIP2(blockchain) != null }?.toCAIP2(blockchain) requireNotNull(chainId) requireNotNull(address) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt index 65f7b110ee..98dcdf4de2 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt @@ -15,7 +15,7 @@ import com.tangem.domain.walletconnect.model.sdkcopy.WcAppMetaData import com.tangem.domain.walletconnect.repository.WcSessionsManager import com.tangem.domain.walletconnect.usecase.pair.WcPairState import com.tangem.domain.walletconnect.usecase.pair.WcPairUseCase -import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.wallets.models.UserWallet import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first @@ -93,8 +93,8 @@ internal class DefaultWcPairUseCase( ).left() is Wallet.Model.SettledSessionResponse.Result -> { - val newSession = settledSession.session.toDomain(sessionForApprove.walletId) - sessionsManager.saveSession(sessionForApprove.walletId, newSession) + val newSession = settledSession.session.toDomain(sessionForApprove.wallet) + sessionsManager.saveSession(newSession) newSession.right() } } @@ -148,7 +148,7 @@ internal class DefaultWcPairUseCase( ): Either { val namespaces = caipNamespaceDelegate.associate( sdkSessionProposal, - sessionForApprove.walletId, + sessionForApprove.wallet, sessionForApprove.network.map { it.network }, ) val sessionApprove = Wallet.Params.SessionApprove( @@ -209,8 +209,8 @@ internal class DefaultWcPairUseCase( } },) - private fun Wallet.Model.Session.toDomain(walletId: UserWalletId): WcSession = WcSession( - userWalletId = walletId, + private fun Wallet.Model.Session.toDomain(wallet: UserWallet): WcSession = WcSession( + wallet = wallet, sdkModel = WcSdkSessionConverter.convert(this), ) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt index 90ccec710d..6e31f2783d 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt @@ -12,7 +12,8 @@ import com.tangem.domain.walletconnect.model.WcSession import com.tangem.domain.walletconnect.model.WcSessionDTO import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository import com.tangem.domain.walletconnect.repository.WcSessionsManager -import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.CoroutineScope @@ -29,6 +30,7 @@ internal class DefaultWcSessionsManager constructor( private val store: WalletConnectStore, private val legacyStore: WalletConnectSessionsRepository, private val getWallets: GetWalletsUseCase, + private val getUserWallet: GetUserWalletUseCase, private val dispatchers: CoroutineDispatcherProvider, private val scope: CoroutineScope, ) : WcSessionsManager, WcSdkObserver { @@ -36,20 +38,22 @@ internal class DefaultWcSessionsManager constructor( private val onSessionDelete = Channel(capacity = Channel.BUFFERED) private val oneTimeMigration = MutableStateFlow(true) - override val sessions: Flow>> - get() = store.sessions - .transform { inStore -> + override val sessions: Flow>> + get() = combine(getWallets(), store.sessions) { wallets, inStore -> wallets to inStore } + .transform { pair -> + val (wallets, inStore) = pair + val inSdk: List = WalletKit.getListOfActiveSessions() if (oneTimeMigration.value) { oneTimeMigration.value = false - val someMigrated = migrateLegacyStore(inStore) + val someMigrated = migrateLegacyStore(inStore, inSdk, wallets) if (someMigrated) return@transform // ignore emit, wait next one } - val inSdk: List = WalletKit.getListOfActiveSessions() - val associatedSessions: List = associateWithSdk(inSdk, inStore) + val associatedSessions: List = associate(inSdk, inStore, wallets) val someRemove = removeUnknownSessions(inStore, associatedSessions) if (someRemove) return@transform // ignore emit, wait next one - emit(associatedSessions.groupBy { it.userWalletId }) + emit(associatedSessions.groupBy { it.wallet }) } + .distinctUntilChanged() .flowOn(dispatchers.io) override fun onWcSdkInit() { @@ -57,11 +61,11 @@ internal class DefaultWcSessionsManager constructor( listenOnSessionDelete() } - override suspend fun saveSession(userWalletId: UserWalletId, session: WcSession) { - store.saveSession(WcSessionDTO(session.sdkModel.topic, session.userWalletId)) + override suspend fun saveSession(session: WcSession) { + store.saveSession(WcSessionDTO(session.sdkModel.topic, session.wallet.walletId)) } - override suspend fun removeSession(userWalletId: UserWalletId, session: WcSession): Either { + override suspend fun removeSession(session: WcSession): Either { val topic = session.sdkModel.topic val sdkCall = sdkDisconnectSession(topic) sdkCall.onLeft { return it.left() } @@ -82,7 +86,8 @@ internal class DefaultWcSessionsManager constructor( override suspend fun findSessionByTopic(topic: String): WcSession? = withContext(dispatchers.io) { val storedSessions = store.findSessionByTopic(topic) ?: return@withContext null val sdkSession = WalletKit.getActiveSessionByTopic(topic) ?: return@withContext null - WcSession(userWalletId = storedSessions.walletId, sdkModel = WcSdkSessionConverter.convert(sdkSession)) + val wallet = getUserWallet.invoke(storedSessions.walletId).getOrNull() ?: return@withContext null + WcSession(wallet = wallet, sdkModel = WcSdkSessionConverter.convert(sdkSession)) } override fun onSessionDelete(sessionDelete: Wallet.Model.SessionDelete) { @@ -90,28 +95,35 @@ internal class DefaultWcSessionsManager constructor( onSessionDelete.trySend(sessionDelete) } - private suspend fun migrateLegacyStore(inNewStoreSessions: Set): Boolean { - val walletIds = getWallets.invokeSync().mapTo(mutableSetOf()) { it.walletId } - val inLegacyStoreSessions = walletIds + private suspend fun migrateLegacyStore( + inNewStore: Set, + inSdk: List, + wallets: List, + ): Boolean { + val walletIds = wallets.map { wallet -> wallet.walletId } + val inLegacyStore = walletIds .map { walletId -> flow { emit(legacyStore.loadSessions(walletId.stringValue).map { WcSessionDTO(it.topic, walletId) }) } } .merge() .reduce { accumulator, value -> accumulator.plus(value) } + // migrate only active legacySessions + .filter { legacySession -> inSdk.any { inSdkSession -> inSdkSession.topic == legacySession.topic } } - val mustSaveInNewStore = inLegacyStoreSessions.subtract(inNewStoreSessions) + val mustSaveInNewStore = inLegacyStore.subtract(inNewStore) if (mustSaveInNewStore.isNotEmpty()) store.saveSessions(mustSaveInNewStore) return mustSaveInNewStore.isNotEmpty() } - private fun associateWithSdk( - sdkSessions: List, - storeSessions: Set, + private fun associate( + inSdk: List, + inStore: Set, + wallets: List, ): List { - val wcSessions = sdkSessions.mapNotNull { sdkSession -> - val storedSessions = storeSessions.find { it.topic == sdkSession.topic } - ?: return@mapNotNull null - WcSession(userWalletId = storedSessions.walletId, sdkModel = WcSdkSessionConverter.convert(sdkSession)) + val wcSessions = inStore.mapNotNull { session -> + val wallet = wallets.find { it.walletId == session.walletId } ?: return@mapNotNull null + val sdkSession = inSdk.find { it.topic == session.topic } ?: return@mapNotNull null + WcSession(wallet = wallet, sdkModel = WcSdkSessionConverter.convert(sdkSession)) } return wcSessions } @@ -122,9 +134,6 @@ internal class DefaultWcSessionsManager constructor( val haveSomeUnknown = unknownStoredSessions.isNotEmpty() if (haveSomeUnknown) { - unknownStoredSessions.forEach { unknown -> - legacyStore.removeSession(unknown.walletId.stringValue, unknown.topic) - } store.removeSessions(unknownStoredSessions.toSet()) } return haveSomeUnknown diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSession.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSession.kt index 92501d8bba..cc898e4618 100644 --- a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSession.kt +++ b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSession.kt @@ -1,9 +1,9 @@ package com.tangem.domain.walletconnect.model import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSession -import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.wallets.models.UserWallet data class WcSession( - val userWalletId: UserWalletId, + val wallet: UserWallet, val sdkModel: WcSdkSession, ) \ No newline at end of file diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionApprove.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionApprove.kt index 06f4291649..a8f7d6b70a 100644 --- a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionApprove.kt +++ b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionApprove.kt @@ -1,8 +1,8 @@ package com.tangem.domain.walletconnect.model -import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.wallets.models.UserWallet data class WcSessionApprove( - val walletId: UserWalletId, + val wallet: UserWallet, val network: List, ) \ No newline at end of file diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/repository/WcSessionsManager.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/repository/WcSessionsManager.kt index 6c2a996c91..fa207f0263 100644 --- a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/repository/WcSessionsManager.kt +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/repository/WcSessionsManager.kt @@ -2,12 +2,12 @@ package com.tangem.domain.walletconnect.repository import arrow.core.Either import com.tangem.domain.walletconnect.model.WcSession -import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.wallets.models.UserWallet import kotlinx.coroutines.flow.Flow interface WcSessionsManager { - val sessions: Flow>> - suspend fun saveSession(userWalletId: UserWalletId, session: WcSession) - suspend fun removeSession(userWalletId: UserWalletId, session: WcSession): Either + val sessions: Flow>> + suspend fun saveSession(session: WcSession) + suspend fun removeSession(session: WcSession): Either suspend fun findSessionByTopic(topic: String): WcSession? } \ No newline at end of file diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/WcSessionsUseCase.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/WcSessionsUseCase.kt index 0adbf1dff6..138c4a419f 100644 --- a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/WcSessionsUseCase.kt +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/WcSessionsUseCase.kt @@ -2,16 +2,16 @@ package com.tangem.domain.walletconnect.usecase import com.tangem.domain.walletconnect.model.WcSession import com.tangem.domain.walletconnect.repository.WcSessionsManager -import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.wallets.models.UserWallet import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first class WcSessionsUseCase(private val sessionsManager: WcSessionsManager) { - operator fun invoke(): Flow>> { + operator fun invoke(): Flow>> { return sessionsManager.sessions } - suspend fun invokeSync(): Map> { + suspend fun invokeSync(): Map> { return sessionsManager.sessions.first() } } \ No newline at end of file diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/disconnect/WcDisconnectUseCase.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/disconnect/WcDisconnectUseCase.kt index a34bd8e9f6..707dc20180 100644 --- a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/disconnect/WcDisconnectUseCase.kt +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/disconnect/WcDisconnectUseCase.kt @@ -25,6 +25,6 @@ class WcDisconnectUseCase( } suspend fun disconnect(session: WcSession) { - sessionsManager.removeSession(session.userWalletId, session) + sessionsManager.removeSession(session) } } \ No newline at end of file