diff --git a/app/src/main/java/com/tangem/tap/di/domain/QrScanningDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/QrScanningDomainModule.kt index bc33cbeb88..04581dc15c 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/QrScanningDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/QrScanningDomainModule.kt @@ -2,6 +2,7 @@ package com.tangem.tap.di.domain import com.tangem.domain.account.supplier.MultiAccountListSupplier import com.tangem.domain.common.wallets.UserWalletsListRepository +import com.tangem.domain.networks.repository.NetworksRepository import com.tangem.domain.qrscanning.repository.QrScanningEventsRepository import com.tangem.domain.qrscanning.usecases.EmitQrScannedEventUseCase import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase @@ -41,11 +42,13 @@ internal object QrScanningDomainModule { multiAccountListSupplier: MultiAccountListSupplier, qrScanningEventsRepository: QrScanningEventsRepository, userWalletsListRepository: UserWalletsListRepository, + networksRepository: NetworksRepository, ): ResolveQrSendTargetsUseCase { return ResolveQrSendTargetsUseCase( multiAccountListSupplier = multiAccountListSupplier, qrScanningEventsRepository = qrScanningEventsRepository, userWalletsListRepository = userWalletsListRepository, + networksRepository = networksRepository, ) } } \ No newline at end of file diff --git a/data/networks/src/main/java/com/tangem/data/networks/repository/DefaultNetworksRepository.kt b/data/networks/src/main/java/com/tangem/data/networks/repository/DefaultNetworksRepository.kt index 4755e55248..0cb24be71c 100644 --- a/data/networks/src/main/java/com/tangem/data/networks/repository/DefaultNetworksRepository.kt +++ b/data/networks/src/main/java/com/tangem/data/networks/repository/DefaultNetworksRepository.kt @@ -57,7 +57,7 @@ internal class DefaultNetworksRepository( .map { currency -> CryptoCurrencyAddress( cryptoCurrency = currency, - address = getDefaultAddress(userWalletId, network), + address = getDefaultAddress(userWalletId, network).orEmpty(), ) } } @@ -74,11 +74,17 @@ internal class DefaultNetworksRepository( .map { currency -> CryptoCurrencyAddress( cryptoCurrency = currency, - address = getDefaultAddress(userWalletId, currency.network), + address = getDefaultAddress(userWalletId, currency.network).orEmpty(), ) } } + override suspend fun getDefaultAddress(userWalletId: UserWalletId, network: Network): String? { + return withContext(dispatchers.io) { + walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = network) + } + } + override suspend fun hasCachedStatuses(userWalletId: UserWalletId): Boolean { return networksStatusesStore.contains(userWalletId) } @@ -100,10 +106,4 @@ internal class DefaultNetworksRepository( networksStatusesStore.storeStatus(userWalletId = userWalletId, status = networkStatus) } - - private suspend fun getDefaultAddress(userWalletId: UserWalletId, network: Network): String { - return withContext(dispatchers.io) { - walletManagersFacade.getDefaultAddress(userWalletId = userWalletId, network = network).orEmpty() - } - } } \ No newline at end of file diff --git a/domain/networks/src/main/java/com/tangem/domain/networks/repository/NetworksRepository.kt b/domain/networks/src/main/java/com/tangem/domain/networks/repository/NetworksRepository.kt index e27c6b2b47..f5641e9e9f 100644 --- a/domain/networks/src/main/java/com/tangem/domain/networks/repository/NetworksRepository.kt +++ b/domain/networks/src/main/java/com/tangem/domain/networks/repository/NetworksRepository.kt @@ -30,6 +30,11 @@ interface NetworksRepository { */ suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network.RawID): List + /** + * Returns the default address for the given [network] in the selected [userWalletId] + */ + suspend fun getDefaultAddress(userWalletId: UserWalletId, network: Network): String? + /** Checks if there are cached statuses for given [userWalletId] */ suspend fun hasCachedStatuses(userWalletId: UserWalletId): Boolean } \ No newline at end of file diff --git a/domain/qr-scanning/build.gradle.kts b/domain/qr-scanning/build.gradle.kts index 2679da7164..497c075d41 100644 --- a/domain/qr-scanning/build.gradle.kts +++ b/domain/qr-scanning/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { api(projects.domain.models) implementation(projects.domain.account) implementation(projects.domain.common) + implementation(projects.domain.networks) implementation(projects.domain.qrScanning.models) implementation(projects.domain.tokens.models) diff --git a/domain/qr-scanning/models/src/main/java/com/tangem/domain/qrscanning/models/QrSendTarget.kt b/domain/qr-scanning/models/src/main/java/com/tangem/domain/qrscanning/models/QrSendTarget.kt index 7220a7ef95..dcb02f58d4 100644 --- a/domain/qr-scanning/models/src/main/java/com/tangem/domain/qrscanning/models/QrSendTarget.kt +++ b/domain/qr-scanning/models/src/main/java/com/tangem/domain/qrscanning/models/QrSendTarget.kt @@ -39,6 +39,8 @@ sealed class QrSendTarget { ) } + data object AddressSameAsWallet : QrSendTarget() + data class WalletConnect(val uri: String) : QrSendTarget() data class Error(val error: ClassifiedQrContent.Error) : QrSendTarget() diff --git a/domain/qr-scanning/src/main/java/com/tangem/domain/qrscanning/usecases/ResolveQrSendTargetsUseCase.kt b/domain/qr-scanning/src/main/java/com/tangem/domain/qrscanning/usecases/ResolveQrSendTargetsUseCase.kt index a33e20ec2d..9353df0a2a 100644 --- a/domain/qr-scanning/src/main/java/com/tangem/domain/qrscanning/usecases/ResolveQrSendTargetsUseCase.kt +++ b/domain/qr-scanning/src/main/java/com/tangem/domain/qrscanning/usecases/ResolveQrSendTargetsUseCase.kt @@ -5,17 +5,23 @@ import com.tangem.domain.models.account.Account import com.tangem.domain.models.account.AccountId import com.tangem.domain.models.account.AccountName import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.common.wallets.UserWalletsListRepository +import com.tangem.domain.networks.repository.NetworksRepository import com.tangem.domain.qrscanning.models.ClassifiedQrContent import com.tangem.domain.qrscanning.repository.QrScanningEventsRepository +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import java.math.BigDecimal import com.tangem.domain.qrscanning.models.QrSendTarget +import kotlinx.coroutines.awaitAll class ResolveQrSendTargetsUseCase( private val multiAccountListSupplier: MultiAccountListSupplier, private val qrScanningEventsRepository: QrScanningEventsRepository, private val userWalletsListRepository: UserWalletsListRepository, + private val networksRepository: NetworksRepository, ) { suspend operator fun invoke(qrCode: String): QrSendTarget { @@ -49,7 +55,7 @@ class ResolveQrSendTargetsUseCase( return resolve(classified, portfolioIndex) } - private fun resolve(classified: ClassifiedQrContent, portfolioIndex: PortfolioIndex): QrSendTarget { + private suspend fun resolve(classified: ClassifiedQrContent, portfolioIndex: PortfolioIndex): QrSendTarget { return when (classified) { is ClassifiedQrContent.WalletConnect -> QrSendTarget.WalletConnect(classified.uri) is ClassifiedQrContent.Error -> QrSendTarget.Error(classified) @@ -70,14 +76,19 @@ class ResolveQrSendTargetsUseCase( } } - private fun resolveAddressTarget( + private suspend fun resolveAddressTarget( address: String, amount: BigDecimal?, memo: String?, matchingCurrencies: List, portfolioIndex: PortfolioIndex, ): QrSendTarget { - val walletGroups = buildWalletGroups(matchingCurrencies, portfolioIndex) + val ownAddressNetworks = findOwnAddressNetworks(address, matchingCurrencies, portfolioIndex) + val walletGroups = buildWalletGroups(matchingCurrencies, portfolioIndex, ownAddressNetworks) + + if (walletGroups.isEmpty()) { + return QrSendTarget.AddressSameAsWallet + } val singleGroup = walletGroups.singleOrNull() val singleCurrency = singleGroup?.accounts?.singleOrNull()?.currencies?.singleOrNull() @@ -100,9 +111,33 @@ class ResolveQrSendTargetsUseCase( } } + private suspend fun findOwnAddressNetworks( + address: String, + matchingCurrencies: List, + portfolioIndex: PortfolioIndex, + ): Map> = coroutineScope { + matchingCurrencies.distinctBy { it.id } + .flatMap { currency -> + portfolioIndex.currencyLocations[currency.id].orEmpty().map { + it.accountId.userWalletId to currency.network + } + } + .distinct() + .map { (walletId, network) -> + async { + val ownAddress = networksRepository.getDefaultAddress(walletId, network) + if (ownAddress == address) walletId to network else null + } + } + .awaitAll() + .filterNotNull() + .groupBy(keySelector = { it.first }, valueTransform = { it.second.id }) + } + private fun buildWalletGroups( matchingCurrencies: List, portfolioIndex: PortfolioIndex, + ownAddressNetworks: Map>, ): List { val walletMap = linkedMapOf() val uniqueCurrencies = matchingCurrencies.distinctBy { it.id } @@ -110,13 +145,18 @@ class ResolveQrSendTargetsUseCase( for (currency in uniqueCurrencies) { val locations = portfolioIndex.currencyLocations[currency.id].orEmpty() for (location in locations) { - val walletInfo = walletMap.getOrPut(location.accountId.userWalletId) { - WalletInfo(location.walletName, linkedMapOf()) + val walletId = location.accountId.userWalletId + val isOwnAddress = ownAddressNetworks[walletId]?.contains(currency.network.id) == true + + if (!isOwnAddress) { + val walletInfo = walletMap.getOrPut(walletId) { + WalletInfo(location.walletName, linkedMapOf()) + } + val accountInfo = walletInfo.accounts.getOrPut(location.accountId) { + AccountInfo(location.accountName, mutableListOf()) + } + accountInfo.currencies.add(currency) } - val accountInfo = walletInfo.accounts.getOrPut(location.accountId) { - AccountInfo(location.accountName, mutableListOf()) - } - accountInfo.currencies.add(currency) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt index e64451ff14..a65941d7ed 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt @@ -785,6 +785,9 @@ internal class WalletModel @Inject constructor( is QrSendTarget.Multiple -> { innerWalletRouter.openNetworkSelectionBottomSheet(target) } + is QrSendTarget.AddressSameAsWallet -> { + uiMessageSender.send(WalletAlertUM.qrCodeAddressSameAsWallet()) + } is QrSendTarget.Error -> handleQrError(target.error) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertUM.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertUM.kt index 665fcfdafd..9b07b7f428 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertUM.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertUM.kt @@ -94,6 +94,12 @@ internal object WalletAlertUM { ) } + fun qrCodeAddressSameAsWallet(): DialogMessage { + return DialogMessage( + message = resourceReference(R.string.send_error_address_same_as_wallet), + ) + } + fun confirmExpressStatusHide(onConfirmClick: () -> Unit): DialogMessage { return DialogMessage( title = resourceReference(R.string.express_status_hide_dialog_title),