From af243495bd79999b360c6c6da212a16ebdb78a21 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 10 Jun 2025 18:50:05 +0800 Subject: [PATCH] Updated on 2026-08-14 --- .../pair/AssociateNetworksDelegate.kt | 4 +-- .../pair/DefaultWcPairUseCase.kt | 4 +-- .../walletconnect/pair/WcPairSdkDelegate.kt | 29 +++++++++++++++---- .../walletconnect/DefaultWcPairUseCaseTest.kt | 6 ++-- .../domain/walletconnect/model/WcPairError.kt | 22 ++++++++------ .../connections/model/WcPairModel.kt | 7 +++-- 6 files changed, 47 insertions(+), 25 deletions(-) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/AssociateNetworksDelegate.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/AssociateNetworksDelegate.kt index 7208eea4c4..808d399dab 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/AssociateNetworksDelegate.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/AssociateNetworksDelegate.kt @@ -28,7 +28,7 @@ internal class AssociateNetworksDelegate( } } - @Throws(WcPairError.UnsupportedNetworks::class) + @Throws(WcPairError.UnsupportedBlockchains::class) suspend fun associate(sessionProposal: Wallet.Model.SessionProposal): Map { val userWallets = getWallets.invokeSync().filter { it.isMultiCurrency } val requiredNamespaces: Set = sessionProposal.requiredNamespaces.setOfChainId() @@ -74,7 +74,7 @@ internal class AssociateNetworksDelegate( notAdded.add(wcNetwork) } } - if (unknownRequired.isNotEmpty()) throw WcPairError.UnsupportedNetworks(unknownRequired) + if (unknownRequired.isNotEmpty()) throw WcPairError.UnsupportedBlockchains(unknownRequired) return ProposalNetwork( wallet = wallet, missingRequired = missingRequired, 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 fc52444c3b..77b7c18e93 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 @@ -56,7 +56,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor( // check unsupported dApps, just local constant for now, finish if unsupported if (sdkSessionProposal.name in unsupportedDApps) { Timber.tag(WC_TAG).i("Unsupported DApp ${sdkSessionProposal.name}") - val error = WcPairState.Error(WcPairError.UnsupportedDApp) + val error = WcPairState.Error(WcPairError.UnsupportedDomain) emit(error) return@flow } @@ -104,7 +104,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor( ) newSession }.onLeft { - analytics.send(WcAnalyticEvents.DAppConnectionFailed(it.message)) + analytics.send(WcAnalyticEvents.DAppConnectionFailed(it.code)) Timber.tag(WC_TAG).e(it, "Failed to approve session ${sdkSessionProposal.name}") } emit(WcPairState.Approving.Result(sessionForApprove, either)) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/WcPairSdkDelegate.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/WcPairSdkDelegate.kt index 54f931d604..1a72488035 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/WcPairSdkDelegate.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/WcPairSdkDelegate.kt @@ -27,7 +27,11 @@ internal class WcPairSdkDelegate : WcSdkObserver { val pairCall = async { sdkPair(url) } val proposal = async { proposalCallback() } - pairCall.await().map { proposal.await() } + pairCall.await().onLeft { + proposal.cancel() + return@coroutineScope it.left() + } + proposal.await().right() } suspend fun approve( @@ -40,11 +44,14 @@ internal class WcPairSdkDelegate : WcSdkObserver { val approveCall = async { sdkApprove(sessionApprove) } val approveCallback = async { approveCallback() } approveCall.await() - .onLeft { return@coroutineScope it.left() } + .onLeft { + approveCallback.cancel() + return@coroutineScope it.left() + } when (val result = approveCallback.await()) { is Wallet.Model.SettledSessionResponse.Result -> result.right() is Wallet.Model.SettledSessionResponse.Error -> - WcPairError.ExternalApprovalError(result.errorMessage).left() + WcPairError.ApprovalFailed(result.errorMessage).left() } } @@ -77,7 +84,7 @@ internal class WcPairSdkDelegate : WcSdkObserver { WalletKit.approveSession( params = sessionApprove, onSuccess = { continuation.resume(Unit.right()) }, - onError = { continuation.resume(it.throwable.toPairError()) }, + onError = { continuation.resume(it.throwable.toApproveError()) }, ) } } @@ -87,10 +94,20 @@ internal class WcPairSdkDelegate : WcSdkObserver { WalletKit.pair( params = Wallet.Params.Pair(uri), onSuccess = { continuation.resume(Unit.right()) }, - onError = { continuation.resume(it.throwable.toPairError()) }, + onError = { continuation.resume(it.throwable.toPairError().left()) }, ) } } - private fun Throwable.toPairError() = WcPairError.ExternalApprovalError(this.localizedMessage.orEmpty()).left() + private fun Throwable.toPairError() = when { + pairingExpiredMessages.any { message.orEmpty().contains(it) } -> WcPairError.UriAlreadyUsed(message.orEmpty()) + else -> WcPairError.PairingFailed(this.localizedMessage.orEmpty()) + } + + private fun Throwable.toApproveError() = WcPairError.ApprovalFailed(this.localizedMessage.orEmpty()).left() + + companion object { + // com.reown.android.pairing.engine.domain.PairingEngine.pair + private val pairingExpiredMessages = listOf("Pairing URI expired", "Pairing expired") + } } \ No newline at end of file diff --git a/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/DefaultWcPairUseCaseTest.kt b/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/DefaultWcPairUseCaseTest.kt index bd8e58f43b..e493e6b602 100644 --- a/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/DefaultWcPairUseCaseTest.kt +++ b/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/DefaultWcPairUseCaseTest.kt @@ -196,7 +196,7 @@ internal class DefaultWcPairUseCaseTest { @Test fun `success pair and reject unsupported dApp`() = runTest { coEvery { sdkDelegate.pair(url) } returns unsupportedSdkProposal.right() - val unsupportedDAppError = WcPairState.Error(WcPairError.UnsupportedDApp) + val unsupportedDAppError = WcPairState.Error(WcPairError.UnsupportedDomain) val useCase = useCaseFactory() useCase.invoke().test { @@ -211,7 +211,7 @@ internal class DefaultWcPairUseCaseTest { @Test fun `complete on pair error`() = runTest { - val error = WcPairError.ExternalApprovalError("error") + val error = WcPairError.PairingFailed("error") coEvery { sdkDelegate.pair(url) } returns error.left() val errorState = WcPairState.Error(error) @@ -229,7 +229,7 @@ internal class DefaultWcPairUseCaseTest { @Test fun `complete on approve error`() = runTest { val approveLoading = WcPairState.Approving.Loading(sessionForApprove) - val error = WcPairError.ExternalApprovalError("error").left() + val error = WcPairError.ApprovalFailed("error").left() coEvery { sdkDelegate.pair(url) } returns sdkProposal.right() coEvery { sdkDelegate.approve(sdkApprove) } returns error coEvery { blockAidVerifier.verifyDApp(any()) } returns Either.catch { CheckDAppResult.SAFE } diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcPairError.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcPairError.kt index 5a060eaaba..005daafedc 100644 --- a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcPairError.kt +++ b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcPairError.kt @@ -1,15 +1,19 @@ package com.tangem.domain.walletconnect.model -sealed class WcPairError(override val message: String) : Exception(message) { +sealed class WcPairError( + val code: String, + override val message: String = "", +) : Exception() { - data object UnsupportedDApp : WcPairError("UnsupportedDApp") - data class UnsupportedNetworks( - val chains: Set, - ) : WcPairError("ApprovalErrorMissingNetworks") - - data class ExternalApprovalError( - override val message: String, - ) : WcPairError("ExternalApprovalError") + data class UriAlreadyUsed(override val message: String) : WcPairError("107 001 001") + data class PairingFailed(override val message: String) : WcPairError("107 001 002") + data object InvalidDomainURL : WcPairError("107 001 003") + data object UnsupportedDomain : WcPairError("107 001 004") + data class UnsupportedBlockchains(val chains: Set) : WcPairError("107 001 005") + data object InvalidConnectionRequest : WcPairError("107 002 001") + data object ProposalExpired : WcPairError("107 002 002") + data class ApprovalFailed(override val message: String) : WcPairError("107 002 003") + data object RejectionFailed : WcPairError("107 002 004") data class Unknown(override val message: String) : WcPairError(message) } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt index 0167da0484..08a2dbadc7 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt @@ -13,6 +13,7 @@ import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.message.ToastMessage import com.tangem.domain.models.network.Network +import com.tangem.domain.walletconnect.model.WcPairError import com.tangem.domain.walletconnect.model.WcPairRequest import com.tangem.domain.walletconnect.model.WcSessionApprove import com.tangem.domain.walletconnect.model.WcSessionProposal @@ -32,7 +33,6 @@ import com.tangem.features.walletconnect.connections.model.transformers.WcDAppVe import com.tangem.features.walletconnect.connections.routes.WcAppInfoRoutes import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* -import timber.log.Timber import javax.inject.Inject import kotlin.properties.Delegates import com.tangem.utils.transformer.update as transformerUpdate @@ -78,6 +78,7 @@ internal class WcPairModel @Inject constructor( } private fun loadDAppInfo() { + fun errorToast(error: WcPairError) = messageSender.send(ToastMessage(message = stringReference(error.message))) wcPairUseCase() .onEach { pairState -> when (pairState) { @@ -88,14 +89,14 @@ internal class WcPairModel @Inject constructor( appInfoUiState.transformerUpdate( WcConnectButtonProgressTransformer(showProgress = false), ) + pairState.result.onLeft(::errorToast) router.pop() } is WcPairState.Error -> { appInfoUiState.transformerUpdate( WcConnectButtonProgressTransformer(showProgress = false), ) - messageSender.send(ToastMessage(message = stringReference(pairState.error.message))) - Timber.e(pairState.error) + errorToast(pairState.error) } is WcPairState.Loading -> appInfoUiState.update { createLoadingState() } is WcPairState.Proposal -> {