From 68d6a658664b4d560cbacd23b851fb70f3df972e Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 8 Jul 2025 11:13:46 +0500 Subject: [PATCH 01/21] Updated on 2026-08-14 --- .../send/v2/sendnft/analytics/NFTSendAnalyticEvents.kt | 7 +++++++ .../send/v2/sendnft/analytics/NFTSendAnalyticHelper.kt | 2 ++ 2 files changed, 9 insertions(+) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticEvents.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticEvents.kt index ec215b0478..de2ae7cbb4 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticEvents.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticEvents.kt @@ -2,8 +2,11 @@ package com.tangem.features.send.v2.sendnft.analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN import com.tangem.core.analytics.models.AnalyticsParam.Key.FEE_TYPE +import com.tangem.core.analytics.models.AnalyticsParam.Key.NONCE import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM +import com.tangem.core.ui.extensions.capitalize import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents /** @@ -18,11 +21,15 @@ internal sealed class NFTSendAnalyticEvents( data class TransactionScreenOpened( val token: String, val feeType: AnalyticsParam.FeeType, + val blockchain: String, + val nonceNotEmpty: Boolean, ) : NFTSendAnalyticEvents( event = "NFT Sent Screen Opened", params = mapOf( TOKEN_PARAM to token, FEE_TYPE to feeType.value, + BLOCKCHAIN to blockchain, + NONCE to nonceNotEmpty.toString().capitalize(), ), ) } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticHelper.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticHelper.kt index f19c77aec4..b9e53dda49 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticHelper.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/analytics/NFTSendAnalyticHelper.kt @@ -26,6 +26,8 @@ internal class NFTSendAnalyticHelper @Inject constructor( NFTSendAnalyticEvents.TransactionScreenOpened( token = cryptoCurrency.symbol, feeType = feeType, + blockchain = cryptoCurrency.network.name, + nonceNotEmpty = feeSelectorUM.nonce != null, ), ) analyticsEventHandler.send( From 66f1a810c6a791ed5f6b79d74418813bcc6310df Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Jul 2025 19:33:34 +0500 Subject: [PATCH 02/21] Updated on 2026-08-14 --- .../staking/impl/presentation/ui/StakingInitialInfoContent.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt index e984f97d3d..1cccaf852d 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt @@ -240,6 +240,7 @@ private fun StakingRewardBlock( title = resourceReference(R.string.staking_rewards), text = text, iconRes = R.drawable.ic_chevron_right_24.takeIf { isShowIcon }, + onIconClick = onRewardsClick, textColor = textColor, modifier = Modifier .clip(TangemTheme.shapes.roundedCornersXMedium) From 4e2d28e757ce7a1ab328b0c6605a4285bd3e36b9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Jul 2025 19:45:37 +0500 Subject: [PATCH 03/21] Updated on 2026-08-14 --- .../feature/swap/ui/SwapSelectTokenScreen.kt | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt index 03ebfc66ca..f2821d83a4 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt @@ -16,10 +16,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.components.SpacerW2 import com.tangem.core.ui.components.appbar.ExpandableSearchView +import com.tangem.core.ui.components.atoms.text.EllipsisText import com.tangem.core.ui.components.currency.icon.CurrencyIcon import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.decorations.roundedShapeItemDecoration @@ -222,10 +224,11 @@ private fun TokenItem( Column( modifier = Modifier + .weight(1f) .align(Alignment.CenterVertically) .padding(start = TangemTheme.dimens.spacing12), ) { - Text( + EllipsisText( text = token.name, style = TangemTheme.typography.subtitle1, color = if (token.available) { @@ -235,20 +238,19 @@ private fun TokenItem( }, ) SpacerW2() - Text( + EllipsisText( text = token.symbol, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) } - Spacer(modifier = Modifier.weight(1f)) - if (token.addedTokenBalanceData != null) { Column( horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.Center, - modifier = Modifier.padding(start = TangemTheme.dimens.spacing8), + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing8), ) { Text( text = token.addedTokenBalanceData.amountEquivalent.orEmpty().orMaskWithStars( @@ -256,6 +258,9 @@ private fun TokenItem( !token.addedTokenBalanceData.amountEquivalent.isNullOrEmpty(), ), style = TangemTheme.typography.subtitle1, + maxLines = 1, + softWrap = false, + overflow = TextOverflow.Visible, color = if (token.available) { TangemTheme.colors.text.primary1 } else { @@ -268,6 +273,7 @@ private fun TokenItem( maskWithStars = token.addedTokenBalanceData.isBalanceHidden && !token.addedTokenBalanceData.amount.isNullOrEmpty(), ), + maxLines = 1, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) @@ -284,12 +290,11 @@ private val token = TokenToSelectState.TokenToSelect( showCustomBadge = false, ), id = "", - name = "USDC", + name = "Optimistic Ethereum (ETH)", symbol = "USDC", addedTokenBalanceData = TokenBalanceData( amount = "15 000 $", - amountEquivalent = "15 000 " + - "USDT", + amountEquivalent = "15 000 USDT", isBalanceHidden = false, ), ) From 3da8569ae6a8818280682a3aa737956523f54861 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 16 Jul 2025 11:48:05 +0300 Subject: [PATCH 04/21] Updated on 2026-08-14 --- LICENSE | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..b64d4ccedf --- /dev/null +++ b/LICENSE @@ -0,0 +1,18 @@ +Tangem Proprietary and Confidential +Copyright (c) 2025 Tangem AG. All rights reserved. +================================================================================ +NOTICE: THIS IS NOT AN OPEN SOURCE LICENSE. +This software, including all source code, documentation, and accompanying files (the "Software") stored in this repository, is the proprietary and confidential information of Tangem AG ("Tangem"). The publication of this Software on a public platform does not grant any license, express or implied. +1. Definitions. +"Software" refers to all contents of this repository. +"Protocol" refers to the proprietary communication protocol, including the sequence of, data structures, and cryptographic methods used to interact with Tangem hardware. +"SDKs" refer to the official Software Development Kits (e.g., for Android, iOS, React Native) provided by Tangem in separate repositories and under their own distinct licenses. +2. Strict Prohibition of Use. Any use of the Software, in whole or in part, is strictly prohibited without the express prior written consent of Tangem. This prohibition includes, but is not limited to, the following actions for any purpose, whether commercial or non-commercial: +- Copying, modifying, or merging the Software. +- Publishing, distributing, or sublicensing the Software. +- Selling copies of the Software. +- Creating derivative works based on the Software. +3. Protocol Usage. Reverse-engineering, analyzing, decompiling, or attempting to recreate the Protocol is strictly prohibited. The only authorized method for third-party applications to interact with Tangem hardware is by using the official Tangem SDKs. The SDKs are governed by their own license terms (e.g., MIT License), which permit their use in third-party applications. This Software is provided for transparency and reference purposes only. +4. No Implied Rights. The act of Tangem publishing the Software to a public repository does not grant users any implied rights or licenses to use, modify, or distribute the Software. All rights not expressly granted by Tangem in a separate written agreement are reserved. +5. Legal Action. Unauthorized use, reproduction, or distribution of the Software or the Protocol may result in severe civil and criminal penalties, and will be prosecuted to the maximum extent possible under applicable law. +For inquiries about licensing or permissions to use the Software or the Protocol, please contact Tangem AG at: legal@tangem.com. From 352ae036f3e40fb2d79fa729cee3e00d9cd9b997 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 16 Jul 2025 13:17:41 +0400 Subject: [PATCH 05/21] Updated on 2026-08-14 --- .../managetokens/SaveManagedTokensUseCase.kt | 76 ++++++------------- .../intents/WalletWarningsClickIntents.kt | 51 +++++++++++-- 2 files changed, 69 insertions(+), 58 deletions(-) diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt index 0d15310592..2599d4675c 100644 --- a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt +++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt @@ -34,36 +34,33 @@ class SaveManagedTokensUseCase( currenciesToAdd: Map>, currenciesToRemove: Map>, ): Either = Either.catch { - val removingCurrencies = currenciesToRemove.mapToCryptoCurrencies(userWalletId) - val addingCurrencies = currenciesToAdd.mapToCryptoCurrencies(userWalletId) + if (currenciesToRemove.isNotEmpty()) { + val removingCurrencies = currenciesToRemove.mapToCryptoCurrencies(userWalletId) - derivationsRepository.derivePublicKeysByNetworks( - userWalletId = userWalletId, - networks = currenciesToAdd.values.flatten(), - ) + currenciesRepository.removeCurrencies(userWalletId = userWalletId, currencies = removingCurrencies) - val existingCurrencies = currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId) + removeCurrenciesFromWalletManager(userWalletId = userWalletId, currencies = removingCurrencies) + } - val newCurrenciesList = existingCurrencies - .filterNot(removingCurrencies::contains) - .toMutableList() - .also { it.addAll(addingCurrencies) } + if (currenciesToAdd.isNotEmpty()) { + derivationsRepository.derivePublicKeysByNetworks( + userWalletId = userWalletId, + networks = currenciesToAdd.values.flatten(), + ) - currenciesRepository.saveNewCurrenciesList(userWalletId, newCurrenciesList) + val addingCurrencies = currenciesToAdd.mapToCryptoCurrencies(userWalletId) - removeCurrenciesFromWalletManager( - userWalletId = userWalletId, - currencies = removingCurrencies.filterNot(existingCurrencies::contains), - ) - refreshUpdatedNetworks( - userWalletId = userWalletId, - existingCurrencies = existingCurrencies, - currenciesToAdd = addingCurrencies, - ) + val savedCurrencies = currenciesRepository.addCurrencies( + userWalletId = userWalletId, + currencies = addingCurrencies, + ) - refreshUpdatedYieldBalances(userWalletId, existingCurrencies) + refreshUpdatedNetworks(userWalletId = userWalletId, addedCurrencies = savedCurrencies) - refreshUpdatedQuotes(addingCurrencies) + refreshUpdatedYieldBalances(userWalletId = userWalletId, addedCurrencies = savedCurrencies) + + refreshUpdatedQuotes(addedCurrencies = savedCurrencies) + } } private suspend fun removeCurrenciesFromWalletManager( @@ -83,43 +80,30 @@ class SaveManagedTokensUseCase( ) } - private suspend fun refreshUpdatedNetworks( - userWalletId: UserWalletId, - currenciesToAdd: List, - existingCurrencies: List, - ) { - val networksToUpdate = currenciesToAdd - .asSequence() - .filterIsInstance() - .map(CryptoCurrency.Token::network) - .filterTo(hashSetOf()) { hasCoinForNetwork(existingCurrencies, it) } - - val networkToUpdate = currenciesToAdd.map { it.network } - .subtract(existingCurrencies.map { it.network }.toSet()) - + private suspend fun refreshUpdatedNetworks(userWalletId: UserWalletId, addedCurrencies: List) { multiNetworkStatusFetcher( MultiNetworkStatusFetcher.Params( userWalletId = userWalletId, - networks = networksToUpdate + networkToUpdate, + networks = addedCurrencies.map(CryptoCurrency::network).toSet(), ), ) } private suspend fun refreshUpdatedYieldBalances( userWalletId: UserWalletId, - existingCurrencies: List, + addedCurrencies: List, ) { if (tokensFeatureToggles.isStakingLoadingRefactoringEnabled) { multiYieldBalanceFetcher( params = MultiYieldBalanceFetcher.Params( userWalletId = userWalletId, - currencyIdWithNetworkMap = existingCurrencies.associateTo(hashMapOf()) { it.id to it.network }, + currencyIdWithNetworkMap = addedCurrencies.associateTo(hashMapOf()) { it.id to it.network }, ), ) } else { stakingRepository.fetchMultiYieldBalance( userWalletId = userWalletId, - cryptoCurrencies = existingCurrencies, + cryptoCurrencies = addedCurrencies, refresh = true, ) } @@ -134,16 +118,6 @@ class SaveManagedTokensUseCase( ) } - /** - * Determines if the [existingCurrencies] list contains a coin that corresponds - * to the given [network]. - */ - private fun hasCoinForNetwork(existingCurrencies: List, network: Network): Boolean { - return existingCurrencies.any { currency -> - currency is CryptoCurrency.Coin && currency.network == network - } - } - private suspend fun Map>.mapToCryptoCurrencies( userWalletId: UserWalletId, ): List { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt index 724ac04b1b..a75781610c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt @@ -18,12 +18,15 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.promo.ShouldShowPromoWalletUseCase import com.tangem.domain.promo.models.PromoId +import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.settings.NeverToSuggestRateAppUseCase import com.tangem.domain.settings.RemindToRateAppLaterUseCase +import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType import com.tangem.domain.wallets.models.UnlockWalletsError import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.requireColdWallet import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase @@ -40,6 +43,9 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -103,6 +109,8 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase, private val urlOpener: UrlOpener, private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, + private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, + private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, private val appRouter: AppRouter, ) : BaseWalletClickIntents(), WalletWarningsClickIntents { @@ -146,13 +154,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( ).fold( ifLeft = { Timber.e(it, "Failed to derive public keys") }, ifRight = { - multiNetworkStatusFetcher( - params = MultiNetworkStatusFetcher.Params( - userWalletId = userWallet.walletId, - networks = missedAddressCurrencies.map(CryptoCurrency::network).toSet(), - ), - ) - .onLeft { Timber.e("Unable to refresh token list: $it") } + fetchCryptoCurrencies(userWalletId = userWallet.walletId, currencies = missedAddressCurrencies) }, ) } @@ -369,6 +371,41 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( } } + private suspend fun fetchCryptoCurrencies(userWalletId: UserWalletId, currencies: List) { + coroutineScope { + listOf( + async { + multiNetworkStatusFetcher( + params = MultiNetworkStatusFetcher.Params( + userWalletId = userWalletId, + networks = currencies.map(CryptoCurrency::network).toSet(), + ), + ) + .onLeft { Timber.e("Unable to fetch networks: $it") } + }, + async { + multiQuoteStatusFetcher( + params = MultiQuoteStatusFetcher.Params( + currenciesIds = currencies.mapNotNull { it.id.rawCurrencyId }.toSet(), + appCurrencyId = null, + ), + ) + .onLeft { Timber.e("Unable to fetch quotes: $it") } + }, + async { + multiYieldBalanceFetcher( + params = MultiYieldBalanceFetcher.Params( + userWalletId = userWalletId, + currencyIdWithNetworkMap = currencies.associate { it.id to it.network }, + ), + ) + .onLeft { Timber.e("Unable to fetch yield balances: $it") } + }, + ) + .awaitAll() + } + } + private fun getSelectedUserWallet(): UserWallet? { val userWalletId = stateHolder.getSelectedWalletId() return getUserWalletUseCase(userWalletId).getOrElse { From e5f25fdfb155d93004d1f53d3a49ce1ef4c14f2a Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 16 Jul 2025 17:23:44 +0500 Subject: [PATCH 06/21] Updated on 2026-08-14 --- .../datasource/info/AndroidAppInfoProvider.kt | 3 +- .../data/common/currency/UserTokensSaver.kt | 1 + .../repository/DefaultCurrenciesRepository.kt | 78 ++++++++++++++++--- .../managetokens/SaveManagedTokensUseCase.kt | 4 +- .../domain/markets/SaveMarketTokensUseCase.kt | 3 +- .../tokens/AddCryptoCurrenciesUseCase.kt | 7 +- .../tokens/repository/CurrenciesRepository.kt | 20 +++++ 7 files changed, 97 insertions(+), 19 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/info/AndroidAppInfoProvider.kt b/core/datasource/src/main/java/com/tangem/datasource/info/AndroidAppInfoProvider.kt index 08d960e333..0bc5462596 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/info/AndroidAppInfoProvider.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/info/AndroidAppInfoProvider.kt @@ -1,7 +1,6 @@ package com.tangem.datasource.info import android.os.Build -import com.tangem.utils.SupportedLanguages import com.tangem.utils.info.AppInfoProvider import com.tangem.utils.version.AppVersionProvider import java.util.* @@ -17,7 +16,7 @@ internal class AndroidAppInfoProvider @Inject constructor( override val osVersion: String get() = Build.VERSION.RELEASE override val language: String - get() = SupportedLanguages.getCurrentSupportedLanguageCode() + get() = Locale.getDefault().language override val timezone: String get() = TimeZone.getDefault().id override val appVersion: String diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt index 487b96f427..30f78dac03 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt @@ -38,6 +38,7 @@ class UserTokensSaver( userWalletId = userWalletId, response = response, ) + store(userWalletId, enrichedUserTokensResponse, false) push(userWalletId, enrichedUserTokensResponse, false) } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 429979f670..c2135f836f 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -71,7 +71,7 @@ internal class DefaultCurrenciesRepository( isGroupedByNetwork = isGroupedByNetwork, isSortedByBalance = isSortedByBalance, ) - userTokensSaver.storeAndPush(userWalletId, response) + userTokensSaver.store(userWalletId, response) } override suspend fun saveNewCurrenciesList(userWalletId: UserWalletId, currencies: List) { @@ -129,6 +129,61 @@ internal class DefaultCurrenciesRepository( currenciesToAdd } + override suspend fun saveNewCurrenciesListCache(userWalletId: UserWalletId, currencies: List) { + withContext(dispatchers.io) { + val savedResponse = requireNotNull( + value = getSavedUserTokensResponseSync(key = userWalletId), + lazyMessage = { "Saved tokens empty. Can not perform add currencies action." }, + ) + + val newCurrencies = populateCurrenciesWithMissedCoins(currencies) + + val updatedResponse = savedResponse.copy( + tokens = newCurrencies.map(userTokensResponseFactory::createResponseToken), + ) + userTokensSaver.store( + userWalletId = userWalletId, + response = updatedResponse, + ) + + fetchExpressAssetsByNetworkIds( + userWallet = userWalletsStore.getSyncStrict(key = userWalletId), + userTokens = updatedResponse, + ) + } + } + + override suspend fun addCurrenciesCache( + userWalletId: UserWalletId, + currencies: List, + ): List = withContext(dispatchers.io) { + val savedCurrencies = requireNotNull( + value = getSavedUserTokensResponseSync(key = userWalletId), + lazyMessage = { "Saved tokens empty. Can not perform add currencies action" }, + ) + + val currenciesToAdd = filterAlreadyAddedCurrencies( + savedCurrencies = savedCurrencies.tokens, + currenciesToAdd = populateCurrenciesWithMissedCoins(currencies = currencies), + ) + + val updatedResponse = savedCurrencies.copy( + tokens = savedCurrencies.tokens + currenciesToAdd.map(userTokensResponseFactory::createResponseToken), + ) + + userTokensSaver.store( + userWalletId = userWalletId, + response = updatedResponse, + ) + + fetchExpressAssetsByNetworkIds( + userWallet = userWalletsStore.getSyncStrict(key = userWalletId), + userTokens = updatedResponse, + ) + + currenciesToAdd + } + private fun filterAlreadyAddedCurrencies( savedCurrencies: List, currenciesToAdd: List, @@ -589,17 +644,16 @@ internal class DefaultCurrenciesRepository( } override suspend fun syncTokens(userWalletId: UserWalletId) { - val savedCurrencies = requireNotNull( - value = getSavedUserTokensResponseSync(key = userWalletId), - lazyMessage = { "Saved tokens empty. Can not perform add currencies action" }, - ) - userTokensSaver.push( - userWalletId = userWalletId, - response = savedCurrencies, - onFailSend = { - throw IllegalStateException("Unable to push tokens") - }, - ) + runCatching { + val savedCurrencies = requireNotNull( + value = getSavedUserTokensResponseSync(key = userWalletId), + lazyMessage = { "Saved tokens empty. Can not perform add currencies action" }, + ) + userTokensSaver.storeAndPush( + userWalletId = userWalletId, + response = savedCurrencies, + ) + } } override fun getCardTypesResolver(userWalletId: UserWalletId): CardTypesResolver { diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt index 2599d4675c..8db4bc4638 100644 --- a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt +++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt @@ -50,7 +50,7 @@ class SaveManagedTokensUseCase( val addingCurrencies = currenciesToAdd.mapToCryptoCurrencies(userWalletId) - val savedCurrencies = currenciesRepository.addCurrencies( + val savedCurrencies = currenciesRepository.addCurrenciesCache( userWalletId = userWalletId, currencies = addingCurrencies, ) @@ -87,6 +87,8 @@ class SaveManagedTokensUseCase( networks = addedCurrencies.map(CryptoCurrency::network).toSet(), ), ) + + currenciesRepository.syncTokens(userWalletId) } private suspend fun refreshUpdatedYieldBalances( diff --git a/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt b/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt index dd0ad48ebc..ee87a2bd6a 100644 --- a/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt +++ b/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt @@ -66,7 +66,7 @@ class SaveMarketTokensUseCase( ) } - val savedCurrencies = currenciesRepository.addCurrencies( + val savedCurrencies = currenciesRepository.addCurrenciesCache( userWalletId = userWalletId, currencies = addedCurrencies, ) @@ -86,6 +86,7 @@ class SaveMarketTokensUseCase( networks = addedCurrencies.map(CryptoCurrency::network).toSet(), ), ) + currenciesRepository.syncTokens(userWalletId) } private suspend fun refreshUpdatedYieldBalances( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt index 290a8de379..29b564c683 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt @@ -119,7 +119,7 @@ class AddCryptoCurrenciesUseCase( * Refreshes the network statuses for tokens that have corresponding coins in the * [existingCurrencies] list. */ - private suspend fun Raise.refreshUpdatedNetworks( + private suspend fun refreshUpdatedNetworks( userWalletId: UserWalletId, currencyToAdd: CryptoCurrency, existingCurrencies: List, @@ -140,7 +140,8 @@ class AddCryptoCurrenciesUseCase( networks = setOfNotNull(networksToUpdate, networkToUpdate), ), ) - .bind() + + currenciesRepository.syncTokens(userWalletId) } private suspend fun refreshUpdatedYieldBalances(userWalletId: UserWalletId, addedCurrency: CryptoCurrency) { @@ -191,7 +192,7 @@ class AddCryptoCurrenciesUseCase( private suspend fun Raise.addCurrencies(userWalletId: UserWalletId, currency: CryptoCurrency) { catch( - { currenciesRepository.addCurrencies(userWalletId, listOf(currency)) }, + { currenciesRepository.addCurrenciesCache(userWalletId, listOf(currency)) }, ) { raise(it) } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt index 1807cc4a60..99ac7faff7 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt @@ -52,6 +52,26 @@ interface CurrenciesRepository { */ suspend fun addCurrencies(userWalletId: UserWalletId, currencies: List): List + /** + * Saves the given list of cryptocurrencies for a specific multi-currency user wallet. + * + * @param userWalletId The unique identifier of the user wallet. + * @param currencies The list of cryptocurrencies to be saved. + */ + @Deprecated("Tech debt") + suspend fun saveNewCurrenciesListCache(userWalletId: UserWalletId, currencies: List) + + /** + * Add currencies to a specific user wallet. + * + * @param userWalletId The unique identifier of the user wallet. + * @param currencies The currencies which must be added. + * @throws DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. + */ + @Deprecated("Tech debt") + suspend fun addCurrenciesCache(userWalletId: UserWalletId, currencies: List): List + /** * Removes currency from a specific user wallet. * From 2669dd7831f20f016e86d7ead5104a5d72f99780 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 16 Jul 2025 17:26:11 +0500 Subject: [PATCH 07/21] Updated on 2026-08-14 --- .../data/tokens/repository/DefaultCurrenciesRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index c2135f836f..9344253d56 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -71,7 +71,7 @@ internal class DefaultCurrenciesRepository( isGroupedByNetwork = isGroupedByNetwork, isSortedByBalance = isSortedByBalance, ) - userTokensSaver.store(userWalletId, response) + userTokensSaver.storeAndPush(userWalletId, response) } override suspend fun saveNewCurrenciesList(userWalletId: UserWalletId, currencies: List) { From 89ac414e39c36341af72103013ff9395787c98ad Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 16 Jul 2025 17:33:17 +0500 Subject: [PATCH 08/21] Updated on 2026-08-14 --- .../com/tangem/data/common/currency/UserTokensSaver.kt | 1 - .../domain/tokens/repository/MockCurrenciesRepository.kt | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt index 30f78dac03..487b96f427 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/UserTokensSaver.kt @@ -38,7 +38,6 @@ class UserTokensSaver( userWalletId = userWalletId, response = response, ) - store(userWalletId, enrichedUserTokensResponse, false) push(userWalletId, enrichedUserTokensResponse, false) } diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt index bdff6776cf..dfcb15bef7 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt @@ -53,6 +53,13 @@ internal class MockCurrenciesRepository( currencies: List, ): List = emptyList() + override suspend fun saveNewCurrenciesListCache(userWalletId: UserWalletId, currencies: List) = Unit + + override suspend fun addCurrenciesCache( + userWalletId: UserWalletId, + currencies: List, + ): List = emptyList() + override suspend fun removeCurrency(userWalletId: UserWalletId, currency: CryptoCurrency) { removeCurrencyResult.onLeft { throw it } } From b3ea7fa2328cc1d5ebd30b66357ed1584b7c6132 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 16 Jul 2025 17:20:28 +0400 Subject: [PATCH 09/21] Updated on 2026-08-14 --- .../feature/swap/domain/SwapInteractorImpl.kt | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index cc21bc6469..ffec280d8a 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -21,6 +21,7 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.quote.QuoteStatus import com.tangem.domain.quotes.QuotesRepository +import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.tokens.FetchCurrencyStatusUseCase import com.tangem.domain.tokens.GetCurrencyCheckUseCase import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase @@ -66,6 +67,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( private val createApprovalTransactionUseCase: CreateApprovalTransactionUseCase, private val isDemoCardUseCase: IsDemoCardUseCase, private val quotesRepository: QuotesRepository, + private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val swapTransactionRepository: SwapTransactionRepository, private val currencyChecksRepository: CurrencyChecksRepository, private val appCurrencyRepository: AppCurrencyRepository, @@ -324,7 +326,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( ifRight = { quotes -> quotes.allowanceContract?.let { isAllowedToSpend(networkId, fromToken.currency, amount, it) - } ?: true + } != false }, ifLeft = { false }, ) @@ -1279,7 +1281,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( network = network, userWallet = userWallet, ).getOrNull() ?: error("unable to calculate fee") - } catch (e: IllegalStateException) { + } catch (_: IllegalStateException) { getEthSpecificFeeUseCase( userWallet = userWallet, cryptoCurrency = fromToken, @@ -1796,20 +1798,41 @@ internal class SwapInteractorImpl @AssistedInject constructor( } private suspend fun getQuotes(vararg ids: CryptoCurrency.ID): Map { - val set = ids.mapNotNull { it.rawCurrencyId } - .toSet() + val set = ids.mapNotNullTo(destination = hashSetOf(), transform = CryptoCurrency.ID::rawCurrencyId) .getQuotesOrEmpty() return ids .mapNotNull { id -> - set.find { it.rawCurrencyId == id.rawCurrencyId && it.value is QuoteStatus.Data } - ?.let { id to (it.value as QuoteStatus.Data).fiatRate } + val found = set.find { it.rawCurrencyId == id.rawCurrencyId && it.value is QuoteStatus.Data } + ?: return@mapNotNull null + + id to (found.value as QuoteStatus.Data).fiatRate } .toMap() } private suspend fun Set.getQuotesOrEmpty(): Set { - return runCatching { quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = this) } + return runCatching { + val cachedQuotes = quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = this) + + val allQuotesFound = cachedQuotes?.all { it.value !is QuoteStatus.Empty } == true + + if (allQuotesFound) return@runCatching cachedQuotes + + val currenciesIds = if (cachedQuotes.isNullOrEmpty()) { + this + } else { + cachedQuotes.mapNotNullTo(hashSetOf()) { + if (it.value is QuoteStatus.Empty) it.rawCurrencyId else null + } + } + + multiQuoteStatusFetcher( + params = MultiQuoteStatusFetcher.Params(currenciesIds = currenciesIds, appCurrencyId = null), + ) + + quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = this) + } .getOrNull() .orEmpty() } From 47f0bb91e65e22707cca4364480190524c222d6a Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 16 Jul 2025 21:35:35 +0500 Subject: [PATCH 10/21] Updated on 2026-08-14 --- .../DefaultNotificationsRepository.kt | 4 ++++ .../DefaultNotificationsRepositoryTest.kt | 23 +++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt b/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt index 661b93086d..d1f92e8262 100644 --- a/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt +++ b/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt @@ -68,6 +68,10 @@ internal class DefaultNotificationsRepository @Inject constructor( appId.value, NotificationApplicationCreateBody( pushToken = pushToken, + systemVersion = appInfoProvider.osVersion, + language = appInfoProvider.language, + timezone = appInfoProvider.timezone, + version = appInfoProvider.appVersion, ), ).getOrThrow() } diff --git a/data/notifications/src/test/java/com/tangem/data/notifications/DefaultNotificationsRepositoryTest.kt b/data/notifications/src/test/java/com/tangem/data/notifications/DefaultNotificationsRepositoryTest.kt index 7602280459..13d4dbb6b1 100644 --- a/data/notifications/src/test/java/com/tangem/data/notifications/DefaultNotificationsRepositoryTest.kt +++ b/data/notifications/src/test/java/com/tangem/data/notifications/DefaultNotificationsRepositoryTest.kt @@ -111,16 +111,20 @@ class DefaultNotificationsRepositoryTest { // GIVEN val appId = ApplicationId("test-app-id") val pushToken = "test-push-token" + coEvery { appInfoProvider.device } returns "test-device" + coEvery { appInfoProvider.osVersion } returns "11" + coEvery { appInfoProvider.language } returns "en" + coEvery { appInfoProvider.appVersion } returns "5.21.1" + coEvery { appInfoProvider.timezone } returns "UTC" coEvery { tangemTechApi.updatePushTokenForApplicationId( appId.value, NotificationApplicationCreateBody( pushToken = pushToken, - platform = null, - device = null, - systemVersion = null, - language = null, - timezone = null, + systemVersion = "11", + language = "en", + timezone = "UTC", + version = "5.21.1", ), ) } returns ApiResponse.Success(Unit) @@ -134,11 +138,10 @@ class DefaultNotificationsRepositoryTest { appId.value, NotificationApplicationCreateBody( pushToken = pushToken, - platform = null, - device = null, - systemVersion = null, - language = null, - timezone = null, + systemVersion = "11", + language = "en", + timezone = "UTC", + version = "5.21.1", ), ) } From 5e95f63d68196c6672384e59a662a18dfdac2de8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 16 Jul 2025 21:07:18 +0400 Subject: [PATCH 11/21] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 01294e530b..3d9798f74b 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -7,7 +7,7 @@ tangemBlockchainSdk = "releases-5.26.0-1109" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "releases-5.26.0-488" +tangemCardSdk = "releases-5.26.0-492" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ From fd4b5a3011091e77f35fa703d778b48c3d4c4447 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 17 Jul 2025 12:12:11 +0500 Subject: [PATCH 12/21] Updated on 2026-08-14 --- .../v2/subcomponents/notifications/model/NotificationsModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt index 726be9421e..58ce91e9fa 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt @@ -302,7 +302,7 @@ internal class NotificationsModel @Inject constructor( ) { val validationError = validateTransactionUseCase( userWalletId = userWalletId, - amount = enteredAmount.convertToSdkAmount(currency), + amount = sendingAmount.convertToSdkAmount(currency), fee = fee, memo = memo, destination = destinationAddress, From 8e824ed49013df1be074bca1bbaa36cf6c41c815 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 17 Jul 2025 14:47:18 +0500 Subject: [PATCH 13/21] Updated on 2026-08-14 --- .../tap/di/domain/WalletsDomainModule.kt | 4 +- .../BiometricUserWalletsListManager.kt | 5 + .../GeneralUserWalletsListManager.kt | 8 + .../RuntimeUserWalletsListManager.kt | 5 + .../tangem/tap/features/main/MainViewModel.kt | 4 +- .../wallets/legacy/UserWalletsListManager.kt | 3 + ...Case.kt => GetSavedWalletsCountUseCase.kt} | 20 ++- .../GetSavedWalletChangesUseCaseTest.kt | 76 ---------- .../GetSavedWalletsCountUseCaseTest.kt | 137 ++++++++++++++++++ .../model/WalletSettingsModel.kt | 14 -- 10 files changed, 171 insertions(+), 105 deletions(-) rename domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/{GetSavedWalletChangesUseCase.kt => GetSavedWalletsCountUseCase.kt} (55%) delete mode 100644 domain/wallets/src/test/java/com/tangem/domain/wallets/usecase/GetSavedWalletChangesUseCaseTest.kt create mode 100644 domain/wallets/src/test/java/com/tangem/domain/wallets/usecase/GetSavedWalletsCountUseCaseTest.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt index 684a7f0cf7..f2f9912270 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt @@ -231,8 +231,8 @@ internal object WalletsDomainModule { @Singleton fun providesGetSavedWalletChangesIdUseCase( userWalletsListManager: UserWalletsListManager, - ): GetSavedWalletChangesUseCase { - return GetSavedWalletChangesUseCase( + ): GetSavedWalletsCountUseCase { + return GetSavedWalletsCountUseCase( userWalletsListManager = userWalletsListManager, ) } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt index 2d52d344fe..4774cebee1 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt @@ -37,6 +37,11 @@ internal class BiometricUserWalletsListManager( .mapLatest { it.userWallets } .distinctUntilChanged() + override val savedWalletsCount: Flow + get() = state + .mapLatest { walletsCount } + .distinctUntilChanged() + override val userWalletsSync: List get() = state.value.userWallets diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt index 4cad89ba5d..1be31c95b6 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt @@ -60,6 +60,14 @@ internal class GeneralUserWalletsListManager( // As a result subscription occurs on empty flow, than will not change if user wallets are available .filter { requireImplementation.hasUserWallets } + override val savedWalletsCount: Flow + get() = implementation + .transformLatest { impl -> + if (impl != null) { + emitAll(impl.savedWalletsCount) + } + } + override val userWalletsSync: List get() = requireImplementation.userWalletsSync diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt index 4d211d9b88..43bd7646e0 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt @@ -20,6 +20,11 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager { .mapLatest { listOfNotNull(it.userWallet) } .distinctUntilChanged() + override val savedWalletsCount: Flow + get() = state + .mapLatest { walletsCount } + .distinctUntilChanged() + override val selectedUserWallet: Flow get() = state .mapLatest { it.userWallet } diff --git a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt index 404a978379..d3a65f5ace 100644 --- a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt @@ -39,7 +39,7 @@ import com.tangem.domain.staking.FetchStakingTokensUseCase import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.models.requireColdWallet import com.tangem.domain.wallets.usecase.AssociateWalletsWithApplicationIdUseCase -import com.tangem.domain.wallets.usecase.GetSavedWalletChangesUseCase +import com.tangem.domain.wallets.usecase.GetSavedWalletsCountUseCase import com.tangem.domain.wallets.usecase.UpdateRemoteWalletsInfoUseCase import com.tangem.feature.swap.analytics.StoriesEvents import com.tangem.features.onramp.deeplink.OnrampDeepLink @@ -82,7 +82,7 @@ internal class MainViewModel @Inject constructor( private val onrampDeepLinkFactory: OnrampDeepLink.Factory, private val notificationsToggles: NotificationsFeatureToggles, private val getApplicationIdUseCase: GetApplicationIdUseCase, - private val subscribeOnWalletsUseCase: GetSavedWalletChangesUseCase, + private val subscribeOnWalletsUseCase: GetSavedWalletsCountUseCase, private val associateWalletsWithApplicationIdUseCase: AssociateWalletsWithApplicationIdUseCase, private val updateRemoteWalletsInfoUseCase: UpdateRemoteWalletsInfoUseCase, private val sendPushTokenUseCase: SendPushTokenUseCase, diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManager.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManager.kt index bd117a8c8e..c61566c02c 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManager.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManager.kt @@ -15,6 +15,9 @@ interface UserWalletsListManager { /** [Flow] with all saved [UserWallet]s updates */ val userWallets: Flow> + /** Count saved wallets updates */ + val savedWalletsCount: Flow + /** [Flow] with selected [UserWallet] updates */ @Deprecated("You should provide the selected wallet via routing parameters due to the scalability of the features") val selectedUserWallet: Flow diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSavedWalletChangesUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSavedWalletsCountUseCase.kt similarity index 55% rename from domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSavedWalletChangesUseCase.kt rename to domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSavedWalletsCountUseCase.kt index afcfe40a89..8bc40b9305 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSavedWalletChangesUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSavedWalletsCountUseCase.kt @@ -4,23 +4,21 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.asLockable import com.tangem.domain.wallets.legacy.isLockedSync import com.tangem.domain.wallets.models.UserWallet -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.* -class GetSavedWalletChangesUseCase( +class GetSavedWalletsCountUseCase( private val userWalletsListManager: UserWalletsListManager, ) { operator fun invoke(): Flow> { - return userWalletsListManager.userWallets - .filter { + return userWalletsListManager.savedWalletsCount + .filter { count -> + if (count == 0) return@filter true userWalletsListManager.asLockable() ?: return@filter false - return@filter if (userWalletsListManager.isLockedSync.not()) { - it.isNotEmpty() - } else { - false - } + return@filter userWalletsListManager.isLockedSync.not() + } + .map { + userWalletsListManager.userWalletsSync } .distinctUntilChanged() } diff --git a/domain/wallets/src/test/java/com/tangem/domain/wallets/usecase/GetSavedWalletChangesUseCaseTest.kt b/domain/wallets/src/test/java/com/tangem/domain/wallets/usecase/GetSavedWalletChangesUseCaseTest.kt deleted file mode 100644 index 25e0cb17db..0000000000 --- a/domain/wallets/src/test/java/com/tangem/domain/wallets/usecase/GetSavedWalletChangesUseCaseTest.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.tangem.domain.wallets.usecase - -import com.google.common.truth.Truth.assertThat -import com.tangem.domain.wallets.legacy.UserWalletsListManager -import com.tangem.domain.wallets.legacy.asLockable -import com.tangem.domain.wallets.legacy.isLockedSync -import com.tangem.domain.wallets.models.UserWallet -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkStatic -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test - -class GetSavedWalletChangesUseCaseTest { - - private lateinit var useCase: GetSavedWalletChangesUseCase - private lateinit var userWalletsListManager: UserWalletsListManager - - @Before - fun setup() { - userWalletsListManager = mockk() - useCase = GetSavedWalletChangesUseCase(userWalletsListManager) - mockkStatic("com.tangem.domain.wallets.legacy.UserWalletsListManagerExtensionsKt") - } - - @Test - fun `GIVEN manager is not lockable WHEN invoke THEN return empty list`() = runTest { - // GIVEN - every { userWalletsListManager.isLockable } returns false - every { userWalletsListManager.userWallets } returns flowOf(emptyList()) - every { userWalletsListManager.isLockedSync } returns false - every { userWalletsListManager.asLockable() } returns null - - // WHEN - val result = useCase().firstOrNull() - - // THEN - assertThat(result).isNull() - } - - @Test - fun `GIVEN manager is locked WHEN invoke THEN return empty list`() = runTest { - // GIVEN - val mockLockable = mockk() - every { userWalletsListManager.isLockable } returns true - every { userWalletsListManager.userWallets } returns flowOf(emptyList()) - every { userWalletsListManager.isLockedSync } returns true - every { userWalletsListManager.asLockable() } returns mockLockable - - // WHEN - val result = useCase().firstOrNull() - - // THEN - assertThat(result).isNull() - } - - @Test - fun `GIVEN manager is not locked and has wallets WHEN invoke THEN return wallets list`() = runTest { - // GIVEN - val mockLockable = mockk() - val wallets = listOf(mockk(), mockk()) - every { userWalletsListManager.isLockable } returns true - every { userWalletsListManager.userWallets } returns flowOf(wallets) - every { userWalletsListManager.isLockedSync } returns false - every { userWalletsListManager.asLockable() } returns mockLockable - - // WHEN - val result = useCase().firstOrNull() - - // THEN - assertThat(result).isEqualTo(wallets) - } -} \ No newline at end of file diff --git a/domain/wallets/src/test/java/com/tangem/domain/wallets/usecase/GetSavedWalletsCountUseCaseTest.kt b/domain/wallets/src/test/java/com/tangem/domain/wallets/usecase/GetSavedWalletsCountUseCaseTest.kt new file mode 100644 index 0000000000..7949741300 --- /dev/null +++ b/domain/wallets/src/test/java/com/tangem/domain/wallets/usecase/GetSavedWalletsCountUseCaseTest.kt @@ -0,0 +1,137 @@ +package com.tangem.domain.wallets.usecase + +import com.google.common.truth.Truth.assertThat +import com.tangem.domain.wallets.legacy.UserWalletsListManager +import com.tangem.domain.wallets.legacy.asLockable +import com.tangem.domain.wallets.legacy.isLockedSync +import com.tangem.domain.wallets.models.UserWallet +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test + +class GetSavedWalletsCountUseCaseTest { + + private lateinit var useCase: GetSavedWalletsCountUseCase + private lateinit var userWalletsListManager: UserWalletsListManager + + @Before + fun setup() { + userWalletsListManager = mockk() + useCase = GetSavedWalletsCountUseCase(userWalletsListManager) + mockkStatic("com.tangem.domain.wallets.legacy.UserWalletsListManagerExtensionsKt") + } + + @Test + fun `GIVEN manager is not lockable WHEN invoke THEN return empty list`() = runTest { + // GIVEN + every { userWalletsListManager.isLockable } returns false + every { userWalletsListManager.savedWalletsCount } returns flowOf(0) + every { userWalletsListManager.userWallets } returns flowOf(emptyList()) + every { userWalletsListManager.userWalletsSync } returns emptyList() + every { userWalletsListManager.isLockedSync } returns false + every { userWalletsListManager.asLockable() } returns null + + // WHEN + val result = useCase().firstOrNull() + + // THEN + assertThat(result).isEmpty() + } + + @Test + fun `GIVEN manager is locked WHEN invoke THEN return empty list`() = runTest { + // GIVEN + val mockLockable = mockk() + every { userWalletsListManager.isLockable } returns true + every { userWalletsListManager.savedWalletsCount } returns flowOf(0) + every { userWalletsListManager.userWallets } returns flowOf(emptyList()) + every { userWalletsListManager.userWalletsSync } returns emptyList() + every { userWalletsListManager.isLockedSync } returns true + every { userWalletsListManager.asLockable() } returns mockLockable + + // WHEN + val result = useCase().firstOrNull() + + // THEN + assertThat(result).isEmpty() + } + + @Test + fun `GIVEN manager is not locked and has wallets WHEN invoke THEN return wallets list`() = runTest { + // GIVEN + val mockLockable = mockk() + val wallets = listOf(mockk(), mockk()) + every { userWalletsListManager.isLockable } returns true + every { userWalletsListManager.savedWalletsCount } returns flowOf(wallets.size) + every { userWalletsListManager.userWallets } returns flowOf(wallets) + every { userWalletsListManager.userWalletsSync } returns wallets + every { userWalletsListManager.isLockedSync } returns false + every { userWalletsListManager.asLockable() } returns mockLockable + + // WHEN + val result = useCase().firstOrNull() + + // THEN + assertThat(result).isEqualTo(wallets) + } + + @Test + fun `GIVEN manager is not lockable and savedWalletsCount is zero WHEN invoke THEN return empty list`() = runTest { + // GIVEN + every { userWalletsListManager.isLockable } returns false + every { userWalletsListManager.savedWalletsCount } returns flowOf(0) + every { userWalletsListManager.userWallets } returns flowOf(emptyList()) + every { userWalletsListManager.userWalletsSync } returns emptyList() + every { userWalletsListManager.isLockedSync } returns false + every { userWalletsListManager.asLockable() } returns null + + // WHEN + val result = useCase().firstOrNull() + + // THEN + assertThat(result).isEmpty() + } + + @Test + fun `GIVEN manager is lockable and locked and savedWalletsCount is zero WHEN invoke THEN return empty list`() = + runTest { + // GIVEN + val mockLockable = mockk() + every { userWalletsListManager.isLockable } returns true + every { userWalletsListManager.savedWalletsCount } returns flowOf(0) + every { userWalletsListManager.userWallets } returns flowOf(emptyList()) + every { userWalletsListManager.userWalletsSync } returns emptyList() + every { userWalletsListManager.isLockedSync } returns true + every { userWalletsListManager.asLockable() } returns mockLockable + + // WHEN + val result = useCase().firstOrNull() + + // THEN + assertThat(result).isEmpty() + } + + @Test + fun `GIVEN manager is lockable and unlocked and savedWalletsCount is zero WHEN invoke THEN return empty list`() = + runTest { + // GIVEN + val mockLockable = mockk() + every { userWalletsListManager.isLockable } returns true + every { userWalletsListManager.savedWalletsCount } returns flowOf(0) + every { userWalletsListManager.userWallets } returns flowOf(emptyList()) + every { userWalletsListManager.userWalletsSync } returns emptyList() + every { userWalletsListManager.isLockedSync } returns false + every { userWalletsListManager.asLockable() } returns mockLockable + + // WHEN + val result = useCase().firstOrNull() + + // THEN + assertThat(result).isEmpty() + } +} \ No newline at end of file diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt index 8861cf7891..b85bb4de06 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt @@ -24,7 +24,6 @@ import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.nft.DisableWalletNFTUseCase import com.tangem.domain.nft.EnableWalletNFTUseCase import com.tangem.domain.nft.GetWalletNFTEnabledUseCase -import com.tangem.domain.notifications.GetApplicationIdUseCase import com.tangem.domain.notifications.toggles.NotificationsFeatureToggles import com.tangem.domain.settings.repositories.PermissionRepository import com.tangem.domain.wallets.models.UserWallet @@ -45,7 +44,6 @@ import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnaly import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber @@ -74,8 +72,6 @@ internal class WalletSettingsModel @Inject constructor( private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase, private val settingsManager: SettingsManager, private val permissionsRepository: PermissionRepository, - private val getApplicationIdUseCase: GetApplicationIdUseCase, - private val associateWalletsWithApplicationIdUseCase: AssociateWalletsWithApplicationIdUseCase, ) : Model() { val params: WalletSettingsComponent.Params = paramsContainer.require() @@ -198,20 +194,10 @@ internal class WalletSettingsModel @Inject constructor( if (hasUserWallets) { router.pop() } else { - clearWalletsAssociatedWithApplicationId() router.replaceAll(AppRoute.Home) } } - private fun clearWalletsAssociatedWithApplicationId() = modelScope.launch(NonCancellable) { - getApplicationIdUseCase().onRight { applicationId -> - associateWalletsWithApplicationIdUseCase(applicationId, emptyList()) - .onLeft { - Timber.e("Unable to associate empty wallets with application ID: $it") - } - } - } - private fun onLinkMoreCardsClick(scanResponse: ScanResponse) { analyticsEventHandler.send(Settings.ButtonCreateBackup) analyticsContextProxy.addContext(scanResponse) From 8a9c0e45b755ac07a3aa01251ebef42252fe6c14 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 17 Jul 2025 14:06:04 +0300 Subject: [PATCH 14/21] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 4 ++-- .../com/tangem/sdk/api/di/CardSdkModule.kt | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 3d9798f74b..8ea0ad7933 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,9 +5,9 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "releases-5.26.0-1109" +tangemBlockchainSdk = "releases-5.26.0-1113" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "releases-5.26.0-492" +tangemCardSdk = "releases-5.26.0-493" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ diff --git a/libs/tangem-sdk-api/src/main/kotlin/com/tangem/sdk/api/di/CardSdkModule.kt b/libs/tangem-sdk-api/src/main/kotlin/com/tangem/sdk/api/di/CardSdkModule.kt index 1f27687d60..bf009b1a7c 100644 --- a/libs/tangem-sdk-api/src/main/kotlin/com/tangem/sdk/api/di/CardSdkModule.kt +++ b/libs/tangem-sdk-api/src/main/kotlin/com/tangem/sdk/api/di/CardSdkModule.kt @@ -1,12 +1,15 @@ package com.tangem.sdk.api.di +import android.content.Context import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.operations.attestation.CardArtworksProvider import com.tangem.operations.attestation.OnlineCardVerifier import dagger.Module import dagger.Provides import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import java.io.File import javax.inject.Singleton @Module @@ -19,9 +22,16 @@ internal object CardSdkModule { @Provides @Singleton - fun provideCardArtworksProvider(sdkRepository: CardSdkConfigRepository): CardArtworksProvider = - CardArtworksProvider( - sdkRepository.sdk.config.isTangemAttestationProdEnv, - sdkRepository.sdk.secureStorage, + fun provideCardArtworksProvider( + sdkRepository: CardSdkConfigRepository, + @ApplicationContext context: Context, + ): CardArtworksProvider { + return CardArtworksProvider( + isTangemAttestationProdEnv = sdkRepository.sdk.config.isTangemAttestationProdEnv, + artworksDirectory = File( + context.getExternalFilesDir(null) ?: context.filesDir, + "card_artworks", + ).also { it.mkdirs() }, ) + } } \ No newline at end of file From 9244326396bb36dc0495f39c8578540556ab2a41 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 17 Jul 2025 16:44:05 +0300 Subject: [PATCH 15/21] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 8ea0ad7933..600a157d50 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,7 +5,7 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "releases-5.26.0-1113" +tangemBlockchainSdk = "releases-5.26.0-1114" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "releases-5.26.0-493" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 9fa341d0df2e9de07b292663a282cd65589e2662 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 18 Jul 2025 14:52:36 +0500 Subject: [PATCH 16/21] Updated on 2026-08-14 --- .../send/v2/subcomponents/fee/ui/SendCustomFee.kt | 11 +++++++---- .../send/v2/subcomponents/fee/ui/SendFeeContent.kt | 4 ++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/ui/SendCustomFee.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/ui/SendCustomFee.kt index 2486f3bbd9..577020369e 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/ui/SendCustomFee.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/ui/SendCustomFee.kt @@ -29,6 +29,7 @@ internal fun SendCustomFee( hasNotifications: Boolean, onValueChange: (Int, String) -> Unit, onNonceChange: (String) -> Unit, + displayNonceInput: Boolean, nonce: String?, modifier: Modifier = Modifier, ) { @@ -85,10 +86,12 @@ internal fun SendCustomFee( } } } - Nonce( - onNonceChange = onNonceChange, - nonce = nonce, - ) + if (displayNonceInput) { + Nonce( + onNonceChange = onNonceChange, + nonce = nonce, + ) + } } } } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/ui/SendFeeContent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/ui/SendFeeContent.kt index c017356770..ff60febdfa 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/ui/SendFeeContent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/ui/SendFeeContent.kt @@ -41,6 +41,7 @@ internal fun SendFeeContent(state: FeeUM, clickIntents: SendFeeClickIntents) { feeSelectorUM = feeSelectorUM, onValueChange = clickIntents::onCustomFeeValueChange, onNonceChange = clickIntents::onNonceChange, + displayNonceInput = state.displayNonceInput, nonce = state.nonce?.toString().orEmpty(), hasNotifications = hasNotifications, ) @@ -59,11 +60,13 @@ private fun LazyListScope.feeSelector(state: FeeUM.Content, clickIntents: SendFe } } +@Suppress("LongParameterList") internal fun LazyListScope.customFee( feeSelectorUM: FeeSelectorUM.Content, hasNotifications: Boolean, onValueChange: (Int, String) -> Unit, onNonceChange: (String) -> Unit, + displayNonceInput: Boolean, nonce: String?, modifier: Modifier = Modifier, ) { @@ -74,6 +77,7 @@ internal fun LazyListScope.customFee( hasNotifications = hasNotifications, onValueChange = onValueChange, onNonceChange = onNonceChange, + displayNonceInput = displayNonceInput, nonce = nonce, modifier = modifier .fillMaxWidth() From ea657bbba7dfc1bb1bc23f636fb702951a0af4d0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 18 Jul 2025 16:11:18 +0500 Subject: [PATCH 17/21] Updated on 2026-08-14 --- .../v2/subcomponents/amount/model/SendAmountModel.kt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt index 383cb1f3eb..cfbca33bde 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt @@ -81,6 +81,10 @@ internal class SendAmountModel @Inject constructor( configAmountNavigation() initAppCurrency() subscribeOnCryptoCurrencyStatusFlow() + subscribeOnAmountReduceByTriggerUpdates() + subscribeOnAmountReduceToTriggerUpdates() + subscribeOnAmountIgnoreReduceTriggerUpdates() + subscribeOnAmountUpdateQRTriggerUpdates() } private fun initAppCurrency() { @@ -95,12 +99,6 @@ internal class SendAmountModel @Inject constructor( .onEach { newCryptoCurrencyStatus -> cryptoCurrencyStatus = newCryptoCurrencyStatus maxAmountBoundary = MaxEnterAmountConverter().convert(cryptoCurrencyStatus) - if (uiState.value is AmountState.Empty) { - subscribeOnAmountReduceByTriggerUpdates() - subscribeOnAmountReduceToTriggerUpdates() - subscribeOnAmountIgnoreReduceTriggerUpdates() - subscribeOnAmountUpdateQRTriggerUpdates() - } initMinBoundary() } .launchIn(modelScope) From a24f66a4b23bba63497e2d92f7f807898550cd89 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 18 Jul 2025 11:36:35 +0000 Subject: [PATCH 18/21] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 600a157d50..c1b01e6007 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,12 +5,14 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "releases-5.26.0-1114" +tangemBlockchainSdk = "develop-1110" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "releases-5.26.0-493" +tangemCardSdk = "develop-489" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ +tangemHotSdk = "develop-438" +#tangemHotSdk = "0.0.1" # Keep it! - used for local builds ^ @@ -18,6 +20,8 @@ tangemVico = "2.0.0-alpha.25-tangem12" blockchain = { module = "com.tangem:blockchain", version.ref = "tangemBlockchainSdk" } card-android = { module = "com.tangem.tangem-sdk-kotlin:android", version.ref = "tangemCardSdk" } card-core = { module = "com.tangem.tangem-sdk-kotlin:core", version.ref = "tangemCardSdk" } +hot-core = { module = "com.tangem.tangem-hot-sdk-kotlin:core", version.ref = "tangemHotSdk" } +hot-android = { module = "com.tangem.tangem-hot-sdk-kotlin:android", version.ref = "tangemHotSdk" } vico-compose = { group = "com.tangem.vico", name = "compose", version.ref = "tangemVico" } vico-compose-m3 = { group = "com.tangem.vico", name = "compose-m3", version.ref = "tangemVico" } From 27d87a51b51d8f815c9129870d4cbc3c511c8906 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 21 Jul 2025 18:35:04 +0300 Subject: [PATCH 19/21] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index c1b01e6007..80b280a86a 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,9 +5,9 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "develop-1110" +tangemBlockchainSdk = "develop-1121" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-489" +tangemCardSdk = "develop-501" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ From f7cd9ebff67eb850861cc0239ca090a7b59bf0de Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 21 Jul 2025 18:45:28 +0300 Subject: [PATCH 20/21] Updated on 2026-08-14 --- .../tangem/domain/managetokens/SaveManagedTokensUseCase.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt index 56ae2c1fb9..20bf0decd0 100644 --- a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt +++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt @@ -10,8 +10,6 @@ import com.tangem.domain.models.network.Network import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher -import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier -import com.tangem.domain.tokens.TokensFeatureToggles import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWalletId @@ -25,8 +23,6 @@ class SaveManagedTokensUseCase( private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher, private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher, - private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, - private val tokensFeatureToggles: TokensFeatureToggles, ) { suspend operator fun invoke( From d11e469c70a77b61ddc748e6dce458b4bd776996 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 21 Jul 2025 18:50:20 +0300 Subject: [PATCH 21/21] Updated on 2026-08-14 --- .../com/tangem/tap/di/domain/ManageTokensDomainModule.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt index d89dd6d8ce..55d82f0af5 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt @@ -7,8 +7,6 @@ import com.tangem.domain.managetokens.repository.ManageTokensRepository import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher -import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier -import com.tangem.domain.tokens.TokensFeatureToggles import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -74,8 +72,6 @@ internal object ManageTokensDomainModule { multiNetworkStatusFetcher: MultiNetworkStatusFetcher, multiQuoteStatusFetcher: MultiQuoteStatusFetcher, multiYieldBalanceFetcher: MultiYieldBalanceFetcher, - multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, - tokensFeatureToggles: TokensFeatureToggles, ): SaveManagedTokensUseCase { return SaveManagedTokensUseCase( customTokensRepository = customTokensRepository, @@ -85,8 +81,6 @@ internal object ManageTokensDomainModule { multiNetworkStatusFetcher = multiNetworkStatusFetcher, multiQuoteStatusFetcher = multiQuoteStatusFetcher, multiYieldBalanceFetcher = multiYieldBalanceFetcher, - multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, - tokensFeatureToggles = tokensFeatureToggles, ) }