Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-24 13:02:08 +04:00
parent b0649c93df
commit 632b0dd663
13 changed files with 86 additions and 13 deletions

View file

@ -15,7 +15,7 @@ private const val SUCCESS_STATUS = "Success"
private const val DOMAIN_CHECKED_STATUS = "hit"
private const val VALIDATION_SAFE_STATUS = "Benign"
internal class BlockAidMapper {
internal object BlockAidMapper {
fun mapToDomain(from: DomainScanResponse): CheckDAppResult {
return when {

View file

@ -12,19 +12,19 @@ import kotlinx.coroutines.withContext
internal class DefaultBlockAidRepository(
private val api: BlockAidApi,
private val dispatcherProvider: CoroutineDispatcherProvider,
private val dispatchers: CoroutineDispatcherProvider,
private val mapper: BlockAidMapper,
) : BlockAidRepository {
override suspend fun verifyDAppDomain(data: DAppData): CheckDAppResult {
val response = withContext(dispatcherProvider.io) {
val response = withContext(dispatchers.io) {
api.scanDomain(DomainScanRequest(data.url))
}
return mapper.mapToDomain(response)
}
override suspend fun verifyTransaction(data: TransactionData): CheckTransactionResult {
val response = withContext(dispatcherProvider.io) {
val response = withContext(dispatchers.io) {
when (data.params) {
is TransactionParams.Evm -> {
api.scanJsonRpc(mapper.mapToEvmRequest(data))

View file

@ -17,11 +17,11 @@ internal object BlockAidDataInternalModule {
@Provides
@Singleton
fun provideRepository(api: BlockAidApi, dispatcherProvider: CoroutineDispatcherProvider): BlockAidRepository {
fun provideRepository(api: BlockAidApi, dispatchers: CoroutineDispatcherProvider): BlockAidRepository {
return DefaultBlockAidRepository(
api = api,
dispatcherProvider = dispatcherProvider,
mapper = BlockAidMapper(),
dispatchers = dispatchers,
mapper = BlockAidMapper,
)
}
}

View file

@ -11,7 +11,7 @@ import java.math.BigDecimal
class BlockAidMapperTest {
private val mapper = BlockAidMapper()
private val mapper = BlockAidMapper
@Test
fun `when status hit and is malicious false then map to domain returns safe`() {

View file

@ -29,12 +29,12 @@ class DefaultBlockAidRepositoryTest {
private lateinit var repository: DefaultBlockAidRepository
private val testDispatcherProvider = TestingCoroutineDispatcherProvider()
private val dispatchers = TestingCoroutineDispatcherProvider()
@Before
fun setup() {
MockKAnnotations.init(this)
repository = DefaultBlockAidRepository(api, testDispatcherProvider, mapper)
repository = DefaultBlockAidRepository(api, dispatchers, mapper)
}
@Test

View file

@ -0,0 +1,50 @@
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))
},
)
}
}

View file

@ -30,9 +30,12 @@ internal class DefaultWcEthMessageSignUseCase @AssistedInject constructor(
@Assisted private val method: WcEthMethod.MessageSign,
private val walletManagersFacade: WalletManagersFacade,
private val signUseCase: SignUseCase,
blockAidDelegate: BlockAidEthereumVerificationDelegate,
) : BaseWcSignUseCase<Nothing, WcEthMessageSignUseCase.SignModel>(),
WcEthMessageSignUseCase {
override val securityStatus = blockAidDelegate.getSecurityStatus(network, method, rawSdkRequest, session)
override suspend fun SignCollector<WcEthMessageSignUseCase.SignModel>.onSign(
state: WcSignState<WcEthMessageSignUseCase.SignModel>,
) {

View file

@ -25,9 +25,12 @@ internal class DefaultWcEthSendTransactionUseCase @AssistedInject constructor(
@Assisted private val method: WcEthMethod.SendTransaction,
override val respondService: WcRespondService,
private val sendTransaction: SendTransactionUseCase,
blockAidDelegate: BlockAidEthereumVerificationDelegate,
) : BaseWcSignUseCase<Fee, WcEthTransaction>(),
WcEthSendTransactionUseCase {
override val securityStatus = blockAidDelegate.getSecurityStatus(network, method, rawSdkRequest, session)
private val converter = EthTransactionParamsConverter(context)
override suspend fun SignCollector<WcEthTransaction>.onSign(state: WcSignState<WcEthTransaction>) {

View file

@ -27,9 +27,12 @@ internal class DefaultWcEthSignTransactionUseCase @AssistedInject constructor(
private val prepareForSend: PrepareForSendUseCase,
@Assisted override val context: WcMethodUseCaseContext,
@Assisted private val method: WcEthMethod.SignTransaction,
blockAidDelegate: BlockAidEthereumVerificationDelegate,
) : BaseWcSignUseCase<Fee, WcEthTransaction>(),
WcEthSignTransactionUseCase {
override val securityStatus = blockAidDelegate.getSecurityStatus(network, method, rawSdkRequest, session)
private val converter = EthTransactionParamsConverter(context)
override suspend fun SignCollector<WcEthTransaction>.onSign(state: WcSignState<WcEthTransaction>) {

View file

@ -18,7 +18,7 @@ import com.tangem.domain.walletconnect.usecase.WcMethodUseCase
import com.tangem.domain.wallets.models.UserWallet
import jakarta.inject.Inject
internal class WcEthNetwork constructor(
internal class WcEthNetwork(
private val moshi: Moshi,
private val excludedBlockchains: ExcludedBlockchains,
private val sessionsManager: WcSessionsManager,
@ -34,7 +34,6 @@ internal class WcEthNetwork constructor(
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.MessageSign -> factories.messageSign.create(context, method)
is WcEthMethod.SendTransaction -> factories.sendTransaction.create(context, method)

View file

@ -0,0 +1,10 @@
package com.tangem.domain.walletconnect.usecase.blockaid
import com.domain.blockaid.models.transaction.CheckTransactionResult
import com.tangem.domain.core.lce.Lce
import kotlinx.coroutines.flow.Flow
interface WcBlockAidEligibleTransactionUseCase {
val securityStatus: Flow<Lce<Throwable, CheckTransactionResult>>
}

View file

@ -1,10 +1,12 @@
package com.tangem.domain.walletconnect.usecase.ethereum
import com.tangem.domain.walletconnect.usecase.blockaid.WcBlockAidEligibleTransactionUseCase
import com.tangem.domain.walletconnect.usecase.sign.WcSignUseCase
interface WcEthMessageSignUseCase :
WcSignUseCase,
WcSignUseCase.SimpleRun<WcEthMessageSignUseCase.SignModel> {
WcSignUseCase.SimpleRun<WcEthMessageSignUseCase.SignModel>,
WcBlockAidEligibleTransactionUseCase {
data class SignModel(
val humanMsg: String,

View file

@ -6,11 +6,13 @@ 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.WcMutableFee
import com.tangem.domain.walletconnect.usecase.blockaid.WcBlockAidEligibleTransactionUseCase
import com.tangem.domain.walletconnect.usecase.sign.WcSignUseCase
interface WcEthSendTransactionUseCase :
WcSignUseCase,
WcSignUseCase.SimpleRun<WcEthTransaction>,
WcBlockAidEligibleTransactionUseCase,
WcMutableFee {
override val session: WcSession
@ -25,6 +27,7 @@ interface WcEthSendTransactionUseCase :
interface WcEthSignTransactionUseCase :
WcSignUseCase,
WcSignUseCase.SimpleRun<WcEthTransaction>,
WcBlockAidEligibleTransactionUseCase,
WcMutableFee {
override val session: WcSession