Updated on 2026-08-14
This commit is contained in:
parent
6373b5a8a3
commit
c1ad1e43ce
8 changed files with 127 additions and 57 deletions
|
|
@ -1,5 +1,9 @@
|
|||
package com.tangem.data.walletconnect.network.ethereum
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
|
|
@ -35,12 +39,18 @@ internal class WcEthNetwork(
|
|||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
override suspend fun toUseCase(request: WcSdkSessionRequest): WcMethodUseCase? {
|
||||
val name = toWcMethodName(request) ?: return null
|
||||
val session = sessionsManager.findSessionByTopic(request.topic) ?: return null
|
||||
override suspend fun toUseCase(
|
||||
request: WcSdkSessionRequest,
|
||||
): Either<WcRequestError.HandleMethodError, WcMethodUseCase> {
|
||||
fun error(message: String) = WcRequestError.HandleMethodError(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}")
|
||||
val wallet = session.wallet
|
||||
val chainId = request.chainId.orEmpty()
|
||||
val method: WcEthMethod = name.toMethod(request, wallet) ?: return null
|
||||
val method: WcEthMethod = name.toMethod(request, wallet)
|
||||
.getOrElse { return error(it.message.orEmpty()) }
|
||||
?: return error("Failed to parse $name")
|
||||
suspend fun anyExistNetwork() = networksConverter.mainOrAnyWalletNetworkForRequest(chainId, wallet)
|
||||
|
||||
val accountAddress = when (method) {
|
||||
|
|
@ -60,7 +70,7 @@ internal class WcEthNetwork(
|
|||
is WcEthMethod.SignTransaction,
|
||||
-> networksConverter.findWalletNetworkForRequest(request, session, accountAddress)
|
||||
is WcEthMethod.AddEthereumChain -> anyExistNetwork()
|
||||
} ?: return null
|
||||
} ?: return error("Failed to find walletNetwork for accountAddress $accountAddress")
|
||||
|
||||
val context = WcMethodUseCaseContext(
|
||||
session = session,
|
||||
|
|
@ -74,10 +84,13 @@ internal class WcEthNetwork(
|
|||
is WcEthMethod.SignTransaction -> factories.signTransaction.create(context, method)
|
||||
is WcEthMethod.SignTypedData -> factories.signTypedData.create(context, method)
|
||||
is WcEthMethod.AddEthereumChain -> factories.addNetwork.create(context, method)
|
||||
}
|
||||
}.right()
|
||||
}
|
||||
|
||||
private suspend fun WcEthMethodName.toMethod(request: WcSdkSessionRequest, wallet: UserWallet): WcEthMethod? {
|
||||
private suspend fun WcEthMethodName.toMethod(
|
||||
request: WcSdkSessionRequest,
|
||||
wallet: UserWallet,
|
||||
): Either<Throwable, WcEthMethod?> {
|
||||
val rawParams = request.request.params
|
||||
return when (this) {
|
||||
WcEthMethodName.EthSign,
|
||||
|
|
@ -89,39 +102,48 @@ internal class WcEthNetwork(
|
|||
WcEthMethodName.SignTransaction,
|
||||
WcEthMethodName.SendTransaction,
|
||||
-> moshi.fromJson<List<WcEthTransactionParams>>(rawParams)
|
||||
.getOrElse { return it.left() }
|
||||
?.firstOrNull()
|
||||
?.let {
|
||||
if (this == WcEthMethodName.SignTransaction) {
|
||||
WcEthMethod.SignTransaction(transaction = it)
|
||||
WcEthMethod.SignTransaction(transaction = it).right()
|
||||
} else {
|
||||
WcEthMethod.SendTransaction(transaction = it)
|
||||
WcEthMethod.SendTransaction(transaction = it).right()
|
||||
}
|
||||
}
|
||||
?: return null.right()
|
||||
WcEthMethodName.AddEthereumChain -> moshi.fromJson<List<WcEthAddChain>>(rawParams)
|
||||
.getOrElse { return it.left() }
|
||||
?.firstOrNull()
|
||||
?.let {
|
||||
val newNetwork = networksConverter.mainOrAnyWalletNetworkForRequest(it.chainId, wallet)
|
||||
?: return null
|
||||
WcEthMethod.AddEthereumChain(rawChain = it, network = newNetwork)
|
||||
val newNetwork = networksConverter
|
||||
.mainOrAnyWalletNetworkForRequest(it.chainId, wallet)
|
||||
?: return null.right()
|
||||
WcEthMethod.AddEthereumChain(rawChain = it, network = newNetwork).right()
|
||||
}
|
||||
?: null.right()
|
||||
}
|
||||
}
|
||||
|
||||
private fun WcEthMethodName.parseMessageSign(rawParams: String): WcEthMethod.MessageSign? {
|
||||
val list = moshi.fromJson<List<String>>(rawParams) ?: return null
|
||||
private fun WcEthMethodName.parseMessageSign(rawParams: String): Either<Throwable, WcEthMethod.MessageSign?> {
|
||||
val list = moshi.fromJson<List<String>>(rawParams)
|
||||
.getOrElse { return it.left() }
|
||||
?: return null.right()
|
||||
val accountIndex = if (this == WcEthMethodName.EthSign) 0 else 1
|
||||
val messageIndex = if (this == WcEthMethodName.EthSign) 1 else 0
|
||||
val account = list.getOrNull(accountIndex) ?: return null
|
||||
val message = list.getOrNull(messageIndex) ?: return null
|
||||
val account = list.getOrNull(accountIndex) ?: return null.right()
|
||||
val message = list.getOrNull(messageIndex) ?: return null.right()
|
||||
val humanMsg = LegacySdkHelper.hexToAscii(message).orEmpty()
|
||||
return WcEthMethod.MessageSign(account = account, rawMessage = message, humanMsg = humanMsg)
|
||||
return WcEthMethod.MessageSign(account = account, rawMessage = message, humanMsg = humanMsg).right()
|
||||
}
|
||||
|
||||
private fun parseTypeData(params: String): WcEthMethod.SignTypedData? {
|
||||
private fun parseTypeData(params: String): Either<Throwable, WcEthMethod.SignTypedData?> {
|
||||
val account = params.substring(params.indexOf("\"") + 1, params.indexOf("\"", startIndex = 2))
|
||||
val data = params.substring(params.indexOfFirst { it == '{' }, params.indexOfLast { it == '}' } + 1)
|
||||
val parsedParams = moshi.fromJson<WcEthSignTypedDataParams>(data) ?: return null
|
||||
return WcEthMethod.SignTypedData(params = parsedParams, account = account, dataForSign = data)
|
||||
val parsedParams = moshi.fromJson<WcEthSignTypedDataParams>(data)
|
||||
.getOrElse { return it.left() }
|
||||
?: return null.right()
|
||||
return WcEthMethod.SignTypedData(params = parsedParams, account = account, dataForSign = data).right()
|
||||
}
|
||||
|
||||
internal class NamespaceConverter(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
package com.tangem.data.walletconnect.network.solana
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.extensions.decodeBase58
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.data.walletconnect.model.CAIP2
|
||||
import com.tangem.data.walletconnect.model.NamespaceKey
|
||||
import com.tangem.data.walletconnect.request.WcRequestToUseCaseConverter
|
||||
import com.tangem.data.walletconnect.request.WcRequestToUseCaseConverter.Companion.fromJson
|
||||
|
|
@ -13,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.data.walletconnect.model.CAIP2
|
||||
import com.tangem.domain.walletconnect.model.WcRequestError
|
||||
import com.tangem.domain.walletconnect.model.WcSolanaMethod
|
||||
import com.tangem.domain.walletconnect.model.WcSolanaMethodName
|
||||
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
|
||||
|
|
@ -37,10 +42,16 @@ internal class WcSolanaNetwork(
|
|||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
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
|
||||
override suspend fun toUseCase(
|
||||
request: WcSdkSessionRequest,
|
||||
): Either<WcRequestError.HandleMethodError, WcMethodUseCase> {
|
||||
fun error(message: String) = WcRequestError.HandleMethodError(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}")
|
||||
val wallet = session.wallet
|
||||
val chainId = request.chainId.orEmpty()
|
||||
suspend fun anyExistNetwork() = networksConverter.mainOrAnyWalletNetworkForRequest(chainId, wallet)
|
||||
|
|
@ -56,7 +67,7 @@ internal class WcSolanaNetwork(
|
|||
val walletNetwork = networksConverter
|
||||
.findWalletNetworkForRequest(request, session, accountAddress)
|
||||
?: anyExistNetwork()
|
||||
?: return null
|
||||
?: return error("Failed to find walletNetwork for accountAddress $accountAddress")
|
||||
|
||||
val context = WcMethodUseCaseContext(
|
||||
session = session,
|
||||
|
|
@ -68,7 +79,7 @@ internal class WcSolanaNetwork(
|
|||
is WcSolanaMethod.SignMessage -> factories.messageSign.create(context, method)
|
||||
is WcSolanaMethod.SignTransaction -> factories.signTransaction.create(context, method)
|
||||
is WcSolanaMethod.SignAllTransaction -> factories.signAllTransaction.create(context, method)
|
||||
}
|
||||
}.right()
|
||||
}
|
||||
|
||||
internal class NamespaceConverter @Inject constructor(
|
||||
|
|
@ -101,23 +112,26 @@ internal class WcSolanaNetwork(
|
|||
}
|
||||
}
|
||||
|
||||
private fun WcSolanaMethodName.toMethod(request: WcSdkSessionRequest): WcSolanaMethod? {
|
||||
private fun WcSolanaMethodName.toMethod(request: WcSdkSessionRequest): Either<Throwable, WcSolanaMethod?> {
|
||||
val rawParams = request.request.params
|
||||
return when (this) {
|
||||
WcSolanaMethodName.SignMessage -> moshi.fromJson<WcSolanaSignMessageRequest>(rawParams)?.let { request ->
|
||||
val humanMsg = request.message.decodeBase58()?.toHexString().orEmpty()
|
||||
WcSolanaMethod.SignMessage(
|
||||
pubKey = request.publicKey,
|
||||
rawMessage = request.message,
|
||||
humanMsg = humanMsg,
|
||||
)
|
||||
}
|
||||
WcSolanaMethodName.SignMessage -> moshi.fromJson<WcSolanaSignMessageRequest>(rawParams)
|
||||
.getOrElse { return it.left() }
|
||||
?.let { request ->
|
||||
val humanMsg = request.message.decodeBase58()?.toHexString().orEmpty()
|
||||
WcSolanaMethod.SignMessage(
|
||||
pubKey = request.publicKey,
|
||||
rawMessage = request.message,
|
||||
humanMsg = humanMsg,
|
||||
)
|
||||
}
|
||||
WcSolanaMethodName.SignTransaction -> moshi.fromJson<WcSolanaSignTransactionRequest>(rawParams)
|
||||
.getOrElse { return it.left() }
|
||||
?.let { request -> WcSolanaMethod.SignTransaction(request.transaction, request.feePayer) }
|
||||
WcSolanaMethodName.SendAllTransaction -> moshi.fromJson<List<String>>(rawParams)?.let { list ->
|
||||
WcSolanaMethod.SignAllTransaction(list)
|
||||
}
|
||||
}
|
||||
WcSolanaMethodName.SendAllTransaction -> moshi.fromJson<List<String>>(rawParams)
|
||||
.getOrElse { return it.left() }
|
||||
?.let { list -> WcSolanaMethod.SignAllTransaction(list) }
|
||||
}.right()
|
||||
}
|
||||
|
||||
internal class Factories @Inject constructor(
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@ package com.tangem.data.walletconnect.request
|
|||
import arrow.core.Either
|
||||
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.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
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultWcRequestUseCaseFactory @Inject constructor(
|
||||
|
|
@ -17,7 +20,17 @@ internal class DefaultWcRequestUseCaseFactory @Inject constructor(
|
|||
override suspend fun <T : WcMethodUseCase> createUseCase(
|
||||
request: WcSdkSessionRequest,
|
||||
): Either<WcMethod.Unsupported, T> {
|
||||
val useCase = requestConverters.firstNotNullOfOrNull { converter -> converter.toUseCase(request) }
|
||||
return (useCase as? T)?.let { useCase.right() } ?: WcMethod.Unsupported(request).left()
|
||||
val useCase = requestConverters
|
||||
.find { it.toWcMethodName(request) != null }
|
||||
?.toUseCase(request)
|
||||
?: WcRequestError.HandleMethodError("Failed to create WcUseCase").left()
|
||||
|
||||
return useCase.fold(
|
||||
ifLeft = {
|
||||
Timber.tag(WC_TAG).e("$it")
|
||||
WcMethod.Unsupported(request).left()
|
||||
},
|
||||
ifRight = { (it as? T)?.right() ?: WcMethod.Unsupported(request).left() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,27 @@
|
|||
package com.tangem.data.walletconnect.request
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapter
|
||||
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): WcMethodUseCase?
|
||||
suspend fun toUseCase(request: WcSdkSessionRequest): Either<WcRequestError.HandleMethodError, WcMethodUseCase>
|
||||
|
||||
companion object {
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified T> Moshi.fromJson(params: String): T? =
|
||||
runCatching { this.adapter<T>().fromJson(params) }.getOrNull()
|
||||
inline fun <reified T> Moshi.fromJson(params: String): Either<Throwable, T?> {
|
||||
return runCatching { this.adapter<T>().fromJson(params) }
|
||||
.fold(
|
||||
onSuccess = { it.right() },
|
||||
onFailure = { it.left() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,13 +26,18 @@ internal class DefaultWcRespondService : WcRespondService {
|
|||
),
|
||||
onSuccess = {
|
||||
if (continuation.isCompleted) return@respondSessionRequest
|
||||
Timber.tag(WC_TAG).i("Successful respond for request $request")
|
||||
val result = when (val response = it.jsonRpcResponse) {
|
||||
is Wallet.Model.JsonRpcResponse.JsonRpcError -> WcRequestError.WcRespondError(
|
||||
code = response.code,
|
||||
message = response.message,
|
||||
).left()
|
||||
is Wallet.Model.JsonRpcResponse.JsonRpcResult -> response.result.right()
|
||||
is Wallet.Model.JsonRpcResponse.JsonRpcError -> {
|
||||
Timber.tag(WC_TAG).e("Failed respond $response for request $request")
|
||||
WcRequestError.WcRespondError(
|
||||
code = response.code,
|
||||
message = response.message,
|
||||
).left()
|
||||
}
|
||||
is Wallet.Model.JsonRpcResponse.JsonRpcResult -> {
|
||||
Timber.tag(WC_TAG).i("Successful respond $response for request $request")
|
||||
response.result.right()
|
||||
}
|
||||
}
|
||||
continuation.resume(result)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ sealed interface WcEthMethod : WcMethod {
|
|||
val account: String,
|
||||
val dataForSign: String,
|
||||
) : WcEthMethod {
|
||||
val humanMsg: String = params.message.contents
|
||||
val humanMsg: String = params.message.contents.orEmpty()
|
||||
}
|
||||
|
||||
data class SendTransaction(
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ data class WcEthSignTypedDataParams(
|
|||
@Json(name = "primaryType")
|
||||
val primaryType: String,
|
||||
@Json(name = "types")
|
||||
val types: Types,
|
||||
val types: Map<String, List<Types.Type>>,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Domain(
|
||||
|
|
@ -29,11 +29,11 @@ data class WcEthSignTypedDataParams(
|
|||
@JsonClass(generateAdapter = true)
|
||||
data class Message(
|
||||
@Json(name = "contents")
|
||||
val contents: String,
|
||||
val contents: String?,
|
||||
@Json(name = "from")
|
||||
val from: Address,
|
||||
val from: Address?,
|
||||
@Json(name = "to")
|
||||
val to: Address,
|
||||
val to: Address?,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Address(
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ sealed class WcRequestError {
|
|||
val message: String,
|
||||
) : WcRequestError()
|
||||
|
||||
data class HandleMethodError(
|
||||
val message: String,
|
||||
) : WcRequestError()
|
||||
|
||||
data class UnknownError(val ex: Throwable? = null) : WcRequestError()
|
||||
|
||||
companion object {
|
||||
|
|
@ -21,10 +25,13 @@ sealed class WcRequestError {
|
|||
is UnknownError -> ex?.message
|
||||
is WcRespondError -> message
|
||||
is WrappedSendError -> sendTransactionError.message()
|
||||
is HandleMethodError -> message
|
||||
}
|
||||
|
||||
fun WcRequestError.code(): String? = when (this) {
|
||||
is UnknownError -> null
|
||||
is HandleMethodError,
|
||||
is UnknownError,
|
||||
-> null
|
||||
is WcRespondError -> this.code.toString()
|
||||
is WrappedSendError -> sendTransactionError.code()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue