Updated on 2026-08-14
This commit is contained in:
parent
ce4df5bee6
commit
6fab6d1ce3
9 changed files with 59 additions and 47 deletions
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<NamespaceKey, WcNamespaceConverter>,
|
||||
|
|
@ -16,7 +16,7 @@ internal class CaipNamespaceDelegate constructor(
|
|||
|
||||
suspend fun associate(
|
||||
sessionProposal: Wallet.Model.SessionProposal,
|
||||
userWalletId: UserWalletId,
|
||||
userWallet: UserWallet,
|
||||
networks: List<Network>,
|
||||
): Map<String, Wallet.Model.Namespace.Session> {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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<Throwable, Unit> {
|
||||
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),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Wallet.Model.SessionDelete>(capacity = Channel.BUFFERED)
|
||||
private val oneTimeMigration = MutableStateFlow(true)
|
||||
|
||||
override val sessions: Flow<Map<UserWalletId, List<WcSession>>>
|
||||
get() = store.sessions
|
||||
.transform { inStore ->
|
||||
override val sessions: Flow<Map<UserWallet, List<WcSession>>>
|
||||
get() = combine(getWallets(), store.sessions) { wallets, inStore -> wallets to inStore }
|
||||
.transform { pair ->
|
||||
val (wallets, inStore) = pair
|
||||
val inSdk: List<Wallet.Model.Session> = 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<Wallet.Model.Session> = WalletKit.getListOfActiveSessions()
|
||||
val associatedSessions: List<WcSession> = associateWithSdk(inSdk, inStore)
|
||||
val associatedSessions: List<WcSession> = 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<Throwable, Unit> {
|
||||
override suspend fun removeSession(session: WcSession): Either<Throwable, Unit> {
|
||||
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<WcSessionDTO>): Boolean {
|
||||
val walletIds = getWallets.invokeSync().mapTo(mutableSetOf()) { it.walletId }
|
||||
val inLegacyStoreSessions = walletIds
|
||||
private suspend fun migrateLegacyStore(
|
||||
inNewStore: Set<WcSessionDTO>,
|
||||
inSdk: List<Wallet.Model.Session>,
|
||||
wallets: List<UserWallet>,
|
||||
): 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<Wallet.Model.Session>,
|
||||
storeSessions: Set<WcSessionDTO>,
|
||||
private fun associate(
|
||||
inSdk: List<Wallet.Model.Session>,
|
||||
inStore: Set<WcSessionDTO>,
|
||||
wallets: List<UserWallet>,
|
||||
): List<WcSession> {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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<WcNetwork.Supported>,
|
||||
)
|
||||
|
|
@ -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<Map<UserWalletId, List<WcSession>>>
|
||||
suspend fun saveSession(userWalletId: UserWalletId, session: WcSession)
|
||||
suspend fun removeSession(userWalletId: UserWalletId, session: WcSession): Either<Throwable, Unit>
|
||||
val sessions: Flow<Map<UserWallet, List<WcSession>>>
|
||||
suspend fun saveSession(session: WcSession)
|
||||
suspend fun removeSession(session: WcSession): Either<Throwable, Unit>
|
||||
suspend fun findSessionByTopic(topic: String): WcSession?
|
||||
}
|
||||
|
|
@ -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<Map<UserWalletId, List<WcSession>>> {
|
||||
operator fun invoke(): Flow<Map<UserWallet, List<WcSession>>> {
|
||||
return sessionsManager.sessions
|
||||
}
|
||||
|
||||
suspend fun invokeSync(): Map<UserWalletId, List<WcSession>> {
|
||||
suspend fun invokeSync(): Map<UserWallet, List<WcSession>> {
|
||||
return sessionsManager.sessions.first()
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,6 @@ class WcDisconnectUseCase(
|
|||
}
|
||||
|
||||
suspend fun disconnect(session: WcSession) {
|
||||
sessionsManager.removeSession(session.userWalletId, session)
|
||||
sessionsManager.removeSession(session)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue