diff --git a/data/blockaid/build.gradle.kts b/data/blockaid/build.gradle.kts index c1855c991d..344e76b061 100644 --- a/data/blockaid/build.gradle.kts +++ b/data/blockaid/build.gradle.kts @@ -38,4 +38,5 @@ dependencies { testImplementation(deps.test.junit) testImplementation(deps.test.mockk) testImplementation(deps.test.turbine) + testImplementation(deps.test.truth) } \ No newline at end of file diff --git a/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/BlockAidMapper.kt b/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/BlockAidMapper.kt index 5336bf9657..a36dea8518 100644 --- a/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/BlockAidMapper.kt +++ b/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/BlockAidMapper.kt @@ -10,13 +10,12 @@ import com.tangem.datasource.api.common.blockaid.models.request.EvmTransactionSc 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() { +internal class BlockAidMapper { fun mapToDomain(from: DomainScanResponse): CheckDAppResult { return when { diff --git a/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/DefaultBlockAidRepository.kt b/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/DefaultBlockAidRepository.kt index 837b699192..8659672ffe 100644 --- a/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/DefaultBlockAidRepository.kt +++ b/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/DefaultBlockAidRepository.kt @@ -9,9 +9,8 @@ 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( +internal class DefaultBlockAidRepository( private val api: BlockAidApi, private val dispatcherProvider: CoroutineDispatcherProvider, private val mapper: BlockAidMapper, diff --git a/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/di/BlockAidDataInternalModule.kt b/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/di/BlockAidDataInternalModule.kt index 894e9aca12..4f4ff71605 100644 --- a/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/di/BlockAidDataInternalModule.kt +++ b/data/blockaid/src/main/kotlin/com/tangem/data/blockaid/di/BlockAidDataInternalModule.kt @@ -1,18 +1,27 @@ package com.tangem.data.blockaid.di +import com.tangem.data.blockaid.BlockAidMapper import com.tangem.data.blockaid.BlockAidRepository import com.tangem.data.blockaid.DefaultBlockAidRepository -import dagger.Binds +import com.tangem.datasource.api.common.blockaid.BlockAidApi +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module +import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -internal interface BlockAidDataInternalModule { +internal object BlockAidDataInternalModule { - @Binds + @Provides @Singleton - fun bindRepository(repository: DefaultBlockAidRepository): BlockAidRepository + fun provideRepository(api: BlockAidApi, dispatcherProvider: CoroutineDispatcherProvider): BlockAidRepository { + return DefaultBlockAidRepository( + api = api, + dispatcherProvider = dispatcherProvider, + mapper = BlockAidMapper(), + ) + } } \ No newline at end of file diff --git a/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/BlockAidMapperTest.kt b/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/BlockAidMapperTest.kt index ac9639b167..faaba4926c 100644 --- a/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/BlockAidMapperTest.kt +++ b/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/BlockAidMapperTest.kt @@ -4,8 +4,8 @@ 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.google.common.truth.Truth import com.tangem.datasource.api.common.blockaid.models.response.* -import org.junit.Assert.* import org.junit.Test import java.math.BigDecimal @@ -14,28 +14,28 @@ class BlockAidMapperTest { private val mapper = BlockAidMapper() @Test - fun whenStatusHitAndIsMaliciousFalseThenMapToDomainReturnsSafe() { + fun `when status hit and is malicious false then map to domain returns safe`() { val response = DomainScanResponse(status = "hit", isMalicious = false) val result = mapper.mapToDomain(response) - assertEquals(CheckDAppResult.SAFE, result) + Truth.assertThat(result).isEqualTo(CheckDAppResult.SAFE) } @Test - fun whenStatusHitAndIsMaliciousTrueThenMapToDomainReturnsUnsafe() { + fun `when status hit and is malicious true then map to domain returns unsafe`() { val response = DomainScanResponse(status = "hit", isMalicious = true) val result = mapper.mapToDomain(response) - assertEquals(CheckDAppResult.UNSAFE, result) + Truth.assertThat(result).isEqualTo(CheckDAppResult.UNSAFE) } @Test - fun whenStatusNotHitThenMapToDomainReturnsFailedToVerify() { + fun `when status not hit then map to domain returns failed to verify`() { val response = DomainScanResponse(status = "miss", isMalicious = false) val result = mapper.mapToDomain(response) - assertEquals(CheckDAppResult.FAILED_TO_VERIFY, result) + Truth.assertThat(result).isEqualTo(CheckDAppResult.FAILED_TO_VERIFY) } @Test - fun whenResponseBenignValidationThenReturnsSafeValidation() { + fun `when response benign validation then returns safe validation`() { val spenderDetails = SpenderDetails( isApprovedForAll = true, exposure = listOf(ExposureDetail(value = "1000.0", rawValue = "0x123")), @@ -53,20 +53,20 @@ class BlockAidMapperTest { ) val result = mapper.mapToDomain(response) - assertEquals(ValidationResult.SAFE, result.validation) + Truth.assertThat(result.validation).isEqualTo(ValidationResult.SAFE) val simulation = result.simulation as? SimulationResult.Success - assertNotNull(simulation) + Truth.assertThat(simulation).isNotNull() 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) + Truth.assertThat(approve).isNotNull() + Truth.assertThat(approve?.approvedAmounts?.size).isEqualTo(1) + Truth.assertThat(approve?.approvedAmounts?.first()?.approvedAmount).isEqualTo(BigDecimal("1000.0")) + Truth.assertThat(approve?.approvedAmounts?.first()?.isUnlimited).isTrue() } @Test - fun whenResponseBenignValidationAndSuccessSimulationThenReturnsSendReceiveResult() { + fun `when response benign validation and success simulation then returns send receive result`() { val assetDiff = AssetDiff( assetType = "ERC20", asset = Asset(chainId = 1, logoUrl = "logo", symbol = "ETH"), @@ -82,19 +82,19 @@ class BlockAidMapperTest { ) val result = mapper.mapToDomain(response) - assertEquals(ValidationResult.SAFE, result.validation) + Truth.assertThat(result.validation).isEqualTo(ValidationResult.SAFE) val simulation = result.simulation as? SimulationResult.Success - assertNotNull(simulation) + Truth.assertThat(simulation).isNotNull() 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) + Truth.assertThat(data).isNotNull() + Truth.assertThat(data?.send?.first()?.amount).isEqualTo(BigDecimal("1.5")) + Truth.assertThat(data?.receive?.first()?.amount).isEqualTo(BigDecimal("2.0")) } @Test - fun whenResponseErrorValidationThenReturnsFailedToValidate() { + fun `when response error validation rhen returns failed to validate`() { val response = TransactionScanResponse( validation = ValidationResponse(status = "Error", resultType = "Benign"), simulation = SimulationResponse( @@ -104,12 +104,12 @@ class BlockAidMapperTest { ) val result = mapper.mapToDomain(response) - assertEquals(ValidationResult.FAILED_TO_VALIDATE, result.validation) - assertTrue(result.simulation is SimulationResult.FailedToSimulate) + Truth.assertThat(result.validation).isEqualTo(ValidationResult.FAILED_TO_VALIDATE) + Truth.assertThat(result.simulation is SimulationResult.FailedToSimulate).isTrue() } @Test - fun whenResponseNotBenignThenReturnsValidationUnsafe() { + fun `when response not benign then returns validation unsafe`() { val response = TransactionScanResponse( validation = ValidationResponse(status = "Success", resultType = "Phishing"), simulation = SimulationResponse( @@ -119,11 +119,11 @@ class BlockAidMapperTest { ) val result = mapper.mapToDomain(response) - assertEquals(ValidationResult.UNSAFE, result.validation) + Truth.assertThat(result.validation).isEqualTo(ValidationResult.UNSAFE) } @Test - fun whenResponseSimulationNotSuccessThenReturnsSimulationFailedToSimulate() { + fun `when response simulation not success then returns simulation failed ro simulate`() { val response = TransactionScanResponse( validation = ValidationResponse(status = "Success", resultType = "Benign"), simulation = SimulationResponse( @@ -133,11 +133,11 @@ class BlockAidMapperTest { ) val result = mapper.mapToDomain(response) - assertTrue(result.simulation is SimulationResult.FailedToSimulate) + Truth.assertThat(result.simulation is SimulationResult.FailedToSimulate).isTrue() } @Test - fun whenResponseSimulationIsEmptyThenReturnsFailedToSimulate() { + fun `when response simulation is empty then returns failed to simulate`() { val txResponse = TransactionScanResponse( validation = ValidationResponse(status = "Success", resultType = "Benign"), simulation = SimulationResponse( @@ -150,6 +150,6 @@ class BlockAidMapperTest { ) val result = mapper.mapToDomain(txResponse) - assertTrue(result.simulation is SimulationResult.FailedToSimulate) + Truth.assertThat(result.simulation is SimulationResult.FailedToSimulate).isTrue() } } \ No newline at end of file diff --git a/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/DefaultBlockAidRepositoryTest.kt b/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/DefaultBlockAidRepositoryTest.kt index bb919dbdb7..036ed43815 100644 --- a/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/DefaultBlockAidRepositoryTest.kt +++ b/data/blockaid/src/test/kotlin/com/tangem/data/blockaid/DefaultBlockAidRepositoryTest.kt @@ -5,24 +5,20 @@ 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.google.common.truth.Truth 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 com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider 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 kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) class DefaultBlockAidRepositoryTest { @MockK @@ -31,28 +27,18 @@ class DefaultBlockAidRepositoryTest { @MockK private lateinit var mapper: BlockAidMapper - @MockK - private lateinit var dispatcherProvider: CoroutineDispatcherProvider - private lateinit var repository: DefaultBlockAidRepository - private val testDispatcher = StandardTestDispatcher() + private val testDispatcherProvider = TestingCoroutineDispatcherProvider() @Before fun setup() { MockKAnnotations.init(this) - Dispatchers.setMain(testDispatcher) - every { dispatcherProvider.io } returns testDispatcher - repository = DefaultBlockAidRepository(api, dispatcherProvider, mapper) - } - - @After - fun tearDown() { - Dispatchers.resetMain() + repository = DefaultBlockAidRepository(api, testDispatcherProvider, mapper) } @Test - fun whenVerifyAppDomainThenCallsApiAndMapsResult() = runTest { + fun `when verify app domain then calls api and maps result`() = runTest { val url = "https://example.com" val domainData = DAppData(url) val domainResponse = DomainScanResponse(status = "hit", isMalicious = false) @@ -63,13 +49,13 @@ class DefaultBlockAidRepositoryTest { val result = repository.verifyDAppDomain(domainData) - assertEquals(expectedResult, result) + Truth.assertThat(result).isEqualTo(expectedResult) coVerify { api.scanDomain(DomainScanRequest(url)) } verify { mapper.mapToDomain(domainResponse) } } @Test - fun whenVerifyEvmTransactionThenCallsScanJsonRpcAndMaps() = runTest { + fun `when verify evm transaction then calls scan json rpc and maps result`() = runTest { val data = TransactionData( chain = "ethereum", accountAddress = "0xabc", @@ -88,14 +74,14 @@ class DefaultBlockAidRepositoryTest { val result = repository.verifyTransaction(data) - assertEquals(expectedResult, result) + Truth.assertThat(result).isEqualTo(expectedResult) coVerify { api.scanJsonRpc(request) } verify { mapper.mapToEvmRequest(data) } verify { mapper.mapToDomain(response) } } @Test - fun whenVerifySolanaTransactionThenCallsScanSolanaMessageAndMapsResult() = runTest { + fun `when verify solana transaction then calls scan solana message and maps result`() = runTest { val data = TransactionData( chain = "mainnet", accountAddress = "/Rd2TLl...", @@ -114,7 +100,7 @@ class DefaultBlockAidRepositoryTest { val result = repository.verifyTransaction(data) - assertEquals(expectedResult, result) + Truth.assertThat(result).isEqualTo(expectedResult) coVerify { api.scanSolanaMessage(request) } verify { mapper.mapToSolanaRequest(data) } verify { mapper.mapToDomain(response) } diff --git a/data/wallet-connect/build.gradle.kts b/data/wallet-connect/build.gradle.kts index cb90e05209..d03ae4e940 100644 --- a/data/wallet-connect/build.gradle.kts +++ b/data/wallet-connect/build.gradle.kts @@ -50,6 +50,8 @@ dependencies { /* Other */ implementation(deps.kotlin.coroutines) implementation(deps.arrow.core) + implementation(projects.domain.blockaid) + implementation(projects.domain.blockaid.models) /* Tests */ testImplementation(projects.common.test) diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt index 1a5345959e..385089f187 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt @@ -20,6 +20,7 @@ import com.tangem.data.walletconnect.utils.WcNamespaceConverter import com.tangem.datasource.di.SdkMoshi import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.walletconnect.WalletConnectStore +import com.tangem.domain.blockaid.BlockAidVerifier import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository import com.tangem.domain.walletconnect.repository.WalletConnectRepository @@ -71,11 +72,13 @@ internal object WalletConnectDataModule { associateNetworksDelegate: AssociateNetworksDelegate, caipNamespaceDelegate: CaipNamespaceDelegate, sdkDelegate: WcPairSdkDelegate, + blockAidVerifier: BlockAidVerifier, ): DefaultWcPairUseCase = DefaultWcPairUseCase( sessionsManager = sessionsManager, associateNetworksDelegate = associateNetworksDelegate, caipNamespaceDelegate = caipNamespaceDelegate, sdkDelegate = sdkDelegate, + blockAidVerifier = blockAidVerifier, ) @Provides diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt index 28776d97c2..107fb0289f 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt @@ -1,10 +1,14 @@ package com.tangem.data.walletconnect.pair import arrow.core.Either +import arrow.core.getOrElse import arrow.core.left import arrow.core.right +import com.domain.blockaid.models.dapp.CheckDAppResult +import com.domain.blockaid.models.dapp.DAppData import com.reown.walletkit.client.Wallet import com.tangem.data.walletconnect.utils.WcSdkSessionConverter +import com.tangem.domain.blockaid.BlockAidVerifier import com.tangem.domain.walletconnect.model.WcPairError import com.tangem.domain.walletconnect.model.WcSession import com.tangem.domain.walletconnect.model.WcSessionApprove @@ -28,6 +32,7 @@ internal class DefaultWcPairUseCase( private val associateNetworksDelegate: AssociateNetworksDelegate, private val caipNamespaceDelegate: CaipNamespaceDelegate, private val sdkDelegate: WcPairSdkDelegate, + private val blockAidVerifier: BlockAidVerifier, ) : WcPairUseCase { private val onCallTerminalAction = Channel() @@ -71,7 +76,10 @@ internal class DefaultWcPairUseCase( sessionForApprove = sessionForApprove, sdkSessionProposal = sdkSessionProposal, ).map { settledSession -> - val newSession = settledSession.session.toDomain(sessionForApprove.wallet) + val newSession = settledSession.session.toDomain( + wallet = sessionForApprove.wallet, + securityStatus = proposalState.dAppSession.securityStatus, + ) sessionsManager.saveSession(newSession) newSession } @@ -107,6 +115,10 @@ internal class DefaultWcPairUseCase( sessionProposal: Wallet.Model.SessionProposal, ): Either = runCatching { val proposalNetwork = associateNetworksDelegate.associate(sessionProposal) + val verificationInfo = blockAidVerifier.verifyDApp(DAppData(sessionProposal.url)).getOrElse { + Timber.e("Failed to verify DApp: ${it.localizedMessage}") + CheckDAppResult.FAILED_TO_VERIFY + } val appMetaData = WcAppMetaData( name = sessionProposal.name, description = sessionProposal.description, @@ -117,7 +129,7 @@ internal class DefaultWcPairUseCase( val dAppSession = WcSessionProposal( dAppMetaData = appMetaData, proposalNetwork = proposalNetwork, - securityStatus = Any(), + securityStatus = verificationInfo, ) WcPairState.Proposal(dAppSession) }.fold(onSuccess = { it.right() }, onFailure = { @@ -127,10 +139,13 @@ internal class DefaultWcPairUseCase( } },) - private fun Wallet.Model.Session.toDomain(wallet: UserWallet): WcSession = WcSession( - wallet = wallet, - sdkModel = WcSdkSessionConverter.convert(this), - ) + private fun Wallet.Model.Session.toDomain(wallet: UserWallet, securityStatus: CheckDAppResult): WcSession { + return WcSession( + wallet = wallet, + sdkModel = WcSdkSessionConverter.convert(this), + securityStatus = securityStatus, + ) + } private sealed interface TerminalAction { data class Approve(val sessionForApprove: WcSessionApprove) : TerminalAction diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt index 341a80d075..d23e135111 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/sessions/DefaultWcSessionsManager.kt @@ -3,6 +3,7 @@ package com.tangem.data.walletconnect.sessions import arrow.core.Either import arrow.core.left import arrow.core.right +import com.domain.blockaid.models.dapp.CheckDAppResult import com.reown.walletkit.client.Wallet import com.reown.walletkit.client.WalletKit import com.tangem.data.walletconnect.utils.WcSdkObserver @@ -25,7 +26,7 @@ import timber.log.Timber import kotlin.coroutines.resume import kotlin.time.Duration.Companion.seconds -internal class DefaultWcSessionsManager constructor( +internal class DefaultWcSessionsManager( private val store: WalletConnectStore, private val legacyStore: WalletConnectSessionsRepository, private val getWallets: GetWalletsUseCase, @@ -60,7 +61,7 @@ internal class DefaultWcSessionsManager constructor( } override suspend fun saveSession(session: WcSession) { - store.saveSession(WcSessionDTO(session.sdkModel.topic, session.wallet.walletId)) + store.saveSession(WcSessionDTO(session.sdkModel.topic, session.wallet.walletId, session.securityStatus)) } override suspend fun removeSession(session: WcSession): Either { @@ -82,13 +83,17 @@ internal class DefaultWcSessionsManager constructor( } override suspend fun findSessionByTopic(topic: String): WcSession? = withContext(dispatchers.io) { - val storedSessions = sessions.firstOrNull() + val storedSession = sessions.firstOrNull() ?.values?.flatten() ?.firstOrNull { it.sdkModel.topic == topic } ?: return@withContext null val sdkSession = WalletKit.getActiveSessionByTopic(topic) ?: return@withContext null - val wallet = storedSessions.wallet - WcSession(wallet = wallet, sdkModel = WcSdkSessionConverter.convert(sdkSession)) + val wallet = storedSession.wallet + WcSession( + wallet = wallet, + sdkModel = WcSdkSessionConverter.convert(sdkSession), + securityStatus = storedSession.securityStatus, + ) } override fun onSessionDelete(sessionDelete: Wallet.Model.SessionDelete) { @@ -104,7 +109,13 @@ internal class DefaultWcSessionsManager constructor( val walletIds = wallets.map { wallet -> wallet.walletId } val inLegacyStore = walletIds .map { walletId -> - flow { emit(legacyStore.loadSessions(walletId.stringValue).map { WcSessionDTO(it.topic, walletId) }) } + flow { + emit( + legacyStore.loadSessions(walletId.stringValue).map { + WcSessionDTO(it.topic, walletId, CheckDAppResult.FAILED_TO_VERIFY) + }, + ) + } } .merge() .reduce { accumulator, value -> accumulator.plus(value) } @@ -124,7 +135,7 @@ internal class DefaultWcSessionsManager constructor( val wcSessions = inStore.mapNotNull { session -> val wallet = wallets.find { it.walletId == session.walletId } ?: return@mapNotNull null val sdkSession = inSdk.find { it.topic == session.topic } ?: return@mapNotNull null - WcSession(wallet = wallet, sdkModel = WcSdkSessionConverter.convert(sdkSession)) + WcSession(wallet = wallet, sdkModel = WcSdkSessionConverter.convert(sdkSession), session.securityStatus) } return wcSessions } diff --git a/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/DefaultWcPairUseCaseTest.kt b/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/DefaultWcPairUseCaseTest.kt index 86ed85e03e..2a6827c8c9 100644 --- a/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/DefaultWcPairUseCaseTest.kt +++ b/data/wallet-connect/src/test/kotlin/com/tangem/domain/walletconnect/DefaultWcPairUseCaseTest.kt @@ -1,8 +1,11 @@ package com.tangem.domain.walletconnect import app.cash.turbine.test +import arrow.core.Either import arrow.core.left import arrow.core.right +import com.domain.blockaid.models.dapp.CheckDAppResult +import com.domain.blockaid.models.dapp.DAppData import com.reown.walletkit.client.Wallet import com.tangem.common.test.domain.wallet.MockUserWalletFactory import com.tangem.data.walletconnect.pair.AssociateNetworksDelegate @@ -10,6 +13,7 @@ import com.tangem.data.walletconnect.pair.CaipNamespaceDelegate import com.tangem.data.walletconnect.pair.DefaultWcPairUseCase import com.tangem.data.walletconnect.pair.WcPairSdkDelegate import com.tangem.data.walletconnect.utils.WcSdkSessionConverter +import com.tangem.domain.blockaid.BlockAidVerifier import com.tangem.domain.walletconnect.model.WcPairError import com.tangem.domain.walletconnect.model.WcSession import com.tangem.domain.walletconnect.model.WcSessionApprove @@ -30,6 +34,7 @@ internal class DefaultWcPairUseCaseTest { private val associateNetworksDelegate: AssociateNetworksDelegate = mockk() private val caipNamespaceDelegate: CaipNamespaceDelegate = mockk() private val sdkDelegate: WcPairSdkDelegate = mockk() + private val blockAidVerifier: BlockAidVerifier = mockk() private val url = "testUrl" private val source = WcPairUseCase.Source.QR @@ -85,6 +90,7 @@ internal class DefaultWcPairUseCaseTest { get() = WcSession( wallet = sessionForApprove.wallet, sdkModel = WcSdkSessionConverter.convert(this), + securityStatus = CheckDAppResult.SAFE, ) private val useCase = DefaultWcPairUseCase( @@ -92,6 +98,7 @@ internal class DefaultWcPairUseCaseTest { associateNetworksDelegate = associateNetworksDelegate, caipNamespaceDelegate = caipNamespaceDelegate, sdkDelegate = sdkDelegate, + blockAidVerifier = blockAidVerifier, ) @Before @@ -109,11 +116,13 @@ internal class DefaultWcPairUseCaseTest { @Test fun `pair, emmit proposal state and wait actions`() = runTest { coEvery { sdkDelegate.pair(url) } returns sdkProposal.right() + coEvery { blockAidVerifier.verifyDApp(any()) } returns Either.catch { CheckDAppResult.SAFE } useCase.pairFlow(url, source).test { assertEquals(loading, awaitItem()) coVerifyOrder { sdkDelegate.pair(url) + blockAidVerifier.verifyDApp(DAppData(sdkProposal.url)) } assert(awaitItem() is WcPairState.Proposal) expectNoEvents() @@ -129,11 +138,13 @@ internal class DefaultWcPairUseCaseTest { coEvery { sdkDelegate.pair(url) } returns sdkProposal.right() coEvery { sdkDelegate.approve(sdkApprove) } returns sdkApproveSuccess.right() coEvery { sessionsManager.saveSession(sessionForSave) } returns Unit + coEvery { blockAidVerifier.verifyDApp(any()) } returns Either.catch { CheckDAppResult.SAFE } useCase.pairFlow(url, source).test { assertEquals(loading, awaitItem()) coVerifyOrder { sdkDelegate.pair(url) + blockAidVerifier.verifyDApp(DAppData(sdkProposal.url)) } assert(awaitItem() is WcPairState.Proposal) useCase.approve(sessionForApprove) @@ -156,11 +167,13 @@ internal class DefaultWcPairUseCaseTest { coEvery { sdkDelegate.pair(url) } returns sdkProposal.right() coEvery { sdkDelegate.rejectSession(proposerPublicKey) } returns Unit + coEvery { blockAidVerifier.verifyDApp(any()) } returns Either.catch { CheckDAppResult.SAFE } useCase.pairFlow(url, source).test { assertEquals(loading, awaitItem()) coVerifyOrder { sdkDelegate.pair(url) + blockAidVerifier.verifyDApp(DAppData(sdkProposal.url)) } assert(awaitItem() is WcPairState.Proposal) useCase.reject() @@ -208,6 +221,7 @@ internal class DefaultWcPairUseCaseTest { val error = WcPairError.ExternalApprovalError("error").left() coEvery { sdkDelegate.pair(url) } returns sdkProposal.right() coEvery { sdkDelegate.approve(sdkApprove) } returns error + coEvery { blockAidVerifier.verifyDApp(any()) } returns Either.catch { CheckDAppResult.SAFE } val errorResult = WcPairState.Approving.Result(sessionForApprove, error) @@ -216,6 +230,7 @@ internal class DefaultWcPairUseCaseTest { coVerifyOrder { sdkDelegate.pair(url) associateNetworksDelegate.associate(sdkProposal) + blockAidVerifier.verifyDApp(DAppData(sdkProposal.url)) } assert(awaitItem() is WcPairState.Proposal) useCase.approve(sessionForApprove) diff --git a/domain/wallet-connect/build.gradle.kts b/domain/wallet-connect/build.gradle.kts index f20c31f3c4..a2178f32a7 100644 --- a/domain/wallet-connect/build.gradle.kts +++ b/domain/wallet-connect/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) implementation(projects.domain.walletConnect.models) + implementation(projects.domain.blockaid.models) /* Other */ implementation(deps.moshi.adapters) diff --git a/domain/wallet-connect/models/build.gradle.kts b/domain/wallet-connect/models/build.gradle.kts index c471057d7c..4fd593d3d4 100644 --- a/domain/wallet-connect/models/build.gradle.kts +++ b/domain/wallet-connect/models/build.gradle.kts @@ -8,6 +8,7 @@ dependencies { /* Domain */ implementation(projects.domain.wallets.models) implementation(projects.domain.tokens.models) + implementation(projects.domain.blockaid.models) /* Other */ implementation(deps.moshi) diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSession.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSession.kt index cc898e4618..d3707d52f6 100644 --- a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSession.kt +++ b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSession.kt @@ -1,9 +1,11 @@ package com.tangem.domain.walletconnect.model +import com.domain.blockaid.models.dapp.CheckDAppResult import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSession import com.tangem.domain.wallets.models.UserWallet data class WcSession( val wallet: UserWallet, val sdkModel: WcSdkSession, + val securityStatus: CheckDAppResult, ) \ No newline at end of file diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionDTO.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionDTO.kt index 63a946b9c6..5698f8421b 100644 --- a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionDTO.kt +++ b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionDTO.kt @@ -1,5 +1,6 @@ package com.tangem.domain.walletconnect.model +import com.domain.blockaid.models.dapp.CheckDAppResult import com.squareup.moshi.JsonClass import com.tangem.domain.wallets.models.UserWalletId @@ -7,4 +8,5 @@ import com.tangem.domain.wallets.models.UserWalletId data class WcSessionDTO( val topic: String, val walletId: UserWalletId, + val securityStatus: CheckDAppResult, ) \ No newline at end of file diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionProposal.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionProposal.kt index 50d299303f..4a110229c4 100644 --- a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionProposal.kt +++ b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcSessionProposal.kt @@ -1,5 +1,6 @@ package com.tangem.domain.walletconnect.model +import com.domain.blockaid.models.dapp.CheckDAppResult import com.tangem.domain.tokens.model.Network import com.tangem.domain.walletconnect.model.sdkcopy.WcAppMetaData import com.tangem.domain.wallets.models.UserWallet @@ -7,7 +8,7 @@ import com.tangem.domain.wallets.models.UserWallet data class WcSessionProposal( val dAppMetaData: WcAppMetaData, val proposalNetwork: Map, - val securityStatus: Any, + val securityStatus: CheckDAppResult, ) { data class ProposalNetwork(