Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-17 21:50:36 +07:00
parent 887ea85b3b
commit e6de882b3b
16 changed files with 273 additions and 62 deletions

View file

@ -15,6 +15,7 @@ dependencies {
implementation(projects.domain.walletConnect)
implementation(projects.domain.walletConnect.models)
implementation(projects.domain.transaction)
implementation(projects.domain.transaction.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
implementation(projects.domain.tokens)

View file

@ -131,10 +131,12 @@ internal object WalletConnectDataModule {
@SdkMoshi moshi: Moshi,
excludedBlockchains: ExcludedBlockchains,
sessionsManager: WcSessionsManager,
factories: WcEthNetwork.Factories,
): WcEthNetwork = WcEthNetwork(
moshi = moshi,
excludedBlockchains = excludedBlockchains,
sessionsManager = sessionsManager,
factories = factories,
)
@Provides

View file

@ -14,23 +14,26 @@ import com.tangem.data.walletconnect.sign.SignStateConverter.toResult
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
import com.tangem.domain.transaction.usecase.SignUseCase
import com.tangem.domain.walletconnect.model.WcEthMethod
import com.tangem.domain.walletconnect.usecase.ethereum.WcPersonalEthSignUseCase
import com.tangem.domain.walletconnect.usecase.ethereum.WcEthMessageSignUseCase
import com.tangem.domain.walletconnect.usecase.sign.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 DefaultWcPersonalEthSignUseCase(
internal class DefaultWcEthMessageSignUseCase @AssistedInject constructor(
override val respondService: WcRespondService,
override val context: WcMethodUseCaseContext,
private val method: WcEthMethod.PersonalEthSign,
@Assisted override val context: WcMethodUseCaseContext,
@Assisted private val method: WcEthMethod.MessageSign,
private val walletManagersFacade: WalletManagersFacade,
private val signUseCase: SignUseCase,
) : BaseWcSignUseCase<Nothing, WcPersonalEthSignUseCase.SignModel>(),
WcPersonalEthSignUseCase {
) : BaseWcSignUseCase<Nothing, WcEthMessageSignUseCase.SignModel>(),
WcEthMessageSignUseCase {
override val onSign: OnSign<WcPersonalEthSignUseCase.SignModel> = collector@{ state ->
override val onSign: OnSign<WcEthMessageSignUseCase.SignModel> = collector@{ state ->
val hashToSign = LegacySdkHelper.createMessageData(state.signModel.rawMsg)
val userWallet = session.wallet
val walletManager = walletManagersFacade.getOrCreateWalletManager(userWallet.walletId, network)
@ -50,14 +53,19 @@ internal class DefaultWcPersonalEthSignUseCase(
emit(state.toResult(wcRespondResult))
}
override fun invoke(): Flow<WcSignState<WcPersonalEthSignUseCase.SignModel>> = flow {
val model = WcPersonalEthSignUseCase.SignModel(
override fun invoke(): Flow<WcSignState<WcEthMessageSignUseCase.SignModel>> = flow {
val model = WcEthMessageSignUseCase.SignModel(
rawMsg = method.message,
account = method.account,
humanMsg = LegacySdkHelper.hexToAscii(method.message).orEmpty(),
)
emitAll(delegate(model))
}
@AssistedFactory
interface Factory {
fun create(context: WcMethodUseCaseContext, method: WcEthMethod.MessageSign): DefaultWcEthMessageSignUseCase
}
}
object LegacySdkHelper {

View file

@ -0,0 +1,57 @@
package com.tangem.data.walletconnect.network.ethereum
import arrow.core.left
import com.tangem.blockchain.common.HEX_PREFIX
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.data.walletconnect.respond.WcRespondService
import com.tangem.data.walletconnect.sign.BaseWcSignUseCase
import com.tangem.data.walletconnect.sign.OnSign
import com.tangem.data.walletconnect.sign.SignStateConverter.toResult
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.walletconnect.model.WcEthMethod
import com.tangem.domain.walletconnect.usecase.ethereum.WcEthSendTransactionUseCase
import com.tangem.domain.walletconnect.usecase.sign.WcSignState
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
internal class DefaultWcEthSendTransactionUseCase @AssistedInject constructor(
@Assisted override val context: WcMethodUseCaseContext,
@Assisted private val method: WcEthMethod.SendTransaction,
override val respondService: WcRespondService,
private val sendTransaction: SendTransactionUseCase,
) : BaseWcSignUseCase<Nothing, TransactionData>(),
WcEthSendTransactionUseCase {
override val onSign: OnSign<TransactionData> = collector@{ state ->
val hash = sendTransaction(state.signModel, wallet, network)
.onLeft { error ->
val sendError = IllegalArgumentException(error.toString()) // todo(wc) use domain error
emit(state.toResult(sendError.left()))
}
.getOrNull() ?: return@collector
val respondHash = if (hash.startsWith(HEX_PREFIX)) hash else HEX_PREFIX + hash
val respondResult = respondService.respond(rawSdkRequest, respondHash)
emit(state.toResult(respondResult))
}
override fun updateFee(fee: TransactionFee) {
TODO("Not yet implemented")
}
override fun invoke(): Flow<WcSignState<TransactionData>> {
method
TODO("Not yet implemented")
}
@AssistedFactory
interface Factory {
fun create(
context: WcMethodUseCaseContext,
method: WcEthMethod.SendTransaction,
): DefaultWcEthSendTransactionUseCase
}
}

View file

@ -0,0 +1,58 @@
package com.tangem.data.walletconnect.network.ethereum
import arrow.core.left
import com.tangem.blockchain.common.HEX_PREFIX
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.extensions.toHexString
import com.tangem.data.walletconnect.respond.WcRespondService
import com.tangem.data.walletconnect.sign.BaseWcSignUseCase
import com.tangem.data.walletconnect.sign.OnSign
import com.tangem.data.walletconnect.sign.SignStateConverter.toResult
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
import com.tangem.domain.transaction.usecase.PrepareForSendUseCase
import com.tangem.domain.walletconnect.model.WcEthMethod
import com.tangem.domain.walletconnect.usecase.ethereum.WcEthSignTransactionUseCase
import com.tangem.domain.walletconnect.usecase.sign.WcSignState
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
internal class DefaultWcEthSignTransactionUseCase @AssistedInject constructor(
override val respondService: WcRespondService,
private val prepareForSend: PrepareForSendUseCase,
@Assisted override val context: WcMethodUseCaseContext,
@Assisted private val method: WcEthMethod.SignTransaction,
) : BaseWcSignUseCase<Nothing, TransactionData>(),
WcEthSignTransactionUseCase {
override val onSign: OnSign<TransactionData> = collector@{ state ->
val hash = prepareForSend(state.signModel, wallet, network)
.onLeft { error ->
emit(state.toResult(error.left()))
}
.getOrNull() ?: return@collector
val hashString = hash.toHexString()
val respondHash = if (hashString.startsWith(HEX_PREFIX)) hashString else HEX_PREFIX + hashString
val respondResult = respondService.respond(rawSdkRequest, respondHash)
emit(state.toResult(respondResult))
}
override fun updateFee(fee: TransactionFee) {
TODO("Not yet implemented")
}
override fun invoke(): Flow<WcSignState<TransactionData>> {
method
TODO("Not yet implemented")
}
@AssistedFactory
interface Factory {
fun create(
context: WcMethodUseCaseContext,
method: WcEthMethod.SignTransaction,
): DefaultWcEthSignTransactionUseCase
}
}

View file

@ -7,18 +7,22 @@ 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
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
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.WcEthTransactionParams
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.domain.walletconnect.repository.WcSessionsManager
import com.tangem.domain.walletconnect.usecase.WcMethodUseCase
import com.tangem.domain.wallets.models.UserWallet
import jakarta.inject.Inject
internal class WcEthNetwork constructor(
private val moshi: Moshi,
private val excludedBlockchains: ExcludedBlockchains,
private val sessionsManager: WcSessionsManager,
private val factories: Factories,
) : WcRequestToUseCaseConverter, WcNamespaceConverter {
override val namespaceKey: NamespaceKey = NamespaceKey("eip155")
@ -29,8 +33,12 @@ internal class WcEthNetwork constructor(
val method: WcEthMethod = name.toMethod(request) ?: return null
val session = sessionsManager.findSessionByTopic(request.topic) ?: return null
val network = toNetwork(request.chainId.orEmpty(), session.wallet) ?: return null
val context = WcMethodUseCaseContext(session = session, rawSdkRequest = request, network = network)
return when (method) {
is WcEthMethod.PersonalEthSign -> TODO()
is WcEthMethod.MessageSign -> factories.messageSign.create(context, method)
is WcEthMethod.SendTransaction -> factories.sendTransaction.create(context, method)
is WcEthMethod.SignTransaction -> factories.signTransaction.create(context, method)
}
}
@ -44,12 +52,21 @@ internal class WcEthNetwork constructor(
val messageIndex = if (this == Name.EthSign) 1 else 0
val account = list.getOrNull(accountIndex) ?: return@let null
val message = list.getOrNull(messageIndex) ?: return@let null
WcEthMethod.PersonalEthSign(account = account, message = message)
WcEthMethod.MessageSign(account = account, message = message)
}
Name.SignTypeData -> TODO()
Name.SignTypeDataV4 -> TODO()
Name.SignTransaction -> TODO()
Name.SendTransaction -> TODO()
Name.SignTransaction,
Name.SendTransaction,
-> moshi.fromJson<List<WcEthTransactionParams>>(rawParams)
?.firstOrNull()
?.let {
if (this == Name.SignTransaction) {
WcEthMethod.SignTransaction(transaction = it)
} else {
WcEthMethod.SendTransaction(transaction = it)
}
}
}
}
@ -81,4 +98,10 @@ internal class WcEthNetwork constructor(
reference = chainId.toString(),
)
}
internal class Factories @Inject constructor(
val messageSign: DefaultWcEthMessageSignUseCase.Factory,
val sendTransaction: DefaultWcEthSendTransactionUseCase.Factory,
val signTransaction: DefaultWcEthSignTransactionUseCase.Factory,
)
}

View file

@ -1,17 +1,18 @@
package com.tangem.data.walletconnect.sign
import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.data.walletconnect.respond.WcRespondService
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.domain.walletconnect.usecase.WcMethodUseCase
import com.tangem.domain.walletconnect.usecase.sign.WcSignState
import com.tangem.domain.walletconnect.usecase.sign.WcSignUseCase
import com.tangem.domain.wallets.models.UserWallet
import kotlinx.coroutines.flow.FlowCollector
internal abstract class BaseWcSignUseCase<MiddleAction, SignModel> :
WcMethodUseCase,
WcSignUseCase.FinalAction,
WcSignUseCase,
FinalActionCollector<SignModel>,
MiddleActionCollector<MiddleAction, SignModel> {
@ -21,6 +22,7 @@ internal abstract class BaseWcSignUseCase<MiddleAction, SignModel> :
override val network: Network get() = context.network
override val session: WcSession get() = context.session
override val rawSdkRequest: WcSdkSessionRequest get() = context.rawSdkRequest
val wallet: UserWallet get() = session.wallet
protected val delegate by lazy {
WcSignUseCaseDelegate(