Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-24 20:04:44 +05:00
parent 75e6da7cee
commit 9383eb5264
3 changed files with 38 additions and 19 deletions

View file

@ -46,13 +46,14 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
analytics.send(WcAnalyticEvents.NewPairInitiated(source))
emit(WcPairState.Loading)
val sdkSessionProposal = sdkDelegate.pair(uri)
val pairResult = sdkDelegate.pair(uri)
.onLeft {
Timber.tag(WC_TAG).e(it, "Failed to call pair $pairRequest")
analytics.send(WcAnalyticEvents.PairFailed)
emit(WcPairState.Error(it))
}
.getOrNull() ?: return@flow
val (sdkSessionProposal, sdkVerifyContext) = pairResult
// check unsupported dApps, just local constant for now, finish if unsupported
if (UnsupportedDApps.list.any { sdkSessionProposal.url.contains(it, ignoreCase = true) }) {
@ -62,7 +63,7 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
return@flow
}
val proposalState = buildProposalState(sdkSessionProposal)
val proposalState = buildProposalState(sdkSessionProposal, sdkVerifyContext)
.onLeft {
analytics.send(WcAnalyticEvents.PairFailed)
emit(WcPairState.Error(it))
@ -147,11 +148,16 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
private suspend fun buildProposalState(
sessionProposal: Wallet.Model.SessionProposal,
verifyContext: Wallet.Model.VerifyContext,
): Either<WcPairError, WcPairState.Proposal> = runCatching {
val proposalNetwork = associateNetworksDelegate.associate(sessionProposal)
val verificationInfo = blockAidVerifier.verifyDApp(DAppData(sessionProposal.url)).getOrElse {
Timber.tag(WC_TAG).e(it, "Failed to verify DApp ${sessionProposal.name}")
CheckDAppResult.FAILED_TO_VERIFY
val verificationInfo = when {
verifyContext.validation == Wallet.Model.Validation.INVALID -> CheckDAppResult.UNSAFE
verifyContext.isScam == true -> CheckDAppResult.UNSAFE
else -> blockAidVerifier.verifyDApp(DAppData(sessionProposal.url)).getOrElse {
Timber.tag(WC_TAG).e(it, "Failed to verify DApp ${sessionProposal.name}")
CheckDAppResult.FAILED_TO_VERIFY
}
}
val requestedNetworks = proposalNetwork
.values.map { it.available.plus(it.required) }.flatten().toSet()
@ -174,12 +180,15 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
securityStatus = verificationInfo,
)
WcPairState.Proposal(dAppSession)
}.fold(onSuccess = { it.right() }, onFailure = {
when (it) {
is WcPairError -> it.left()
else -> WcPairError.Unknown(it.localizedMessage.orEmpty()).left()
}
},)
}.fold(
onSuccess = { it.right() },
onFailure = {
when (it) {
is WcPairError -> it.left()
else -> WcPairError.Unknown(it.localizedMessage.orEmpty()).left()
}
},
)
private sealed interface TerminalAction {
data class Approve(val sessionForApprove: WcSessionApprove) : TerminalAction

View file

@ -21,11 +21,13 @@ import kotlin.time.Duration.Companion.seconds
internal class WcPairSdkDelegate : WcSdkObserver {
private val onSessionProposal = Channel<Wallet.Model.SessionProposal>()
private val onSessionProposal = Channel<Pair<Wallet.Model.SessionProposal, Wallet.Model.VerifyContext>>()
private val onSdkErrorCallback = Channel<Wallet.Model.Error>()
private val onSessionSettleResponse = Channel<Wallet.Model.SettledSessionResponse>()
suspend fun pair(url: String): Either<WcPairError, Wallet.Model.SessionProposal> = coroutineScope {
suspend fun pair(
url: String,
): Either<WcPairError, Pair<Wallet.Model.SessionProposal, Wallet.Model.VerifyContext>> = coroutineScope {
val proposalCallback = async { withTimeout(CALLBACK_TIMEOUT.seconds) { proposalCallback() } }
val pairCall = async { sdkPair(url) }
pairCall.await().onLeft {
@ -112,7 +114,7 @@ internal class WcPairSdkDelegate : WcSdkObserver {
verifyContext: Wallet.Model.VerifyContext,
) {
// Triggered when wallet receives the session proposal sent by a Dapp
onSessionProposal.trySend(sessionProposal)
onSessionProposal.trySend(sessionProposal to verifyContext)
}
override fun onSessionSettleResponse(settleSessionResponse: Wallet.Model.SettledSessionResponse) {

View file

@ -57,6 +57,14 @@ internal class DefaultWcPairUseCaseTest {
relayProtocol = "",
relayData = "",
)
private val sdkVerifyContext: Wallet.Model.VerifyContext
get() = Wallet.Model.VerifyContext(
id = 9259,
origin = "vituperata",
validation = Wallet.Model.Validation.VALID,
verifyUrl = "https://search.yahoo.com/search?p=id",
isScam = false,
)
private val unsupportedDAppUrl = "dydx.trade"
private val unsupportedSdkProposal get() = sdkProposal.copy(url = unsupportedDAppUrl)
@ -122,7 +130,7 @@ internal class DefaultWcPairUseCaseTest {
@Test
fun `pair, emmit proposal state and wait actions`() = runTest {
coEvery { sdkDelegate.pair(url) } returns sdkProposal.right()
coEvery { sdkDelegate.pair(url) } returns (sdkProposal to sdkVerifyContext).right()
coEvery { blockAidVerifier.verifyDApp(any()) } returns Either.catch { CheckDAppResult.SAFE }
val useCase = useCaseFactory()
@ -143,7 +151,7 @@ internal class DefaultWcPairUseCaseTest {
val sessionForSave = sdkSession.sessionForSave
val result = WcPairState.Approving.Result(sessionForApprove, sessionForSave.right())
coEvery { sdkDelegate.pair(url) } returns sdkProposal.right()
coEvery { sdkDelegate.pair(url) } returns (sdkProposal to sdkVerifyContext).right()
coEvery { sdkDelegate.approve(sdkApprove) } returns sdkApproveSuccess.right()
coEvery { sessionsManager.saveSession(any()) } returns Unit
coEvery { blockAidVerifier.verifyDApp(any()) } returns Either.catch { CheckDAppResult.SAFE }
@ -177,7 +185,7 @@ internal class DefaultWcPairUseCaseTest {
fun `success pair and reject approving`() = runTest {
val proposerPublicKey = sdkProposal.proposerPublicKey
coEvery { sdkDelegate.pair(url) } returns sdkProposal.right()
coEvery { sdkDelegate.pair(url) } returns (sdkProposal to sdkVerifyContext).right()
coEvery { sdkDelegate.rejectSession(proposerPublicKey) } returns Unit
coEvery { blockAidVerifier.verifyDApp(any()) } returns Either.catch { CheckDAppResult.SAFE }
@ -199,7 +207,7 @@ internal class DefaultWcPairUseCaseTest {
@Test
fun `success pair and reject unsupported dApp`() = runTest {
coEvery { sdkDelegate.pair(url) } returns unsupportedSdkProposal.right()
coEvery { sdkDelegate.pair(url) } returns (unsupportedSdkProposal to sdkVerifyContext).right()
val unsupportedDAppError = WcPairState.Error(WcPairError.UnsupportedDApp(unsupportedSdkProposal.name))
val useCase = useCaseFactory()
@ -234,7 +242,7 @@ internal class DefaultWcPairUseCaseTest {
fun `complete on approve error`() = runTest {
val approveLoading = WcPairState.Approving.Loading(sessionForApprove)
val error = WcPairError.ApprovalFailed("error").left()
coEvery { sdkDelegate.pair(url) } returns sdkProposal.right()
coEvery { sdkDelegate.pair(url) } returns (sdkProposal to sdkVerifyContext).right()
coEvery { sdkDelegate.approve(sdkApprove) } returns error
coEvery { sdkDelegate.rejectSession(sdkApprove.proposerPublicKey) } returns Unit
coEvery { blockAidVerifier.verifyDApp(any()) } returns Either.catch { CheckDAppResult.SAFE }