diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountPortfolioItemUMConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountPortfolioItemUMConverter.kt index 150bc43608..c2403a7266 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountPortfolioItemUMConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountPortfolioItemUMConverter.kt @@ -19,23 +19,20 @@ class AccountPortfolioItemUMConverter( private val isBalanceHidden: Boolean = false, private val isEnabled: Boolean = true, private val endIcon: UserWalletItemUM.EndIcon = UserWalletItemUM.EndIcon.None, -) : Converter { +) : Converter { - override fun convert(value: Account): UserWalletItemUM { - return when (value) { - is Account.CryptoPortfolio -> with(value) { - UserWalletItemUM( - id = accountId.value, - name = accountName.toUM().value, - information = getInfo(account = this), - balance = getBalanceInfo(), - isEnabled = isEnabled, - endIcon = endIcon, - onClick = onClick, - imageState = getImageState(account = this), - ) - } - is Account.Payment -> TODO("[REDACTED_JIRA]") + override fun convert(value: Account.CryptoPortfolio): UserWalletItemUM { + return with(value) { + UserWalletItemUM( + id = accountId.value, + name = accountName.toUM().value, + information = getInfo(account = this), + balance = getBalanceInfo(), + isEnabled = isEnabled, + endIcon = endIcon, + onClick = onClick, + imageState = getImageState(account = this), + ) } } diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayCryptoCurrencyFactory.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayCryptoCurrencyFactory.kt index 6a903986b1..48607769ea 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayCryptoCurrencyFactory.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultTangemPayCryptoCurrencyFactory.kt @@ -8,6 +8,7 @@ import com.tangem.core.error.UniversalError import com.tangem.data.common.currency.CryptoCurrencyFactory import com.tangem.data.common.network.NetworkFactory import com.tangem.data.pay.util.TangemPayErrorConverter +import com.tangem.domain.card.common.visa.VisaUtilities import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory @@ -57,4 +58,25 @@ internal class DefaultTangemPayCryptoCurrencyFactory @Inject constructor( errorConverter.convert(exception) } } + + override fun create(userWallet: UserWallet): Either { + return catch { + val network = networkFactory.create( + blockchain = VisaUtilities.visaBlockchain, + extraDerivationPath = null, + userWallet = userWallet, + ) + cryptoCurrencyFactory.createToken( + network = requireNotNull(network), + rawId = CryptoCurrency.RawID(TOKEN_ID), + name = TOKEN_NAME, + symbol = TOKEN_NAME, + contractAddress = TOKEN_CONTRACT_ADDRESS, + decimals = TOKEN_DECIMALS, + ) + }.mapLeft { exception -> + TangemLogger.withTag(TAG).e("Error", exception) + errorConverter.convert(exception) + } + } } \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPayCryptoCurrencyFactory.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPayCryptoCurrencyFactory.kt index 5f3223e514..1004e00447 100644 --- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPayCryptoCurrencyFactory.kt +++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/TangemPayCryptoCurrencyFactory.kt @@ -8,4 +8,5 @@ import com.tangem.domain.models.wallet.UserWallet interface TangemPayCryptoCurrencyFactory { fun create(userWallet: UserWallet, chainId: Int): Either + fun create(userWallet: UserWallet): Either } \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/selector/PortfolioSelectorModel.kt b/features/account/impl/src/main/java/com/tangem/features/account/selector/PortfolioSelectorModel.kt index b344d503d8..b70b65e101 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/selector/PortfolioSelectorModel.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/selector/PortfolioSelectorModel.kt @@ -12,6 +12,7 @@ import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.models.account.AccountId import com.tangem.domain.models.account.AccountStatus +import com.tangem.domain.models.account.filterCryptoPortfolio import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.account.PortfolioFetcher @@ -179,13 +180,10 @@ internal class PortfolioSelectorModel @Inject constructor( } } - portfolio.accountsBalance.accountStatuses.forEach { accountStatus -> + portfolio.accountsBalance.accountStatuses.filterCryptoPortfolio().forEach { accountStatus -> val isEnabledByFeature = isEnabled(wallet, accountStatus) val account = accountStatus.account - val accountBalance = when (accountStatus) { - is AccountStatus.CryptoPortfolio -> accountStatus.tokenList.totalFiatBalance - is AccountStatus.Payment -> accountStatus.value.totalFiatBalance - } + val accountBalance = accountStatus.tokenList.totalFiatBalance val accountItemUM = AccountPortfolioItemUMConverter( onClick = { selectorController.selectAccount(account.accountId) }, appCurrency = appCurrency, diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/converter/AvailableToAddDataConverter.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/converter/AvailableToAddDataConverter.kt index a6b02044fb..946ab12577 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/converter/AvailableToAddDataConverter.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/converter/AvailableToAddDataConverter.kt @@ -8,6 +8,7 @@ import com.tangem.domain.markets.TokenMarketParams import com.tangem.domain.models.account.Account import com.tangem.domain.models.account.AccountId import com.tangem.domain.models.account.AccountStatus +import com.tangem.domain.models.account.filterCryptoPortfolio import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId @@ -28,7 +29,7 @@ internal class AvailableToAddDataConverter @Inject constructor( availableNetworks: Set, marketParams: TokenMarketParams, ): AvailableToAddData { - suspend fun AccountStatus.getAvailableToAddAccount(wallet: UserWallet): AvailableToAddAccount? { + suspend fun AccountStatus.CryptoPortfolio.getAvailableToAddAccount(wallet: UserWallet): AvailableToAddAccount? { val currencies = availableNetworks .mapNotNull { network -> createCryptoCurrency( @@ -64,7 +65,7 @@ internal class AvailableToAddDataConverter @Inject constructor( val (_, balance) = entry val wallet = balance.userWallet val filteredNetworks = wallet.filteredAvailableNetworks(availableNetworks) - val accounts = balance.accountsBalance.accountStatuses + val accounts = balance.accountsBalance.accountStatuses.filterCryptoPortfolio() val availableToAddAccounts: Map = accounts .mapNotNull { accountStatus -> val availableToAddAccount = accountStatus.getAvailableToAddAccount(wallet) ?: return@mapNotNull null @@ -103,17 +104,13 @@ internal class AvailableToAddDataConverter @Inject constructor( userWallet: UserWallet, network: TokenMarketInfo.Network, marketParams: TokenMarketParams, - account: Account, + account: Account.CryptoPortfolio, ): CryptoCurrency? { - val derivationIndex = when (account) { - is Account.CryptoPortfolio -> account.derivationIndex - is Account.Payment -> TODO("[REDACTED_JIRA]") - } return getTokenMarketCryptoCurrency( userWalletId = userWallet.walletId, tokenMarketParams = marketParams, network = network, - accountIndex = derivationIndex, + accountIndex = account.derivationIndex, ) } } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioDelegate.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioDelegate.kt index 50cf8f9881..e1ed13a89e 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioDelegate.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioDelegate.kt @@ -18,6 +18,7 @@ import com.tangem.domain.markets.TokenMarketInfo import com.tangem.domain.markets.TokenMarketParams import com.tangem.domain.models.account.Account import com.tangem.domain.models.account.AccountStatus +import com.tangem.domain.models.account.filterCryptoPortfolio import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet @@ -192,15 +193,12 @@ internal class MarketsPortfolioDelegate @AssistedInject constructor( }.distinctUntilChanged() private fun AccountStatusList.filterByRawID(): List { - fun AccountStatus.filterByRawID(): List = when (this) { - is AccountStatus.CryptoPortfolio -> this.tokenList.flattenCurrencies() - .filter { status -> - val currencyId = status.currency.id.rawCurrencyId ?: return@filter false - getTokenIdIfL2Network(currencyId.value) == currencyRawId.value - } - is AccountStatus.Payment -> TODO("[REDACTED_JIRA]") - } - return accountStatuses.map { accountStatus -> + fun AccountStatus.CryptoPortfolio.filterByRawID(): List = tokenList.flattenCurrencies() + .filter { status -> + val currencyId = status.currency.id.rawCurrencyId ?: return@filter false + getTokenIdIfL2Network(currencyId.value) == currencyRawId.value + } + return accountStatuses.filterCryptoPortfolio().map { accountStatus -> AccountWithAdded( accountStatus = accountStatus, addedCurrency = accountStatus.filterByRawID(), diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt index 73df61850a..6d844d1315 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt @@ -13,9 +13,13 @@ import com.tangem.core.decompose.navigation.Router import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase -import com.tangem.domain.models.account.filterCryptoPortfolio +import com.tangem.domain.models.account.AccountStatus +import com.tangem.domain.models.account.PaymentAccountStatusValue +import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.CryptoCurrencyAddress +import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.isLocked +import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory import com.tangem.domain.qrscanning.models.SourceType import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase @@ -65,6 +69,7 @@ internal class SendDestinationModel @Inject constructor( private val listenToQrScanningUseCase: ListenToQrScanningUseCase, private val parseQrCodeUseCase: ParseQrCodeUseCase, private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase, + private val tangemPayCryptoCurrencyFactory: TangemPayCryptoCurrencyFactory, private val analyticsEventHandler: AnalyticsEventHandler, private val multiAccountStatusListSupplier: MultiAccountStatusListSupplier, ) : Model(), SendDestinationClickIntents { @@ -217,8 +222,6 @@ internal class SendDestinationModel @Inject constructor( flow = getWalletsUseCase().conflate(), flow2 = multiAccountStatusListSupplier().conflate(), ) { wallets, accountStatusLists -> - val cryptoCurrencyNetwork = cryptoCurrency.network - coroutineScope { accountStatusLists.mapNotNull { accountStatusList -> val wallet = wallets @@ -227,35 +230,55 @@ internal class SendDestinationModel @Inject constructor( ?: return@mapNotNull null async { - accountStatusList.flattenCurrencies() - .filter { it.currency.network.rawId == cryptoCurrencyNetwork.rawId } - .mapNotNull { cryptoCurrencyStatus -> - val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value - ?: return@mapNotNull null - - // Find the corresponding account from accountStatuses - val account = accountStatusList.accountStatuses - .filterCryptoPortfolio() - .firstOrNull { accountStatus -> - accountStatus.tokenList - .flattenCurrencies() - .any { it.currency.id == cryptoCurrencyStatus.currency.id } - }?.account - - DestinationWalletUM( - name = wallet.name, - address = address, - cryptoCurrency = cryptoCurrencyStatus.currency, - userWalletId = wallet.walletId, - account = account, - ) + accountStatusList.accountStatuses.flatMap { accountStatus -> + when (accountStatus) { + is AccountStatus.CryptoPortfolio -> accountStatus.getDestinationWalletUM(wallet) + is AccountStatus.Payment -> listOfNotNull(accountStatus.getDestinationWalletUM(wallet)) } + } } }.awaitAll().flatten() } }.flowOn(dispatchers.default) } + private fun AccountStatus.CryptoPortfolio.getDestinationWalletUM(wallet: UserWallet): List { + return this.flattenCurrencies() + .filter { it.currency.network.rawId == cryptoCurrency.network.rawId } + .mapNotNull { cryptoCurrencyStatus -> + val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value + ?: return@mapNotNull null + DestinationWalletUM( + name = wallet.name, + address = address, + cryptoCurrency = cryptoCurrencyStatus.currency, + userWalletId = wallet.walletId, + account = account, + ) + } + } + + private fun AccountStatus.Payment.getDestinationWalletUM(wallet: UserWallet): DestinationWalletUM? { + val contractAddress = (cryptoCurrency as? CryptoCurrency.Token)?.contractAddress ?: return null + val address = when (val status = this.value) { + is PaymentAccountStatusValue.Loaded -> status.cryptoBalance.depositAddress + is PaymentAccountStatusValue.Locked -> status.cryptoBalance.depositAddress + else -> return null + } + val currency = tangemPayCryptoCurrencyFactory.create(wallet).getOrNull() ?: return null + return if (contractAddress.equals(currency.contractAddress, true)) { + DestinationWalletUM( + name = wallet.name, + address = address, + cryptoCurrency = currency, + userWalletId = wallet.walletId, + account = account, + ) + } else { + null + } + } + private fun validate(address: String, memo: String?, type: EnterAddressSource? = null) { modelScope.launch { _uiState.update(SendDestinationValidationStartedTransformer) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/converters/SendRecipientWalletListConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/converters/SendRecipientWalletListConverter.kt index 5b47c7de51..6ac08e8caf 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/converters/SendRecipientWalletListConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/converters/SendRecipientWalletListConverter.kt @@ -35,10 +35,11 @@ internal class SendRecipientWalletListConverter( return this.filterNotNull() .filter { destinationWallet -> val isCoin = destinationWallet.cryptoCurrency is CryptoCurrency.Coin + val isPaymentAccount = destinationWallet.account is Account.Payment val isNotSameAddress = destinationWallet.address != senderAddress val isNotBlankAddress = destinationWallet.address.isNotBlank() - isNotBlankAddress && isCoin && (isNotSameAddress || isSelfSendAvailable) + isNotBlankAddress && (isCoin || isPaymentAccount) && (isNotSameAddress || isSelfSendAvailable) } .groupBy { item -> item.name } .values.map { wallets ->