Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-23 16:53:37 +07:00
parent 15e7df8d41
commit 4882daf1a2
14 changed files with 79 additions and 33 deletions

View file

@ -39,13 +39,11 @@ internal class WcEthNetwork(
}
@Suppress("CyclomaticComplexMethod")
override suspend fun toUseCase(
request: WcSdkSessionRequest,
): Either<WcRequestError.HandleMethodError, WcMethodUseCase> {
fun error(message: String) = WcRequestError.HandleMethodError(message).left()
override suspend fun toUseCase(request: WcSdkSessionRequest): Either<HandleMethodError, WcMethodUseCase> {
fun error(message: String) = HandleMethodError.UnknownError(message).left()
val name = toWcMethodName(request) ?: return error("Unknown method name")
val session = sessionsManager.findSessionByTopic(request.topic)
?: return error("Failed to find session for topic ${request.topic}")
?: return HandleMethodError.UnknownSession.left()
val wallet = session.wallet
val chainId = request.chainId.orEmpty()
val method: WcEthMethod = name.toMethod(request, wallet)

View file

@ -18,7 +18,7 @@ import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
import com.tangem.data.walletconnect.utils.WcNamespaceConverter
import com.tangem.data.walletconnect.utils.WcNetworksConverter
import com.tangem.domain.models.network.Network
import com.tangem.domain.walletconnect.model.WcRequestError
import com.tangem.domain.walletconnect.model.HandleMethodError
import com.tangem.domain.walletconnect.model.WcSolanaMethod
import com.tangem.domain.walletconnect.model.WcSolanaMethodName
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
@ -42,16 +42,14 @@ internal class WcSolanaNetwork(
}
@Suppress("CyclomaticComplexMethod")
override suspend fun toUseCase(
request: WcSdkSessionRequest,
): Either<WcRequestError.HandleMethodError, WcMethodUseCase> {
fun error(message: String) = WcRequestError.HandleMethodError(message).left()
override suspend fun toUseCase(request: WcSdkSessionRequest): Either<HandleMethodError, WcMethodUseCase> {
fun error(message: String) = HandleMethodError.UnknownError(message).left()
val name = toWcMethodName(request) ?: return error("Unknown method name")
val method: WcSolanaMethod = name.toMethod(request)
.getOrElse { return error(it.message.orEmpty()) }
?: return error("Failed to parse $name")
val session = sessionsManager.findSessionByTopic(request.topic)
?: return error("Failed to find session for topic ${request.topic}")
?: return HandleMethodError.UnknownSession.left()
val wallet = session.wallet
val chainId = request.chainId.orEmpty()
suspend fun anyExistNetwork() = networksConverter.mainOrAnyWalletNetworkForRequest(chainId, wallet)

View file

@ -5,8 +5,8 @@ import arrow.core.left
import arrow.core.right
import com.tangem.data.walletconnect.utils.WC_TAG
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
import com.tangem.domain.walletconnect.model.HandleMethodError
import com.tangem.domain.walletconnect.model.WcMethod
import com.tangem.domain.walletconnect.model.WcRequestError
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.domain.walletconnect.usecase.method.WcMethodUseCase
import timber.log.Timber
@ -19,18 +19,18 @@ internal class DefaultWcRequestUseCaseFactory @Inject constructor(
@Suppress("UNCHECKED_CAST")
override suspend fun <T : WcMethodUseCase> createUseCase(
request: WcSdkSessionRequest,
): Either<WcMethod.Unsupported, T> {
): Either<HandleMethodError, T> {
val useCase = requestConverters
.find { it.toWcMethodName(request) != null }
?.toUseCase(request)
?: WcRequestError.HandleMethodError("Failed to create WcUseCase").left()
?: HandleMethodError.UnknownError("Failed to create WcUseCase").left()
return useCase.fold(
ifLeft = {
Timber.tag(WC_TAG).e("$it")
WcMethod.Unsupported(request).left()
it.left()
},
ifRight = { (it as? T)?.right() ?: WcMethod.Unsupported(request).left() },
ifRight = { (it as? T)?.right() ?: HandleMethodError.Unsupported(WcMethod.Unsupported(request)).left() },
)
}
}

View file

@ -5,14 +5,14 @@ import arrow.core.left
import arrow.core.right
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapter
import com.tangem.domain.walletconnect.model.HandleMethodError
import com.tangem.domain.walletconnect.model.WcMethodName
import com.tangem.domain.walletconnect.model.WcRequestError
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.domain.walletconnect.usecase.method.WcMethodUseCase
interface WcRequestToUseCaseConverter {
fun toWcMethodName(request: WcSdkSessionRequest): WcMethodName?
suspend fun toUseCase(request: WcSdkSessionRequest): Either<WcRequestError.HandleMethodError, WcMethodUseCase>
suspend fun toUseCase(request: WcSdkSessionRequest): Either<HandleMethodError, WcMethodUseCase>
companion object {
@OptIn(ExperimentalStdlibApi::class)

View file

@ -151,7 +151,22 @@ internal class DefaultWcSessionsManager(
Timber.tag(WC_TAG).i("removeUnknownSessions $unknownStoredSessions")
store.removeSessions(unknownStoredSessions.toSet())
}
return haveSomeUnknown
val emptyNetworkSessions = wcSessions.filter { it.networks.isEmpty() }
val emptyNetworksDto = storeSessions
.filter { dto -> emptyNetworkSessions.find { it.sdkModel.topic == dto.topic } != null }
.toSet()
val haveEmptySessions = emptyNetworkSessions.isNotEmpty()
val haveEmptyDto = emptyNetworksDto.isNotEmpty()
if (haveEmptyDto) {
Timber.tag(WC_TAG).i("remove sessions without networks $emptyNetworksDto")
store.removeSessions(emptyNetworksDto)
}
if (haveEmptySessions) {
emptyNetworkSessions.map { scope.launch { sdkDisconnectSession(it.sdkModel.topic) } }
}
return haveSomeUnknown || haveEmptyDto
}
private fun extendSessions() {

View file

@ -13,10 +13,6 @@ sealed class WcRequestError {
val message: String,
) : WcRequestError()
data class HandleMethodError(
val message: String,
) : WcRequestError()
data class UnknownError(val ex: Throwable? = null) : WcRequestError()
companion object {
@ -60,4 +56,17 @@ sealed class WcRequestError {
-> null
}
}
}
sealed class HandleMethodError(
open val message: String,
) : WcRequestError() {
data class Unsupported(
val method: WcMethod.Unsupported,
) : HandleMethodError(message = "Unsupported method ${method.request.request.method}")
data object UnknownSession : HandleMethodError(message = "WalletConnect session was disconnected")
data class UnknownError(override val message: String) : HandleMethodError(message)
}

View file

@ -1,11 +1,11 @@
package com.tangem.domain.walletconnect
import arrow.core.Either
import com.tangem.domain.walletconnect.model.WcMethod
import com.tangem.domain.walletconnect.model.HandleMethodError
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.domain.walletconnect.usecase.method.WcMethodUseCase
interface WcRequestUseCaseFactory {
suspend fun <T : WcMethodUseCase> createUseCase(request: WcSdkSessionRequest): Either<WcMethod.Unsupported, T>
suspend fun <T : WcMethodUseCase> createUseCase(request: WcSdkSessionRequest): Either<HandleMethodError, T>
}

View file

@ -91,6 +91,12 @@ internal class DefaultWcRoutingComponent @AssistedInject constructor(
alertType = AlertsComponent.AlertType.UnsupportedMethod { model.innerRouter.pop() },
),
)
is WcInnerRoute.WcDappDisconnected -> AlertsComponent(
childContext,
AlertsComponent.Params(
alertType = AlertsComponent.AlertType.WcDisconnected { model.innerRouter.pop() },
),
)
}
}

View file

@ -26,5 +26,8 @@ internal sealed interface WcInnerRoute : Route {
data class Pair(val request: WcPairRequest) : WcInnerRoute
@Serializable
data class UnsupportedMethodAlert(override val rawRequest: WcSdkSessionRequest) : Method
data object UnsupportedMethodAlert : WcInnerRoute
@Serializable
data object WcDappDisconnected : WcInnerRoute
}

View file

@ -57,7 +57,7 @@ internal class WcRoutingModel @Inject constructor(
WcSolanaMethodName.SendAllTransaction,
-> WcInnerRoute.Send(rawRequest)
is WcMethodName.Unsupported,
-> WcInnerRoute.UnsupportedMethodAlert(rawRequest)
-> WcInnerRoute.UnsupportedMethodAlert
}
}

View file

@ -0,0 +1,16 @@
package com.tangem.features.walletconnect.transaction.converter
import com.tangem.domain.walletconnect.model.HandleMethodError
import com.tangem.features.walletconnect.connections.routing.WcInnerRoute
import com.tangem.utils.converter.Converter
internal object WcHandleMethodErrorConverter :
Converter<HandleMethodError, WcInnerRoute> {
override fun convert(value: HandleMethodError): WcInnerRoute = when (value) {
is HandleMethodError.Unsupported,
is HandleMethodError.UnknownError,
-> WcInnerRoute.UnsupportedMethodAlert
HandleMethodError.UnknownSession -> WcInnerRoute.WcDappDisconnected
}
}

View file

@ -10,9 +10,9 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
import com.tangem.domain.walletconnect.usecase.method.WcAddNetworkUseCase
import com.tangem.features.walletconnect.connections.routing.WcInnerRoute
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
import com.tangem.features.walletconnect.transaction.converter.WcAddEthereumChainUMConverter
import com.tangem.features.walletconnect.transaction.converter.WcHandleMethodErrorConverter
import com.tangem.features.walletconnect.transaction.entity.chain.WcAddEthereumChainUM
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
@ -45,7 +45,7 @@ internal class WcAddNetworkModel @Inject constructor(
init {
modelScope.launch {
val useCase: WcAddNetworkUseCase = useCaseFactory.createUseCase<WcAddNetworkUseCase>(params.rawRequest)
.onLeft { router.push(WcInnerRoute.UnsupportedMethodAlert(params.rawRequest)) }
.onLeft { router.push(WcHandleMethodErrorConverter.convert(it)) }
.getOrNull() ?: return@launch
_uiState.emit(
wcAddEthereumChainUMConverter.convert(

View file

@ -36,6 +36,7 @@ import com.tangem.features.send.v2.api.params.FeeSelectorParams
import com.tangem.features.walletconnect.connections.routing.WcInnerRoute
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
import com.tangem.features.walletconnect.transaction.converter.WcCommonTransactionUMConverter
import com.tangem.features.walletconnect.transaction.converter.WcHandleMethodErrorConverter
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
@ -84,9 +85,9 @@ internal class WcSendTransactionModel @Inject constructor(
init {
@Suppress("UnusedPrivateMember")
modelScope.launch {
val unknownMethodRunnable = { router.push(WcInnerRoute.UnsupportedMethodAlert(params.rawRequest)) }
val unknownMethodRunnable = { router.push(WcInnerRoute.UnsupportedMethodAlert) }
val useCase = useCaseFactory.createUseCase<WcSignUseCase<*>>(params.rawRequest)
.onLeft { unknownMethodRunnable() }
.onLeft { router.push(WcHandleMethodErrorConverter.convert(it)) }
.getOrNull() ?: return@launch
when (useCase) {
is WcListTransactionUseCase,

View file

@ -14,9 +14,9 @@ import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase
import com.tangem.domain.walletconnect.usecase.method.WcSignState
import com.tangem.domain.walletconnect.usecase.method.WcSignStep
import com.tangem.domain.walletconnect.usecase.method.WcSignUseCase
import com.tangem.features.walletconnect.connections.routing.WcInnerRoute
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
import com.tangem.features.walletconnect.transaction.converter.WcCommonTransactionUMConverter
import com.tangem.features.walletconnect.transaction.converter.WcHandleMethodErrorConverter
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM
@ -50,7 +50,7 @@ internal class WcSignTransactionModel @Inject constructor(
init {
modelScope.launch {
val useCase = useCaseFactory.createUseCase<WcMessageSignUseCase>(params.rawRequest)
.onLeft { router.push(WcInnerRoute.UnsupportedMethodAlert(params.rawRequest)) }
.onLeft { router.push(WcHandleMethodErrorConverter.convert(it)) }
.getOrNull() ?: return@launch
useCase.invoke()
.onEach { signState ->