Updated on 2026-08-14
This commit is contained in:
parent
f49f013aa2
commit
37dc463808
14 changed files with 157 additions and 64 deletions
|
|
@ -52,6 +52,7 @@ dependencies {
|
|||
implementation(deps.arrow.core)
|
||||
implementation(projects.domain.blockaid)
|
||||
implementation(projects.domain.blockaid.models)
|
||||
implementation(projects.libs.crypto)
|
||||
|
||||
/* Tests */
|
||||
testImplementation(projects.common.test)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase
|
|||
import com.tangem.domain.walletconnect.usecase.pair.WcPairUseCase
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.lib.crypto.UserWalletManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -146,11 +147,13 @@ internal object WalletConnectDataModule {
|
|||
excludedBlockchains: ExcludedBlockchains,
|
||||
sessionsManager: WcSessionsManager,
|
||||
factories: WcSolanaNetwork.Factories,
|
||||
walletManager: UserWalletManager,
|
||||
): WcSolanaNetwork = WcSolanaNetwork(
|
||||
moshi = moshi,
|
||||
sessionsManager = sessionsManager,
|
||||
factories = factories,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
walletManager = walletManager,
|
||||
)
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
package com.tangem.data.walletconnect.network.ethereum
|
||||
|
||||
import com.domain.blockaid.models.transaction.CheckTransactionResult
|
||||
import com.domain.blockaid.models.transaction.TransactionData
|
||||
import com.domain.blockaid.models.transaction.TransactionParams
|
||||
import com.tangem.domain.blockaid.BlockAidVerifier
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.walletconnect.model.WcEthMethod
|
||||
import com.tangem.domain.walletconnect.model.WcSession
|
||||
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class BlockAidEthereumVerificationDelegate @Inject constructor(
|
||||
private val blockAidVerifier: BlockAidVerifier,
|
||||
) {
|
||||
|
||||
fun getSecurityStatus(
|
||||
network: Network,
|
||||
method: WcEthMethod,
|
||||
rawSdkRequest: WcSdkSessionRequest,
|
||||
session: WcSession,
|
||||
): Flow<Lce<Throwable, CheckTransactionResult>> = flow {
|
||||
emit(Lce.Loading(partialContent = null))
|
||||
blockAidVerifier.verifyTransaction(
|
||||
TransactionData(
|
||||
chain = network.name,
|
||||
accountAddress = when (method) {
|
||||
is WcEthMethod.MessageSign -> method.account
|
||||
is WcEthMethod.SendTransaction -> method.transaction.from
|
||||
is WcEthMethod.SignTransaction -> method.transaction.from
|
||||
},
|
||||
method = rawSdkRequest.request.method,
|
||||
domainUrl = session.sdkModel.appMetaData.url,
|
||||
params = TransactionParams.Evm(rawSdkRequest.request.params),
|
||||
),
|
||||
).fold(
|
||||
ifLeft = {
|
||||
Timber.e("Failed to verify transaction: ${it.localizedMessage}")
|
||||
emit(Lce.Error(it))
|
||||
},
|
||||
ifRight = {
|
||||
emit(Lce.Content(it))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import com.tangem.blockchain.common.HEX_PREFIX
|
|||
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.data.walletconnect.respond.WcRespondService
|
||||
import com.tangem.data.walletconnect.sign.BaseWcSignUseCase
|
||||
import com.tangem.data.walletconnect.sign.SignCollector
|
||||
|
|
@ -30,11 +31,17 @@ internal class DefaultWcEthMessageSignUseCase @AssistedInject constructor(
|
|||
@Assisted private val method: WcEthMethod.MessageSign,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val signUseCase: SignUseCase,
|
||||
blockAidDelegate: BlockAidEthereumVerificationDelegate,
|
||||
blockAidDelegate: BlockAidVerificationDelegate,
|
||||
) : BaseWcSignUseCase<Nothing, WcEthMessageSignUseCase.SignModel>(),
|
||||
WcEthMessageSignUseCase {
|
||||
|
||||
override val securityStatus = blockAidDelegate.getSecurityStatus(network, method, rawSdkRequest, session)
|
||||
override val securityStatus = blockAidDelegate.getSecurityStatus(
|
||||
network = network,
|
||||
method = method,
|
||||
rawSdkRequest = rawSdkRequest,
|
||||
session = session,
|
||||
accountAddress = context.accountAddress,
|
||||
)
|
||||
|
||||
override suspend fun SignCollector<WcEthMessageSignUseCase.SignModel>.onSign(
|
||||
state: WcSignState<WcEthMessageSignUseCase.SignModel>,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.data.walletconnect.network.ethereum
|
|||
import arrow.core.left
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.extensions.formatHex
|
||||
import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate
|
||||
import com.tangem.data.walletconnect.respond.WcRespondService
|
||||
import com.tangem.data.walletconnect.sign.BaseWcSignUseCase
|
||||
import com.tangem.data.walletconnect.sign.SignCollector
|
||||
|
|
@ -25,11 +26,17 @@ internal class DefaultWcEthSendTransactionUseCase @AssistedInject constructor(
|
|||
@Assisted private val method: WcEthMethod.SendTransaction,
|
||||
override val respondService: WcRespondService,
|
||||
private val sendTransaction: SendTransactionUseCase,
|
||||
blockAidDelegate: BlockAidEthereumVerificationDelegate,
|
||||
blockAidDelegate: BlockAidVerificationDelegate,
|
||||
) : BaseWcSignUseCase<Fee, WcEthTransaction>(),
|
||||
WcEthSendTransactionUseCase {
|
||||
|
||||
override val securityStatus = blockAidDelegate.getSecurityStatus(network, method, rawSdkRequest, session)
|
||||
override val securityStatus = blockAidDelegate.getSecurityStatus(
|
||||
network = network,
|
||||
method = method,
|
||||
rawSdkRequest = rawSdkRequest,
|
||||
session = session,
|
||||
accountAddress = context.accountAddress,
|
||||
)
|
||||
|
||||
private val converter = EthTransactionParamsConverter(context)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import arrow.core.left
|
|||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.extensions.formatHex
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate
|
||||
import com.tangem.data.walletconnect.respond.WcRespondService
|
||||
import com.tangem.data.walletconnect.sign.BaseWcSignUseCase
|
||||
import com.tangem.data.walletconnect.sign.SignCollector
|
||||
|
|
@ -27,11 +28,17 @@ internal class DefaultWcEthSignTransactionUseCase @AssistedInject constructor(
|
|||
private val prepareForSend: PrepareForSendUseCase,
|
||||
@Assisted override val context: WcMethodUseCaseContext,
|
||||
@Assisted private val method: WcEthMethod.SignTransaction,
|
||||
blockAidDelegate: BlockAidEthereumVerificationDelegate,
|
||||
blockAidDelegate: BlockAidVerificationDelegate,
|
||||
) : BaseWcSignUseCase<Fee, WcEthTransaction>(),
|
||||
WcEthSignTransactionUseCase {
|
||||
|
||||
override val securityStatus = blockAidDelegate.getSecurityStatus(network, method, rawSdkRequest, session)
|
||||
override val securityStatus = blockAidDelegate.getSecurityStatus(
|
||||
network = network,
|
||||
method = method,
|
||||
rawSdkRequest = rawSdkRequest,
|
||||
session = session,
|
||||
accountAddress = context.accountAddress,
|
||||
)
|
||||
|
||||
private val converter = EthTransactionParamsConverter(context)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,17 @@ internal class WcEthNetwork(
|
|||
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)
|
||||
val accountAddress = when (method) {
|
||||
is WcEthMethod.MessageSign -> method.account
|
||||
is WcEthMethod.SendTransaction -> method.transaction.from
|
||||
is WcEthMethod.SignTransaction -> method.transaction.from
|
||||
}
|
||||
val context = WcMethodUseCaseContext(
|
||||
session = session,
|
||||
rawSdkRequest = request,
|
||||
network = network,
|
||||
accountAddress = accountAddress,
|
||||
)
|
||||
return when (method) {
|
||||
is WcEthMethod.MessageSign -> factories.messageSign.create(context, method)
|
||||
is WcEthMethod.SendTransaction -> factories.sendTransaction.create(context, method)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ 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.PrepareForSendUseCase
|
||||
import com.tangem.domain.walletconnect.model.WcSolanaMethod
|
||||
import com.tangem.domain.walletconnect.usecase.sign.WcSignState
|
||||
|
|
@ -25,9 +26,18 @@ internal class DefaultWcSolanaSignAllTransactionUseCase @AssistedInject construc
|
|||
private val prepareForSend: PrepareForSendUseCase,
|
||||
@Assisted override val context: WcMethodUseCaseContext,
|
||||
@Assisted private val method: WcSolanaMethod.SignAllTransaction,
|
||||
blockAidDelegate: BlockAidVerificationDelegate,
|
||||
) : BaseWcSignUseCase<Nothing, List<TransactionData.Compiled>>(),
|
||||
WcSolanaSignAllTransactionUseCase {
|
||||
|
||||
override val securityStatus = blockAidDelegate.getSecurityStatus(
|
||||
network = network,
|
||||
method = method,
|
||||
rawSdkRequest = rawSdkRequest,
|
||||
session = session,
|
||||
accountAddress = context.accountAddress,
|
||||
)
|
||||
|
||||
override suspend fun SignCollector<List<TransactionData.Compiled>>.onSign(
|
||||
state: WcSignState<List<TransactionData.Compiled>>,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ 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.PrepareForSendUseCase
|
||||
import com.tangem.domain.walletconnect.model.WcSolanaMethod
|
||||
import com.tangem.domain.walletconnect.usecase.sign.WcSignState
|
||||
|
|
@ -23,9 +24,18 @@ internal class DefaultWcSolanaSignTransactionUseCase @AssistedInject constructor
|
|||
private val prepareForSend: PrepareForSendUseCase,
|
||||
@Assisted override val context: WcMethodUseCaseContext,
|
||||
@Assisted private val method: WcSolanaMethod.SignTransaction,
|
||||
blockAidDelegate: BlockAidVerificationDelegate,
|
||||
) : BaseWcSignUseCase<Nothing, TransactionData.Compiled>(),
|
||||
WcSolanaSignTransactionUseCase {
|
||||
|
||||
override val securityStatus = blockAidDelegate.getSecurityStatus(
|
||||
network = network,
|
||||
method = method,
|
||||
rawSdkRequest = rawSdkRequest,
|
||||
session = session,
|
||||
accountAddress = context.accountAddress,
|
||||
)
|
||||
|
||||
override suspend fun SignCollector<TransactionData.Compiled>.onSign(state: WcSignState<TransactionData.Compiled>) {
|
||||
val hash = prepareForSend.invoke(transactionData = state.signModel, userWallet = wallet, network = network)
|
||||
.onLeft { error ->
|
||||
|
|
|
|||
|
|
@ -16,13 +16,16 @@ 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 com.tangem.lib.crypto.UserWalletManager
|
||||
import jakarta.inject.Inject
|
||||
import timber.log.Timber
|
||||
|
||||
internal class WcSolanaNetwork constructor(
|
||||
internal class WcSolanaNetwork(
|
||||
private val moshi: Moshi,
|
||||
private val sessionsManager: WcSessionsManager,
|
||||
private val factories: Factories,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val walletManager: UserWalletManager,
|
||||
) : WcNamespaceConverter, WcRequestToUseCaseConverter {
|
||||
|
||||
override val namespaceKey: NamespaceKey = NamespaceKey("solana")
|
||||
|
|
@ -38,7 +41,13 @@ internal class WcSolanaNetwork constructor(
|
|||
val method: WcSolanaMethod = 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)
|
||||
val accountAddress = getAccountAddress(network)
|
||||
val context = WcMethodUseCaseContext(
|
||||
session = session,
|
||||
rawSdkRequest = request,
|
||||
network = network,
|
||||
accountAddress = accountAddress,
|
||||
)
|
||||
return when (method) {
|
||||
is WcSolanaMethod.SignMessage -> TODO()
|
||||
is WcSolanaMethod.SignTransaction -> factories.signTransaction.create(context, method)
|
||||
|
|
@ -46,6 +55,15 @@ internal class WcSolanaNetwork constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getAccountAddress(network: Network): String? {
|
||||
return try {
|
||||
walletManager.getWalletAddress(network.id.value, network.derivationPath.value)
|
||||
} catch (exception: Exception) {
|
||||
Timber.e(exception)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun toBlockchain(chainId: CAIP2): Blockchain? {
|
||||
if (chainId.namespace != namespaceKey.key) return null
|
||||
return when (chainId.reference) {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ internal class WcMethodUseCaseContext(
|
|||
val session: WcSession,
|
||||
val rawSdkRequest: WcSdkSessionRequest,
|
||||
val network: Network,
|
||||
val accountAddress: String?,
|
||||
)
|
||||
|
||||
internal typealias SignCollector<SignModel> = FlowCollector<WcSignState<SignModel>>
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.tangem.data.walletconnect.utils
|
||||
|
||||
import com.domain.blockaid.models.transaction.*
|
||||
import com.tangem.domain.blockaid.BlockAidVerifier
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.walletconnect.model.WcEthMethod
|
||||
import com.tangem.domain.walletconnect.model.WcMethod
|
||||
import com.tangem.domain.walletconnect.model.WcSession
|
||||
import com.tangem.domain.walletconnect.model.WcSolanaMethod
|
||||
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class BlockAidVerificationDelegate @Inject constructor(
|
||||
private val blockAidVerifier: BlockAidVerifier,
|
||||
) {
|
||||
|
||||
fun getSecurityStatus(
|
||||
network: Network,
|
||||
method: WcMethod,
|
||||
rawSdkRequest: WcSdkSessionRequest,
|
||||
session: WcSession,
|
||||
accountAddress: String?,
|
||||
): LceFlow<Throwable, CheckTransactionResult> = flow {
|
||||
emit(Lce.Loading(partialContent = null))
|
||||
val failedResult = CheckTransactionResult(
|
||||
validation = ValidationResult.FAILED_TO_VALIDATE,
|
||||
simulation = SimulationResult.FailedToSimulate,
|
||||
)
|
||||
if (accountAddress.isNullOrEmpty()) {
|
||||
emit(Lce.Content(failedResult))
|
||||
return@flow
|
||||
}
|
||||
when (method) {
|
||||
is WcEthMethod -> TransactionParams.Evm(rawSdkRequest.request.params)
|
||||
is WcSolanaMethod.SignAllTransaction -> TransactionParams.Solana(method.transaction)
|
||||
is WcSolanaMethod.SignTransaction -> TransactionParams.Solana(listOf(method.transaction))
|
||||
is WcSolanaMethod.SignMessage -> {
|
||||
// BlockAid doesn't support solana_signMessage
|
||||
emit(Lce.Content(failedResult))
|
||||
return@flow
|
||||
}
|
||||
else -> null
|
||||
}?.let { params ->
|
||||
blockAidVerifier.verifyTransaction(
|
||||
TransactionData(
|
||||
chain = network.name,
|
||||
accountAddress = accountAddress,
|
||||
method = rawSdkRequest.request.method,
|
||||
domainUrl = session.sdkModel.appMetaData.url,
|
||||
params = params,
|
||||
),
|
||||
).fold(
|
||||
ifLeft = {
|
||||
Timber.e("Failed to verify transaction: ${it.localizedMessage}")
|
||||
emit(Lce.Error(it))
|
||||
},
|
||||
ifRight = {
|
||||
emit(Lce.Content(it))
|
||||
},
|
||||
)
|
||||
} ?: emit(Lce.Content(failedResult))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue