From 150026e2bd06c0424d8afef121f50b0360406bdb Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 25 Apr 2025 16:36:36 +0700 Subject: [PATCH] Updated on 2026-08-14 --- .../di/WalletConnectDataModule.kt | 21 +++++++--- .../network/ethereum/WcEthNetwork.kt | 39 +++++++++---------- .../network/solana/WcSolanaNetwork.kt | 27 +++++++------ .../request/DefaultWcRequestService.kt | 23 +++++------ .../request/DefaultWcRequestUseCaseFactory.kt | 17 ++++++++ .../request/WcRequestToUseCaseConverter.kt | 2 + .../walletconnect/model/WcMethodName.kt | 22 +++++++++++ .../domain/walletconnect/WcRequestService.kt | 9 +++++ .../walletconnect/WcRequestUseCaseFactory.kt | 9 +++++ 9 files changed, 115 insertions(+), 54 deletions(-) create mode 100644 data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestUseCaseFactory.kt create mode 100644 domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcMethodName.kt create mode 100644 domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcRequestService.kt create mode 100644 domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcRequestUseCaseFactory.kt 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 5035e927cd..0afaeb4dd7 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 @@ -12,7 +12,9 @@ import com.tangem.data.walletconnect.pair.CaipNamespaceDelegate import com.tangem.data.walletconnect.pair.DefaultWcPairService import com.tangem.data.walletconnect.pair.DefaultWcPairUseCase import com.tangem.data.walletconnect.pair.WcPairSdkDelegate +import com.tangem.data.walletconnect.pair.* import com.tangem.data.walletconnect.request.DefaultWcRequestService +import com.tangem.data.walletconnect.request.DefaultWcRequestUseCaseFactory import com.tangem.data.walletconnect.request.WcRequestToUseCaseConverter import com.tangem.data.walletconnect.respond.DefaultWcRespondService import com.tangem.data.walletconnect.respond.WcRespondService @@ -24,6 +26,8 @@ import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.walletconnect.WalletConnectStore import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.walletconnect.WcPairService +import com.tangem.domain.walletconnect.WcRequestService +import com.tangem.domain.walletconnect.WcRequestUseCaseFactory import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository import com.tangem.domain.walletconnect.repository.WalletConnectRepository import com.tangem.domain.walletconnect.repository.WcSessionsManager @@ -107,14 +111,13 @@ internal object WalletConnectDataModule { @Provides @Singleton - fun defaultWcRequestService( - dispatchers: CoroutineDispatcherProvider, - diHelperBox: DiHelperBox, - ): DefaultWcRequestService { - val scope = CoroutineScope(SupervisorJob() + dispatchers.io) + fun wcRequestService(default: DefaultWcRequestService): WcRequestService = default + + @Provides + @Singleton + fun defaultWcRequestService(diHelperBox: DiHelperBox): DefaultWcRequestService { return DefaultWcRequestService( requestConverters = diHelperBox.handlers, - scope = scope, ) } @@ -185,6 +188,12 @@ internal object WalletConnectDataModule { ), ) + @Provides + @Singleton + fun wcRequestUseCaseFactory(diHelperBox: DiHelperBox): WcRequestUseCaseFactory { + return DefaultWcRequestUseCaseFactory(diHelperBox.handlers) + } + internal class DiHelperBox( val converters: Set, val handlers: Set, diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthNetwork.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthNetwork.kt index 4f05896657..0611504329 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthNetwork.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/ethereum/WcEthNetwork.kt @@ -11,6 +11,7 @@ import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext import com.tangem.data.walletconnect.utils.WcNamespaceConverter import com.tangem.domain.tokens.model.Network import com.tangem.domain.walletconnect.model.WcEthMethod +import com.tangem.domain.walletconnect.model.WcEthMethodName import com.tangem.domain.walletconnect.model.WcEthTransactionParams import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest import com.tangem.domain.walletconnect.repository.WcSessionsManager @@ -27,9 +28,14 @@ internal class WcEthNetwork( override val namespaceKey: NamespaceKey = NamespaceKey("eip155") - override suspend fun toUseCase(request: WcSdkSessionRequest): WcMethodUseCase? { + override fun toWcMethodName(request: WcSdkSessionRequest): WcEthMethodName? { val methodKey = request.request.method - val name = Name.entries.find { it.raw == methodKey } ?: return null + val name = WcEthMethodName.entries.find { it.raw == methodKey } ?: return null + return name + } + + override suspend fun toUseCase(request: WcSdkSessionRequest): WcMethodUseCase? { + val name = toWcMethodName(request) ?: return null val method: WcEthMethod = name.toMethod(request) ?: return null val session = sessionsManager.findSessionByTopic(request.topic) ?: return null val network = toNetwork(request.chainId.orEmpty(), session.wallet) ?: return null @@ -41,26 +47,26 @@ internal class WcEthNetwork( } } - private fun Name.toMethod(request: WcSdkSessionRequest): WcEthMethod? { + private fun WcEthMethodName.toMethod(request: WcSdkSessionRequest): WcEthMethod? { val rawParams = request.request.params return when (this) { - Name.EthSign, - Name.PersonalSign, + WcEthMethodName.EthSign, + WcEthMethodName.PersonalSign, -> moshi.fromJson>(rawParams)?.let { list -> - val accountIndex = if (this == Name.EthSign) 0 else 1 - val messageIndex = if (this == Name.EthSign) 1 else 0 + val accountIndex = if (this == WcEthMethodName.EthSign) 0 else 1 + val messageIndex = if (this == WcEthMethodName.EthSign) 1 else 0 val account = list.getOrNull(accountIndex) ?: return@let null val message = list.getOrNull(messageIndex) ?: return@let null WcEthMethod.MessageSign(account = account, message = message) } - Name.SignTypeData -> TODO() - Name.SignTypeDataV4 -> TODO() - Name.SignTransaction, - Name.SendTransaction, + WcEthMethodName.SignTypeData -> TODO() + WcEthMethodName.SignTypeDataV4 -> TODO() + WcEthMethodName.SignTransaction, + WcEthMethodName.SendTransaction, -> moshi.fromJson>(rawParams) ?.firstOrNull() ?.let { - if (this == Name.SignTransaction) { + if (this == WcEthMethodName.SignTransaction) { WcEthMethod.SignTransaction(transaction = it) } else { WcEthMethod.SendTransaction(transaction = it) @@ -69,15 +75,6 @@ internal class WcEthNetwork( } } - enum class Name(val raw: String) { - EthSign("eth_sign"), - PersonalSign("personal_sign"), - SignTypeData("eth_signTypedData"), - SignTypeDataV4("eth_signTypedData_v4"), - SignTransaction("eth_signTransaction"), - SendTransaction("eth_sendTransaction"), - } - override fun toNetwork(chainId: String, wallet: UserWallet): Network? { return toNetwork(chainId, wallet, excludedBlockchains) } diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaNetwork.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaNetwork.kt index d376a58417..fd4e3b5832 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaNetwork.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/network/solana/WcSolanaNetwork.kt @@ -11,6 +11,7 @@ import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext import com.tangem.data.walletconnect.utils.WcNamespaceConverter import com.tangem.domain.tokens.model.Network import com.tangem.domain.walletconnect.model.WcSolanaMethod +import com.tangem.domain.walletconnect.model.WcSolanaMethodName import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest import com.tangem.domain.walletconnect.repository.WcSessionsManager import com.tangem.domain.walletconnect.usecase.WcMethodUseCase @@ -26,9 +27,14 @@ internal class WcSolanaNetwork constructor( override val namespaceKey: NamespaceKey = NamespaceKey("solana") - override suspend fun toUseCase(request: WcSdkSessionRequest): WcMethodUseCase? { + override fun toWcMethodName(request: WcSdkSessionRequest): WcSolanaMethodName? { val methodKey = request.request.method - val name = Name.entries.find { it.raw == methodKey } ?: return null + val name = WcSolanaMethodName.entries.find { it.raw == methodKey } ?: return null + return name + } + + override suspend fun toUseCase(request: WcSdkSessionRequest): WcMethodUseCase? { + val name = toWcMethodName(request) ?: return null val method: WcSolanaMethod = name.toMethod(request) ?: return null val session = sessionsManager.findSessionByTopic(request.topic) ?: return null val network = toNetwork(request.chainId.orEmpty(), session.wallet) ?: return null @@ -67,27 +73,20 @@ internal class WcSolanaNetwork constructor( ) } - private fun Name.toMethod(request: WcSdkSessionRequest): WcSolanaMethod? { + private fun WcSolanaMethodName.toMethod(request: WcSdkSessionRequest): WcSolanaMethod? { val rawParams = request.request.params return when (this) { - Name.SignMessage -> moshi.fromJson(rawParams)?.let { request -> + WcSolanaMethodName.SignMessage -> moshi.fromJson(rawParams)?.let { request -> WcSolanaMethod.SignMessage(pubKey = request.publicKey, message = request.message) } - Name.SignTransaction -> moshi.fromJson(rawParams)?.let { request -> - WcSolanaMethod.SignTransaction(request.transaction) - } - Name.SendAllTransaction -> moshi.fromJson>(rawParams)?.let { list -> + WcSolanaMethodName.SignTransaction -> moshi.fromJson(rawParams) + ?.let { request -> WcSolanaMethod.SignTransaction(request.transaction) } + WcSolanaMethodName.SendAllTransaction -> moshi.fromJson>(rawParams)?.let { list -> WcSolanaMethod.SignAllTransaction(list) } } } - private enum class Name(val raw: String) { - SignMessage("solana_signMessage"), - SignTransaction("solana_signTransaction"), - SendAllTransaction("solana_signAllTransactions"), - } - internal class Factories @Inject constructor( val signTransaction: DefaultWcSolanaSignTransactionUseCase.Factory, val signAllTransaction: DefaultWcSolanaSignAllTransactionUseCase.Factory, diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestService.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestService.kt index d4b9fdbf33..cb39b52a5e 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestService.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestService.kt @@ -3,20 +3,19 @@ package com.tangem.data.walletconnect.request import com.reown.walletkit.client.Wallet import com.tangem.data.walletconnect.utils.WcSdkObserver import com.tangem.data.walletconnect.utils.WcSdkSessionRequestConverter -import com.tangem.domain.walletconnect.usecase.WcMethodUseCase -import com.tangem.domain.walletconnect.usecase.WcUseCasesFlowProvider -import kotlinx.coroutines.CoroutineScope +import com.tangem.domain.walletconnect.WcRequestService +import com.tangem.domain.walletconnect.model.WcMethodName +import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.launch internal class DefaultWcRequestService( private val requestConverters: Set, - private val scope: CoroutineScope, -) : WcSdkObserver, WcUseCasesFlowProvider { +) : WcSdkObserver, WcRequestService { - private val _useCases: Channel = Channel(Channel.BUFFERED) - override val useCases = _useCases.receiveAsFlow() + private val _wcRequest: Channel> = Channel(Channel.BUFFERED) + override val wcRequest: Flow> = _wcRequest.receiveAsFlow() override fun onSessionRequest( sessionRequest: Wallet.Model.SessionRequest, @@ -24,10 +23,8 @@ internal class DefaultWcRequestService( ) { // Triggered when a Dapp sends SessionRequest to sign a transaction or a message val sr = WcSdkSessionRequestConverter.convert(sessionRequest) - scope.launch { - val useCase = requestConverters.firstNotNullOfOrNull { it.toUseCase(sr) } - ?: return@launch // todo(wc) handle unsupported - _useCases.trySend(useCase) - } + val name = requestConverters.firstNotNullOfOrNull { it.toWcMethodName(sr) } + ?: WcMethodName.Unsupported(sr.request.method) + _wcRequest.trySend(name to sr) } } \ No newline at end of file diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestUseCaseFactory.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestUseCaseFactory.kt new file mode 100644 index 0000000000..c0bb10ffcc --- /dev/null +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestUseCaseFactory.kt @@ -0,0 +1,17 @@ +package com.tangem.data.walletconnect.request + +import com.tangem.domain.walletconnect.WcRequestUseCaseFactory +import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest +import com.tangem.domain.walletconnect.usecase.WcMethodUseCase +import javax.inject.Inject + +internal class DefaultWcRequestUseCaseFactory @Inject constructor( + private val requestConverters: Set, +) : WcRequestUseCaseFactory { + + @Suppress("UNCHECKED_CAST") + override suspend fun createUseCase(request: WcSdkSessionRequest): T { + return requestConverters.firstNotNullOf { it.toUseCase(request) } as? T + ?: error("Should check request before in WcRouting") + } +} \ No newline at end of file diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/WcRequestToUseCaseConverter.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/WcRequestToUseCaseConverter.kt index 624440c0df..d4503c8f73 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/WcRequestToUseCaseConverter.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/WcRequestToUseCaseConverter.kt @@ -2,10 +2,12 @@ package com.tangem.data.walletconnect.request import com.squareup.moshi.Moshi import com.squareup.moshi.adapter +import com.tangem.domain.walletconnect.model.WcMethodName import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest import com.tangem.domain.walletconnect.usecase.WcMethodUseCase interface WcRequestToUseCaseConverter { + fun toWcMethodName(request: WcSdkSessionRequest): WcMethodName? suspend fun toUseCase(request: WcSdkSessionRequest): WcMethodUseCase? companion object { diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcMethodName.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcMethodName.kt new file mode 100644 index 0000000000..7b557f0223 --- /dev/null +++ b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcMethodName.kt @@ -0,0 +1,22 @@ +package com.tangem.domain.walletconnect.model + +sealed interface WcMethodName { + val raw: String + + data class Unsupported(override val raw: String) : WcMethodName +} + +enum class WcEthMethodName(override val raw: String) : WcMethodName { + EthSign("eth_sign"), + PersonalSign("personal_sign"), + SignTypeData("eth_signTypedData"), + SignTypeDataV4("eth_signTypedData_v4"), + SignTransaction("eth_signTransaction"), + SendTransaction("eth_sendTransaction"), +} + +enum class WcSolanaMethodName(override val raw: String) : WcMethodName { + SignMessage("solana_signMessage"), + SignTransaction("solana_signTransaction"), + SendAllTransaction("solana_signAllTransactions"), +} \ No newline at end of file diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcRequestService.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcRequestService.kt new file mode 100644 index 0000000000..ea6a1dbd17 --- /dev/null +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcRequestService.kt @@ -0,0 +1,9 @@ +package com.tangem.domain.walletconnect + +import com.tangem.domain.walletconnect.model.WcMethodName +import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest +import kotlinx.coroutines.flow.Flow + +interface WcRequestService { + val wcRequest: Flow> +} \ No newline at end of file diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcRequestUseCaseFactory.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcRequestUseCaseFactory.kt new file mode 100644 index 0000000000..4b6858edd6 --- /dev/null +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcRequestUseCaseFactory.kt @@ -0,0 +1,9 @@ +package com.tangem.domain.walletconnect + +import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest +import com.tangem.domain.walletconnect.usecase.WcMethodUseCase + +interface WcRequestUseCaseFactory { + + suspend fun createUseCase(request: WcSdkSessionRequest): T +} \ No newline at end of file