Updated on 2026-08-14
This commit is contained in:
parent
3f0e976558
commit
8619b424f5
5 changed files with 179 additions and 16 deletions
|
|
@ -4,16 +4,18 @@ import arrow.core.left
|
|||
import com.tangem.blockchain.blockchains.ethereum.EthereumUtils.toKeccak
|
||||
import com.tangem.blockchain.common.HEX_PREFIX
|
||||
import com.tangem.blockchain.common.UnmarshalHelper
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.extensions.isAscii
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toDecompressedPublicKey
|
||||
import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.data.walletconnect.network.ethereum.LegacySdkHelper.prepareToSendMessageData
|
||||
import com.tangem.data.walletconnect.respond.WcRespondService
|
||||
import com.tangem.data.walletconnect.sign.BaseWcSignUseCase
|
||||
import com.tangem.data.walletconnect.sign.SignCollector
|
||||
import com.tangem.data.walletconnect.sign.SignStateConverter.toResult
|
||||
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
|
||||
import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate
|
||||
import com.tangem.domain.transaction.usecase.SignUseCase
|
||||
import com.tangem.domain.walletconnect.model.WcEthMethod
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase
|
||||
|
|
@ -56,11 +58,7 @@ internal class WcEthMessageSignUseCase @AssistedInject constructor(
|
|||
.onLeft { emit(state.toResult(it.left())) }
|
||||
.getOrNull() ?: return
|
||||
|
||||
val respond = UnmarshalHelper.unmarshalSignatureExtended(
|
||||
signature = signedHash,
|
||||
hash = hashToSign,
|
||||
publicKey = walletManager.wallet.publicKey.blockchainKey.toDecompressedPublicKey(),
|
||||
).asRSVLegacyEVM().toHexString()
|
||||
val respond = prepareToSendMessageData(signedHash, hashToSign, walletManager)
|
||||
|
||||
val wcRespondResult = respondService.respond(rawSdkRequest, respond)
|
||||
emit(state.toResult(wcRespondResult))
|
||||
|
|
@ -80,6 +78,16 @@ internal class WcEthMessageSignUseCase @AssistedInject constructor(
|
|||
object LegacySdkHelper {
|
||||
private const val ETH_MESSAGE_PREFIX = "\u0019Ethereum Signed Message:\n"
|
||||
|
||||
internal fun prepareToSendMessageData(
|
||||
signedHash: ByteArray,
|
||||
hashToSign: ByteArray,
|
||||
walletManager: WalletManager,
|
||||
): String = UnmarshalHelper.unmarshalSignatureExtended(
|
||||
signature = signedHash,
|
||||
hash = hashToSign,
|
||||
publicKey = walletManager.wallet.publicKey.blockchainKey.toDecompressedPublicKey(),
|
||||
).asRSVLegacyEVM().toHexString()
|
||||
|
||||
fun createMessageData(message: String): ByteArray {
|
||||
val messageData = try {
|
||||
message.removePrefix(HEX_PREFIX).hexToBytes()
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ 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.WcEthSignTypedDataParams
|
||||
import com.tangem.domain.walletconnect.model.WcEthTransactionParams
|
||||
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
|
||||
import com.tangem.domain.walletconnect.repository.WcSessionsManager
|
||||
|
|
@ -43,6 +44,7 @@ internal class WcEthNetwork(
|
|||
is WcEthMethod.MessageSign -> method.account
|
||||
is WcEthMethod.SendTransaction -> method.transaction.from
|
||||
is WcEthMethod.SignTransaction -> method.transaction.from
|
||||
is WcEthMethod.SignTypedData -> method.account
|
||||
}
|
||||
val context = WcMethodUseCaseContext(
|
||||
session = session,
|
||||
|
|
@ -54,6 +56,7 @@ internal class WcEthNetwork(
|
|||
is WcEthMethod.MessageSign -> factories.messageSign.create(context, method)
|
||||
is WcEthMethod.SendTransaction -> factories.sendTransaction.create(context, method)
|
||||
is WcEthMethod.SignTransaction -> factories.signTransaction.create(context, method)
|
||||
is WcEthMethod.SignTypedData -> factories.signTypedData.create(context, method)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,16 +65,10 @@ internal class WcEthNetwork(
|
|||
return when (this) {
|
||||
WcEthMethodName.EthSign,
|
||||
WcEthMethodName.PersonalSign,
|
||||
-> moshi.fromJson<List<String>>(rawParams)?.let { list ->
|
||||
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
|
||||
val humanMsg = LegacySdkHelper.hexToAscii(message).orEmpty()
|
||||
WcEthMethod.MessageSign(account = account, rawMessage = message, humanMsg = humanMsg)
|
||||
}
|
||||
WcEthMethodName.SignTypeData -> TODO()
|
||||
WcEthMethodName.SignTypeDataV4 -> TODO()
|
||||
-> parseMessageSign(rawParams)
|
||||
WcEthMethodName.SignTypeData,
|
||||
WcEthMethodName.SignTypeDataV4,
|
||||
-> parseTypeData(rawParams)
|
||||
WcEthMethodName.SignTransaction,
|
||||
WcEthMethodName.SendTransaction,
|
||||
-> moshi.fromJson<List<WcEthTransactionParams>>(rawParams)
|
||||
|
|
@ -86,6 +83,23 @@ internal class WcEthNetwork(
|
|||
}
|
||||
}
|
||||
|
||||
private fun WcEthMethodName.parseMessageSign(rawParams: String): WcEthMethod.MessageSign? {
|
||||
val list = moshi.fromJson<List<String>>(rawParams) ?: return null
|
||||
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 humanMsg = LegacySdkHelper.hexToAscii(message).orEmpty()
|
||||
return WcEthMethod.MessageSign(account = account, rawMessage = message, humanMsg = humanMsg)
|
||||
}
|
||||
|
||||
private fun parseTypeData(params: String): 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 params = moshi.fromJson<WcEthSignTypedDataParams>(data) ?: return null
|
||||
return WcEthMethod.SignTypedData(params = params, account = account, dataForSign = data)
|
||||
}
|
||||
|
||||
override fun toNetwork(chainId: String, wallet: UserWallet): Network? {
|
||||
return toNetwork(chainId, wallet, excludedBlockchains)
|
||||
}
|
||||
|
|
@ -108,6 +122,7 @@ internal class WcEthNetwork(
|
|||
|
||||
internal class Factories @Inject constructor(
|
||||
val messageSign: WcEthMessageSignUseCase.Factory,
|
||||
val signTypedData: WcEthSignTypedDataUseCase.Factory,
|
||||
val sendTransaction: WcEthSendTransactionUseCase.Factory,
|
||||
val signTransaction: WcEthSignTransactionUseCase.Factory,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
package com.tangem.data.walletconnect.network.ethereum
|
||||
|
||||
import arrow.core.left
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumUtils
|
||||
import com.tangem.data.walletconnect.network.ethereum.LegacySdkHelper.prepareToSendMessageData
|
||||
import com.tangem.data.walletconnect.respond.WcRespondService
|
||||
import com.tangem.data.walletconnect.sign.BaseWcSignUseCase
|
||||
import com.tangem.data.walletconnect.sign.SignCollector
|
||||
import com.tangem.data.walletconnect.sign.SignStateConverter.toResult
|
||||
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
|
||||
import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate
|
||||
import com.tangem.domain.transaction.usecase.SignUseCase
|
||||
import com.tangem.domain.walletconnect.model.WcEthMethod
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcSignState
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
internal class WcEthSignTypedDataUseCase @AssistedInject constructor(
|
||||
override val respondService: WcRespondService,
|
||||
@Assisted override val context: WcMethodUseCaseContext,
|
||||
@Assisted override val method: WcEthMethod.SignTypedData,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val signUseCase: SignUseCase,
|
||||
blockAidDelegate: BlockAidVerificationDelegate,
|
||||
) : BaseWcSignUseCase<Nothing, WcMessageSignUseCase.SignModel>(),
|
||||
WcMessageSignUseCase {
|
||||
|
||||
override val securityStatus = blockAidDelegate.getSecurityStatus(
|
||||
network = network,
|
||||
method = method,
|
||||
rawSdkRequest = rawSdkRequest,
|
||||
session = session,
|
||||
accountAddress = context.accountAddress,
|
||||
)
|
||||
|
||||
override suspend fun SignCollector<WcMessageSignUseCase.SignModel>.onSign(
|
||||
state: WcSignState<WcMessageSignUseCase.SignModel>,
|
||||
) {
|
||||
val hashToSign = EthereumUtils.makeTypedDataHash(method.dataForSign)
|
||||
val userWallet = session.wallet
|
||||
val walletManager = walletManagersFacade.getOrCreateWalletManager(userWallet.walletId, network)
|
||||
?: return
|
||||
|
||||
val signedHash = signUseCase(hashToSign, userWallet, network)
|
||||
.onLeft { emit(state.toResult(it.left())) }
|
||||
.getOrNull() ?: return
|
||||
|
||||
val respond = prepareToSendMessageData(signedHash, hashToSign, walletManager)
|
||||
val wcRespondResult = respondService.respond(rawSdkRequest, respond)
|
||||
emit(state.toResult(wcRespondResult))
|
||||
}
|
||||
|
||||
override fun invoke(): Flow<WcSignState<WcMessageSignUseCase.SignModel>> = flow {
|
||||
val model = WcMessageSignUseCase.SignModel(humanMsg = method.humanMsg)
|
||||
emitAll(delegate(model))
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(context: WcMethodUseCaseContext, method: WcEthMethod.SignTypedData): WcEthSignTypedDataUseCase
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,14 @@ sealed interface WcEthMethod : WcMethod {
|
|||
val humanMsg: String,
|
||||
) : WcEthMethod
|
||||
|
||||
data class SignTypedData(
|
||||
val params: WcEthSignTypedDataParams,
|
||||
val account: String,
|
||||
val dataForSign: String,
|
||||
) : WcEthMethod {
|
||||
val humanMsg: String = params.message.contents
|
||||
}
|
||||
|
||||
data class SendTransaction(
|
||||
val transaction: WcEthTransactionParams,
|
||||
) : WcEthMethod
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
package com.tangem.domain.walletconnect.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class WcEthSignTypedDataParams(
|
||||
@Json(name = "domain")
|
||||
val domain: Domain,
|
||||
@Json(name = "message")
|
||||
val message: Message,
|
||||
@Json(name = "primaryType")
|
||||
val primaryType: String,
|
||||
@Json(name = "types")
|
||||
val types: Types,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Domain(
|
||||
@Json(name = "chainId")
|
||||
val chainId: Int,
|
||||
@Json(name = "name")
|
||||
val name: String,
|
||||
@Json(name = "verifyingContract")
|
||||
val verifyingContract: String,
|
||||
@Json(name = "version")
|
||||
val version: String,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Message(
|
||||
@Json(name = "contents")
|
||||
val contents: String,
|
||||
@Json(name = "from")
|
||||
val from: Address,
|
||||
@Json(name = "to")
|
||||
val to: Address,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Address(
|
||||
@Json(name = "name")
|
||||
val name: String,
|
||||
@Json(name = "wallet")
|
||||
val wallet: String,
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Types(
|
||||
@Json(name = "EIP712Domain")
|
||||
val eIP712Domain: List<Type> = listOf(),
|
||||
@Json(name = "Mail")
|
||||
val mail: List<Type> = listOf(),
|
||||
@Json(name = "Person")
|
||||
val person: List<Type> = listOf(),
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Type(
|
||||
@Json(name = "name")
|
||||
val name: String,
|
||||
@Json(name = "type")
|
||||
val type: String,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue