From 79b9aae69c2df6c01755c96e74cd1b5318faf02a Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 2 Jul 2025 21:19:47 +0300 Subject: [PATCH 01/23] 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 dbed89f8e7..1569dd4caf 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-1102" +tangemBlockchainSdk = "releases-5.26.0-1103" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-487" +tangemCardSdk = "releases-5.26.0-488" #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 bab1c44495c91e9dbfb256043ff2c9b6dee080da Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 8 Jul 2025 14:15:26 +0700 Subject: [PATCH 02/23] 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 1569dd4caf..29b3a90433 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-1103" +tangemBlockchainSdk = "releases-5.26.0-1105" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "releases-5.26.0-488" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From cd4227fcb18c9e0c09dafa2c771ab62bca8e9d97 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 8 Jul 2025 14:16:12 +0500 Subject: [PATCH 03/23] Updated on 2026-08-14 --- .../nft/collections/ui/NFTCollectionsEmpty.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollectionsEmpty.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollectionsEmpty.kt index 7e2c62d33b..167988824f 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollectionsEmpty.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollectionsEmpty.kt @@ -52,14 +52,14 @@ internal fun NFTCollectionsEmpty(state: NFTCollectionsUM.Empty, modifier: Modifi color = TangemTheme.colors.text.secondary, textAlign = TextAlign.Center, ) - PrimaryButton( - modifier = Modifier - .padding(top = TangemTheme.dimens.spacing48) - .widthIn(min = TangemTheme.dimens.size158), - text = stringResourceSafe(R.string.nft_collections_receive), - onClick = state.onReceiveClick, - ) } + PrimaryButton( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.BottomCenter), + text = stringResourceSafe(R.string.nft_collections_receive), + onClick = state.onReceiveClick, + ) } } From f76f28ae0c3fb4165e2a6bc53799a0247cf02b0f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 8 Jul 2025 16:02:56 +0500 Subject: [PATCH 04/23] Updated on 2026-08-14 --- .../transformer/NFTPriceChangeTransformer.kt | 69 ++++++++++++------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/entity/transformer/NFTPriceChangeTransformer.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/entity/transformer/NFTPriceChangeTransformer.kt index 8ab05b6d7b..ff99321696 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/entity/transformer/NFTPriceChangeTransformer.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/entity/transformer/NFTPriceChangeTransformer.kt @@ -1,5 +1,6 @@ package com.tangem.features.nft.details.entity.transformer +import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat @@ -8,6 +9,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.nft.models.NFTSalePrice import com.tangem.features.nft.details.entity.NFTAssetUM import com.tangem.features.nft.details.entity.NFTDetailsUM +import com.tangem.features.nft.impl.R import com.tangem.utils.transformer.Transformer internal class NFTPriceChangeTransformer( @@ -18,35 +20,50 @@ internal class NFTPriceChangeTransformer( override fun transform(prevState: NFTDetailsUM): NFTDetailsUM { val topInfo = prevState.nftAsset.topInfo as? NFTAssetUM.TopInfo.Content ?: return prevState - return prevState.copy( - nftAsset = prevState.nftAsset.copy( - topInfo = topInfo.copy( - salePrice = when (nftSalePrice) { - is NFTSalePrice.Empty, - is NFTSalePrice.Error, - -> NFTAssetUM.SalePrice.Empty - is NFTSalePrice.Loading -> NFTAssetUM.SalePrice.Loading - is NFTSalePrice.Value -> NFTAssetUM.SalePrice.Content( - isFlickering = false, - cryptoPrice = stringReference( - nftSalePrice.value.format { - crypto( - symbol = nftSalePrice.symbol, - decimals = nftSalePrice.decimals, - ) - }, - ), - fiatPrice = stringReference( - nftSalePrice.fiatValue.format { - fiat( - fiatCurrencyCode = appCurrency.code, - fiatCurrencySymbol = appCurrency.symbol, - ) - }, - ), + val salePrice = when (nftSalePrice) { + is NFTSalePrice.Empty, + is NFTSalePrice.Error, + -> NFTAssetUM.SalePrice.Empty + is NFTSalePrice.Loading -> NFTAssetUM.SalePrice.Loading + is NFTSalePrice.Value -> NFTAssetUM.SalePrice.Content( + isFlickering = false, + cryptoPrice = stringReference( + nftSalePrice.value.format { + crypto( + symbol = nftSalePrice.symbol, + decimals = nftSalePrice.decimals, ) }, ), + fiatPrice = stringReference( + nftSalePrice.fiatValue.format { + fiat( + fiatCurrencyCode = appCurrency.code, + fiatCurrencySymbol = appCurrency.symbol, + ) + }, + ), + ) + } + + val hasSalePrice = salePrice !is NFTAssetUM.SalePrice.Empty + + val newTopInfo = if ( + topInfo.rarity is NFTAssetUM.Rarity.Empty && + topInfo.description.isNullOrEmpty() && + !hasSalePrice + ) { + NFTAssetUM.TopInfo.Empty + } else { + topInfo.copy( + title = resourceReference(R.string.nft_details_last_sale_price).takeIf { hasSalePrice }, + salePrice = salePrice, + ) + } + + return prevState.copy( + nftAsset = prevState.nftAsset.copy( + topInfo = newTopInfo, ), ) } From 1f2bd425f1aa03331791b78539ef96b06e5ea193 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 8 Jul 2025 16:03:17 +0500 Subject: [PATCH 05/23] Updated on 2026-08-14 --- .../tangem/features/nft/details/ui/NFTDetailsBlocksGroup.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetailsBlocksGroup.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetailsBlocksGroup.kt index df471fec36..049d91b0cd 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetailsBlocksGroup.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetailsBlocksGroup.kt @@ -173,8 +173,7 @@ internal fun NFTDetailsGroupBlock( } else { onBlockClick?.invoke() } - } - .padding(top = TangemTheme.dimens.spacing4), + }, text = value.resolveReference(), style = TangemTheme.typography.body1, color = TangemTheme.colors.text.primary1, From 56052513116a76526a7dce59d857e2542c303a96 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 9 Jul 2025 07:07:08 +0000 Subject: [PATCH 06/23] 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 b741e3bfec..29b3a90433 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.25.0-1100" +tangemBlockchainSdk = "releases-5.26.0-1105" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "releases-5.25.0-486" +tangemCardSdk = "releases-5.26.0-488" #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 a8cd6f1d8b8766f14528eaeefc7f258e62220d95 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 9 Jul 2025 18:21:50 +0500 Subject: [PATCH 07/23] Updated on 2026-08-14 --- .../nft/collections/ui/NFTCollections.kt | 51 +++++++-------- .../features/nft/common/ui/NFTContent.kt | 4 +- .../features/nft/details/ui/NFTDetails.kt | 56 ++++++++--------- .../features/nft/receive/ui/NFTReceive.kt | 62 ++++++++----------- .../features/nft/traits/ui/NFTAssetTraits.kt | 43 ++++++------- 5 files changed, 100 insertions(+), 116 deletions(-) diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollections.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollections.kt index 2d42caaf85..3036cc073f 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollections.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/ui/NFTCollections.kt @@ -1,8 +1,8 @@ package com.tangem.features.nft.collections.ui import androidx.activity.compose.BackHandler +import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.core.ui.components.appbar.AppBarWithBackButton @@ -17,31 +17,28 @@ import com.tangem.features.nft.impl.R internal fun NFTCollections(state: NFTCollectionsStateUM, modifier: Modifier = Modifier) { BackHandler(onBack = state.onBackClick) - Scaffold( - modifier = modifier, - containerColor = TangemTheme.colors.background.secondary, - topBar = { - AppBarWithBackButton( - modifier = Modifier.statusBarsPadding(), - onBackClick = state.onBackClick, - text = stringResourceSafe(id = R.string.nft_collections_title), - iconRes = R.drawable.ic_back_24, - ) - }, - content = { innerPadding -> - TangemPullToRefreshContainer( - config = state.pullToRefreshConfig, - modifier = Modifier - .padding(innerPadding) - .fillMaxSize(), - ) { - when (val content = state.content) { - is NFTCollectionsUM.Content -> NFTCollectionsContent(content) - is NFTCollectionsUM.Empty -> NFTCollectionsEmpty(content) - is NFTCollectionsUM.Failed -> NFTCollectionsFailed(content) - is NFTCollectionsUM.Loading -> NFTCollectionsLoading(content) - } + Column( + modifier = modifier + .background(TangemTheme.colors.background.secondary), + ) { + AppBarWithBackButton( + modifier = Modifier, + onBackClick = state.onBackClick, + text = stringResourceSafe(id = R.string.nft_collections_title), + iconRes = R.drawable.ic_back_24, + ) + + TangemPullToRefreshContainer( + config = state.pullToRefreshConfig, + modifier = Modifier + .fillMaxSize(), + ) { + when (val content = state.content) { + is NFTCollectionsUM.Content -> NFTCollectionsContent(content) + is NFTCollectionsUM.Empty -> NFTCollectionsEmpty(content) + is NFTCollectionsUM.Failed -> NFTCollectionsFailed(content) + is NFTCollectionsUM.Loading -> NFTCollectionsLoading(content) } - }, - ) + } + } } \ No newline at end of file diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/common/ui/NFTContent.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/common/ui/NFTContent.kt index ef907f1e0f..f906d793ee 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/common/ui/NFTContent.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/common/ui/NFTContent.kt @@ -19,8 +19,8 @@ import com.tangem.features.nft.common.NFTRoute @Composable internal fun NFTContent(stackState: ChildStack) { Column( - modifier = Modifier.Companion - .background(color = TangemTheme.colors.background.tertiary) + modifier = Modifier + .background(color = TangemTheme.colors.background.secondary) .fillMaxSize() .imePadding() .systemBarsPadding(), diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetails.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetails.kt index 203288ef78..2c3ad2ede5 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetails.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/ui/NFTDetails.kt @@ -1,13 +1,10 @@ package com.tangem.features.nft.details.ui import androidx.activity.compose.BackHandler -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.material3.FabPosition -import androidx.compose.material3.Scaffold +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.components.appbar.TangemTopAppBar @@ -22,25 +19,25 @@ import com.tangem.features.nft.impl.R internal fun NFTDetails(state: NFTDetailsUM, modifier: Modifier = Modifier) { BackHandler(onBack = state.onBackClick) - Scaffold( - modifier = modifier, - containerColor = TangemTheme.colors.background.secondary, - topBar = { + Box( + modifier = modifier + .background(TangemTheme.colors.background.secondary), + ) { + Column( + modifier = Modifier.fillMaxSize(), + ) { TangemTopAppBar( - modifier = Modifier.statusBarsPadding(), + modifier = Modifier, startButton = TopAppBarButtonUM( iconRes = R.drawable.ic_back_24, onIconClicked = state.onBackClick, ), title = state.nftAsset.name, ) - }, - content = { innerPadding -> + TangemPullToRefreshContainer( config = state.pullToRefreshConfig, - modifier = Modifier - .padding(innerPadding) - .fillMaxSize(), + modifier = Modifier.fillMaxSize(), ) { NFTDetailsAsset( state = state.nftAsset, @@ -49,16 +46,19 @@ internal fun NFTDetails(state: NFTDetailsUM, modifier: Modifier = Modifier) { onExploreClick = state.onExploreClick, ) } - }, - floatingActionButtonPosition = FabPosition.Center, - floatingActionButton = { - PrimaryButton( - modifier = Modifier - .padding(horizontal = TangemTheme.dimens.spacing16) - .fillMaxWidth(), - text = stringResourceSafe(id = R.string.common_send), - onClick = state.onSendClick, - ) - }, - ) + } + + PrimaryButton( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding( + start = TangemTheme.dimens.spacing16, + end = TangemTheme.dimens.spacing16, + bottom = TangemTheme.dimens.spacing16, + ) + .fillMaxWidth(), + text = stringResourceSafe(id = R.string.common_send), + onClick = state.onSendClick, + ) + } } \ No newline at end of file diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt index 4dac3a8d5c..21c147c3a9 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt @@ -1,8 +1,8 @@ package com.tangem.features.nft.receive.ui import androidx.activity.compose.BackHandler +import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet @@ -22,41 +22,33 @@ import com.tangem.features.nft.receive.entity.NFTReceiveUM internal fun NFTReceive(state: NFTReceiveUM, modifier: Modifier = Modifier) { BackHandler(onBack = state.onBackClick) - Scaffold( - modifier = modifier, - containerColor = TangemTheme.colors.background.secondary, - topBar = { - TangemTopAppBar( - modifier = Modifier.statusBarsPadding(), - startButton = TopAppBarButtonUM( - iconRes = R.drawable.ic_close_24, - onIconClicked = state.onBackClick, - ), - title = stringResourceSafe(id = R.string.nft_receive_title), - subtitle = state.appBarSubtitle.resolveReference(), - ) - }, - content = { innerPadding -> - Column( - modifier = Modifier - .padding(innerPadding) - .fillMaxSize(), - ) { - SearchBar( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = TangemTheme.dimens.spacing16), - state = state.search, - colors = TangemSearchBarDefaults.secondaryTextFieldColors, - ) + Column( + modifier = modifier + .background(TangemTheme.colors.background.secondary), + ) { + TangemTopAppBar( + modifier = Modifier, + startButton = TopAppBarButtonUM( + iconRes = R.drawable.ic_close_24, + onIconClicked = state.onBackClick, + ), + title = stringResourceSafe(id = R.string.nft_receive_title), + subtitle = state.appBarSubtitle.resolveReference(), + ) - when (val networks = state.networks) { - is NFTReceiveUM.Networks.Content -> NFTReceiveNetworksContent(networks) - is NFTReceiveUM.Networks.Empty -> NFTReceiveNetworksEmpty() - } - } - }, - ) + SearchBar( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = TangemTheme.dimens.spacing16), + state = state.search, + colors = TangemSearchBarDefaults.secondaryTextFieldColors, + ) + + when (val networks = state.networks) { + is NFTReceiveUM.Networks.Content -> NFTReceiveNetworksContent(networks) + is NFTReceiveUM.Networks.Empty -> NFTReceiveNetworksEmpty() + } + } ShowBottomSheet(state.bottomSheetConfig) } diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/traits/ui/NFTAssetTraits.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/traits/ui/NFTAssetTraits.kt index f6e27276f2..9f18df108f 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/traits/ui/NFTAssetTraits.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/traits/ui/NFTAssetTraits.kt @@ -1,9 +1,8 @@ package com.tangem.features.nft.traits.ui import androidx.activity.compose.BackHandler -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.material3.Scaffold +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.core.ui.components.appbar.TangemTopAppBar @@ -17,25 +16,21 @@ import com.tangem.features.nft.traits.entity.NFTAssetTraitsUM internal fun NFTAssetTraits(state: NFTAssetTraitsUM, modifier: Modifier = Modifier) { BackHandler(onBack = state.onBackClick) - Scaffold( - modifier = modifier, - containerColor = TangemTheme.colors.background.secondary, - topBar = { - TangemTopAppBar( - modifier = Modifier.statusBarsPadding(), - startButton = TopAppBarButtonUM( - iconRes = R.drawable.ic_back_24, - onIconClicked = state.onBackClick, - ), - title = stringResourceSafe(R.string.nft_traits_title), - ) - }, - content = { innerPadding -> - NFTAssetTraitsContent( - modifier = Modifier - .padding(innerPadding), - state = state, - ) - }, - ) + Column( + modifier = modifier + .background(TangemTheme.colors.background.secondary), + ) { + TangemTopAppBar( + modifier = Modifier, + startButton = TopAppBarButtonUM( + iconRes = R.drawable.ic_back_24, + onIconClicked = state.onBackClick, + ), + title = stringResourceSafe(R.string.nft_traits_title), + ) + + NFTAssetTraitsContent( + state = state, + ) + } } \ No newline at end of file From 6f47c78b2337239b73c1a59fc1fbf1ba1031ba3e Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 10 Jul 2025 17:02:30 +0500 Subject: [PATCH 08/23] Updated on 2026-08-14 --- .../tangem/tap/di/domain/StakingDomainModule.kt | 8 ++++++++ .../data/staking/DefaultStakingRepository.kt | 9 +++++++++ .../data/staking/utils/StakingIdFactory.kt | 2 ++ .../staking/GetActionRequirementAmountUseCase.kt | 16 ++++++++++++++++ .../staking/repositories/StakingRepository.kt | 7 +++++++ .../tokens/repository/MockStakingRepository.kt | 2 ++ .../impl/presentation/model/StakingModel.kt | 15 ++++++++++++++- .../presentation/state/InnerYieldBalanceState.kt | 1 + .../state/converters/YieldBalancesConverter.kt | 1 + .../previewdata/InitialStakingStatePreview.kt | 1 + gradle/tangem_dependencies.toml | 2 +- 11 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt index 22f61dc1f1..c852c1ff14 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt @@ -224,4 +224,12 @@ internal object StakingDomainModule { ): CheckAccountInitializedUseCase { return CheckAccountInitializedUseCase(walletManagersFacade) } + + @Provides + @Singleton + fun provideGetActionRequirementAmountUseCase( + stakingRepository: StakingRepository, + ): GetActionRequirementAmountUseCase { + return GetActionRequirementAmountUseCase(stakingRepository) + } } \ No newline at end of file diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index 4c145ce44c..ac813e234c 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt @@ -72,6 +72,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull import timber.log.Timber +import java.math.BigDecimal import kotlin.time.Duration.Companion.seconds @Suppress("LargeClass", "LongParameterList", "TooManyFunctions") @@ -662,6 +663,14 @@ internal class DefaultStakingRepository( } } + override fun getActionRequirementAmount(integrationId: String, stakingActionType: StakingActionType): BigDecimal? { + return when { + stakingIdFactory.isPolygonIntegrationId(integrationId) && + stakingActionType == StakingActionType.CLAIM_REWARDS -> BigDecimal.ONE + else -> null + } + } + private suspend fun createActionRequestBody( userWalletId: UserWalletId, network: Network, diff --git a/data/staking/src/main/java/com/tangem/data/staking/utils/StakingIdFactory.kt b/data/staking/src/main/java/com/tangem/data/staking/utils/StakingIdFactory.kt index d1b760b601..b27a5c5061 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/utils/StakingIdFactory.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/utils/StakingIdFactory.kt @@ -35,6 +35,8 @@ internal class StakingIdFactory @Inject constructor( return integrationIdMap[integrationKey] } + fun isPolygonIntegrationId(integrationId: String): Boolean = integrationId == ETHEREUM_POLYGON_INTEGRATION_ID + @Suppress("UnusedPrivateMember", "unused") companion object { diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt new file mode 100644 index 0000000000..34d0c03b27 --- /dev/null +++ b/domain/staking/src/main/java/com/tangem/domain/staking/GetActionRequirementAmountUseCase.kt @@ -0,0 +1,16 @@ +package com.tangem.domain.staking + +import arrow.core.Either +import com.tangem.domain.staking.model.stakekit.action.StakingActionType +import com.tangem.domain.staking.repositories.StakingRepository +import java.math.BigDecimal + +class GetActionRequirementAmountUseCase( + private val stakingRepository: StakingRepository, +) { + + operator fun invoke(integrationId: String, actionType: StakingActionType): Either = + Either.catch { + stakingRepository.getActionRequirementAmount(integrationId, actionType) + } +} \ No newline at end of file diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt index e399fcd26c..cbb5d358d6 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt @@ -14,11 +14,13 @@ import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.model.stakekit.YieldBalanceList import com.tangem.domain.staking.model.stakekit.action.StakingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus +import com.tangem.domain.staking.model.stakekit.action.StakingActionType import com.tangem.domain.staking.model.stakekit.transaction.ActionParams import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow +import java.math.BigDecimal @Suppress("TooManyFunctions") interface StakingRepository { @@ -100,4 +102,9 @@ interface StakingRepository { fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean + + /** + * Return action requirement amount + */ + fun getActionRequirementAmount(integrationId: String, stakingActionType: StakingActionType): BigDecimal? } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt index 303da59cda..19b368f075 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt @@ -188,6 +188,8 @@ class MockStakingRepository : StakingRepository { override fun getStakingApproval(cryptoCurrency: CryptoCurrency): StakingApproval = StakingApproval.Empty override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean = false + override fun getActionRequirementAmount(integrationId: String, stakingActionType: StakingActionType): BigDecimal? = + null private companion object { val yield = Yield( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt index 2454768e9c..895a139203 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt @@ -37,6 +37,7 @@ import com.tangem.domain.staking.model.StakingApproval import com.tangem.domain.staking.model.stakekit.* import com.tangem.domain.staking.model.stakekit.action.StakingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType +import com.tangem.domain.staking.model.stakekit.action.StakingActionType import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction import com.tangem.domain.staking.utils.getValidatorsCount import com.tangem.domain.tokens.* @@ -120,6 +121,7 @@ internal class StakingModel @Inject constructor( private val getActionsUseCase: GetActionsUseCase, private val getYieldUseCase: GetYieldUseCase, private val checkAccountInitializedUseCase: CheckAccountInitializedUseCase, + private val getActionRequirementAmountUseCase: GetActionRequirementAmountUseCase, private val paramsInterceptorHolder: ParamsInterceptorHolder, private val shareManager: ShareManager, @DelayedWork private val coroutineScope: CoroutineScope, @@ -527,9 +529,20 @@ internal class StakingModel @Inject constructor( val rewardPendingActionConstraints = yieldBalance?.reward?.rewardConstraints if (rewardBlockType == RewardBlockType.RewardsRequirementsError) { + val minimumAmount = rewardPendingActionConstraints?.amountArg?.minimum + // Temporary fix, until StakeKit adds minimum requirement amount to balance response + val minimumAmountValue = if (minimumAmount == null && yieldBalance.integrationId != null) { + getActionRequirementAmountUseCase.invoke( + integrationId = yieldBalance.integrationId, + actionType = StakingActionType.CLAIM_REWARDS, + ).getOrNull() + } else { + minimumAmount + } + stakingEventFactory.createStakingRewardsMinimumRequirementsErrorAlert( cryptoCurrencyName = cryptoCurrencyStatus.currency.name, - cryptoAmountValue = rewardPendingActionConstraints?.amountArg?.minimum?.format { + cryptoAmountValue = minimumAmountValue?.format { crypto(cryptoCurrencyStatus.currency) }.orEmpty(), ) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt index db102751d3..a4b3ea96fd 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt @@ -9,6 +9,7 @@ import java.math.BigDecimal @Immutable internal sealed class InnerYieldBalanceState { data class Data( + val integrationId: String?, val reward: YieldReward, val isActionable: Boolean, val balances: ImmutableList, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt index 9d5d52d214..ee906adb33 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt @@ -50,6 +50,7 @@ internal class YieldBalancesConverter( ?.firstOrNull { it.type == StakingActionType.CLAIM_REWARDS } InnerYieldBalanceState.Data( + integrationId = yieldBalance?.integrationId, reward = YieldReward( rewardsCrypto = cryptoRewardsValue.format { crypto(cryptoCurrency) }, rewardsFiat = fiatRewardsValue.format { diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt index 1c9a799920..d7af6496e2 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt @@ -64,6 +64,7 @@ internal object InitialStakingStatePreview { val stateWithYield = defaultState.copy( yieldBalance = InnerYieldBalanceState.Data( + integrationId = null, reward = YieldReward( rewardsFiat = "100 $", rewardsCrypto = "100 SOL", diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 29b3a90433..448f0a8772 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-1105" +tangemBlockchainSdk = "releases-5.26.0-1107" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "releases-5.26.0-488" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 209e387059dad83b40155d9bb3004ebfed13197f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 10 Jul 2025 17:44:31 +0500 Subject: [PATCH 09/23] Updated on 2026-08-14 --- .../details/ui/cardsettings/DefaultCardSettingsComponent.kt | 1 - .../features/details/ui/cardsettings/model/CardSettingsModel.kt | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/DefaultCardSettingsComponent.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/DefaultCardSettingsComponent.kt index bb92d1a9a1..5c31eb375c 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/DefaultCardSettingsComponent.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/DefaultCardSettingsComponent.kt @@ -22,7 +22,6 @@ internal class DefaultCardSettingsComponent @AssistedInject constructor( @Composable override fun Content(modifier: Modifier) { val state by model.screenState.collectAsStateWithLifecycle() - CardSettingsScreen(modifier = modifier, state = state) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt index 3ae653038c..689afdf90d 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt @@ -75,6 +75,8 @@ internal class CardSettingsModel @Inject constructor( override fun onDestroy() { super.onDestroy() + // Reset card scanned data + cardSettingsInteractor.clear() // Restore the previous value of access code request policy cardSdkConfigRepository.isBiometricsRequestPolicy = previousBiometricsRequestPolicy } From 68071d5e6ae6686a577006e3beb9beace952810c Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 11 Jul 2025 13:30:18 +0500 Subject: [PATCH 10/23] Updated on 2026-08-14 --- .../state/utils/StakingPendingActionUtils.kt | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt index c6249834ec..da281e339d 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt @@ -3,6 +3,7 @@ package com.tangem.features.staking.impl.presentation.state.utils import com.tangem.core.ui.R import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.domain.staking.model.stakekit.BalanceType import com.tangem.domain.staking.model.stakekit.PendingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionType import com.tangem.features.staking.impl.presentation.state.BalanceState @@ -44,16 +45,18 @@ internal fun isSingleAction(networkId: String, activeStake: BalanceState): Boole return isSingleAction && !isRestake || isCompositePendingActions } -internal fun withStubUnstakeAction(networkId: String, activeStake: BalanceState) = if (isStubUnstakeAction(networkId)) { - activeStake.pendingActions.plus( - PendingAction( - type = StakingActionType.UNSTAKE, - passthrough = "", - args = null, - ), - ).toPersistentList() -} else { - activeStake.pendingActions +internal fun withStubUnstakeAction(networkId: String, activeStake: BalanceState): ImmutableList { + return if (isStubUnstakeAction(networkId) && activeStake.type != BalanceType.REWARDS) { + activeStake.pendingActions.plus( + PendingAction( + type = StakingActionType.UNSTAKE, + passthrough = "", + args = null, + ), + ).toPersistentList() + } else { + activeStake.pendingActions + } } internal fun isTronStakedBalance(networkId: String, pendingAction: PendingAction?): Boolean { From a3016f459b4f81af6ae005abbdef0e603cf3ec61 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 10 Jul 2025 16:21:35 +0400 Subject: [PATCH 11/23] Updated on 2026-08-14 --- .../state/helpers/StakingBalanceUpdater.kt | 6 +- .../swap/DefaultSwapTransactionRepository.kt | 51 ++++---- .../swap/domain/SwapTransactionRepository.kt | 2 +- .../tokendetails/model/TokenDetailsModel.kt | 113 +++++++++--------- .../factory/express/ExchangeStatusFactory.kt | 2 +- .../factory/express/ExpressStatusFactory.kt | 8 +- 6 files changed, 89 insertions(+), 93 deletions(-) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt index e6c32aa82c..7b11e36afb 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt @@ -64,7 +64,11 @@ internal class StakingBalanceUpdater @AssistedInject constructor( coroutineScope { listOf( async { - fetchCurrencyStatus() + /* + * It is important to use NonCancellable here to ensure the update is not interrupted midway. + * For example, this can happen if the user enters and immediately leaves the screen. + */ + withContext(NonCancellable) { fetchCurrencyStatus() } }, async { updateStakingActions() diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt index 5633a2c403..97d35ceba4 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt @@ -6,7 +6,7 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.getObjectList import com.tangem.datasource.local.preferences.utils.getObjectListSync -import com.tangem.datasource.local.preferences.utils.getObjectMapSync +import com.tangem.datasource.local.preferences.utils.getObjectMap import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId @@ -17,9 +17,8 @@ import com.tangem.feature.swap.domain.models.domain.* import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.extensions.addOrReplace import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.withContext internal class DefaultSwapTransactionRepository( private val appPreferencesStore: AppPreferencesStore, @@ -82,35 +81,35 @@ internal class DefaultSwapTransactionRepository( } } - override suspend fun getTransactions( + override fun getTransactions( userWallet: UserWallet, cryptoCurrencyId: CryptoCurrency.ID, ): Flow?> { - return withContext(dispatchers.io) { - val txStatuses = appPreferencesStore.getObjectMapSync( - key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY, - ) - appPreferencesStore.getObjectList( + return combine( + flow = appPreferencesStore.getObjectList( key = PreferencesKeys.SWAP_TRANSACTIONS_KEY, - ).map { savedTransactions -> - val currencyTxs = savedTransactions - ?.filter { - it.userWalletId == userWallet.walletId.stringValue && - ( - it.toCryptoCurrencyId == cryptoCurrencyId.value || - it.fromCryptoCurrencyId == cryptoCurrencyId.value - ) - } + ), + flow2 = appPreferencesStore.getObjectMap( + key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY, + ), + ) { savedTransactions, txStatuses -> + val currencyTxs = savedTransactions?.filter { + it.userWalletId == userWallet.walletId.stringValue && + ( + it.toCryptoCurrencyId == cryptoCurrencyId.value || + it.fromCryptoCurrencyId == cryptoCurrencyId.value + ) + } - currencyTxs?.mapNotNull { - converter.convertBack( - value = it, - scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY] - txStatuses = txStatuses, - ) - } - }.flowOn(dispatchers.io) + currencyTxs?.mapNotNull { + converter.convertBack( + value = it, + scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY] + txStatuses = txStatuses, + ) + } } + .flowOn(dispatchers.default) } override suspend fun removeTransaction( diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapTransactionRepository.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapTransactionRepository.kt index 6948208c03..c52ee66212 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapTransactionRepository.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapTransactionRepository.kt @@ -17,7 +17,7 @@ interface SwapTransactionRepository { transaction: SavedSwapTransactionModel, ) - suspend fun getTransactions( + fun getTransactions( userWallet: UserWallet, cryptoCurrencyId: CryptoCurrency.ID, ): Flow?> diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index ae48c2de2d..46dba2b2a9 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -206,15 +206,15 @@ internal class TokenDetailsModel @Inject constructor( analyticsEventsHandler.send(TokenScreenAnalyticsEvent.Bought(currency.symbol)) } + fun onResume() { + subscribeOnExpressTransactionsUpdates() + } + fun onPause() { expressTxStatusTaskScheduler.cancelTask() expressTxJobHolder.cancel() } - fun onResume() { - subscribeOnExpressTransactionsUpdates() - } - override fun onDestroy() { expressTxStatusTaskScheduler.cancelTask() expressTxJobHolder.cancel() @@ -305,65 +305,60 @@ internal class TokenDetailsModel @Inject constructor( } private fun subscribeOnCurrencyStatusUpdates() { - modelScope.launch(dispatchers.main) { - getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( - userWalletId = userWalletId, - currencyId = cryptoCurrency.id, - isSingleWalletWithTokens = userWallet is UserWallet.Cold && - userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(), - ) - .distinctUntilChanged() - .onEach { maybeCurrencyStatus -> - internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus) - maybeCurrencyStatus.onRight { status -> - cryptoCurrencyStatus = status - updateButtons(currencyStatus = status) - updateWarnings(status) - subscribeOnUpdateStakingInfo(status) - } - currencyStatusAnalyticsSender.send(maybeCurrencyStatus) + getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( + userWalletId = userWalletId, + currencyId = cryptoCurrency.id, + isSingleWalletWithTokens = userWallet is UserWallet.Cold && + userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(), + ) + .distinctUntilChanged() + .onEach { maybeCurrencyStatus -> + internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus) + maybeCurrencyStatus.onRight { status -> + cryptoCurrencyStatus = status + updateButtons(currencyStatus = status) + updateWarnings(status) + subscribeOnUpdateStakingInfo(status) } - .flowOn(dispatchers.main) - .launchIn(modelScope) - .saveIn(marketPriceJobHolder) - } + currencyStatusAnalyticsSender.send(maybeCurrencyStatus) + } + .flowOn(dispatchers.main) + .launchIn(modelScope) + .saveIn(marketPriceJobHolder) } private fun subscribeOnExpressTransactionsUpdates() { - modelScope.launch(dispatchers.main) { - expressTxStatusTaskScheduler.cancelTask() - expressStatusFactory - .getExpressStatuses() - .distinctUntilChanged() - .onEach { expressTxs -> - internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs( - expressTxs, - ::updateNetworkToSwapBalance, - ) - expressTxStatusTaskScheduler.scheduleTask( - modelScope, - PeriodicTask( - isDelayFirst = false, - delay = EXPRESS_STATUS_UPDATE_DELAY, - task = { - runCatching { - expressStatusFactory.getUpdatedExpressStatuses(internalUiState.value.expressTxs) - } - }, - onSuccess = { updatedTxs -> - internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs( - updatedTxs, - ::updateNetworkToSwapBalance, - ) - }, - onError = { /* no-op */ }, - ), - ) - } - .flowOn(dispatchers.main) - .launchIn(modelScope) - .saveIn(expressTxJobHolder) - } + expressTxStatusTaskScheduler.cancelTask() + expressStatusFactory.getExpressStatuses() + .distinctUntilChanged() + .onEach { expressTxs -> + internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs( + expressTxs = expressTxs, + updateBalance = ::updateNetworkToSwapBalance, + ) + expressTxStatusTaskScheduler.scheduleTask( + scope = modelScope, + task = PeriodicTask( + isDelayFirst = false, + delay = EXPRESS_STATUS_UPDATE_DELAY, + task = { + runCatching { + expressStatusFactory.getUpdatedExpressStatuses(internalUiState.value.expressTxs) + } + }, + onSuccess = { updatedTxs -> + internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs( + updatedTxs, + ::updateNetworkToSwapBalance, + ) + }, + onError = { /* no-op */ }, + ), + ) + } + .flowOn(dispatchers.main) + .launchIn(modelScope) + .saveIn(expressTxJobHolder) } private fun updateNetworkToSwapBalance(toCryptoCurrency: CryptoCurrency) { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt index c12a0e8048..1e567bfc6a 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt @@ -52,7 +52,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( ) } - suspend operator fun invoke(): Flow> { + operator fun invoke(): Flow> { return swapTransactionRepository.getTransactions( userWallet = userWallet, cryptoCurrencyId = cryptoCurrency.id, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt index 4e9ee06a08..2378fa4b52 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt @@ -65,14 +65,12 @@ internal class ExpressStatusFactory @AssistedInject constructor( ) } - suspend fun getExpressStatuses(): Flow> = combine( + fun getExpressStatuses(): Flow> = combine( flow = exchangeStatusFactory(), flow2 = onrampStatusFactory(), ) { maybeExchange, maybeOnramp -> - persistentListOf( - maybeOnramp, - maybeExchange, - ).flatten() + persistentListOf(maybeOnramp, maybeExchange) + .flatten() .sortedByDescending { it.info.timestamp } .toPersistentList() } From 2996bf217c37a288a054c9d3415921c5471ea874 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 11 Jul 2025 16:08:26 +0300 Subject: [PATCH 12/23] 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 448f0a8772..01294e530b 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-1107" +tangemBlockchainSdk = "releases-5.26.0-1109" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "releases-5.26.0-488" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From e78794533d2e7c6fec9f054de8a76319ddb3e1e4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 11 Jul 2025 21:00:30 +0500 Subject: [PATCH 13/23] Updated on 2026-08-14 --- .../api/onramp/models/response/OnrampQuoteResponse.kt | 4 ++-- .../com/tangem/data/onramp/DefaultOnrampRepository.kt | 9 ++++++--- .../kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/onramp/models/response/OnrampQuoteResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/onramp/models/response/OnrampQuoteResponse.kt index 46ca99bbb6..ec0d6eb2c7 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/onramp/models/response/OnrampQuoteResponse.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/onramp/models/response/OnrampQuoteResponse.kt @@ -33,10 +33,10 @@ data class OnrampQuoteResponse( val providerId: String, @Json(name = "minFromAmount") - val minFromAmount: String, + val minFromAmount: String?, @Json(name = "maxFromAmount") - val maxFromAmount: String, + val maxFromAmount: String?, @Json(name = "minToAmount") val minToAmount: String?, diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt index 8b279d736b..64fd63a63a 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt @@ -52,7 +52,6 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.withContext import org.joda.time.DateTime import timber.log.Timber @@ -303,8 +302,12 @@ internal class DefaultOnrampRepository( OnrampQuote.Data( fromAmount = fromOnrampAmount, toAmount = convertToAmount(response.toAmount, cryptoCurrency), - minFromAmount = convertToAmount(response.minFromAmount, cryptoCurrency), - maxFromAmount = convertToAmount(response.maxFromAmount, cryptoCurrency), + minFromAmount = response.minFromAmount?.let { + convertToAmount(it, cryptoCurrency) + }, + maxFromAmount = response.maxFromAmount?.let { + convertToAmount(it, cryptoCurrency) + }, paymentMethod = paymentMethod, provider = provider, countryCode = response.countryCode, diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt index dde5f19064..59a0ddefa4 100644 --- a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt @@ -15,8 +15,8 @@ sealed class OnrampQuote { override val fromAmount: OnrampAmount, override val countryCode: String, val toAmount: OnrampAmount, - val minFromAmount: OnrampAmount, - val maxFromAmount: OnrampAmount, + val minFromAmount: OnrampAmount?, + val maxFromAmount: OnrampAmount?, ) : OnrampQuote() data class AmountError( From a07ee6d71f576895019cf1fd65b15005a664ce1c Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Jul 2025 13:07:15 +0500 Subject: [PATCH 14/23] Updated on 2026-08-14 --- .../walletsettings/model/WalletSettingsModel.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 b85bb4de06..8861cf7891 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,6 +24,7 @@ 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 @@ -44,6 +45,7 @@ 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 @@ -72,6 +74,8 @@ 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() @@ -194,10 +198,20 @@ 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 04655b7564f61a7bc8729346b6b58c7bb29ece37 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 13 Jul 2025 20:19:30 +0300 Subject: [PATCH 15/23] Updated on 2026-08-14 --- .../staking/impl/presentation/model/StakingModel.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt index 895a139203..1f75fd46e4 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt @@ -28,7 +28,6 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType -import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.staking.* import com.tangem.domain.staking.analytics.StakeScreenSource @@ -944,11 +943,6 @@ internal class StakingModel @Inject constructor( isSingleWalletWithTokens = false, ) .conflate() - .filter { - val sources = it.getOrNull()?.value?.sources ?: return@filter true - - sources.networkSource == StatusSource.ACTUAL && sources.yieldBalanceSource == StatusSource.ACTUAL - } .distinctUntilChanged() .filter { value.currentStep == StakingStep.InitialInfo } .onEach { maybeStatus -> From f338f79230d04f62e6797f164be67d89be1ec99c Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 12 Jul 2025 20:33:15 +0400 Subject: [PATCH 16/23] Updated on 2026-08-14 --- .../GetExtendedPublicKeyForCurrencyUseCase.kt | 20 ++++++++----- .../tokendetails/model/TokenDetailsModel.kt | 30 ++++++++++++++++++- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt index 7e6414e4ed..cf3ef97782 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt @@ -1,6 +1,7 @@ package com.tangem.domain.card import arrow.core.Either +import arrow.core.right import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.common.extensions.ByteArrayKey @@ -22,16 +23,17 @@ class GetExtendedPublicKeyForCurrencyUseCase( private val derivationsRepository: DerivationsRepository, private val walletManagersFacade: WalletManagersFacade, ) { + suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either { return Either.catch { - val userWallet = walletManagersFacade.getOrCreateWalletManager(userWalletId, network) + val walletManager = walletManagersFacade.getOrCreateWalletManager(userWalletId, network) ?: error("Wallet not found") val blockchain = network.toBlockchain() val isSecp256k1Blockchain = Blockchain.secp256k1Blockchains(network.isTestnet).contains(blockchain) val hdKey = if (isSecp256k1Blockchain) { - userWallet.wallet.publicKey.derivationType?.hdKey ?: error("No derivation found") + walletManager.wallet.publicKey.derivationType?.hdKey ?: error("No derivation found") } else { error("No derivation found") } @@ -50,7 +52,7 @@ class GetExtendedPublicKeyForCurrencyUseCase( val pendingDerivations = getPendingDerivations(childKey, parentKey) val derivedKeys = deriveKeys( userWalletId = userWalletId, - seedKey = userWallet.wallet.publicKey.seedKey, + seedKey = walletManager.wallet.publicKey.seedKey, paths = pendingDerivations, ) @@ -73,15 +75,17 @@ class GetExtendedPublicKeyForCurrencyUseCase( /** * @return true if xpub generation is supported, false otherwise */ - suspend fun isSupported(userWalletId: UserWalletId, network: Network): Boolean { - val userWallet = walletManagersFacade.getOrCreateWalletManager(userWalletId, network) - ?: error("Wallet not found") + suspend fun isSupported(userWalletId: UserWalletId, network: Network): Either = Either.catch { + val walletManager = walletManagersFacade.getOrCreateWalletManager(userWalletId, network) + ?: error("Wallet not found for user wallet $userWalletId and network ${network.id}") val blockchain = network.toBlockchain() val isSecp256k1Blockchain = Blockchain.secp256k1Blockchains(network.isTestnet).contains(blockchain) - val isHdKey = userWallet.wallet.publicKey.derivationType?.hdKey + val isHdKey = walletManager.wallet.publicKey.derivationType?.hdKey - return isSecp256k1Blockchain && isHdKey != null + val isSupported = isSecp256k1Blockchain && isHdKey != null + + return isSupported.right() } private suspend fun deriveKeys( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index 46dba2b2a9..d845947e08 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -3,6 +3,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.model import androidx.compose.runtime.Stable import androidx.paging.cachedIn import arrow.core.getOrElse +import arrow.core.merge import com.tangem.blockchain.common.address.AddressType import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter @@ -11,7 +12,9 @@ import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.api.AnalyticsExceptionHandler import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.ExceptionAnalyticsEvent import com.tangem.core.decompose.di.GlobalUiMessageSender import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model @@ -134,6 +137,7 @@ internal class TokenDetailsModel @Inject constructor( getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase, private val appRouter: AppRouter, private val router: InnerTokenDetailsRouter, + private val analyticsExceptionHandler: AnalyticsExceptionHandler, ) : Model(), TokenDetailsClickIntents { private val params = paramsContainer.require() @@ -444,7 +448,31 @@ internal class TokenDetailsModel @Inject constructor( network = cryptoCurrency.network, ).getOrElse { false } - val isSupported = getExtendedPublicKeyForCurrencyUseCase.isSupported(userWalletId, cryptoCurrency.network) + val isSupported = getExtendedPublicKeyForCurrencyUseCase.isSupported( + userWalletId = userWalletId, + network = cryptoCurrency.network, + ) + .mapLeft { + analyticsExceptionHandler.sendException( + event = ExceptionAnalyticsEvent( + exception = it, + params = mapOf( + "blockchainId" to cryptoCurrency.network.id.rawId.value, + "networkId" to cryptoCurrency.network.backendId, + ), + ), + ) + + Timber.e( + /* t = */ it, + /* message = */ "Unable to get wallet manager for user wallet %s and network %s", + /* ...args = */ userWalletId, + cryptoCurrency.network, + ) + + false + } + .merge() internalUiState.value = stateFactory.getStateWithUpdatedMenu( cardTypesResolver = userWallet.scanResponse.cardTypesResolver, From 01d87ca7c0a01514ca1d93ea6328ddadfe65f00e Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 12 Jul 2025 22:31:10 +0400 Subject: [PATCH 17/23] Updated on 2026-08-14 --- .../GetExtendedPublicKeyForCurrencyUseCase.kt | 2 +- .../tokendetails/model/TokenDetailsModel.kt | 54 ++++++++++--------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt index cf3ef97782..3172b275c4 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/GetExtendedPublicKeyForCurrencyUseCase.kt @@ -27,7 +27,7 @@ class GetExtendedPublicKeyForCurrencyUseCase( suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either { return Either.catch { val walletManager = walletManagersFacade.getOrCreateWalletManager(userWalletId, network) - ?: error("Wallet not found") + ?: error("Wallet not found for userWalletId=$userWalletId and network=$network") val blockchain = network.toBlockchain() val isSecp256k1Blockchain = Blockchain.secp256k1Blockchains(network.isTestnet).contains(blockchain) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index d845947e08..44c3e105b6 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -448,31 +448,7 @@ internal class TokenDetailsModel @Inject constructor( network = cryptoCurrency.network, ).getOrElse { false } - val isSupported = getExtendedPublicKeyForCurrencyUseCase.isSupported( - userWalletId = userWalletId, - network = cryptoCurrency.network, - ) - .mapLeft { - analyticsExceptionHandler.sendException( - event = ExceptionAnalyticsEvent( - exception = it, - params = mapOf( - "blockchainId" to cryptoCurrency.network.id.rawId.value, - "networkId" to cryptoCurrency.network.backendId, - ), - ), - ) - - Timber.e( - /* t = */ it, - /* message = */ "Unable to get wallet manager for user wallet %s and network %s", - /* ...args = */ userWalletId, - cryptoCurrency.network, - ) - - false - } - .merge() + val isSupported = isXPUBSupported() internalUiState.value = stateFactory.getStateWithUpdatedMenu( cardTypesResolver = userWallet.scanResponse.cardTypesResolver, @@ -482,6 +458,34 @@ internal class TokenDetailsModel @Inject constructor( } } + private suspend fun isXPUBSupported(): Boolean { + return getExtendedPublicKeyForCurrencyUseCase.isSupported( + userWalletId = userWalletId, + network = cryptoCurrency.network, + ) + .mapLeft { + analyticsExceptionHandler.sendException( + event = ExceptionAnalyticsEvent( + exception = it, + params = mapOf( + "blockchainId" to cryptoCurrency.network.id.rawId.value, + "networkId" to cryptoCurrency.network.backendId, + ), + ), + ) + + Timber.e( + /* t = */ it, + /* message = */ "Unable to get wallet manager for user wallet %s and network %s", + /* ...args = */ userWalletId, + cryptoCurrency.network, + ) + + false + } + .merge() + } + private fun createSelectedAppCurrencyFlow(): StateFlow { return getSelectedAppCurrencyUseCase() .map { maybeAppCurrency -> From b33cf2d2b12bc5f5aaf4cc125fc4425ebd1e9712 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Jul 2025 15:25:24 +0500 Subject: [PATCH 18/23] Updated on 2026-08-14 --- .../send/v2/common/analytics/CommonSendAnalyticEvents.kt | 6 +++++- .../features/send/v2/send/confirm/model/SendConfirmModel.kt | 1 + .../send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/analytics/CommonSendAnalyticEvents.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/analytics/CommonSendAnalyticEvents.kt index c40b00375d..acaf68a42f 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/analytics/CommonSendAnalyticEvents.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/analytics/CommonSendAnalyticEvents.kt @@ -48,10 +48,14 @@ internal sealed class CommonSendAnalyticEvents( data class TransactionError( val categoryName: String, val token: String, + val blockchain: String, ) : CommonSendAnalyticEvents( category = categoryName, event = "Error - Transaction Rejected", - params = mapOf(TOKEN_PARAM to token), + params = mapOf( + TOKEN_PARAM to token, + BLOCKCHAIN to blockchain, + ), ) /** Close button clicked */ diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt index f8abef07da..f81b7bc06e 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt @@ -376,6 +376,7 @@ internal class SendConfirmModel @Inject constructor( CommonSendAnalyticEvents.TransactionError( categoryName = analyticsCategoryName, token = cryptoCurrency.symbol, + blockchain = cryptoCurrency.network.name, ), ) }, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt index fb152c1b84..d9cce006b2 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt @@ -310,6 +310,7 @@ internal class NFTSendConfirmModel @Inject constructor( CommonSendAnalyticEvents.TransactionError( categoryName = analyticsCategoryName, token = cryptoCurrency.symbol, + blockchain = cryptoCurrency.network.name, ), ) }, From 4bac2a189e39dfe29faa8ea177617851c92059fd Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Jul 2025 14:43:27 +0400 Subject: [PATCH 19/23] Updated on 2026-08-14 --- .../subscribers/BasicTokenListSubscriber.kt | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt index 5370e45d02..796767b539 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt @@ -58,11 +58,9 @@ internal abstract class BasicTokenListSubscriber( } .distinctUntilChanged() .onEach { maybeTokenList -> - if (!routingFeatureToggle.isDeepLinkNavigationEnabled) { - coroutineScope.launch { - onTokenListReceived(maybeTokenList) - }.saveIn(onTokenListReceivedJobHolder) - } + coroutineScope.launch { + onTokenListReceived(maybeTokenList) + }.saveIn(onTokenListReceivedJobHolder) coroutineScope.launch { startCheck(maybeTokenList) } }, @@ -77,8 +75,7 @@ internal abstract class BasicTokenListSubscriber( ifLoading = { maybeContent -> val isRefreshing = stateHolder.getWalletState(userWallet.walletId) ?.pullToRefreshConfig - ?.isRefreshing - ?: false + ?.isRefreshing == true maybeContent ?.takeIf { !isRefreshing } @@ -118,15 +115,17 @@ internal abstract class BasicTokenListSubscriber( } protected open suspend fun onTokenListReceived(maybeTokenList: Lce) { - /* no-op */ - // Handling sell deeplink requires full content in order to correctly open Send screen - if (maybeTokenList.getOrNull(false) != null) { - deepLinksRegistry.triggerDelayedDeeplink(deepLinkClass = SellCurrencyDeepLink::class.java) - } - // Handling referral deeplink requires only selected wallet to be loaded - // This is temporary solution, will be removed with complete deeplink navigation overhaul - if (maybeTokenList.getOrNull(true) != null) { - deepLinksRegistry.triggerDelayedDeeplink(deepLinkClass = ReferralDeepLink::class.java) + if (!routingFeatureToggle.isDeepLinkNavigationEnabled) { + /* no-op */ + // Handling sell deeplink requires full content in order to correctly open Send screen + if (maybeTokenList.getOrNull(false) != null) { + deepLinksRegistry.triggerDelayedDeeplink(deepLinkClass = SellCurrencyDeepLink::class.java) + } + // Handling referral deeplink requires only selected wallet to be loaded + // This is temporary solution, will be removed with complete deeplink navigation overhaul + if (maybeTokenList.getOrNull(true) != null) { + deepLinksRegistry.triggerDelayedDeeplink(deepLinkClass = ReferralDeepLink::class.java) + } } } From cbd9ad505df6b3f04ceaeea29bfe149d2ad69e8b Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 13 Jul 2025 16:51:37 +0400 Subject: [PATCH 20/23] Updated on 2026-08-14 --- .../data/staking/DefaultStakingRepository.kt | 19 +++++----- .../DefaultSingleYieldBalanceProducer.kt | 35 +++++++++++++++++-- .../store/DefaultYieldsBalancesStore.kt | 11 ++++-- .../DefaultSingleYieldBalanceProducerTest.kt | 3 ++ 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index ac813e234c..b3ab55fbeb 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt @@ -120,7 +120,7 @@ internal class DefaultStakingRepository( when (val stakingTokensWithYields = stakeKitApi.getEnabledYields(preferredValidatorsOnly = false)) { is ApiResponse.Success -> stakingYieldsStore.store( stakingTokensWithYields.data.data.filter { - it.isAvailable ?: false + it.isAvailable == true }, ) else -> { @@ -651,15 +651,16 @@ internal class DefaultStakingRepository( } override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean { - return withContext(dispatchers.io) { - stakingBalanceStore.getSyncOrNull(userWalletId) - ?.let { - it.isNotEmpty() && - it.any { yieldBalance -> - (yieldBalance as? YieldBalance.Data)?.balance?.items?.isNotEmpty() == true - } + return withContext(dispatchers.default) { + val balances = stakingBalanceStoreV2.getAllSyncOrNull(userWalletId) ?: return@withContext false + + val hasDataYieldBalance by lazy { + balances.any { yieldBalance -> + (yieldBalance as? YieldBalance.Data)?.balance?.items?.isNotEmpty() == true } - ?: false + } + + balances.isNotEmpty() && hasDataYieldBalance } } diff --git a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt index 887db9af5f..82a820ebae 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducer.kt @@ -1,5 +1,7 @@ package com.tangem.data.staking.single +import com.tangem.core.analytics.api.AnalyticsExceptionHandler +import com.tangem.core.analytics.models.ExceptionAnalyticsEvent import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.domain.staking.model.StakingID import com.tangem.domain.staking.model.stakekit.YieldBalance @@ -7,6 +9,7 @@ import com.tangem.domain.staking.multi.MultiYieldBalanceProducer import com.tangem.domain.staking.multi.MultiYieldBalanceSupplier import com.tangem.domain.staking.single.SingleYieldBalanceProducer import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.extensions.indexOfFirstOrNull import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -14,6 +17,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.mapNotNull +import timber.log.Timber /** * Default implementation of [SingleYieldBalanceProducer] @@ -29,6 +33,7 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor( @Assisted private val params: SingleYieldBalanceProducer.Params, private val multiYieldBalanceSupplier: MultiYieldBalanceSupplier, private val stakingIdFactory: StakingIdFactory, + private val analyticsExceptionHandler: AnalyticsExceptionHandler, private val dispatchers: CoroutineDispatcherProvider, ) : SingleYieldBalanceProducer { @@ -48,8 +53,34 @@ internal class DefaultSingleYieldBalanceProducer @AssistedInject constructor( .mapNotNull { balances -> val currentStakingId = getStakingId() ?: return@mapNotNull YieldBalance.Unsupported - balances.firstOrNull { it.getStakingId() == currentStakingId } - ?: YieldBalance.Unsupported + val currentBalances = balances.filter { it.getStakingId() == currentStakingId } + + if (currentBalances.size > 1) { + analyticsExceptionHandler.sendException( + event = ExceptionAnalyticsEvent( + exception = IllegalStateException("Multiple balances found for staking ID"), + params = mapOf( + "stakingId" to currentStakingId.toString(), + "balances" to currentBalances.joinToString(",") { it.toString() }, + ), + ), + ) + + Timber.w( + "Multiple balances found for staking ID $currentStakingId:\n%s", + currentBalances.joinToString("\n"), + ) + + val dataIndex = currentBalances.indexOfFirstOrNull { it is YieldBalance.Data } + + if (dataIndex != null) { + currentBalances[dataIndex] + } else { + currentBalances.first() + } + } else { + currentBalances.firstOrNull() ?: YieldBalance.Unsupported + } } .distinctUntilChanged() .flowOn(dispatchers.default) diff --git a/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt b/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt index 2f28162955..808ba7a7ae 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt @@ -109,8 +109,15 @@ internal class DefaultYieldsBalancesStore( private suspend fun storeInPersistence(userWalletId: UserWalletId, values: Set) { persistenceStore.updateData { current -> current.toMutableMap().apply { - this[userWalletId.stringValue] = current[userWalletId.stringValue] - ?.addOrReplace(items = values) { old, new -> old.getStakingId() == new.getStakingId() } + this[userWalletId.stringValue] = this[userWalletId.stringValue] + ?.addOrReplace(items = values) { old, new -> + val oldId = old.getStakingId() + val newId = new.getStakingId() + + if (oldId == null || newId == null) return@addOrReplace false + + oldId == newId + } ?: values } } diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt index b05ad2e885..c594ea3515 100644 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/single/DefaultSingleYieldBalanceProducerTest.kt @@ -5,6 +5,7 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory import com.tangem.common.test.utils.getEmittedValues +import com.tangem.core.analytics.api.AnalyticsExceptionHandler import com.tangem.data.staking.toDomain import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.domain.staking.model.StakingID @@ -32,12 +33,14 @@ internal class DefaultSingleYieldBalanceProducerTest { private val multiNetworkStatusSupplier = mockk() private val stakingIdFactory = mockk() + private val analyticsExceptionHandler = mockk(relaxUnitFun = true) private val dispatchers = TestingCoroutineDispatcherProvider() private val producer = DefaultSingleYieldBalanceProducer( params = params, stakingIdFactory = stakingIdFactory, multiYieldBalanceSupplier = multiNetworkStatusSupplier, + analyticsExceptionHandler = analyticsExceptionHandler, dispatchers = dispatchers, ) From 309c9aad6c2a1126aa7ef80e70e0c04b707c46a5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 12 Jul 2025 20:58:28 +0400 Subject: [PATCH 21/23] Updated on 2026-08-14 --- .../repository/DefaultCurrenciesRepository.kt | 52 ++++++++++--------- .../domain/markets/SaveMarketTokensUseCase.kt | 30 ++++++----- .../tokens/repository/CurrenciesRepository.kt | 2 +- .../repository/MockCurrenciesRepository.kt | 5 +- 4 files changed, 49 insertions(+), 40 deletions(-) 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 4f3a819ef0..4d18c1bfe9 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 @@ -97,31 +97,35 @@ internal class DefaultCurrenciesRepository( } } - override suspend fun addCurrencies(userWalletId: UserWalletId, currencies: List) { - withContext(dispatchers.io) { - val savedCurrencies = requireNotNull( - value = getSavedUserTokensResponseSync(key = userWalletId), - lazyMessage = { "Saved tokens empty. Can not perform add currencies action" }, - ) + override suspend fun addCurrencies( + 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 = populateCurrenciesWithMissedCoins( - currencies = currencies, - ).let { - filterAlreadyAddedCurrencies(savedCurrencies.tokens, it) - } - val updatedResponse = savedCurrencies.copy( - tokens = savedCurrencies.tokens + currenciesToAdd.map(userTokensResponseFactory::createResponseToken), - ) - userTokensSaver.storeAndPush( - userWalletId = userWalletId, - response = updatedResponse, - ) + val currenciesToAdd = filterAlreadyAddedCurrencies( + savedCurrencies = savedCurrencies.tokens, + currenciesToAdd = populateCurrenciesWithMissedCoins(currencies = currencies), + ) - fetchExpressAssetsByNetworkIds( - userWallet = userWalletsStore.getSyncStrict(key = userWalletId), - userTokens = updatedResponse, - ) - } + val updatedResponse = savedCurrencies.copy( + tokens = savedCurrencies.tokens + currenciesToAdd.map(userTokensResponseFactory::createResponseToken), + ) + + userTokensSaver.storeAndPush( + userWalletId = userWalletId, + response = updatedResponse, + ) + + fetchExpressAssetsByNetworkIds( + userWallet = userWalletsStore.getSyncStrict(key = userWalletId), + userTokens = updatedResponse, + ) + + currenciesToAdd } private fun filterAlreadyAddedCurrencies( @@ -573,7 +577,7 @@ internal class DefaultCurrenciesRepository( override fun isNetworkFeeZero(userWalletId: UserWalletId, network: Network): Boolean { val blockchain = Blockchain.fromNetworkId(network.backendId) - return blockchain?.isNetworkFeeZero() ?: false + return blockchain?.isNetworkFeeZero() == true } override suspend fun syncTokens(userWalletId: UserWalletId) { 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 96a60f0cc0..dd0ad48ebc 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 @@ -41,16 +41,15 @@ class SaveMarketTokensUseCase( removedNetworks: Set, ): Either = Either.catch { if (removedNetworks.isNotEmpty()) { - currenciesRepository.removeCurrencies( - userWalletId = userWalletId, - currencies = removedNetworks.mapNotNull { - marketsTokenRepository.createCryptoCurrency( - userWalletId = userWalletId, - token = tokenMarketParams, - network = it, - ) - }, - ) + val removedCurrencies = removedNetworks.mapNotNull { + marketsTokenRepository.createCryptoCurrency( + userWalletId = userWalletId, + token = tokenMarketParams, + network = it, + ) + } + + currenciesRepository.removeCurrencies(userWalletId = userWalletId, currencies = removedCurrencies) } if (addedNetworks.isNotEmpty()) { @@ -67,13 +66,16 @@ class SaveMarketTokensUseCase( ) } - currenciesRepository.addCurrencies(userWalletId = userWalletId, currencies = addedCurrencies) + val savedCurrencies = currenciesRepository.addCurrencies( + userWalletId = userWalletId, + currencies = addedCurrencies, + ) - refreshUpdatedNetworks(userWalletId, addedCurrencies) + refreshUpdatedNetworks(userWalletId, savedCurrencies) - refreshUpdatedYieldBalances(userWalletId, addedCurrencies) + refreshUpdatedYieldBalances(userWalletId, savedCurrencies) - refreshUpdatedQuotes(addedCurrencies) + refreshUpdatedQuotes(savedCurrencies) } } 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 63bb8891bd..d58a0257f9 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 @@ -50,7 +50,7 @@ interface CurrenciesRepository { * @throws DataError.UserWalletError.WrongUserWallet If single-currency user wallet * ID provided. */ - suspend fun addCurrencies(userWalletId: UserWalletId, currencies: List) + suspend fun addCurrencies(userWalletId: UserWalletId, currencies: List): List /** * Removes currency from a specific user wallet. 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 475ea789ec..01c6a656e5 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 @@ -48,7 +48,10 @@ internal class MockCurrenciesRepository( override suspend fun saveNewCurrenciesList(userWalletId: UserWalletId, currencies: List) = Unit - override suspend fun addCurrencies(userWalletId: UserWalletId, currencies: List) = Unit + override suspend fun addCurrencies( + userWalletId: UserWalletId, + currencies: List, + ): List = emptyList() override suspend fun removeCurrency(userWalletId: UserWalletId, currency: CryptoCurrency) { removeCurrencyResult.onLeft { throw it } From ee9a381c25423abf42f4b2e7362840d4f6280b3a Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Jul 2025 11:00:57 +0000 Subject: [PATCH 22/23] 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 01294e530b..65d8993bb1 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-1109" +tangemBlockchainSdk = "develop-1104" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "releases-5.26.0-488" +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 a6c04b3ab865ebbddc09efd2b81afc29204d4373 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Jul 2025 16:03:59 +0300 Subject: [PATCH 23/23] 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 65d8993bb1..c1b01e6007 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 = "develop-1104" +tangemBlockchainSdk = "develop-1110" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-489" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^