Updated on 2026-08-14
This commit is contained in:
parent
7a8f55b589
commit
2af08f578c
46 changed files with 1035 additions and 0 deletions
1
data/blockaid/.gitignore
vendored
Normal file
1
data/blockaid/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
41
data/blockaid/build.gradle.kts
Normal file
41
data/blockaid/build.gradle.kts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
plugins {
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.android.library)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.data.blockaid"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
/* Project - Domain */
|
||||
implementation(projects.data.common)
|
||||
implementation(projects.domain.blockaid)
|
||||
implementation(projects.domain.blockaid.models)
|
||||
|
||||
/* Project - Data */
|
||||
implementation(projects.core.datasource)
|
||||
|
||||
/* Project - Core */
|
||||
implementation(projects.core.utils)
|
||||
|
||||
/* DI */
|
||||
implementation(deps.hilt.core)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
||||
/* Tangem libraries */
|
||||
implementation(tangemDeps.blockchain)
|
||||
implementation(tangemDeps.card.core)
|
||||
|
||||
/* Other */
|
||||
implementation(deps.kotlin.coroutines)
|
||||
implementation(deps.arrow.core)
|
||||
|
||||
/* Tests */
|
||||
testImplementation(deps.test.coroutine)
|
||||
testImplementation(deps.test.junit)
|
||||
testImplementation(deps.test.mockk)
|
||||
testImplementation(deps.test.turbine)
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package com.tangem.data.blockaid
|
||||
|
||||
import com.domain.blockaid.models.dapp.CheckDAppResult
|
||||
import com.domain.blockaid.models.transaction.*
|
||||
import com.domain.blockaid.models.transaction.simultation.ApprovedAmount
|
||||
import com.domain.blockaid.models.transaction.simultation.SimulationData
|
||||
import com.domain.blockaid.models.transaction.simultation.TokenInfo
|
||||
import com.domain.blockaid.models.transaction.simultation.AmountInfo
|
||||
import com.tangem.datasource.api.common.blockaid.models.request.EvmTransactionScanRequest
|
||||
import com.tangem.datasource.api.common.blockaid.models.request.RpcData
|
||||
import com.tangem.datasource.api.common.blockaid.models.request.SolanaTransactionScanRequest
|
||||
import com.tangem.datasource.api.common.blockaid.models.response.*
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val SUCCESS_STATUS = "Success"
|
||||
private const val DOMAIN_CHECKED_STATUS = "hit"
|
||||
private const val VALIDATION_SAFE_STATUS = "Benign"
|
||||
|
||||
internal class BlockAidMapper @Inject constructor() {
|
||||
|
||||
fun mapToDomain(from: DomainScanResponse): CheckDAppResult {
|
||||
return when {
|
||||
from.status != DOMAIN_CHECKED_STATUS -> CheckDAppResult.FAILED_TO_VERIFY
|
||||
from.isMalicious == true -> CheckDAppResult.UNSAFE
|
||||
else -> CheckDAppResult.SAFE
|
||||
}
|
||||
}
|
||||
|
||||
fun mapToDomain(from: TransactionScanResponse): CheckTransactionResult {
|
||||
return CheckTransactionResult(
|
||||
validation = when {
|
||||
from.validation.status != SUCCESS_STATUS -> ValidationResult.FAILED_TO_VALIDATE
|
||||
from.validation.resultType == VALIDATION_SAFE_STATUS -> ValidationResult.SAFE
|
||||
else -> ValidationResult.UNSAFE
|
||||
},
|
||||
simulation = if (from.simulation.status != SUCCESS_STATUS) {
|
||||
SimulationResult.FailedToSimulate
|
||||
} else {
|
||||
mapSimulationSuccessResult(from.simulation.accountSummary)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun mapToEvmRequest(from: TransactionData): EvmTransactionScanRequest {
|
||||
return EvmTransactionScanRequest(
|
||||
chain = from.chain,
|
||||
accountAddress = from.accountAddress,
|
||||
method = from.method,
|
||||
data = RpcData(
|
||||
method = from.method,
|
||||
params = (from.params as TransactionParams.Evm).params,
|
||||
),
|
||||
metadata = TransactionMetadata(from.domainUrl),
|
||||
)
|
||||
}
|
||||
|
||||
fun mapToSolanaRequest(from: TransactionData): SolanaTransactionScanRequest {
|
||||
return SolanaTransactionScanRequest(
|
||||
chain = from.chain,
|
||||
accountAddress = from.accountAddress,
|
||||
metadata = TransactionMetadata(from.domainUrl),
|
||||
method = from.method,
|
||||
transactions = (from.params as TransactionParams.Solana).transactions,
|
||||
)
|
||||
}
|
||||
|
||||
private fun mapSimulationSuccessResult(from: AccountSummaryResponse): SimulationResult {
|
||||
return when {
|
||||
from.assetsDiffs.isEmpty() && from.exposures.isNotEmpty() -> mapApproveTransaction(from.exposures)
|
||||
from.assetsDiffs.isNotEmpty() && from.exposures.isEmpty() -> mapSendReceiveTransaction(from.assetsDiffs)
|
||||
else -> SimulationResult.FailedToSimulate
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapApproveTransaction(exposures: List<Exposure>): SimulationResult {
|
||||
val amounts = exposures.flatMap { exposure ->
|
||||
val tokenInfo = TokenInfo(
|
||||
chainId = exposure.asset.chainId,
|
||||
logoUrl = exposure.asset.logoUrl,
|
||||
symbol = exposure.asset.symbol,
|
||||
)
|
||||
exposure.spenders.flatMap { (_, spender) ->
|
||||
val isUnlimited = spender.isApprovedForAll == true
|
||||
spender.exposure.mapNotNull { detail ->
|
||||
ApprovedAmount(
|
||||
approvedAmount = detail.value.toBigDecimalOrNull() ?: return@mapNotNull null,
|
||||
isUnlimited = isUnlimited,
|
||||
tokenInfo = tokenInfo,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return if (amounts.isNotEmpty()) {
|
||||
SimulationResult.Success(SimulationData.Approve(amounts))
|
||||
} else {
|
||||
SimulationResult.FailedToSimulate
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapSendReceiveTransaction(assetDiffs: List<AssetDiff>): SimulationResult {
|
||||
val sendInfo = arrayListOf<AmountInfo>()
|
||||
val receiveInfo = arrayListOf<AmountInfo>()
|
||||
|
||||
assetDiffs.forEach { diff ->
|
||||
val token = TokenInfo(
|
||||
chainId = diff.asset.chainId,
|
||||
logoUrl = diff.asset.logoUrl,
|
||||
symbol = diff.asset.symbol,
|
||||
)
|
||||
diff.outTransfer.orEmpty().forEach { transfer ->
|
||||
transfer.value.toBigDecimalOrNull()?.let { amount ->
|
||||
sendInfo.add(AmountInfo(amount = amount, token = token))
|
||||
}
|
||||
}
|
||||
diff.inTransfer.orEmpty().forEach { transfer ->
|
||||
transfer.value.toBigDecimalOrNull()?.let { amount ->
|
||||
receiveInfo.add(AmountInfo(amount = amount, token = token))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (sendInfo.isNotEmpty() || receiveInfo.isNotEmpty()) {
|
||||
SimulationResult.Success(SimulationData.SendAndReceive(send = sendInfo, receive = receiveInfo))
|
||||
} else {
|
||||
SimulationResult.FailedToSimulate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.data.blockaid
|
||||
|
||||
import com.domain.blockaid.models.dapp.CheckDAppResult
|
||||
import com.domain.blockaid.models.dapp.DAppData
|
||||
import com.domain.blockaid.models.transaction.CheckTransactionResult
|
||||
import com.domain.blockaid.models.transaction.TransactionData
|
||||
|
||||
interface BlockAidRepository {
|
||||
|
||||
suspend fun verifyDAppDomain(data: DAppData): CheckDAppResult
|
||||
|
||||
suspend fun verifyTransaction(data: TransactionData): CheckTransactionResult
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.data.blockaid
|
||||
|
||||
import com.domain.blockaid.models.dapp.CheckDAppResult
|
||||
import com.domain.blockaid.models.dapp.DAppData
|
||||
import com.domain.blockaid.models.transaction.CheckTransactionResult
|
||||
import com.domain.blockaid.models.transaction.TransactionData
|
||||
import com.domain.blockaid.models.transaction.TransactionParams
|
||||
import com.tangem.datasource.api.common.blockaid.BlockAidApi
|
||||
import com.tangem.datasource.api.common.blockaid.models.request.DomainScanRequest
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultBlockAidRepository @Inject constructor(
|
||||
private val api: BlockAidApi,
|
||||
private val dispatcherProvider: CoroutineDispatcherProvider,
|
||||
private val mapper: BlockAidMapper,
|
||||
) : BlockAidRepository {
|
||||
|
||||
override suspend fun verifyDAppDomain(data: DAppData): CheckDAppResult {
|
||||
val response = withContext(dispatcherProvider.io) {
|
||||
api.scanDomain(DomainScanRequest(data.url))
|
||||
}
|
||||
return mapper.mapToDomain(response)
|
||||
}
|
||||
|
||||
override suspend fun verifyTransaction(data: TransactionData): CheckTransactionResult {
|
||||
val response = withContext(dispatcherProvider.io) {
|
||||
when (data.params) {
|
||||
is TransactionParams.Evm -> {
|
||||
api.scanJsonRpc(mapper.mapToEvmRequest(data))
|
||||
}
|
||||
is TransactionParams.Solana -> {
|
||||
api.scanSolanaMessage(mapper.mapToSolanaRequest(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
return mapper.mapToDomain(response)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.data.blockaid
|
||||
|
||||
import arrow.core.Either
|
||||
import com.domain.blockaid.models.dapp.CheckDAppResult
|
||||
import com.domain.blockaid.models.dapp.DAppData
|
||||
import com.domain.blockaid.models.transaction.CheckTransactionResult
|
||||
import com.domain.blockaid.models.transaction.TransactionData
|
||||
import com.tangem.domain.blockaid.BlockAidVerifier
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Verifies the safety of DApps and WalletConnect transactions
|
||||
*/
|
||||
class DefaultBlockAidVerifier @Inject constructor(
|
||||
private val repository: BlockAidRepository,
|
||||
) : BlockAidVerifier {
|
||||
|
||||
/**
|
||||
* Checks if a DApp is safe to use
|
||||
*/
|
||||
override suspend fun verifyDApp(data: DAppData): Either<Throwable, CheckDAppResult> {
|
||||
return Either.catch { repository.verifyDAppDomain(data) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the safety of a WalletConnect transaction and provides a simulation result
|
||||
*/
|
||||
override suspend fun verifyTransaction(data: TransactionData): Either<Throwable, CheckTransactionResult> {
|
||||
return Either.catch { repository.verifyTransaction(data) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.data.blockaid.di
|
||||
|
||||
import com.tangem.data.blockaid.BlockAidRepository
|
||||
import com.tangem.data.blockaid.DefaultBlockAidRepository
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface BlockAidDataInternalModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindRepository(repository: DefaultBlockAidRepository): BlockAidRepository
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.data.blockaid.di
|
||||
|
||||
import com.tangem.data.blockaid.DefaultBlockAidVerifier
|
||||
import com.tangem.domain.blockaid.BlockAidVerifier
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface BlockAidDataModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindVerifier(verifier: DefaultBlockAidVerifier): BlockAidVerifier
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
package com.tangem.data.blockaid
|
||||
|
||||
import com.domain.blockaid.models.dapp.CheckDAppResult
|
||||
import com.domain.blockaid.models.transaction.SimulationResult
|
||||
import com.domain.blockaid.models.transaction.ValidationResult
|
||||
import com.domain.blockaid.models.transaction.simultation.SimulationData
|
||||
import com.tangem.datasource.api.common.blockaid.models.response.*
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import java.math.BigDecimal
|
||||
|
||||
class BlockAidMapperTest {
|
||||
|
||||
private val mapper = BlockAidMapper()
|
||||
|
||||
@Test
|
||||
fun whenStatusHitAndIsMaliciousFalseThenMapToDomainReturnsSafe() {
|
||||
val response = DomainScanResponse(status = "hit", isMalicious = false)
|
||||
val result = mapper.mapToDomain(response)
|
||||
assertEquals(CheckDAppResult.SAFE, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenStatusHitAndIsMaliciousTrueThenMapToDomainReturnsUnsafe() {
|
||||
val response = DomainScanResponse(status = "hit", isMalicious = true)
|
||||
val result = mapper.mapToDomain(response)
|
||||
assertEquals(CheckDAppResult.UNSAFE, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenStatusNotHitThenMapToDomainReturnsFailedToVerify() {
|
||||
val response = DomainScanResponse(status = "miss", isMalicious = false)
|
||||
val result = mapper.mapToDomain(response)
|
||||
assertEquals(CheckDAppResult.FAILED_TO_VERIFY, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenResponseBenignValidationThenReturnsSafeValidation() {
|
||||
val spenderDetails = SpenderDetails(
|
||||
isApprovedForAll = true,
|
||||
exposure = listOf(ExposureDetail(value = "1000.0", rawValue = "0x123")),
|
||||
)
|
||||
val exposure = Exposure(
|
||||
asset = Asset(chainId = 1, logoUrl = "logo", symbol = "PEPE"),
|
||||
spenders = mapOf("spender" to spenderDetails),
|
||||
)
|
||||
val response = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
||||
simulation = SimulationResponse(
|
||||
status = "Success",
|
||||
accountSummary = AccountSummaryResponse(assetsDiffs = emptyList(), exposures = listOf(exposure)),
|
||||
),
|
||||
)
|
||||
|
||||
val result = mapper.mapToDomain(response)
|
||||
assertEquals(ValidationResult.SAFE, result.validation)
|
||||
|
||||
val simulation = result.simulation as? SimulationResult.Success
|
||||
assertNotNull(simulation)
|
||||
|
||||
val approve = simulation?.data as? SimulationData.Approve
|
||||
assertNotNull(approve)
|
||||
assertEquals(1, approve?.approvedAmounts?.size)
|
||||
assertEquals(BigDecimal("1000.0"), approve?.approvedAmounts?.first()?.approvedAmount)
|
||||
assertTrue(approve?.approvedAmounts?.first()?.isUnlimited == true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenResponseBenignValidationAndSuccessSimulationThenReturnsSendReceiveResult() {
|
||||
val assetDiff = AssetDiff(
|
||||
assetType = "ERC20",
|
||||
asset = Asset(chainId = 1, logoUrl = "logo", symbol = "ETH"),
|
||||
inTransfer = listOf(Transfer(value = "2.0", rawValue = "0x1")),
|
||||
outTransfer = listOf(Transfer(value = "1.5", rawValue = "0x2")),
|
||||
)
|
||||
val response = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
||||
simulation = SimulationResponse(
|
||||
status = "Success",
|
||||
accountSummary = AccountSummaryResponse(exposures = emptyList(), assetsDiffs = listOf(assetDiff)),
|
||||
),
|
||||
)
|
||||
|
||||
val result = mapper.mapToDomain(response)
|
||||
assertEquals(ValidationResult.SAFE, result.validation)
|
||||
|
||||
val simulation = result.simulation as? SimulationResult.Success
|
||||
assertNotNull(simulation)
|
||||
|
||||
val data = simulation?.data as? SimulationData.SendAndReceive
|
||||
assertNotNull(data)
|
||||
assertEquals(BigDecimal("1.5"), data?.send?.first()?.amount)
|
||||
assertEquals(BigDecimal("2.0"), data?.receive?.first()?.amount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenResponseErrorValidationThenReturnsFailedToValidate() {
|
||||
val response = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Error", resultType = "Benign"),
|
||||
simulation = SimulationResponse(
|
||||
status = "Success",
|
||||
accountSummary = AccountSummaryResponse(emptyList(), emptyList()),
|
||||
),
|
||||
)
|
||||
|
||||
val result = mapper.mapToDomain(response)
|
||||
assertEquals(ValidationResult.FAILED_TO_VALIDATE, result.validation)
|
||||
assertTrue(result.simulation is SimulationResult.FailedToSimulate)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenResponseNotBenignThenReturnsValidationUnsafe() {
|
||||
val response = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Success", resultType = "Phishing"),
|
||||
simulation = SimulationResponse(
|
||||
status = "Success",
|
||||
accountSummary = AccountSummaryResponse(emptyList(), emptyList()),
|
||||
),
|
||||
)
|
||||
|
||||
val result = mapper.mapToDomain(response)
|
||||
assertEquals(ValidationResult.UNSAFE, result.validation)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenResponseSimulationNotSuccessThenReturnsSimulationFailedToSimulate() {
|
||||
val response = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
||||
simulation = SimulationResponse(
|
||||
status = "Error",
|
||||
accountSummary = AccountSummaryResponse(emptyList(), emptyList()),
|
||||
),
|
||||
)
|
||||
|
||||
val result = mapper.mapToDomain(response)
|
||||
assertTrue(result.simulation is SimulationResult.FailedToSimulate)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenResponseSimulationIsEmptyThenReturnsFailedToSimulate() {
|
||||
val txResponse = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
||||
simulation = SimulationResponse(
|
||||
status = "Success",
|
||||
accountSummary = AccountSummaryResponse(
|
||||
assetsDiffs = emptyList(),
|
||||
exposures = emptyList(),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val result = mapper.mapToDomain(txResponse)
|
||||
assertTrue(result.simulation is SimulationResult.FailedToSimulate)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package com.tangem.data.blockaid
|
||||
|
||||
import com.domain.blockaid.models.dapp.CheckDAppResult
|
||||
import com.domain.blockaid.models.dapp.DAppData
|
||||
import com.domain.blockaid.models.transaction.CheckTransactionResult
|
||||
import com.domain.blockaid.models.transaction.TransactionData
|
||||
import com.domain.blockaid.models.transaction.TransactionParams
|
||||
import com.tangem.datasource.api.common.blockaid.BlockAidApi
|
||||
import com.tangem.datasource.api.common.blockaid.models.request.DomainScanRequest
|
||||
import com.tangem.datasource.api.common.blockaid.models.request.EvmTransactionScanRequest
|
||||
import com.tangem.datasource.api.common.blockaid.models.request.SolanaTransactionScanRequest
|
||||
import com.tangem.datasource.api.common.blockaid.models.response.DomainScanResponse
|
||||
import com.tangem.datasource.api.common.blockaid.models.response.TransactionScanResponse
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import io.mockk.*
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.*
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DefaultBlockAidRepositoryTest {
|
||||
|
||||
@MockK
|
||||
private lateinit var api: BlockAidApi
|
||||
|
||||
@MockK
|
||||
private lateinit var mapper: BlockAidMapper
|
||||
|
||||
@MockK
|
||||
private lateinit var dispatcherProvider: CoroutineDispatcherProvider
|
||||
|
||||
private lateinit var repository: DefaultBlockAidRepository
|
||||
|
||||
private val testDispatcher = StandardTestDispatcher()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
every { dispatcherProvider.io } returns testDispatcher
|
||||
repository = DefaultBlockAidRepository(api, dispatcherProvider, mapper)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenVerifyAppDomainThenCallsApiAndMapsResult() = runTest {
|
||||
val url = "https://example.com"
|
||||
val domainData = DAppData(url)
|
||||
val domainResponse = DomainScanResponse(status = "hit", isMalicious = false)
|
||||
val expectedResult = CheckDAppResult.SAFE
|
||||
|
||||
coEvery { api.scanDomain(DomainScanRequest(url)) } returns domainResponse
|
||||
every { mapper.mapToDomain(domainResponse) } returns expectedResult
|
||||
|
||||
val result = repository.verifyDAppDomain(domainData)
|
||||
|
||||
assertEquals(expectedResult, result)
|
||||
coVerify { api.scanDomain(DomainScanRequest(url)) }
|
||||
verify { mapper.mapToDomain(domainResponse) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenVerifyEvmTransactionThenCallsScanJsonRpcAndMaps() = runTest {
|
||||
val data = TransactionData(
|
||||
chain = "ethereum",
|
||||
accountAddress = "0xabc",
|
||||
domainUrl = "https://uniswap.org",
|
||||
method = "eth_sendTransaction",
|
||||
params = TransactionParams.Evm(params = "some-params"),
|
||||
)
|
||||
|
||||
val request = mockk<EvmTransactionScanRequest>()
|
||||
val response = mockk<TransactionScanResponse>()
|
||||
val expectedResult = mockk<CheckTransactionResult>()
|
||||
|
||||
every { mapper.mapToEvmRequest(data) } returns request
|
||||
coEvery { api.scanJsonRpc(request) } returns response
|
||||
every { mapper.mapToDomain(response) } returns expectedResult
|
||||
|
||||
val result = repository.verifyTransaction(data)
|
||||
|
||||
assertEquals(expectedResult, result)
|
||||
coVerify { api.scanJsonRpc(request) }
|
||||
verify { mapper.mapToEvmRequest(data) }
|
||||
verify { mapper.mapToDomain(response) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenVerifySolanaTransactionThenCallsScanSolanaMessageAndMapsResult() = runTest {
|
||||
val data = TransactionData(
|
||||
chain = "mainnet",
|
||||
accountAddress = "/Rd2TLl...",
|
||||
domainUrl = "https://example.com",
|
||||
method = "signTransaction",
|
||||
params = TransactionParams.Solana(transactions = listOf("TX_PAYLOAD_BASE64")),
|
||||
)
|
||||
|
||||
val request = mockk<SolanaTransactionScanRequest>()
|
||||
val response = mockk<TransactionScanResponse>()
|
||||
val expectedResult = mockk<CheckTransactionResult>()
|
||||
|
||||
every { mapper.mapToSolanaRequest(data) } returns request
|
||||
coEvery { api.scanSolanaMessage(request) } returns response
|
||||
every { mapper.mapToDomain(response) } returns expectedResult
|
||||
|
||||
val result = repository.verifyTransaction(data)
|
||||
|
||||
assertEquals(expectedResult, result)
|
||||
coVerify { api.scanSolanaMessage(request) }
|
||||
verify { mapper.mapToSolanaRequest(data) }
|
||||
verify { mapper.mapToDomain(response) }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue