From f7479822af817cc1fbe96cd7849736d79730c22c Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 27 Sep 2024 09:59:37 +0300 Subject: [PATCH 01/27] Updated on 2026-08-14 --- gradle/dependencies.toml | 4 ++-- .../main/java/com/tangem/blockchainsdk/utils/Blockchain.kt | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index aaf2b34885..d4c53abc59 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -89,9 +89,9 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.15-796" +tangemBlockchainSdk = "release-app_5.16-800" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "release-app_5.15-384" +tangemCardSdk = "release-app_5.16-386" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem16" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt index cddafe6527..6c71737687 100644 --- a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt @@ -367,6 +367,4 @@ private val excludedBlockchains = listOf( Blockchain.Unknown, Blockchain.Nexa, Blockchain.NexaTestnet, - Blockchain.Sei, - Blockchain.InternetComputer, ) \ No newline at end of file From 9264825e2cc95230efe384609cc62f118eab6409 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 27 Sep 2024 10:36:37 +0300 Subject: [PATCH 02/27] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index d4c53abc59..ec34fe2a7d 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -89,7 +89,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.16-800" +tangemBlockchainSdk = "release-app_5.16-801" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.16-386" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From dd77e51465a295b562eec247a08bc1cf2716c061 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 27 Sep 2024 13:08:25 +0500 Subject: [PATCH 03/27] Updated on 2026-08-14 --- .../com/tangem/data/transaction/DefaultTransactionRepository.kt | 1 + .../state/recipient/SendRecipientMemoFieldConverter.kt | 1 + gradle/dependencies.toml | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt index 0452da5a7c..f2b213583f 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt @@ -263,6 +263,7 @@ internal class DefaultTransactionRepository( Blockchain.Binance -> BinanceTransactionExtras(memo) Blockchain.XRP -> memo.toLongOrNull()?.let { XrpTransactionBuilder.XrpTransactionExtras(it) } Blockchain.Cosmos, + Blockchain.Sei, Blockchain.TerraV1, Blockchain.TerraV2, -> CosmosTransactionExtras(memo) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt index 1525d0d6ab..8ea7d24d6d 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt @@ -36,6 +36,7 @@ internal class SendRecipientMemoFieldConverter( Blockchain.Stellar.id, Blockchain.Hedera.id, Blockchain.Algorand.id, + Blockchain.Sei.id, -> { convert( value = Data(memo = memo, label = R.string.send_extras_hint_memo), diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index ec34fe2a7d..8f9ec08516 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -89,7 +89,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.16-801" +tangemBlockchainSdk = "release-app_5.16-802" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.16-386" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 0f9a5e4dad5134720319ac41cf92b6dd283a4533 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 27 Sep 2024 12:22:36 +0300 Subject: [PATCH 04/27] Updated on 2026-08-14 --- .../tangem/data/staking/DefaultStakingRepository.kt | 3 +-- .../staking/converters/YieldBalanceListConverter.kt | 10 ++++------ 2 files changed, 5 insertions(+), 8 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 2ccac4081b..a1fe27bb29 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 @@ -98,8 +98,7 @@ internal class DefaultStakingRepository( ) private val yieldBalanceConverter = YieldBalanceConverter() - - private val yieldBalanceListConverter = YieldBalanceListConverter() + private val yieldBalanceListConverter = YieldBalanceListConverter(yieldBalanceConverter) private val isYieldBalanceFetching = MutableStateFlow( value = emptyMap(), diff --git a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceListConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceListConverter.kt index 6a3f3c793a..0f3d08a7c1 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceListConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceListConverter.kt @@ -4,18 +4,16 @@ import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrap import com.tangem.domain.staking.model.stakekit.YieldBalanceList import com.tangem.utils.converter.Converter -internal class YieldBalanceListConverter : Converter, YieldBalanceList> { - - internal val converter by lazy(LazyThreadSafetyMode.NONE) { - YieldBalanceConverter() - } +internal class YieldBalanceListConverter( + private val yieldBalanceConverter: YieldBalanceConverter, +) : Converter, YieldBalanceList> { override fun convert(value: Set): YieldBalanceList { return if (value.isEmpty()) { YieldBalanceList.Empty } else { YieldBalanceList.Data( - balances = value.map(converter::convert), + balances = value.map(yieldBalanceConverter::convert), ) } } From 9ebefa77adba9eac4040749b905d302f376ba37b Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 27 Sep 2024 13:35:43 +0300 Subject: [PATCH 05/27] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 8f9ec08516..c555bbf340 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -93,7 +93,7 @@ tangemBlockchainSdk = "release-app_5.16-802" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.16-386" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ -tangemVico = "2.0.0-alpha.25-tangem16" +tangemVico = "2.0.0-alpha.25-tangem17" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ # endregion Tangem From a601f1d76d28c44ec6b5980ad121343b0ed0caf3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 27 Sep 2024 13:40:20 +0300 Subject: [PATCH 06/27] Updated on 2026-08-14 --- .../models/response/model/YieldDTO.kt | 2 +- .../data/staking/converters/YieldConverter.kt | 2 +- .../domain/staking/model/stakekit/Yield.kt | 2 +- .../state/converters/BalanceItemConverter.kt | 4 +- .../AddStakingNotificationsTransformer.kt | 47 +++++++++++-------- .../SetInitialDataStateTransformer.kt | 5 +- 6 files changed, 35 insertions(+), 27 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/YieldDTO.kt b/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/YieldDTO.kt index 614184aee4..330df3c666 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/YieldDTO.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/YieldDTO.kt @@ -124,7 +124,7 @@ data class YieldDTO( @Json(name = "rewardSchedule") val rewardSchedule: RewardScheduleDTO, @Json(name = "cooldownPeriod") - val cooldownPeriod: PeriodDTO, + val cooldownPeriod: PeriodDTO?, @Json(name = "warmupPeriod") val warmupPeriod: PeriodDTO, @Json(name = "rewardClaiming") diff --git a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt index c08359c098..8c54ddbf4a 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt @@ -88,7 +88,7 @@ class YieldConverter( tokens = metadataDTO.tokensDTO.map { tokenConverter.convert(it) }, type = metadataDTO.type, rewardSchedule = convertRewardSchedule(metadataDTO.rewardSchedule), - cooldownPeriod = convertPeriod(metadataDTO.cooldownPeriod), + cooldownPeriod = metadataDTO.cooldownPeriod?.let { convertPeriod(it) }, warmupPeriod = convertPeriod(metadataDTO.warmupPeriod), rewardClaiming = convertRewardClaiming(metadataDTO.rewardClaiming), defaultValidator = metadataDTO.defaultValidator, diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt index 4be9bad438..c65303fd35 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt @@ -84,7 +84,7 @@ data class Yield( val tokens: List, val type: String, val rewardSchedule: RewardSchedule, - val cooldownPeriod: Period, + val cooldownPeriod: Period?, val warmupPeriod: Period, val rewardClaiming: RewardClaiming, val defaultValidator: String?, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt index 141759adfb..584fafdd1f 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/BalanceItemConverter.kt @@ -100,8 +100,8 @@ internal class BalanceItemConverter( -> null } - private fun getUnbondingDate(date: DateTime?): TextReference { - val unbondingPeriod = yield.metadata.cooldownPeriod.days + private fun getUnbondingDate(date: DateTime?): TextReference? { + val unbondingPeriod = yield.metadata.cooldownPeriod?.days ?: return null if (date == null) { return combinedReference( resourceReference(R.string.staking_details_unbonding_period), diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt index bd7988cd8f..30877033e3 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt @@ -257,16 +257,19 @@ internal class AddStakingNotificationsTransformer( } private fun MutableList.addExitInfoNotifications() { - add( - StakingNotification.Info.Unstake( - cooldownPeriodDays = yield.metadata.cooldownPeriod.days, - subtitleRes = if (isCosmos(cryptoCurrencyStatusProvider().currency.network.id.value)) { - R.string.staking_notification_unstake_cosmos_text - } else { - R.string.staking_notification_unstake_text - }, - ), - ) + val cooldownPeriodDays = yield.metadata.cooldownPeriod?.days + if (cooldownPeriodDays != null) { + add( + StakingNotification.Info.Unstake( + cooldownPeriodDays = cooldownPeriodDays, + subtitleRes = if (isCosmos(cryptoCurrencyStatusProvider().currency.network.id.value)) { + R.string.staking_notification_unstake_cosmos_text + } else { + R.string.staking_notification_unstake_text + }, + ), + ) + } } private fun MutableList.addEnterInfoNotifications() { @@ -300,17 +303,21 @@ internal class AddStakingNotificationsTransformer( resourceReference(R.string.staking_notification_withdraw_text) } StakingActionType.UNLOCK_LOCKED -> { - val cooldownPeriodDays = yield.metadata.cooldownPeriod.days - resourceReference(R.string.staking_unlocked_locked) to resourceReference( - R.string.staking_notification_unlock_text, - wrappedList( - pluralReference( - id = R.plurals.common_days, - count = cooldownPeriodDays, - formatArgs = wrappedList(cooldownPeriodDays), + val cooldownPeriodDays = yield.metadata.cooldownPeriod?.days + if (cooldownPeriodDays != null) { + resourceReference(R.string.staking_unlocked_locked) to resourceReference( + R.string.staking_notification_unlock_text, + wrappedList( + pluralReference( + id = R.plurals.common_days, + count = cooldownPeriodDays, + formatArgs = wrappedList(cooldownPeriodDays), + ), ), - ), - ) + ) + } else { + null to null + } } else -> null to null } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt index 66b56ea7aa..8c7194ad53 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt @@ -104,7 +104,7 @@ internal class SetInitialDataStateTransformer( return listOfNotNull( createAnnualPercentageRateItem(yield.validators), createAvailableItem(cryptoCurrencyStatus), - createUnbondingPeriodItem(yield.metadata.cooldownPeriod.days), + createUnbondingPeriodItem(yield.metadata.cooldownPeriod?.days), createMinimumRequirementItem( cryptoCurrencyStatus = cryptoCurrencyStatus, minimumCryptoAmount = yield.args.enter.args[Yield.Args.ArgType.AMOUNT]?.minimum, @@ -140,7 +140,8 @@ internal class SetInitialDataStateTransformer( ) } - private fun createUnbondingPeriodItem(cooldownPeriodDays: Int): RoundedListWithDividersItemData { + private fun createUnbondingPeriodItem(cooldownPeriodDays: Int?): RoundedListWithDividersItemData? { + cooldownPeriodDays ?: return null return RoundedListWithDividersItemData( id = R.string.staking_details_unbonding_period, startText = TextReference.Res(R.string.staking_details_unbonding_period), From 9cc289be141a7f46554f09cce4cb890fb2187b16 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 27 Sep 2024 16:34:57 +0500 Subject: [PATCH 07/27] Updated on 2026-08-14 --- .../com/tangem/data/staking/DefaultStakingRepository.kt | 2 +- .../com/tangem/data/staking/converters/YieldConverter.kt | 2 +- .../transformers/SetConfirmationStateLoadingTransformer.kt | 7 +++++-- .../SetConfirmationStateResetAssentTransformer.kt | 3 ++- .../impl/presentation/viewmodel/StakingViewModel.kt | 2 ++ 5 files changed, 11 insertions(+), 5 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 2ccac4081b..9273ee0dff 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 @@ -121,7 +121,7 @@ internal class DefaultStakingRepository( key = YIELDS_STORE_KEY, skipCache = refresh, block = { - val stakingTokensWithYields = stakeKitApi.getMultipleYields(preferredValidatorsOnly = true) + val stakingTokensWithYields = stakeKitApi.getMultipleYields(preferredValidatorsOnly = false) .getOrThrow() stakingYieldsStore.store(stakingTokensWithYields.data) diff --git a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt index c08359c098..46cf6e3ae1 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt @@ -28,7 +28,7 @@ class YieldConverter( metadata = convertMetadata(value.metadata), validators = value.validators .asSequence() - .filter { it.preferred && it.status == ValidatorStatusDTO.ACTIVE } + .filter { it.status == ValidatorStatusDTO.ACTIVE } .sortedByDescending { it.apr } .map { convertValidator(it) } .toImmutableList(), diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt index c2df5db972..0db1e1f2cb 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt @@ -20,9 +20,12 @@ internal class SetConfirmationStateLoadingTransformer( ) : Transformer { override fun transform(prevState: StakingUiState): StakingUiState { + val filteredValidators = yield.validators.filter { + it.preferred + } val possibleConfirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data val possibleValidatorState = possibleConfirmationState?.validatorState as? ValidatorState.Content - val chosenValidator = possibleValidatorState?.chosenValidator ?: yield.validators[0] + val chosenValidator = possibleValidatorState?.chosenValidator ?: filteredValidators[0] return prevState.copy( confirmationState = StakingStates.ConfirmationState.Data( @@ -32,7 +35,7 @@ internal class SetConfirmationStateLoadingTransformer( validatorState = ValidatorState.Content( isClickable = true, chosenValidator = chosenValidator, - availableValidators = yield.validators, + availableValidators = filteredValidators, ), notifications = persistentListOf(), footerText = getFooter(prevState, chosenValidator), diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt index ee866db72c..a3b203d9eb 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateResetAssentTransformer.kt @@ -3,6 +3,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState import com.tangem.features.staking.impl.presentation.state.StakingStates import com.tangem.features.staking.impl.presentation.state.StakingUiState +import com.tangem.features.staking.impl.presentation.state.ValidatorState import com.tangem.utils.transformer.Transformer internal object SetConfirmationStateResetAssentTransformer : Transformer { @@ -13,7 +14,7 @@ internal object SetConfirmationStateResetAssentTransformer : Transformer { + stateController.update(SetConfirmationStateResetAssentTransformer) stateController.update(SetConfirmationStateLoadingTransformer(yield, appCurrency)) stateController.update(SetBalanceStateTransformer(balanceState)) onRefreshSwipe(isRefreshing = false) @@ -328,6 +329,7 @@ internal class StakingViewModel @Inject constructor( if (value.currentStep == StakingStep.Confirmation) { when ((value.confirmationState as? StakingStates.ConfirmationState.Data)?.innerState) { InnerConfirmationStakingState.ASSENT -> { + stateController.update(SetConfirmationStateResetAssentTransformer) stakingStateRouter.onPrevClick() } null, From 3f172933368a08718c70a4725578692c636e9a72 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 27 Sep 2024 17:21:48 +0300 Subject: [PATCH 08/27] Updated on 2026-08-14 --- .../PendingTransactionItemConverter.kt | 4 +- domain/staking/build.gradle.kts | 3 +- .../staking/model/PendingTransaction.kt | 5 +- .../domain/staking/model/SubmitHashData.kt | 9 +-- .../staking/model/stakekit/YieldBalance.kt | 5 ++ .../InvalidatePendingTransactionsUseCase.kt | 39 ++++++++--- .../impl/presentation/state/StakingUiState.kt | 3 +- .../state/helpers/StakingTransactionSender.kt | 68 +++++++++---------- .../ConfirmationStatePreviewData.kt | 2 +- .../SetBalanceStateTransformer.kt | 18 ----- .../SetConfirmationStateLoadingTransformer.kt | 2 +- .../SetInitialDataStateTransformer.kt | 2 +- ...etPossiblePendingTransactionTransformer.kt | 33 +++++++++ .../viewmodel/StakingViewModel.kt | 9 ++- 14 files changed, 121 insertions(+), 81 deletions(-) delete mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetBalanceStateTransformer.kt create mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt diff --git a/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt index a18b61f591..6169fc2b96 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt @@ -10,8 +10,8 @@ internal object PendingTransactionItemConverter : Converter, + yieldBalance: YieldBalance.Data, pending: List>, ): Pair, List> { - val map = real.associateBy { Triple(it.groupId, it.type, it.amount) }.toMutableMap() + val balances = yieldBalance.balance.items.associateBy { Triple(it.groupId, it.type, it.amount) }.toMutableMap() + val newBalancesId = yieldBalance.getBalancesUniqueId() - val toRemove = mutableListOf() + val transactionsToRemove = mutableListOf() pending.forEach { (pendingTransaction, balanceItem) -> val key = Triple(balanceItem.groupId, balanceItem.type, balanceItem.amount) - if (map.containsKey(key)) { - map[key] = balanceItem - } else { - toRemove.add(pendingTransaction) + val oldBalancesId = pendingTransaction.balancesId + + when { + newBalancesId != oldBalancesId -> { + transactionsToRemove.add(pendingTransaction) + } + balances.containsKey(key) -> { + balances[key] = balanceItem + } + else -> { + val groupId = UUID.randomUUID().toString() + balances[Triple(groupId, BalanceType.STAKED, pendingTransaction.amount)] = BalanceItem( + groupId = groupId, + type = pendingTransaction.type, + amount = pendingTransaction.amount, + rawCurrencyId = pendingTransaction.rawCurrencyId, + validatorAddress = pendingTransaction.validator?.address, + date = null, + pendingActions = emptyList(), + isPending = true, + ) + } } } - return map.values.toList() to toRemove + return balances.values.toList() to transactionsToRemove } } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt index c490284116..4bc8b84bda 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt @@ -9,6 +9,7 @@ import com.tangem.core.ui.components.list.RoundedListWithDividersItemData import com.tangem.core.ui.event.StateEvent import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig +import com.tangem.domain.staking.model.PendingTransaction import com.tangem.domain.staking.model.stakekit.PendingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType @@ -98,7 +99,7 @@ internal sealed class StakingStates { val transactionDoneState: TransactionDoneState, val isApprovalNeeded: Boolean, val reduceAmountBy: BigDecimal?, - val balanceState: BalanceState?, + val possiblePendingTransaction: PendingTransaction?, ) : ConfirmationState() data class Empty( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt index 57f98b373b..7edd7024b0 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt @@ -8,10 +8,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.staking.* import com.tangem.domain.staking.model.PendingTransaction import com.tangem.domain.staking.model.SubmitHashData -import com.tangem.domain.staking.model.stakekit.BalanceType -import com.tangem.domain.staking.model.stakekit.PendingAction -import com.tangem.domain.staking.model.stakekit.StakingError -import com.tangem.domain.staking.model.stakekit.Yield +import com.tangem.domain.staking.model.stakekit.* import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.staking.model.stakekit.transaction.ActionParams import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction @@ -35,6 +32,7 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import timber.log.Timber import java.math.BigDecimal +import java.util.UUID @Suppress("LongParameterList") internal class StakingTransactionSender @AssistedInject constructor( @@ -91,12 +89,16 @@ internal class StakingTransactionSender @AssistedInject constructor( sendStakingTransaction( fullTransactionsData = fullTransactionsData, - balanceState = confirmationState.balanceState, + possiblePendingTransaction = composePendingTransaction(confirmationState), onSendSuccess = onSendSuccess, onSendError = onSendError, ) } + private fun composePendingTransaction(confirmationState: StakingStates.ConfirmationState.Data): PendingTransaction { + return confirmationState.possiblePendingTransaction ?: composeStakeTransaction(confirmationState) + } + private suspend fun getStakingTransactions( state: StakingUiState, confirmationState: StakingStates.ConfirmationState.Data, @@ -204,7 +206,7 @@ internal class StakingTransactionSender @AssistedInject constructor( private suspend fun sendStakingTransaction( fullTransactionsData: List, - balanceState: BalanceState?, + possiblePendingTransaction: PendingTransaction, onSendSuccess: (txUrl: String) -> Unit, onSendError: (SendTransactionError?) -> Unit, ) { @@ -218,13 +220,9 @@ internal class StakingTransactionSender @AssistedInject constructor( }, ifRight = { transactionHashes -> submitHash( - transactionIds = fullTransactionsData.map { it.stakeKitTransaction.id }, + transactions = fullTransactionsData.map { it.stakeKitTransaction }, transactionHashes = transactionHashes, - groupId = balanceState?.groupId, - validator = balanceState?.validator, - amount = balanceState?.cryptoDecimal, - balanceType = balanceState?.type, - rawCurrencyId = balanceState?.rawCurrencyId, + pendingTransaction = possiblePendingTransaction, ) val txUrl = getExplorerTransactionUrlUseCase( txHash = transactionHashes.last(), @@ -238,25 +236,18 @@ internal class StakingTransactionSender @AssistedInject constructor( } private suspend fun submitHash( - transactionIds: List, + transactions: List, transactionHashes: List, - groupId: String?, - validator: Yield.Validator?, - amount: BigDecimal?, - balanceType: BalanceType?, - rawCurrencyId: String?, + pendingTransaction: PendingTransaction, ) { - transactionIds + transactions .zip(transactionHashes) - .forEach { (transactionId, transactionHash) -> + .forEach { (transaction, transactionHash) -> submitHashUseCase( SubmitHashData( - transactionId = transactionId, + transactionId = transaction.id, transactionHash = transactionHash, - validator = validator, - amount = amount, - balanceType = balanceType, - rawCurrencyId = rawCurrencyId, + pendingTransaction = pendingTransaction, ), ) .onLeft { error -> @@ -267,20 +258,14 @@ internal class StakingTransactionSender @AssistedInject constructor( ), ) saveUnsubmittedHashUseCase.invoke( - transactionId = transactionId, + transactionId = transaction.id, transactionHash = transactionHash, ) }.onRight { Timber.d("Successful hash submission") - savePendingTransactionUseCase.invoke( - PendingTransaction( - groupId = groupId, - type = balanceType, - amount = amount, - rawCurrencyId = rawCurrencyId, - validator = validator, - ), - ) + if (transaction.type != StakingTransactionType.FREEZE_ENERGY) { + savePendingTransactionUseCase.invoke(pendingTransaction) + } } } } @@ -299,6 +284,19 @@ internal class StakingTransactionSender @AssistedInject constructor( ) } + private fun composeStakeTransaction(confirmationState: StakingStates.ConfirmationState.Data): PendingTransaction { + val state = stateController.value + + return PendingTransaction( + groupId = UUID.randomUUID().toString(), + type = BalanceType.STAKED, + amount = (state.amountState as? AmountState.Data)?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO, + rawCurrencyId = cryptoCurrencyStatus.currency.id.rawCurrencyId, + validator = (confirmationState.validatorState as? ValidatorState.Content)?.chosenValidator, + balancesId = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.getBalancesUniqueId() ?: 0, + ) + } + private data class FullTransactionData( val stakeKitTransaction: StakingTransaction, val tangemTransaction: TransactionData.Compiled, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt index 51b7ec69a2..e68d171b89 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt @@ -93,6 +93,6 @@ internal object ConfirmationStatePreviewData { isApprovalNeeded = false, reduceAmountBy = null, pendingActions = null, - balanceState = null, + possiblePendingTransaction = null, ) } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetBalanceStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetBalanceStateTransformer.kt deleted file mode 100644 index d6fae7cd7f..0000000000 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetBalanceStateTransformer.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.tangem.features.staking.impl.presentation.state.transformers - -import com.tangem.features.staking.impl.presentation.state.* -import com.tangem.utils.transformer.Transformer - -internal class SetBalanceStateTransformer( - private val balanceState: BalanceState? = null, -) : Transformer { - - override fun transform(prevState: StakingUiState): StakingUiState { - val possibleConfirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data - ?: return prevState - - return prevState.copy( - confirmationState = possibleConfirmationState.copy(balanceState = balanceState), - ) - } -} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt index c2df5db972..1fcfee71c7 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmationStateLoadingTransformer.kt @@ -41,7 +41,7 @@ internal class SetConfirmationStateLoadingTransformer( pendingActions = possibleConfirmationState?.pendingActions, isApprovalNeeded = false, reduceAmountBy = null, - balanceState = null, + possiblePendingTransaction = null, ), ) } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt index 66b56ea7aa..5bd24b5b92 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt @@ -231,7 +231,7 @@ internal class SetInitialDataStateTransformer( pendingActions = null, isApprovalNeeded = isApprovalNeeded, reduceAmountBy = null, - balanceState = null, + possiblePendingTransaction = null, ) } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt new file mode 100644 index 0000000000..a176330f37 --- /dev/null +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt @@ -0,0 +1,33 @@ +package com.tangem.features.staking.impl.presentation.state.transformers + +import com.tangem.domain.staking.model.PendingTransaction +import com.tangem.domain.staking.model.stakekit.YieldBalance +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.features.staking.impl.presentation.state.* +import com.tangem.utils.transformer.Transformer + +internal class SetPossiblePendingTransactionTransformer( + private val balanceState: BalanceState, + private val cryptoCurrencyStatus: CryptoCurrencyStatus, +) : Transformer { + + override fun transform(prevState: StakingUiState): StakingUiState { + val possibleConfirmationState = + prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState + + val balancesId = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.getBalancesUniqueId() ?: 0 + + return prevState.copy( + confirmationState = possibleConfirmationState.copy( + possiblePendingTransaction = PendingTransaction( + groupId = balanceState.groupId, + type = balanceState.type, + amount = balanceState.cryptoDecimal, + rawCurrencyId = balanceState.rawCurrencyId, + validator = balanceState.validator, + balancesId = balancesId, + ), + ), + ) + } +} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt index b138f2c300..986ca6b285 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt @@ -219,12 +219,17 @@ internal class StakingViewModel @Inject constructor( when { isInitState() -> { stateController.update(SetConfirmationStateLoadingTransformer(yield, appCurrency)) - stateController.update(SetBalanceStateTransformer(balanceState)) + if (balanceState != null) { + stateController.update(SetPossiblePendingTransactionTransformer(balanceState, cryptoCurrencyStatus)) + } + onRefreshSwipe(isRefreshing = false) } isAssentState() -> { getFee(pendingAction, pendingActions) - stateController.update(SetBalanceStateTransformer(balanceState)) + if (balanceState != null) { + stateController.update(SetPossiblePendingTransactionTransformer(balanceState, cryptoCurrencyStatus)) + } val amountState = value.amountState as? AmountState.Data if (amountState?.amountTextField?.isWarning == true) { stateController.update( From 24f2035453da38e20b3ec60680cc36ac6d1ad218 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 30 Sep 2024 12:52:57 +0300 Subject: [PATCH 09/27] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index c555bbf340..e71e76d7c8 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -89,7 +89,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.16-802" +tangemBlockchainSdk = "release-app_5.16-803" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.16-386" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From bc7d01a1c7b7241e633463614b1e4322266a0f35 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 30 Sep 2024 14:01:02 +0300 Subject: [PATCH 10/27] Updated on 2026-08-14 --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index 8c32b86fe1..78218d1da3 100644 --- a/version.properties +++ b/version.properties @@ -1 +1 @@ -versionName=5.15.0 \ No newline at end of file +versionName=5.16.0 \ No newline at end of file From 170a36148ec8c257ac16efadf0cffbd17032a062 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 30 Sep 2024 14:30:30 +0300 Subject: [PATCH 11/27] Updated on 2026-08-14 --- core/res/src/main/res/values-de/strings.xml | 23 ++++++------ core/res/src/main/res/values-ja/strings.xml | 2 + core/res/src/main/res/values-ru/strings.xml | 3 +- .../src/main/res/values-uk-rUA/strings.xml | 10 ++++- core/res/src/main/res/values/strings.xml | 9 +++-- .../converters/YieldBalanceConverter.kt | 5 +-- .../presentation/state/StakingNotification.kt | 7 +--- .../state/converters/BalanceItemConverter.kt | 7 +++- .../AddStakingNotificationsTransformer.kt | 37 +++++++++++++------ .../SetButtonsStateTransformer.kt | 16 ++++++-- .../ui/StakingConfirmationContent.kt | 4 +- 11 files changed, 78 insertions(+), 45 deletions(-) diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index cc96519ec8..e4f4e75ca9 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -128,7 +128,7 @@ Keine Adresse Jetzt OK - Primär Karte + Hauptkarte Passphrase Einfügen Datenschutzbestimmungen @@ -275,7 +275,7 @@ Keine Token gefunden. Bitte versuche eine andere Anfrage ID: %s Transaktions-ID kopiert - Wechsel eine Ihrer Währungen in eine andere + Tauschen jeden Vermögenswert in Deinem Portfolio gegen diesen Token Die folgenden Angaben sind freiwillig. Du kannst diese löschen, wenn du sie nicht weitergeben möchtest. Teile uns mit, welche Funktionen du vermisst, und wir werden versuchen, dir zu helfen. Bitte sag uns, welche Karte du hast @@ -345,6 +345,7 @@ Das Wallet unterstützt nicht mehr als ein Netzwerk Um mit dem Kauf, Tausch oder Erhalt dieses Vermögenswerts zu beginnen, füge diesen Token zu mindestens 1 Netzwerk hinzu Dieses Asset ist nicht verfügbar + Dieses Asset ist für dieses Wallet nicht verfügbar Zum Portfolio hinzufügen Hinzufügen Verfügbare Netzwerke @@ -561,7 +562,7 @@ %d Wallet %d Wallets - Zurücksetzen der Karte + Karte zurücksetzen Mir ist bewusst, dass ich nach der Durchführung dieser Aktion keinen Zugriff mehr auf die aktuelle Wallet habe. Mir ist klar, dass ich diese Karte nicht verwenden kann, um meinen Zugangscode auf den anderen Karten der aktuellen Wallet wiederherzustellen Durch das Zurücksetzen auf Werkseinstellungen wird die Wallet vollständig von der ausgewählten Karte gelöscht. Du kannst die aktuelle Wallet nicht wiederherstellen oder die Karte verwenden, um den Zugangscode wiederherzustellen. @@ -686,7 +687,7 @@ Der Zeitraum, den du nach der Beantragung der Abhebung von Geldern aus dem Staking warten musst, bevor die Token verfügbar werden. Aufwärmphase Die zugewiesene Zeit für die Aktivierung der Teilnahme am Staking. - Indem Du die Staking-Funktionalität nutzen, stimmst Du den %1$s und %2$s des Anbieters zu. + Indem Du die Staking-Funktionalität nutzt, stimmst Du den %1$s und %2$s des Anbieters zu. Gesperrt Migrieren Natives Staking @@ -718,14 +719,14 @@ Neuwahl Automatisch Händisch - Block - Tag + Pro Block + Täglich Täglich - Epoche - Ära - Stunde - Monat - Woche + Pro Epoche + Pro Ära + Stündlich + Monatlich + Wöchentlich Belohnungen Stake gesperrt Mehr staken diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index aeed4388a0..9066c1ebb1 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -342,6 +342,7 @@ ウォレットは複数のネットワークをサポートしていません。 このアセットの購入、交換、受け取りを開始するには、ポートフォリオに追加してください。 このアセットは利用できません + このアセットはこのウォレットでは使用できません。 ポートフォリオに追加 追加 利用可能なネットワーク @@ -799,6 +800,7 @@ オペレーション 送金元: %s 送金先: %s + バリデーター: %s もう一度やり直してください 同じカードをスキャンしました。ツインウォレットを作成するには、番号%dのカードをスキャンする必要があります。 間違ったツインカードをスキャンしました。別のカードをお試しください。 diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index c7e2221f9d..6f25020e8d 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -434,7 +434,7 @@ Объем торгов (24ч) Общая сумма криптовалюты, которая была продана за последние 24 часа, показывающая уровень активности и ликвидности на рынке. Объем торгов (24ч) - Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из маркета + Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из рынка Добавить токены Функция NFC недоступна на вашем устройстве Вам необходимо установить единый код доступа для защиты всех ваших карт @@ -744,6 +744,7 @@ Застейкать еще Вы стейкаете %1$s и будете получать %2$s ежегодно Нажмите для разблокировки + Нажмите для разблокировки или голосования Нажмите для вывода Застейкать %s Вывести %s diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index ef2696c0e6..611026a7cb 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -141,7 +141,7 @@ Детальніше Отримати Відхилити - Перезавантажити + Оновити Перейменувати Зберегти Зберегти зміни @@ -351,6 +351,7 @@ Гаманець не підтримує більше однієї мережі Щоб почати купувати, обмінювати або отримувати цей актив, додайте цей токен принаймні в 1 мережу Цей актив недоступний + Цей токен недоступний для цього гаманця Додати в портфоліо Додати Доступні мережі @@ -401,6 +402,7 @@ Досвідчені покупці Повн. розведена кап. Загальна теоретична вартість криптовалюти, якщо всі монети, які могли б існувати, перебувають в обігу, включаючи ті, що не перебувають в обігу в даний час + Повністю розведена капіталізація Дата створення Високий Тримачі @@ -415,9 +417,11 @@ Низький Рин. кап. Загальна ринкова вартість криптовалюти, що розраховується шляхом множення поточної ціни монети на загальну кількість монет в обігу + Ринкова капіталізація Рейтинг Позиція в крипторейтингу між усіма монетами на основі ринкової капіталізації Рейтинг + Макс. обсяг Максимальна кількість монет або токенів, яка може коли-небудь існувати для певної криптовалюти Максимальна пропозиція Метрики @@ -713,6 +717,7 @@ Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг зараховуються щотиждня. Стейкайте безпечно та почніть отримувати винагороди щотижня Отримуйте винагороду за стейкінг + Стейкінг тимчасово недоступний через проблеми в мережі. Будь ласка, спробуйте пізніше. Стейкінг в мережі %1$s з новим валідатором автоматично переведе всі раніше застейкані кошти до цього валідатора Реінвестуйте зароблені винагороди у суму стейкінгу, щоб збільшити потенційний прибуток. Розблокуйте свої кошти, щоб вивести їх зі стейкінгу. Розблокування займе %s. @@ -813,12 +818,13 @@ Обміняти контракт: %s У вас ще немає транзакцій - Не вдалося завантажити історію транзакцій.\nНатисніть кнопку перезавантаження, щоб оновити інформацію. + Не вдалося завантажити історію транзакцій.\nНатисніть кнопку нижче, щоб оновити інформацію Кілька адрес Історія транзакцій наразі не підтримується для цього блокчейну. Але не хвилюйтеся, ми працюємо над цим! А поки можете перевірити це в оглядачі. Операція від: %s до: %s + валідатор: %s Спробуйте знову Ви відсканували одну й ту саму картку. Для створення twin-гаманця вам потрібно відсканувати картку з номером %d Ви відсканували не ту twin-картку. Будь ласка, спробуйте відсканувати іншу diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index a75c4fce68..3e97f225bf 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -128,7 +128,7 @@ No address Now OK - Primary Card + Primary card Passphrase Paste Privacy Policy @@ -561,7 +561,7 @@ %d wallet %d wallets - Reset the Card + Reset the card I understand that after performing this action, I will no longer have access to the current wallet I realize that I can\'t use this card to recover my access code on the other cards of the current wallet Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. @@ -577,7 +577,7 @@ Enable biometric authorization Would you like to use biometrics? Note that making a transaction with your funds will still require your card - Scan Card + Scan card Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. Get your card ready! Already included in the entered address @@ -731,6 +731,7 @@ Stake more You stake %1$s and will be receiving %2$s yearly Tap to unlock + Tap to unlock or vote Tap to withdraw Stake %s Unstake %s @@ -831,7 +832,7 @@ This wallet has already been saved, you can add another one The wallet with name %s already exists Wallet name - Rename Wallet + Rename wallet Unlock all Unlock all with %s Blockchain is unreachable. Try later diff --git a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceConverter.kt index 5a7f56b25e..c892e86589 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldBalanceConverter.kt @@ -3,7 +3,6 @@ package com.tangem.data.staking.converters import com.tangem.data.staking.converters.action.PendingActionConverter import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO import com.tangem.domain.staking.model.stakekit.* -import com.tangem.domain.staking.model.stakekit.action.StakingActionType import com.tangem.utils.converter.Converter internal class YieldBalanceConverter : Converter { @@ -27,9 +26,7 @@ internal class YieldBalanceConverter : Converter getUnbondingDate(balance.date) BalanceType.UNSTAKED -> resourceReference(R.string.staking_tap_to_withdraw) - BalanceType.LOCKED -> resourceReference(R.string.staking_tap_to_unlock) + BalanceType.LOCKED -> if (balance.pendingActions.any { it.type == StakingActionType.VOTE_LOCKED }) { + resourceReference(R.string.staking_tap_to_unlock_or_vote) + } else { + resourceReference(R.string.staking_tap_to_unlock) + } BalanceType.PREPARING -> { val warmupPeriod = yield.metadata.warmupPeriod.days combinedReference( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt index bd7988cd8f..2b51941e7b 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt @@ -270,17 +270,7 @@ internal class AddStakingNotificationsTransformer( } private fun MutableList.addEnterInfoNotifications() { - val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - val isTron = isTron(cryptoCurrencyStatus.currency.network.id.value) - val hasStakedBalance = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance - ?.items?.any { - it.type == BalanceType.PREPARING || - it.type == BalanceType.STAKED || - it.type == BalanceType.LOCKED - } == true - if (hasStakedBalance && isTron) { - add(StakingNotification.Info.TronRevote) - } + addTronRevoteNotification() } private fun MutableList.addPendingInfoNotifications(prevState: StakingUiState) { @@ -312,16 +302,39 @@ internal class AddStakingNotificationsTransformer( ), ) } + StakingActionType.VOTE_LOCKED -> { + resourceReference(R.string.staking_revote) to + resourceReference(R.string.staking_notifications_revote_tron_text) + } else -> null to null } if (titleReference != null && textReference != null) { add( - StakingNotification.Info.PendingAction( + StakingNotification.Info.Ordinary( title = titleReference, text = textReference, ), ) } } + + private fun MutableList.addTronRevoteNotification() { + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() + val isTron = isTron(cryptoCurrencyStatus.currency.network.id.value) + val hasStakedBalance = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance + ?.items?.any { + it.type == BalanceType.PREPARING || + it.type == BalanceType.STAKED || + it.type == BalanceType.LOCKED + } == true + if (isTron && hasStakedBalance) { + add( + StakingNotification.Info.Ordinary( + title = resourceReference(R.string.staking_revote), + text = resourceReference(R.string.staking_notifications_revote_tron_text), + ), + ) + } + } } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt index d9f2906670..ea4406d986 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetButtonsStateTransformer.kt @@ -144,9 +144,19 @@ internal class SetButtonsStateTransformer( clickIntents.onAmountValueChange("") // reset amount state clickIntents.onNextClick(StakingActionCommonType.ENTER) } - StakingStep.Validators, - StakingStep.Amount, - -> clickIntents.onNextClick() + StakingStep.Validators -> { + if (confirmationState is StakingStates.ConfirmationState.Data) { + clickIntents.onNextClick( + pendingActions = confirmationState.pendingActions, + pendingAction = confirmationState.pendingAction, + ) + } else { + clickIntents.onNextClick() + } + } + StakingStep.Amount -> { + clickIntents.onNextClick() + } StakingStep.Confirmation -> onConfirmationClick() StakingStep.RewardsValidators -> Unit } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt index 8fc799719a..bcf6eb3cc3 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt @@ -19,6 +19,7 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType +import com.tangem.domain.staking.model.stakekit.action.StakingActionType import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState import com.tangem.features.staking.impl.presentation.state.StakingStates @@ -39,6 +40,7 @@ internal fun StakingConfirmationContent( ) { if (state !is StakingStates.ConfirmationState.Data) return val isEnterAction = type == StakingActionCommonType.ENTER + val showValidatorBlock = isEnterAction || state.pendingAction?.type == StakingActionType.VOTE_LOCKED val isTransactionSent = state.innerState == InnerConfirmationStakingState.COMPLETED Column( modifier = Modifier @@ -63,7 +65,7 @@ internal fun StakingConfirmationContent( isEditingDisabled = !isEnterAction && state.innerState != InnerConfirmationStakingState.COMPLETED, onClick = clickIntents::onPrevClick, ) - if (isEnterAction) { + if (showValidatorBlock) { ValidatorBlock(validatorState = state.validatorState, onClick = clickIntents::openValidators) } StakingFeeBlock(feeState = state.feeState, isTransactionSent = isTransactionSent) From 10ef420b3e2355b624cdb5f5726c5479f4aaf577 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 30 Sep 2024 15:36:19 +0400 Subject: [PATCH 12/27] Updated on 2026-08-14 --- .../tangem/data/managetokens/DefaultCustomTokensRepository.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt index 249608663c..cacf327062 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt @@ -53,8 +53,8 @@ internal class DefaultCustomTokensRepository( Blockchain.Binance, Blockchain.BinanceTestnet, -> true - Blockchain.Cardano -> blockchain.validateContractAddress(contractAddress.lowercase()) - else -> blockchain.validateAddress(contractAddress.lowercase()) + Blockchain.Cardano -> blockchain.validateContractAddress(contractAddress) + else -> blockchain.validateAddress(contractAddress) } } From 0b65861f586dac90915985a148cb5f68b28ae41d Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Oct 2024 10:50:06 +0300 Subject: [PATCH 13/27] Updated on 2026-08-14 --- .../src/main/assets/configs/feature_toggles_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index 652af05e72..59111da17e 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -37,6 +37,6 @@ }, { "name": "IS_ETHEREUM_EIP_1559_ENABLED", - "version": "5.16.0" + "version": "5.17.0" } ] From ecfdb944c5ebd9a7246ce16f3a45ca45761cc4db Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 30 Sep 2024 18:54:57 +0300 Subject: [PATCH 14/27] Updated on 2026-08-14 --- .../tap/di/domain/StakingDomainModule.kt | 4 ++-- ...> GetStakingPendingTransactionsUseCase.kt} | 2 +- .../TokenDetailsLoadedBalanceConverter.kt | 19 +++++++++++++++++-- .../state/factory/TokenDetailsStateFactory.kt | 3 +++ .../viewmodels/TokenDetailsViewModel.kt | 6 ++++++ 5 files changed, 29 insertions(+), 5 deletions(-) rename domain/staking/src/main/java/com/tangem/domain/staking/{GetPendingTransactionsUseCase.kt => GetStakingPendingTransactionsUseCase.kt} (94%) 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 0807a628f1..6753cdf12a 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 @@ -152,8 +152,8 @@ internal object StakingDomainModule { fun provideGetPendingTransactionsUseCase( stakingPendingTransactionRepository: StakingPendingTransactionRepository, stakingErrorResolver: StakingErrorResolver, - ): GetPendingTransactionsUseCase { - return GetPendingTransactionsUseCase( + ): GetStakingPendingTransactionsUseCase { + return GetStakingPendingTransactionsUseCase( stakingPendingTransactionRepository = stakingPendingTransactionRepository, stakingErrorResolver = stakingErrorResolver, ) diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/GetPendingTransactionsUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingPendingTransactionsUseCase.kt similarity index 94% rename from domain/staking/src/main/java/com/tangem/domain/staking/GetPendingTransactionsUseCase.kt rename to domain/staking/src/main/java/com/tangem/domain/staking/GetStakingPendingTransactionsUseCase.kt index 1f4be9dea2..741456a4a8 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/GetPendingTransactionsUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingPendingTransactionsUseCase.kt @@ -9,7 +9,7 @@ import com.tangem.domain.staking.repositories.StakingPendingTransactionRepositor /** * Use case for getting saved staking pending transactions. */ -class GetPendingTransactionsUseCase( +class GetStakingPendingTransactionsUseCase( private val stakingPendingTransactionRepository: StakingPendingTransactionRepository, private val stakingErrorResolver: StakingErrorResolver, ) { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index dafdd53e8c..b92a09ac94 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -13,6 +13,7 @@ import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.staking.model.StakingEntryInfo +import com.tangem.domain.staking.model.stakekit.BalanceItem import com.tangem.domain.staking.model.stakekit.RewardBlockType import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.tokens.error.CurrencyStatusError @@ -37,6 +38,7 @@ internal class TokenDetailsLoadedBalanceConverter( private val currentStateProvider: Provider, private val appCurrencyProvider: Provider, private val stakingEntryInfoProvider: Provider, + private val pendingBalancesProvider: Provider>, private val symbol: String, private val decimals: Int, private val clickIntents: TokenDetailsClickIntents, @@ -143,16 +145,29 @@ internal class TokenDetailsLoadedBalanceConverter( val stakingEntryInfo = stakingEntryInfoProvider.invoke() val iconState = state.tokenInfoBlockState.iconState + val pendingBalances = pendingBalancesProvider.invoke() + val fiatRate = status.value.fiatRate return when { stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null -> { - getStakeAvailableState(stakingEntryInfo, iconState) + if (pendingBalances.isEmpty()) { + getStakeAvailableState(stakingEntryInfo, iconState) + } else { + val pendingBalancesCryptoAmount = pendingBalances.sumOf { it.amount } + + val stakingFiatAmount = fiatRate?.multiply(pendingBalancesCryptoAmount) + getStakedState( + status = status, + stakingCryptoAmount = pendingBalancesCryptoAmount, + stakingFiatAmount = stakingFiatAmount, + stakingRewardAmount = null, + ) + } } stakingCryptoAmount.isNullOrZero() && stakingEntryInfo == null -> { StakingBlockUM.Error(iconState = iconState) } else -> { - val fiatRate = status.value.fiatRate val stakingRewardAmount = yieldBalance?.getRewardStakingBalance()?.let { fiatRate?.multiply(it) } val stakingFiatAmount = stakingCryptoAmount?.let { fiatRate?.multiply(it) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 934b75a0d1..67a0d59d99 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -19,6 +19,7 @@ import com.tangem.domain.card.NetworkHasDerivationUseCase import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo +import com.tangem.domain.staking.model.stakekit.BalanceItem import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.model.* @@ -52,6 +53,7 @@ internal class TokenDetailsStateFactory( private val appCurrencyProvider: Provider, private val stakingEntryInfoProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val pendingBalancesProvider: Provider>, private val clickIntents: TokenDetailsClickIntents, private val featureToggles: TokenDetailsFeatureToggles, private val networkHasDerivationUseCase: NetworkHasDerivationUseCase, @@ -81,6 +83,7 @@ internal class TokenDetailsStateFactory( currentStateProvider = currentStateProvider, appCurrencyProvider = appCurrencyProvider, stakingEntryInfoProvider = stakingEntryInfoProvider, + pendingBalancesProvider = pendingBalancesProvider, symbol = symbol, decimals = decimals, clickIntents = clickIntents, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 5d65adaf1c..154f26312f 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -32,11 +32,13 @@ import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase +import com.tangem.domain.staking.GetStakingPendingTransactionsUseCase import com.tangem.domain.staking.GetStakingAvailabilityUseCase import com.tangem.domain.staking.GetStakingEntryInfoUseCase import com.tangem.domain.staking.GetYieldUseCase import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo +import com.tangem.domain.staking.model.stakekit.BalanceItem import com.tangem.domain.tokens.* import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.legacy.TradeCryptoAction.TransactionInfo @@ -119,6 +121,7 @@ internal class TokenDetailsViewModel @Inject constructor( private val swapTransactionStatusStore: SwapTransactionStatusStore, private val isDemoCardUseCase: IsDemoCardUseCase, private val associateAssetUseCase: AssociateAssetUseCase, + private val getStakingPendingTransactionsUseCase: GetStakingPendingTransactionsUseCase, private val reduxStateHolder: ReduxStateHolder, private val analyticsEventsHandler: AnalyticsEventHandler, private val vibratorHapticManager: VibratorHapticManager, @@ -148,6 +151,8 @@ internal class TokenDetailsViewModel @Inject constructor( private val warningsJobHolder = JobHolder() private val swapTxJobHolder = JobHolder() private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() + private val stakingPendingBalances: List + get() = getStakingPendingTransactionsUseCase().getOrElse { emptyList() } private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null private var stakingEntryInfo: StakingEntryInfo? = null @@ -158,6 +163,7 @@ internal class TokenDetailsViewModel @Inject constructor( appCurrencyProvider = Provider(selectedAppCurrencyFlow::value), stakingEntryInfoProvider = Provider { stakingEntryInfo }, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, + pendingBalancesProvider = Provider { stakingPendingBalances }, clickIntents = this, symbol = cryptoCurrency.symbol, decimals = cryptoCurrency.decimals, From f004aaf1f33b72cd0cd5e75f5fba9a7ed4afdb50 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 30 Sep 2024 20:43:26 +0300 Subject: [PATCH 15/27] Updated on 2026-08-14 --- .../com/tangem/domain/staking/model/PendingTransaction.kt | 2 ++ .../com/tangem/domain/staking/model/stakekit/YieldBalance.kt | 5 ++++- .../presentation/state/helpers/StakingTransactionSender.kt | 1 + .../transformers/SetPossiblePendingTransactionTransformer.kt | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt index b01eab990b..088a823cca 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt @@ -1,5 +1,6 @@ package com.tangem.domain.staking.model +import com.tangem.domain.staking.model.stakekit.BalanceItem import com.tangem.domain.staking.model.stakekit.BalanceType import com.tangem.domain.staking.model.stakekit.Yield import java.math.BigDecimal @@ -11,4 +12,5 @@ data class PendingTransaction( val rawCurrencyId: String?, val validator: Yield.Validator?, val balancesId: Int, + val balanceItems: List, // TODO staking remove it ) \ No newline at end of file diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt index 33b6ce8443..2c881ded88 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt @@ -35,7 +35,10 @@ sealed class YieldBalance { fun getBalancesUniqueId(): Int { // need to exclude rewards because their amount may change frequently - return balance.items.filter { it.type != BalanceType.REWARDS }.hashCode() + return balance.items + .filter { it.type != BalanceType.REWARDS } + .map { it.amount.toString() + it.type.toString() + it.groupId + it.validatorAddress } + .hashCode() } } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt index 7edd7024b0..1fb3bbfbd7 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt @@ -294,6 +294,7 @@ internal class StakingTransactionSender @AssistedInject constructor( rawCurrencyId = cryptoCurrencyStatus.currency.id.rawCurrencyId, validator = (confirmationState.validatorState as? ValidatorState.Content)?.chosenValidator, balancesId = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.getBalancesUniqueId() ?: 0, + balanceItems = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance?.items ?: emptyList() ) } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt index a176330f37..cd352d6058 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt @@ -26,6 +26,7 @@ internal class SetPossiblePendingTransactionTransformer( rawCurrencyId = balanceState.rawCurrencyId, validator = balanceState.validator, balancesId = balancesId, + balanceItems = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance?.items ?: emptyList() ), ), ) From b051579757af2ee49d673ab78f473c3770c95951 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 30 Sep 2024 22:59:14 +0300 Subject: [PATCH 16/27] Updated on 2026-08-14 --- ...aultStakingPendingTransactionRepository.kt | 21 ++++++++++++------- .../staking/model/stakekit/YieldBalance.kt | 2 +- .../GetStakingPendingTransactionsUseCase.kt | 5 +++-- .../InvalidatePendingTransactionsUseCase.kt | 10 ++++++--- .../staking/SavePendingTransactionUseCase.kt | 8 +++++-- .../StakingPendingTransactionRepository.kt | 7 ++++--- .../state/helpers/StakingTransactionSender.kt | 5 +++-- ...etPossiblePendingTransactionTransformer.kt | 3 ++- .../viewmodel/StakingViewModel.kt | 2 +- .../viewmodels/TokenDetailsViewModel.kt | 2 +- 10 files changed, 41 insertions(+), 24 deletions(-) diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingPendingTransactionRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingPendingTransactionRepository.kt index 0b637744ab..008ea7161a 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingPendingTransactionRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingPendingTransactionRepository.kt @@ -3,25 +3,30 @@ package com.tangem.data.staking import com.tangem.domain.staking.model.PendingTransaction import com.tangem.domain.staking.model.stakekit.BalanceItem import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository +import com.tangem.domain.wallets.models.UserWalletId +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList internal class DefaultStakingPendingTransactionRepository : StakingPendingTransactionRepository { - private val pendingTransactions = CopyOnWriteArrayList() + private val pendingTransactionsMap = ConcurrentHashMap>() - override fun saveTransaction(transaction: PendingTransaction) { - pendingTransactions.add(transaction) + override fun saveTransaction(userWalletId: UserWalletId, transaction: PendingTransaction) { + val transactions = pendingTransactionsMap.computeIfAbsent(userWalletId) { CopyOnWriteArrayList() } + transactions.add(transaction) } - override fun removeTransactions(transactions: Set) { - pendingTransactions.removeAll(transactions) + override fun removeTransactions(userWalletId: UserWalletId, transactions: Set) { + pendingTransactionsMap[userWalletId]?.removeAll(transactions) } - override fun getTransactionsWithBalanceItems(): List> { - return pendingTransactions.mapNotNull { pendingTransaction -> + override fun getTransactionsWithBalanceItems( + userWalletId: UserWalletId, + ): List> { + return pendingTransactionsMap[userWalletId]?.mapNotNull { pendingTransaction: PendingTransaction -> PendingTransactionItemConverter.convert(pendingTransaction)?.let { balanceItem -> pendingTransaction to balanceItem } - } + } ?: emptyList() } } \ No newline at end of file diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt index 2c881ded88..01ac5388c9 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt @@ -37,7 +37,7 @@ sealed class YieldBalance { // need to exclude rewards because their amount may change frequently return balance.items .filter { it.type != BalanceType.REWARDS } - .map { it.amount.toString() + it.type.toString() + it.groupId + it.validatorAddress } + .map { it.amount.toString() + it.type.toString() + it.groupId } .hashCode() } } diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingPendingTransactionsUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingPendingTransactionsUseCase.kt index 741456a4a8..15557232b0 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingPendingTransactionsUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingPendingTransactionsUseCase.kt @@ -5,6 +5,7 @@ import com.tangem.domain.staking.model.stakekit.BalanceItem import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.repositories.StakingErrorResolver import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository +import com.tangem.domain.wallets.models.UserWalletId /** * Use case for getting saved staking pending transactions. @@ -14,9 +15,9 @@ class GetStakingPendingTransactionsUseCase( private val stakingErrorResolver: StakingErrorResolver, ) { - operator fun invoke(): Either> { + operator fun invoke(userWalletId: UserWalletId): Either> { return Either.catch { - stakingPendingTransactionRepository.getTransactionsWithBalanceItems().map { it.second } + stakingPendingTransactionRepository.getTransactionsWithBalanceItems(userWalletId).map { it.second } }.mapLeft { stakingErrorResolver.resolve(it) } diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt index 06a2c6cb68..82a55f65e7 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt @@ -8,6 +8,7 @@ import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.repositories.StakingErrorResolver import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository +import com.tangem.domain.wallets.models.UserWalletId import java.util.UUID class InvalidatePendingTransactionsUseCase( @@ -15,15 +16,18 @@ class InvalidatePendingTransactionsUseCase( private val stakingErrorResolver: StakingErrorResolver, ) { - operator fun invoke(yieldBalance: YieldBalance): Either> { + operator fun invoke( + userWalletId: UserWalletId, + yieldBalance: YieldBalance, + ): Either> { return Either.catch { if (yieldBalance is YieldBalance.Data) { val (balancesToDisplay, transactionsToRemove) = mergeRealAndPendingTransactions( yieldBalance = yieldBalance, - pending = stakingPendingTransactionRepository.getTransactionsWithBalanceItems(), + pending = stakingPendingTransactionRepository.getTransactionsWithBalanceItems(userWalletId), ) - stakingPendingTransactionRepository.removeTransactions(transactionsToRemove.toSet()) + stakingPendingTransactionRepository.removeTransactions(userWalletId, transactionsToRemove.toSet()) balancesToDisplay } else { diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/SavePendingTransactionUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/SavePendingTransactionUseCase.kt index 64ce855ae5..ef80e06e64 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/SavePendingTransactionUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/SavePendingTransactionUseCase.kt @@ -5,6 +5,7 @@ import com.tangem.domain.staking.model.PendingTransaction import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.repositories.StakingErrorResolver import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository +import com.tangem.domain.wallets.models.UserWalletId /** * Use case for saving hash that failed to submit during staking confirmation @@ -14,9 +15,12 @@ class SavePendingTransactionUseCase( private val stakingErrorResolver: StakingErrorResolver, ) { - operator fun invoke(pendingTransaction: PendingTransaction): Either { + operator fun invoke(userWalletId: UserWalletId, transaction: PendingTransaction): Either { return Either.catch { - stakingPendingTransactionRepository.saveTransaction(pendingTransaction) + stakingPendingTransactionRepository.saveTransaction( + userWalletId = userWalletId, + transaction = transaction, + ) }.mapLeft { stakingErrorResolver.resolve(it) } diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingPendingTransactionRepository.kt b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingPendingTransactionRepository.kt index 02bb96806b..708b8e1352 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingPendingTransactionRepository.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingPendingTransactionRepository.kt @@ -2,12 +2,13 @@ package com.tangem.domain.staking.repositories import com.tangem.domain.staking.model.PendingTransaction import com.tangem.domain.staking.model.stakekit.BalanceItem +import com.tangem.domain.wallets.models.UserWalletId interface StakingPendingTransactionRepository { - fun getTransactionsWithBalanceItems(): List> + fun getTransactionsWithBalanceItems(userWalletId: UserWalletId): List> - fun saveTransaction(transaction: PendingTransaction) + fun saveTransaction(userWalletId: UserWalletId, transaction: PendingTransaction) - fun removeTransactions(transactions: Set) + fun removeTransactions(userWalletId: UserWalletId, transactions: Set) } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt index 1fb3bbfbd7..f5b5d73ed4 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt @@ -264,7 +264,7 @@ internal class StakingTransactionSender @AssistedInject constructor( }.onRight { Timber.d("Successful hash submission") if (transaction.type != StakingTransactionType.FREEZE_ENERGY) { - savePendingTransactionUseCase.invoke(pendingTransaction) + savePendingTransactionUseCase.invoke(userWallet.walletId, pendingTransaction) } } } @@ -294,7 +294,8 @@ internal class StakingTransactionSender @AssistedInject constructor( rawCurrencyId = cryptoCurrencyStatus.currency.id.rawCurrencyId, validator = (confirmationState.validatorState as? ValidatorState.Content)?.chosenValidator, balancesId = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.getBalancesUniqueId() ?: 0, - balanceItems = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance?.items ?: emptyList() + balanceItems = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance?.items + ?: emptyList(), ) } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt index cd352d6058..3c4b5e9c04 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt @@ -26,7 +26,8 @@ internal class SetPossiblePendingTransactionTransformer( rawCurrencyId = balanceState.rawCurrencyId, validator = balanceState.validator, balancesId = balancesId, - balanceItems = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance?.items ?: emptyList() + balanceItems = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance?.items + ?: emptyList(), ), ), ) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt index d945d70c2f..6590a3b1c7 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt @@ -137,7 +137,7 @@ internal class StakingViewModel @Inject constructor( private val balancesToShow: List get() { return cryptoCurrencyStatus.value.yieldBalance?.let { - invalidatePendingTransactionsUseCase(it).getOrElse { emptyList() } + invalidatePendingTransactionsUseCase(userWallet.walletId, it).getOrElse { emptyList() } } ?: emptyList() } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 154f26312f..fc41e514a8 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -152,7 +152,7 @@ internal class TokenDetailsViewModel @Inject constructor( private val swapTxJobHolder = JobHolder() private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() private val stakingPendingBalances: List - get() = getStakingPendingTransactionsUseCase().getOrElse { emptyList() } + get() = getStakingPendingTransactionsUseCase(userWalletId).getOrElse { emptyList() } private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null private var stakingEntryInfo: StakingEntryInfo? = null From e0e8fbcb2299fdf35a0f3c63b8c9b1dcc6113fdc Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Oct 2024 07:40:40 +0300 Subject: [PATCH 17/27] Updated on 2026-08-14 --- .../InvalidatePendingTransactionsUseCase.kt | 31 +++++++++---------- .../converters/YieldBalancesConverter.kt | 11 ++++--- .../viewmodel/StakingViewModel.kt | 21 +++++++++++-- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt index 82a55f65e7..b7c4c597ef 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt @@ -5,7 +5,6 @@ import com.tangem.domain.staking.model.PendingTransaction import com.tangem.domain.staking.model.stakekit.BalanceItem import com.tangem.domain.staking.model.stakekit.BalanceType import com.tangem.domain.staking.model.stakekit.StakingError -import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.repositories.StakingErrorResolver import com.tangem.domain.staking.repositories.StakingPendingTransactionRepository import com.tangem.domain.wallets.models.UserWalletId @@ -18,36 +17,34 @@ class InvalidatePendingTransactionsUseCase( operator fun invoke( userWalletId: UserWalletId, - yieldBalance: YieldBalance, + balanceItems: List, + balancesId: Int, ): Either> { return Either.catch { - if (yieldBalance is YieldBalance.Data) { - val (balancesToDisplay, transactionsToRemove) = mergeRealAndPendingTransactions( - yieldBalance = yieldBalance, - pending = stakingPendingTransactionRepository.getTransactionsWithBalanceItems(userWalletId), - ) + val (balancesToDisplay, transactionsToRemove) = mergeRealAndPendingTransactions( + realData = balanceItems, + newBalancesId = balancesId, + pendingData = stakingPendingTransactionRepository.getTransactionsWithBalanceItems(userWalletId), + ) - stakingPendingTransactionRepository.removeTransactions(userWalletId, transactionsToRemove.toSet()) + stakingPendingTransactionRepository.removeTransactions(userWalletId, transactionsToRemove.toSet()) - balancesToDisplay - } else { - emptyList() - } + balancesToDisplay }.mapLeft { stakingErrorResolver.resolve(it) } } private fun mergeRealAndPendingTransactions( - yieldBalance: YieldBalance.Data, - pending: List>, + realData: List, + newBalancesId: Int, + pendingData: List>, ): Pair, List> { - val balances = yieldBalance.balance.items.associateBy { Triple(it.groupId, it.type, it.amount) }.toMutableMap() - val newBalancesId = yieldBalance.getBalancesUniqueId() + val balances = realData.associateBy { Triple(it.groupId, it.type, it.amount) }.toMutableMap() val transactionsToRemove = mutableListOf() - pending.forEach { (pendingTransaction, balanceItem) -> + pendingData.forEach { (pendingTransaction, balanceItem) -> val key = Triple(balanceItem.groupId, balanceItem.type, balanceItem.amount) val oldBalancesId = pendingTransaction.balancesId 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 5c82de46c3..1280175dbb 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 @@ -28,12 +28,15 @@ internal class YieldBalancesConverter( val cryptoCurrency = cryptoCurrencyStatus.currency val yieldBalance = cryptoCurrencyStatus.value.yieldBalance + val balanceToShowItems = balancesToShowProvider() - return if (yieldBalance is YieldBalance.Data) { - val cryptoRewardsValue = yieldBalance.getRewardStakingBalance() - val fiatRewardsValue = cryptoCurrencyStatus.value.fiatRate?.times(cryptoRewardsValue) + return if (yieldBalance is YieldBalance.Data || balanceToShowItems.any { it.isPending }) { + val cryptoRewardsValue = (yieldBalance as? YieldBalance.Data)?.getRewardStakingBalance() - val balanceToShowItems = balancesToShowProvider() + val fiatRate = cryptoCurrencyStatus.value.fiatRate + val fiatRewardsValue = if (fiatRate != null && cryptoRewardsValue != null) { + fiatRate.times(cryptoRewardsValue) + } else null InnerYieldBalanceState.Data( rewardsCrypto = BigDecimalFormatter.formatCryptoAmount( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt index 6590a3b1c7..2c0c1833ed 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt @@ -136,9 +136,12 @@ internal class StakingViewModel @Inject constructor( private val balancesToShow: List get() { - return cryptoCurrencyStatus.value.yieldBalance?.let { - invalidatePendingTransactionsUseCase(userWallet.walletId, it).getOrElse { emptyList() } - } ?: emptyList() + val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data + return invalidatePendingTransactionsUseCase( + userWalletId = userWallet.walletId, + balanceItems = yieldBalance?.balance?.items ?: emptyList(), + balancesId = yieldBalance?.getBalancesUniqueId() ?: 0, + ).getOrElse { emptyList() } } private var isInitialInfoAnalyticSent: Boolean = false @@ -224,6 +227,18 @@ internal class StakingViewModel @Inject constructor( stateController.update(SetPossiblePendingTransactionTransformer(balanceState, cryptoCurrencyStatus)) } + stateController.update( + transformer = SetInitialDataStateTransformer( + clickIntents = this@StakingViewModel, + yield = yield, + isAnyTokenStaked = true, + isApprovalNeeded = stakingApproval is StakingApproval.Needed, + cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, + userWalletProvider = Provider { userWallet }, + appCurrencyProvider = Provider { appCurrency }, + balancesToShowProvider = Provider { balancesToShow }, + ), + ) onRefreshSwipe(isRefreshing = false) } isAssentState() -> { From 480b488bf8aa2a2309ac746029e6363b4d4a1335 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Oct 2024 08:53:00 +0300 Subject: [PATCH 18/27] Updated on 2026-08-14 --- .../com/tangem/domain/staking/model/PendingTransaction.kt | 2 -- .../presentation/state/converters/YieldBalancesConverter.kt | 4 +++- .../presentation/state/helpers/StakingTransactionSender.kt | 2 -- .../transformers/SetPossiblePendingTransactionTransformer.kt | 2 -- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt index 088a823cca..b01eab990b 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt @@ -1,6 +1,5 @@ package com.tangem.domain.staking.model -import com.tangem.domain.staking.model.stakekit.BalanceItem import com.tangem.domain.staking.model.stakekit.BalanceType import com.tangem.domain.staking.model.stakekit.Yield import java.math.BigDecimal @@ -12,5 +11,4 @@ data class PendingTransaction( val rawCurrencyId: String?, val validator: Yield.Validator?, val balancesId: Int, - val balanceItems: List, // TODO staking remove it ) \ No newline at end of file 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 1280175dbb..fc1f8a834e 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 @@ -36,7 +36,9 @@ internal class YieldBalancesConverter( val fiatRate = cryptoCurrencyStatus.value.fiatRate val fiatRewardsValue = if (fiatRate != null && cryptoRewardsValue != null) { fiatRate.times(cryptoRewardsValue) - } else null + } else { + null + } InnerYieldBalanceState.Data( rewardsCrypto = BigDecimalFormatter.formatCryptoAmount( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt index f5b5d73ed4..b1f5017c82 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt @@ -294,8 +294,6 @@ internal class StakingTransactionSender @AssistedInject constructor( rawCurrencyId = cryptoCurrencyStatus.currency.id.rawCurrencyId, validator = (confirmationState.validatorState as? ValidatorState.Content)?.chosenValidator, balancesId = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.getBalancesUniqueId() ?: 0, - balanceItems = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance?.items - ?: emptyList(), ) } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt index 3c4b5e9c04..a176330f37 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt @@ -26,8 +26,6 @@ internal class SetPossiblePendingTransactionTransformer( rawCurrencyId = balanceState.rawCurrencyId, validator = balanceState.validator, balancesId = balancesId, - balanceItems = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance?.items - ?: emptyList(), ), ), ) From f5d62ce062a789c73e3615fe19b6084ea8f1f5a3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Oct 2024 08:56:26 +0300 Subject: [PATCH 19/27] Updated on 2026-08-14 --- .../TokenDetailsLoadedBalanceConverter.kt | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index b92a09ac94..14687af414 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -149,20 +149,19 @@ internal class TokenDetailsLoadedBalanceConverter( val fiatRate = status.value.fiatRate return when { - stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null -> { - if (pendingBalances.isEmpty()) { - getStakeAvailableState(stakingEntryInfo, iconState) - } else { - val pendingBalancesCryptoAmount = pendingBalances.sumOf { it.amount } + stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null && pendingBalances.isEmpty() -> { + getStakeAvailableState(stakingEntryInfo, iconState) + } + stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null && pendingBalances.isNotEmpty() -> { + val pendingBalancesCryptoAmount = pendingBalances.sumOf { it.amount } - val stakingFiatAmount = fiatRate?.multiply(pendingBalancesCryptoAmount) - getStakedState( - status = status, - stakingCryptoAmount = pendingBalancesCryptoAmount, - stakingFiatAmount = stakingFiatAmount, - stakingRewardAmount = null, - ) - } + val stakingFiatAmount = fiatRate?.multiply(pendingBalancesCryptoAmount) + getStakedState( + status = status, + stakingCryptoAmount = pendingBalancesCryptoAmount, + stakingFiatAmount = stakingFiatAmount, + stakingRewardAmount = null, + ) } stakingCryptoAmount.isNullOrZero() && stakingEntryInfo == null -> { StakingBlockUM.Error(iconState = iconState) From ef7675fe8a125d6ae0f3095fb7e4565b0207c1a1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Oct 2024 12:20:45 +0300 Subject: [PATCH 20/27] Updated on 2026-08-14 --- .../tangem/data/transaction/DefaultTransactionRepository.kt | 3 +++ .../state/recipient/SendRecipientMemoFieldConverter.kt | 1 + 2 files changed, 4 insertions(+) diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt index f2b213583f..b18a5b0fa4 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt @@ -6,6 +6,7 @@ import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras import com.tangem.blockchain.blockchains.cosmos.CosmosTransactionExtras import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras import com.tangem.blockchain.blockchains.hedera.HederaTransactionExtras +import com.tangem.blockchain.blockchains.icp.ICPTransactionExtras import com.tangem.blockchain.blockchains.stellar.StellarMemo import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras import com.tangem.blockchain.blockchains.ton.TonTransactionExtras @@ -249,6 +250,7 @@ internal class DefaultTransactionRepository( ) } + @Suppress("CyclomaticComplexMethod") private fun getMemoExtras(networkId: String, memo: String?): TransactionExtras? { val blockchain = Blockchain.fromId(networkId) if (memo == null) return null @@ -270,6 +272,7 @@ internal class DefaultTransactionRepository( Blockchain.TON -> TonTransactionExtras(memo) Blockchain.Hedera -> HederaTransactionExtras(memo) Blockchain.Algorand -> AlgorandTransactionExtras(memo) + Blockchain.InternetComputer -> memo.toLongOrNull()?.let { ICPTransactionExtras(it) } else -> null } } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt index 8ea7d24d6d..5c9e9b0679 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt @@ -37,6 +37,7 @@ internal class SendRecipientMemoFieldConverter( Blockchain.Hedera.id, Blockchain.Algorand.id, Blockchain.Sei.id, + Blockchain.InternetComputer.id, -> { convert( value = Data(memo = memo, label = R.string.send_extras_hint_memo), From 93dd76610b74d02bcfaa1e64dd8753b78845925e Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Oct 2024 15:46:12 +0400 Subject: [PATCH 21/27] Updated on 2026-08-14 --- .../DefaultManageTokensRepository.kt | 143 ++++++++++++------ .../managetokens/di/ManageTokensDataModule.kt | 3 + .../utils/ManagedCryptoCurrencyFactory.kt | 62 ++++++-- 3 files changed, 148 insertions(+), 60 deletions(-) diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt index 49b58cb102..955401f019 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt @@ -1,8 +1,8 @@ package com.tangem.data.managetokens import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.compatibility.l2BlockchainsCoinIds +import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.utils.isSupportedInApp import com.tangem.blockchainsdk.utils.toNetworkId import com.tangem.data.common.currency.getBlockchain @@ -15,7 +15,9 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull +import com.tangem.datasource.local.testnet.TestnetTokensStorage import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.common.extensions.canHandleBlockchain import com.tangem.domain.common.extensions.canHandleToken import com.tangem.domain.common.extensions.supportedBlockchains @@ -31,6 +33,7 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.pagination.BatchFetchResult import com.tangem.pagination.BatchListSource import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher +import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher.Request import com.tangem.pagination.toBatchFlow import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -40,6 +43,7 @@ internal class DefaultManageTokensRepository( private val userWalletsStore: UserWalletsStore, private val manageTokensUpdateFetcher: ManageTokensUpdateFetcher, private val appPreferencesStore: AppPreferencesStore, + private val testnetTokensStorage: TestnetTokensStorage, private val dispatchers: CoroutineDispatcherProvider, ) : ManageTokensRepository { @@ -59,7 +63,6 @@ internal class DefaultManageTokensRepository( ).toBatchFlow() } - @Suppress("ComplexCondition") private fun createFetcher( batchSize: Int, ): LimitOffsetBatchFetcher> = LimitOffsetBatchFetcher( @@ -67,59 +70,101 @@ internal class DefaultManageTokensRepository( batchSize = batchSize, subFetcher = { request, _, isFirstBatchFetching -> val userWallet = request.params.userWalletId?.let { getUserWallet(it) } - val supportedBlockchains = getSupportedBlockchains(userWallet) - val searchText = request.params.searchText?.takeIf { it.isNotBlank() } - val call = suspend { - tangemTechApi.getCoins( - networkIds = supportedBlockchains.joinToString( - separator = ",", - transform = Blockchain::toNetworkId, - ), - active = true, - searchText = searchText, - offset = request.offset * request.limit, - limit = request.limit, - ).getOrThrow() - } - - val coinsResponse = if (isFirstBatchFetching) { - call() + if (userWallet?.scanResponse?.card?.isTestCard == true) { + fetchTestnetCurrencies(userWallet, request) } else { - retryOnError(call = call) + fetchCurrencies(userWallet, request, isFirstBatchFetching) } - // filter l2 coins - val updatedCoinsResponse = coinsResponse.copy( - coins = coinsResponse.coins.filterNot { l2BlockchainsCoinIds.contains(it.id) }, - ) - - val tokensResponse = request.params.userWalletId?.let { getSavedUserTokensResponseSync(it) } - val items = if (isFirstBatchFetching && - tokensResponse != null && - userWallet != null && - request.params.searchText.isNullOrBlank() - ) { - managedCryptoCurrencyFactory.createWithCustomTokens( - coinsResponse = updatedCoinsResponse, - tokensResponse = tokensResponse, - derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider, - ) - } else { - managedCryptoCurrencyFactory.create( - coinsResponse = updatedCoinsResponse, - tokensResponse = tokensResponse, - derivationStyleProvider = userWallet?.scanResponse?.derivationStyleProvider, - ) - } - - BatchFetchResult.Success( - data = items, - empty = items.isEmpty(), - last = items.size < request.limit, - ) }, ) + @Suppress("ComplexCondition") + private suspend fun fetchCurrencies( + userWallet: UserWallet?, + request: Request, + isFirstBatchFetching: Boolean, + ): BatchFetchResult.Success> { + val supportedBlockchains = getSupportedBlockchains(userWallet) + + val call = suspend { + tangemTechApi.getCoins( + networkIds = supportedBlockchains.joinToString( + separator = ",", + transform = Blockchain::toNetworkId, + ), + active = true, + searchText = request.params.searchText, + offset = request.offset * request.limit, + limit = request.limit, + ).getOrThrow() + } + + val coinsResponse = if (isFirstBatchFetching) { + call() + } else { + retryOnError(call = call) + } + // filter l2 coins + val updatedCoinsResponse = coinsResponse.copy( + coins = coinsResponse.coins.filterNot { l2BlockchainsCoinIds.contains(it.id) }, + ) + + val tokensResponse = request.params.userWalletId?.let { getSavedUserTokensResponseSync(it) } + val items = if (isFirstBatchFetching && + tokensResponse != null && + userWallet != null && + request.params.searchText.isNullOrBlank() + ) { + managedCryptoCurrencyFactory.createWithCustomTokens( + coinsResponse = updatedCoinsResponse, + tokensResponse = tokensResponse, + derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider, + ) + } else { + managedCryptoCurrencyFactory.create( + coinsResponse = updatedCoinsResponse, + tokensResponse = tokensResponse, + derivationStyleProvider = userWallet?.scanResponse?.derivationStyleProvider, + ) + } + + return BatchFetchResult.Success( + data = items, + empty = items.isEmpty(), + last = items.size < request.limit, + ) + } + + private suspend fun fetchTestnetCurrencies( + userWallet: UserWallet, + request: Request, + ): BatchFetchResult.Success> { + val searchText = request.params.searchText + val testnetTokensConfig = testnetTokensStorage.getConfig() + + val items = managedCryptoCurrencyFactory.createTestnetWithCustomTokens( + testnetTokensConfig = if (!searchText.isNullOrBlank()) { + testnetTokensConfig.copy( + tokens = testnetTokensConfig.tokens.filter { token -> + token.symbol.contains(other = searchText, ignoreCase = true) || + token.name.contains(other = searchText, ignoreCase = true) + }, + ) + } else { + testnetTokensConfig + }, + tokensResponse = getSavedUserTokensResponseSync(userWallet.walletId), + derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider, + ) + + return BatchFetchResult.Success( + data = items, + empty = items.isEmpty(), + last = true, + ) + } + private suspend fun getUserWallet(userWalletId: UserWalletId): UserWallet { return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "Unable to find a user wallet with provided ID: $userWalletId" diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt index d5d9009ec3..5433dc0701 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt @@ -5,6 +5,7 @@ import com.tangem.data.managetokens.DefaultManageTokensRepository import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.testnet.TestnetTokensStorage import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.managetokens.repository.CustomTokensRepository import com.tangem.domain.managetokens.repository.ManageTokensRepository @@ -27,6 +28,7 @@ internal object ManageTokensDataModule { userWalletsStore: UserWalletsStore, manageTokensUpdateFetcher: ManageTokensUpdateFetcher, appPreferencesStore: AppPreferencesStore, + testnetTokensStorage: TestnetTokensStorage, dispatchers: CoroutineDispatcherProvider, ): ManageTokensRepository { return DefaultManageTokensRepository( @@ -34,6 +36,7 @@ internal object ManageTokensDataModule { userWalletsStore, manageTokensUpdateFetcher, appPreferencesStore, + testnetTokensStorage, dispatchers, ) } diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt index 5a5b9fd6eb..ffdbb9e58d 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/ManagedCryptoCurrencyFactory.kt @@ -12,6 +12,7 @@ import com.tangem.data.common.currency.getNetwork import com.tangem.data.common.currency.getTokenId import com.tangem.datasource.api.tangemTech.models.CoinsResponse import com.tangem.datasource.api.tangemTech.models.UserTokensResponse +import com.tangem.datasource.local.testnet.models.TestnetTokensConfig import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.managetokens.model.ManagedCryptoCurrency import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork @@ -34,16 +35,49 @@ internal class ManagedCryptoCurrencyFactory { tokensResponse: UserTokensResponse, derivationStyleProvider: DerivationStyleProvider, ): List { - val customTokens = tokensResponse.tokens - .mapNotNull { token -> - maybeCreateCustomToken(token, coinsResponse.imageHost, derivationStyleProvider) - } - + val customTokens = createCustomTokens(tokensResponse, derivationStyleProvider) val tokens = create(coinsResponse, tokensResponse, derivationStyleProvider) return customTokens + tokens } + fun createTestnetWithCustomTokens( + testnetTokensConfig: TestnetTokensConfig, + tokensResponse: UserTokensResponse?, + derivationStyleProvider: DerivationStyleProvider, + ): List { + val customTokens = tokensResponse + ?.let { createCustomTokens(it, derivationStyleProvider) } + ?: emptyList() + val testnetTokens = testnetTokensConfig.tokens.map { testnetToken -> + ManagedCryptoCurrency.Token( + id = ManagedCryptoCurrency.ID(testnetToken.id), + name = testnetToken.name, + symbol = testnetToken.symbol, + iconUrl = getIconUrl(testnetToken.id), + availableNetworks = testnetToken.networks?.mapNotNull { network -> + createSource( + networkId = network.id, + contractAddress = network.address, + decimals = network.decimalCount ?: return@mapNotNull null, + derivationStyleProvider = derivationStyleProvider, + ) + } ?: emptyList(), + addedIn = findAddedInNetworks(testnetToken.id, tokensResponse, derivationStyleProvider), + ) + } + + return customTokens + testnetTokens + } + + private fun createCustomTokens( + tokensResponse: UserTokensResponse, + derivationStyleProvider: DerivationStyleProvider, + ): List = tokensResponse.tokens + .mapNotNull { token -> + maybeCreateCustomToken(token, null, derivationStyleProvider) + } + private fun maybeCreateCustomToken( token: UserTokensResponse.Token, imageHost: String?, @@ -100,23 +134,29 @@ internal class ManagedCryptoCurrencyFactory { symbol = coinResponse.symbol, iconUrl = getIconUrl(coinResponse.id, imageHost), availableNetworks = updatedNetworks.mapNotNull { network -> - createSource(network, derivationStyleProvider) + createSource( + networkId = network.networkId, + contractAddress = network.contractAddress, + decimals = network.decimalCount?.toInt() ?: return@mapNotNull null, + derivationStyleProvider = derivationStyleProvider, + ) }, addedIn = findAddedInNetworks(coinResponse.id, tokensResponse, derivationStyleProvider), ) } private fun createSource( - networkResponse: CoinsResponse.Coin.Network, + networkId: String, + contractAddress: String?, + decimals: Int, derivationStyleProvider: DerivationStyleProvider?, extraDerivationPath: String? = null, ): SourceNetwork? { - val blockchain = Blockchain.fromNetworkId(networkResponse.networkId) + val blockchain = Blockchain.fromNetworkId(networkId) ?.takeIf { it.isSupportedInApp() } ?: return null val network = getNetwork(blockchain, extraDerivationPath, derivationStyleProvider) ?: return null - val contractAddress = networkResponse.contractAddress return if (contractAddress.isNullOrBlank()) { SourceNetwork.Main( @@ -127,7 +167,7 @@ internal class ManagedCryptoCurrencyFactory { } else { SourceNetwork.Default( network = network, - decimals = requireNotNull(networkResponse.decimalCount?.toInt()), + decimals = decimals, contractAddress = contractAddress, ) } @@ -157,7 +197,7 @@ internal class ManagedCryptoCurrencyFactory { } } - private fun getIconUrl(id: String, imageHost: String?): String { + private fun getIconUrl(id: String, imageHost: String? = DEFAULT_IMAGE_HOST): String { return "${imageHost ?: DEFAULT_IMAGE_HOST}large/$id.png" } From 8e97517459f2488bc3c4043a7f397b665e76bce7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 1 Oct 2024 15:34:04 +0400 Subject: [PATCH 22/27] Updated on 2026-08-14 --- .../tangem/core/ui/extensions/BlockchainIcons.kt | 5 +++++ core/ui/src/main/res/drawable/ic_sui_22.xml | 9 +++++++++ core/ui/src/main/res/drawable/img_sui_22.xml | 15 +++++++++++++++ gradle/dependencies.toml | 2 +- .../com/tangem/blockchainsdk/utils/Blockchain.kt | 5 +++++ 5 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 core/ui/src/main/res/drawable/ic_sui_22.xml create mode 100644 core/ui/src/main/res/drawable/img_sui_22.xml diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt index 59fe1fa692..d014d038e0 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt @@ -75,6 +75,7 @@ fun getActiveIconRes(blockchainId: String): Int { "cyber", "cyber/test" -> R.drawable.img_cyber_22 "sei", "sei/test" -> R.drawable.img_sei_22 "internet-computer" -> R.drawable.img_icp_22 + "sui", "sui/test" -> R.drawable.img_sui_22 else -> R.drawable.ic_alert_24 } } @@ -151,6 +152,7 @@ fun getActiveIconResByNetworkId(networkId: String): Int { "cyber", "cyber/test" -> R.drawable.img_cyber_22 "sei", "sei/test" -> R.drawable.img_sei_22 "internet-computer" -> R.drawable.img_icp_22 + "sui", "sui/test" -> R.drawable.img_sui_22 else -> R.drawable.ic_alert_24 } } @@ -224,6 +226,7 @@ fun getActiveIconResByCoinId(coinId: String): Int { "cyber", "cyber/test" -> R.drawable.img_cyber_22 "sei", "sei/test" -> R.drawable.img_sei_22 "internet-computer" -> R.drawable.img_icp_22 + "sui", "sui/test" -> R.drawable.img_sui_22 else -> R.drawable.ic_alert_24 } } @@ -300,6 +303,7 @@ fun getGreyedOutIconRes(blockchainId: String): Int { "cyber", "cyber/test" -> R.drawable.ic_cyber_22 "sei", "sei/test" -> R.drawable.ic_sei_22 "internet-computer" -> R.drawable.ic_icp_22 + "sui", "sui/test" -> R.drawable.ic_sui_22 else -> R.drawable.ic_alert_24 } } @@ -376,6 +380,7 @@ fun getGreyedOutIconResByNetworkId(networkId: String): Int { "cyber", "cyber/test" -> R.drawable.ic_cyber_22 "sei", "sei/test" -> R.drawable.ic_sei_22 "internet-computer" -> R.drawable.ic_icp_22 + "sui", "sui/test" -> R.drawable.ic_sui_22 else -> R.drawable.ic_alert_24 } } \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/ic_sui_22.xml b/core/ui/src/main/res/drawable/ic_sui_22.xml new file mode 100644 index 0000000000..c6595dd4d7 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_sui_22.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/img_sui_22.xml b/core/ui/src/main/res/drawable/img_sui_22.xml new file mode 100644 index 0000000000..750a7815c8 --- /dev/null +++ b/core/ui/src/main/res/drawable/img_sui_22.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index e71e76d7c8..3ebdc9787b 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -89,7 +89,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.16-803" +tangemBlockchainSdk = "release-app_5.16-804" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.16-386" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt index 6c71737687..ed07736123 100644 --- a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt @@ -126,6 +126,8 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { "sei-network" -> Blockchain.Sei "sei-network/test" -> Blockchain.SeiTestnet "internet-computer" -> Blockchain.InternetComputer + "sui" -> Blockchain.Sui + "sui/test" -> Blockchain.SuiTestnet else -> null } } @@ -253,6 +255,8 @@ fun Blockchain.toNetworkId(): String { Blockchain.Sei -> "sei-network" Blockchain.SeiTestnet -> "sei-network/test" Blockchain.InternetComputer -> "internet-computer" + Blockchain.Sui -> "sui" + Blockchain.SuiTestnet -> "sui/test" } } @@ -337,6 +341,7 @@ fun Blockchain.toCoinId(): String { Blockchain.Cyber, Blockchain.CyberTestnet -> "cyber-ethereum" Blockchain.Sei, Blockchain.SeiTestnet -> "sei-network" Blockchain.InternetComputer -> "internet-computer" + Blockchain.Sui, Blockchain.SuiTestnet -> "sui" } } From 3e8d7219156152e203798783d5dfb7ea4e7947da Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 2 Oct 2024 10:51:52 +0300 Subject: [PATCH 23/27] Updated on 2026-08-14 --- .../markets/DefaultMarketsTokenRepository.kt | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt b/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt index 20ca3a308c..6416fdf0f5 100644 --- a/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt +++ b/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt @@ -10,11 +10,6 @@ import com.tangem.data.common.currency.getNetwork import com.tangem.data.common.utils.retryOnError import com.tangem.data.markets.analytics.MarketsDataAnalyticsEvent import com.tangem.data.markets.converters.* -import com.tangem.data.markets.converters.TokenChartConverter -import com.tangem.data.markets.converters.TokenMarketInfoConverter -import com.tangem.data.markets.converters.TokenMarketListConverter -import com.tangem.data.markets.converters.TokenQuotesShortConverter -import com.tangem.data.markets.converters.toRequestParam import com.tangem.datasource.api.common.response.ApiResponseError import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.markets.TangemTechMarketsApi @@ -29,6 +24,7 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.pagination.* import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicLong @@ -125,7 +121,7 @@ internal class DefaultMarketsTokenRepository( interval: PriceChangeInterval, tokenId: String, tokenSymbol: String, - ): TokenChart { + ) = withContext(dispatcherProvider.io) { val mappedTokenId = getTokenIdIfL2Network(tokenId) val response = marketsApi.getCoinChart( currency = fiatCurrencyCode, @@ -142,7 +138,7 @@ internal class DefaultMarketsTokenRepository( response.getOrThrow() } - return TokenChartConverter.convert( + return@withContext TokenChartConverter.convert( interval = interval, value = result, @@ -162,7 +158,7 @@ internal class DefaultMarketsTokenRepository( interval: PriceChangeInterval, tokenId: String, tokenSymbol: String, - ): TokenChart { + ) = withContext(dispatcherProvider.io) { val mappedTokenId = getTokenIdIfL2Network(tokenId) val response = marketsApi.getCoinsListCharts( coinIds = mappedTokenId, @@ -176,7 +172,7 @@ internal class DefaultMarketsTokenRepository( ) } - return TokenChartConverter.convert( + return@withContext TokenChartConverter.convert( interval = interval, value = chart, @@ -196,7 +192,7 @@ internal class DefaultMarketsTokenRepository( tokenId: String, tokenSymbol: String, languageCode: String, - ): TokenMarketInfo { + ) = withContext(dispatcherProvider.io) { val response = marketsApi.getCoinMarketData( currency = fiatCurrencyCode, coinId = tokenId, @@ -213,29 +209,30 @@ internal class DefaultMarketsTokenRepository( } val resultResponse = result.applyL2Compatibility(tokenId) - return TokenMarketInfoConverter.convert(resultResponse) + return@withContext TokenMarketInfoConverter.convert(resultResponse) } - override suspend fun getTokenQuotes(fiatCurrencyCode: String, tokenId: String, tokenSymbol: String): TokenQuotes { - // for second markets iteration we should use extended api method with all required fields - val response = tangemTechApi.getQuotes( - currencyId = fiatCurrencyCode, - coinIds = tokenId, - fields = marketsQuoteFields.joinToString(separator = ","), - ) + override suspend fun getTokenQuotes(fiatCurrencyCode: String, tokenId: String, tokenSymbol: String) = + withContext(dispatcherProvider.io) { + // for second markets iteration we should use extended api method with all required fields + val response = tangemTechApi.getQuotes( + currencyId = fiatCurrencyCode, + coinIds = tokenId, + fields = marketsQuoteFields.joinToString(separator = ","), + ) - val result = catchApiErrorAndSendEvent( - errorEvent = MarketsDataAnalyticsEvent.Details.Error( - request = MarketsDataAnalyticsEvent.Details.Error.Request.Info, - tokenSymbol = tokenSymbol, - ), - ) { - response.getOrThrow() + val result = catchApiErrorAndSendEvent( + errorEvent = MarketsDataAnalyticsEvent.Details.Error( + request = MarketsDataAnalyticsEvent.Details.Error.Request.Info, + tokenSymbol = tokenSymbol, + ), + ) { + response.getOrThrow() + } + + return@withContext TokenQuotesShortConverter.convert(tokenId, result).toFull() } - return TokenQuotesShortConverter.convert(tokenId, result).toFull() - } - override suspend fun createCryptoCurrency( userWalletId: UserWalletId, token: TokenMarketParams, From 78ab5c30784869b9a69a2daab62336cea7ab6371 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 2 Oct 2024 13:51:23 +0300 Subject: [PATCH 24/27] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 328c13826f..d52c8382e9 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 328c13826f7e52ba783db1ba39de5b17b1b79bc5 +Subproject commit d52c8382e9e94b2b44ceecd7cb7bd897e091073c From 11ccaf56e02123e63ab740365806884188eea7ee Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 2 Oct 2024 19:44:14 +0400 Subject: [PATCH 25/27] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 3ebdc9787b..ab6f07950a 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -89,7 +89,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.16-804" +tangemBlockchainSdk = "release-app_5.16-807" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.16-386" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 47a1b16137a6712a082297666955a52a56081a77 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 3 Oct 2024 12:48:35 +0300 Subject: [PATCH 26/27] Updated on 2026-08-14 --- core/res/src/main/res/values-de/strings.xml | 2 ++ core/res/src/main/res/values-es/strings.xml | 8 ++++-- core/res/src/main/res/values-fr/strings.xml | 4 ++- core/res/src/main/res/values-ja/strings.xml | 23 +++++++++++++-- core/res/src/main/res/values-ru/strings.xml | 27 ++++++++++++++++-- .../src/main/res/values-uk-rUA/strings.xml | 2 ++ core/res/src/main/res/values/strings.xml | 28 ++++++++++++++++--- 7 files changed, 82 insertions(+), 12 deletions(-) diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index e4f4e75ca9..516445f46f 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -722,6 +722,7 @@ Pro Block Täglich Täglich + Alle %1$s Sek. Pro Epoche Pro Ära Stündlich @@ -744,6 +745,7 @@ Unstake Assets %s Validator/ Prüfer Validatoren + Strategischer Partner Abstimmung Abstimmung gesperrt Zurückziehen diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml index 961dbdaafb..ba3e1b02eb 100644 --- a/core/res/src/main/res/values-es/strings.xml +++ b/core/res/src/main/res/values-es/strings.xml @@ -338,7 +338,7 @@ Eliminar por ejemplo, BTC I trust, hodl I must - Su cartera ha sido actualizada + Su portafolio ha sido actualizada El token seleccionado no está disponible actualmente para acciones dentro de la billetera cripto. Pero no se preocupa, puede expresar su interés votando a favor. Votar a favor Elegir wallet @@ -373,7 +373,7 @@ Compradores experimentados Evaluación Ordenar por - Mejores Ganadores + Top Ganadores Top Perdedores Tendencias Acerca de %s @@ -720,6 +720,7 @@ Bloquear Día Cada día + Cada %1$s segundo(s) Época Era Hora @@ -742,8 +743,9 @@ Detenando el staking de activos %s Validador Validadores + Socio estratégico Votar - Voto bloqueado + Votar Retirar Sus stakes Almacene sus activos crypto de forma segura manteniendo las claves privadas contenidas en su tarjeta diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 17c960b9e2..e310d6723c 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -720,6 +720,7 @@ Bloquer Jour Chaque jour + Toute(s) les %1$s seconde(s) Époque Ère Heure @@ -742,8 +743,9 @@ En train de destaker les actifs %s Validateur Validateurs + Partenaire stratégique Voter - Vote bloqué + Voter Retirer Vos stakes Stockez vos actifs crypto en toute sécurité tout en conservant les clés privées contenues dans votre carte diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 9066c1ebb1..0d15d6edfc 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -95,6 +95,9 @@ %d 日 + + + 削除 無効 完了 @@ -340,7 +343,7 @@ 賛成票を投じる ウォレットを選択 ウォレットは複数のネットワークをサポートしていません。 - このアセットの購入、交換、受け取りを開始するには、ポートフォリオに追加してください。 + このアセットを購入・交換・受け取るには、ポートフォリオに追加してください。 このアセットは利用できません このアセットはこのウォレットでは使用できません。 ポートフォリオに追加 @@ -375,6 +378,9 @@ 下落率上位 トレンド %sについて + + %d取引所 + %d のレーティングに基づいて @@ -385,6 +391,12 @@ 循環供給量 取引可能で市場に流通しているコインの総数 循環供給量 + 取引所が見つかりません + 取引所 + 注意 + 危険 + 信頼できる + 取引所 経験豊富な買い手 少なくとも100の発信取引を持つネット・バイヤー 経験豊富な買い手 @@ -402,6 +414,7 @@ 指定された期間中のトークンに利用可能な流動性の変化 流動性 流動性指数 + 上場先 低い 時価総額 暗号資産の市場価値の合計。コインの現在の価格と、流通しているコインの総数を掛けて計算されます。 @@ -424,6 +437,7 @@ 取引量(24時間) 過去24時間以内に取引された暗号資産の合計額。市場の活発さと流動性を示します。 取引量(24時間) + 取引量 これをドラッグするか、検索窓をタップして、マーケットから直接トークンを追加します トークンを追加 お使いのデバイスではNFCが使用できません @@ -712,7 +726,10 @@ 手動 ブロック毎 毎日 + %sごと 毎日 + %1$s日ごとに + %1$s秒毎 エポック毎 時代毎 毎時間 @@ -723,6 +740,7 @@ もっとステーキングする %1$sをステーキングし、年間 %2$sを受け取ります タップしてロック解除 + タップしてロック解除もしくは投票 タップして引き出す %sをステーキングする %sのステーキング解除 @@ -735,8 +753,9 @@ ステーキング解除中の資産%s バリデーター バリデーター + 戦略的パートナー 投票する - 投票はロックされています + 投票する 引き出す あなたのステーキング カード内に秘密鍵を保管しながら暗号資産を安全に保管します diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 6f25020e8d..e8db31eab3 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -101,6 +101,12 @@ %d дней %d дней + + день + дня + дней + дней + Удалить Отключено Готово @@ -382,6 +388,12 @@ Лидеры падения В тренде О %s + + %d биржа + %d биржи + %d бирж + %d бирж + На основе %d оценки На основе %d оценок @@ -395,6 +407,12 @@ Циркулир. предл. Общее количество монет, доступных для торговли и находящихся в обращении на рынке Циркулирующее предложение + Биржи не найдены + Биржа + Осторожно + Рискованно + Надежно + Биржи Опытные трейдеры Покупатели, у которых было как минимум 100 исходящих транзакций. Опытные трейдеры @@ -412,6 +430,7 @@ Изменение объема ликвидности, доступной для токена в течение указанного периода времени. Ликвидность Индекс ликвидности + Листинг на Низкий Рын. кап. Общая рыночная стоимость криптовалюты, рассчитываемая путем умножения текущей цены монеты на общее количество монет в обращении. @@ -434,6 +453,7 @@ Объем торгов (24ч) Общая сумма криптовалюты, которая была продана за последние 24 часа, показывающая уровень активности и ликвидности на рынке. Объем торгов (24ч) + Объем Потяните вверх или коснитесь поисковой строки, чтобы добавить токены напрямую из рынка Добавить токены Функция NFC недоступна на вашем устройстве @@ -509,7 +529,7 @@ Подготовьте основную карту с номером %s Ваша карта настроена и готова к использованию. Добавлено максимальное количество карт. Завершите процесс резервного копирования. - Активация карты + Активация кошелька Резервная карта #%d Нет резервных карт Уведомления @@ -734,6 +754,8 @@ За блок Ежедневно Каждый день + Каждые %1$s дня + Каждые %1$s сек За эпоху За эру Ежечасно @@ -757,8 +779,9 @@ Завершение стейкинга %s Валидатор Валидаторы + Стратегический партнер Проголосовать - Голосование заблокировано + Проголосовать Вывод Застейканные средства Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте. diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index 611026a7cb..5a9fb13979 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -737,6 +737,7 @@ Блок День Щодня + Кожні %1$s сек Епоха Ера Година @@ -759,6 +760,7 @@ Виведення зі стейкінгу %s Валідатор Валідатори + Стратегічний партнер Проголосувати Голос заблоковано Зняти diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 3e97f225bf..7d4c202a25 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -97,6 +97,10 @@ %d day %d days + + day + days + Delete Disabled Done @@ -378,6 +382,10 @@ Top Losers Trending About %s + + %d exchange + %d exchanges + Based on %d rating Based on %d ratings @@ -389,6 +397,12 @@ Circulating supply The total number of coins that are available for trading and are circulating in the market Circulating supply + No exchanges found + Exchange + Caution + Risky + Trusted + Exchanges Experienced buyers Net buyers with the additional requirement of having at least 100 outgoing transactions Experienced buyers @@ -406,6 +420,7 @@ The change in how much liquidity is available for the token during the specified timeframe Liquidity Liquidity index + Listed on Low Market cap The total market value of a cryptocurrency, calculated by multiplying the current price of the coin by the total number of coins in circulation @@ -428,6 +443,7 @@ Trading volume (24h) The total amount of a cryptocurrency that has been traded within the last 24 hours, indicating the level of activity and liquidity in the market Trading volume (24h) + Volume Pull this up or tap the search bar to add tokens directly from the market Add tokens NFC is not available on your device @@ -499,7 +515,7 @@ Prepare the primary card with number %s Your wallet card is configured and ready for use. Max number of cards added. Finalize the backup process. - Activating card + Activating wallet Backup card #%d No backup cards Notifications @@ -679,7 +695,7 @@ Reward claiming Method of receiving staking rewards.\nIt can be either automatic, where the reward is credited to your address, or manual, where you need to withdraw the reward by creating a transaction to receive it. Reward schedule - This is a schedule that determines when participants in staking receive their rewards. + This is a schedule that determines when participants in staking receive their rewards. The reward distribution time may vary slightly depending on the validator and network load. Rewards: %s Staking %s Unbonding Period @@ -720,7 +736,10 @@ Manual Per Block Daily - Daily + Each %s + Each day + Each %1$s days + Each %1$s sec Per Epoch Per Era Hourly @@ -744,8 +763,9 @@ Unstaking assets %s Validator Validators + Strategic partner Vote - Vote locked + Vote Withdraw Your stakes Store your crypto assets secure while keeping private keys contained in your card From 43738a6b636c6b25f1f2986e58b42c248cce3229 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 3 Oct 2024 13:41:51 +0500 Subject: [PATCH 27/27] Updated on 2026-08-14 --- .../components/inputrow/InputRowImageBase.kt | 32 +++----------- .../components/inputrow/InputRowImageInfo.kt | 42 ++++++++++++++++--- .../inputrow/InputRowImageSelector.kt | 6 ++- .../data/staking/converters/YieldConverter.kt | 9 ++++ .../domain/staking/model/stakekit/Yield.kt | 1 + .../ConfirmationStatePreviewData.kt | 3 ++ .../previewdata/InitialStakingStatePreview.kt | 1 + .../ui/StakingValidatorListContent.kt | 26 ++++++++++++ 8 files changed, 88 insertions(+), 32 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageBase.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageBase.kt index bd65f92ab5..36c14965b1 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageBase.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageBase.kt @@ -24,7 +24,6 @@ import com.tangem.core.ui.components.inputrow.inner.InputRowAsyncImage import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveAnnotatedReference import com.tangem.core.ui.extensions.resolveReference -import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview @@ -33,15 +32,17 @@ import com.tangem.core.ui.res.TangemThemePreview * [Input Row Image](https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=2841-1589&t=u6pOF6lsdpvWLELb-4) * * @param subtitle subtitle text - * @param caption caption text * @param modifier modifier + * @param caption caption text * @param imageUrl icon to load + * @param iconRes icon resource * @param subtitleColor subtitle text color * @param captionColor caption text color + * @param iconTint icon tint * @param isGrayscaleImage whether to display grayscale image - * @param subtitleEndIconRes icon to show after subtitle * @param iconEndRes icon to end of row * @param onImageError composable to show if image loading failed + * @param subtitleExtraContent subtitle extra content * @param extraContent extra content */ @Suppress("LongMethod") @@ -56,10 +57,9 @@ internal fun InputRowImageBase( captionColor: Color = TangemTheme.colors.text.tertiary, iconTint: Color = TangemTheme.colors.icon.informative, isGrayscaleImage: Boolean = false, - @DrawableRes subtitleEndIconRes: Int? = null, - subtitleEndIconTint: Color = TangemColorPalette.Azure, @DrawableRes iconEndRes: Int? = null, onImageError: (@Composable () -> Unit)? = null, + subtitleExtraContent: (@Composable RowScope.() -> Unit)? = null, extraContent: (@Composable RowScope.() -> Unit)? = null, ) { Row( @@ -100,10 +100,7 @@ internal fun InputRowImageBase( style = TangemTheme.typography.subtitle2, color = subtitleColor, ) - SubtitleEndIconRes( - subtitleEndIconRes = subtitleEndIconRes, - subtitleEndIconTint = subtitleEndIconTint, - ) + subtitleExtraContent?.invoke(this) } if (caption != null) { Text( @@ -124,23 +121,6 @@ internal fun InputRowImageBase( } } -@Composable -private fun RowScope.SubtitleEndIconRes(subtitleEndIconRes: Int?, subtitleEndIconTint: Color) { - AnimatedVisibility( - visible = subtitleEndIconRes != null, - label = "Subtitle end icon visibility animation", - modifier = Modifier.align(Alignment.CenterVertically), - ) { - val icon = remember(this) { requireNotNull(subtitleEndIconRes) } - Icon( - painter = rememberVectorPainter(image = ImageVector.vectorResource(id = icon)), - tint = subtitleEndIconTint, - contentDescription = null, - modifier = Modifier.padding(start = TangemTheme.dimens.spacing4), - ) - } -} - @Composable private fun RowScope.InputRowEndIcon(iconRes: Int?) { AnimatedVisibility( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageInfo.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageInfo.kt index 366bd4e48d..c908519e10 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageInfo.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageInfo.kt @@ -2,14 +2,21 @@ package com.tangem.core.ui.components.inputrow import android.content.res.Configuration import androidx.annotation.DrawableRes +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -17,6 +24,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.tangem.core.ui.R import com.tangem.core.ui.components.atoms.text.EllipsisText import com.tangem.core.ui.extensions.* +import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview @@ -24,17 +32,19 @@ import com.tangem.core.ui.res.TangemThemePreview * Input row component with selector * [Input Row Image](https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=2841-1589&t=u6pOF6lsdpvWLELb-4) * - * @param title title text * @param subtitle subtitle text - * @param caption caption text * @param infoTitle info title text - * @param infoSubtitle info subtitle text * @param modifier modifier + * @param title title text + * @param caption caption text + * @param infoSubtitle info subtitle text * @param imageUrl icon to load + * @param iconRes icon resource * @param subtitleColor subtitle text color * @param captionColor caption text color + * @param iconTint icon color * @param isGrayscaleImage whether to display grayscale image - * @param showPendingIcon whether to show pending icon + * @param subtitleEndIconRes subtitle icon to end of row * @param iconEndRes icon to end of row * @param onImageError composable to show if image loading failed */ @@ -78,9 +88,14 @@ fun InputRowImageInfo( subtitleColor = subtitleColor, captionColor = captionColor, isGrayscaleImage = isGrayscaleImage, - subtitleEndIconRes = subtitleEndIconRes, iconEndRes = iconEndRes, onImageError = onImageError, + subtitleExtraContent = { + SubtitleEndIconRes( + subtitleEndIconRes = subtitleEndIconRes, + subtitleEndIconTint = TangemColorPalette.Azure, + ) + }, ) { Column( verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2), @@ -120,6 +135,23 @@ fun InputRowImageInfo( } } +@Composable +private fun RowScope.SubtitleEndIconRes(subtitleEndIconRes: Int?, subtitleEndIconTint: Color) { + AnimatedVisibility( + visible = subtitleEndIconRes != null, + label = "Subtitle end icon visibility animation", + modifier = Modifier.align(Alignment.CenterVertically), + ) { + val icon = remember(this) { requireNotNull(subtitleEndIconRes) } + Icon( + painter = rememberVectorPainter(image = ImageVector.vectorResource(id = icon)), + tint = subtitleEndIconTint, + contentDescription = null, + modifier = Modifier.padding(start = TangemTheme.dimens.spacing4), + ) + } +} + // region Preview @Preview(showBackground = true, widthDp = 360) @Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageSelector.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageSelector.kt index 287edc1197..a9921a83b5 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageSelector.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageSelector.kt @@ -4,6 +4,7 @@ import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.padding import androidx.compose.material3.ripple import androidx.compose.runtime.Composable @@ -40,8 +41,9 @@ import java.math.BigDecimal * @param subtitleColor subtitle text color * @param captionColor caption text color * @param isSelected true if selected - * @param selectorContent selector content * @param onImageError composable to show if image loading failed + * @param subtitleExtraContent subtitle selector content + * @param selectorContent selector content */ @Composable fun InputRowImageSelector( @@ -54,6 +56,7 @@ fun InputRowImageSelector( captionColor: Color = TangemTheme.colors.text.tertiary, isSelected: Boolean = false, onImageError: (@Composable () -> Unit)? = null, + subtitleExtraContent: (@Composable RowScope.() -> Unit)? = null, selectorContent: @Composable ((isSelected: Boolean, isEnabled: Boolean, onSelect: () -> Unit) -> Unit), ) { InputRowImageBase( @@ -63,6 +66,7 @@ fun InputRowImageSelector( subtitleColor = subtitleColor, captionColor = captionColor, onImageError = onImageError, + subtitleExtraContent = subtitleExtraContent, modifier = modifier .clickable( onClick = onSelect, diff --git a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt index 2ff434f089..7499cb4a34 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/converters/YieldConverter.kt @@ -123,6 +123,7 @@ class YieldConverter( stakedBalance = validatorDTO.stakedBalance, votingPower = validatorDTO.votingPower, preferred = validatorDTO.preferred, + isStrategicPartner = isStrategicPartner(validatorDTO.address), ) } @@ -174,4 +175,12 @@ class YieldConverter( else -> Yield.Args.ArgType.UNKNOWN } } + + private fun isStrategicPartner(validatorAddress: String): Boolean = PARTNERS.any { it == validatorAddress } + + private companion object { + val PARTNERS = listOf( + "cosmosvaloper1wrx0x9m9ykdhw9sg04v7uljme53wuj03aa5d4f", + ) + } } \ No newline at end of file diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt index c65303fd35..768d65cbf4 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt @@ -62,6 +62,7 @@ data class Yield( val stakedBalance: String? = null, val votingPower: Double? = null, val preferred: Boolean, + val isStrategicPartner: Boolean, ) { enum class ValidatorStatus { diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt index e68d171b89..d9c7263d8e 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmationStatePreviewData.kt @@ -28,6 +28,7 @@ internal object ConfirmationStatePreviewData { website = "https://luganodes.com/", votingPower = 0.09778360195377911, preferred = true, + isStrategicPartner = false, ), Yield.Validator( address = "0x35b1ca0f398905cf752e6fe122b51c88022fca32", @@ -40,6 +41,7 @@ internal object ConfirmationStatePreviewData { website = "https://infstones.com/", votingPower = 0.0034366257754399774, preferred = true, + isStrategicPartner = false, ), Yield.Validator( address = "0xd14a87025109013b0a2354a775cb335f926af65a", @@ -52,6 +54,7 @@ internal object ConfirmationStatePreviewData { website = "https://infstones.com/", votingPower = 0.023487238579718264, preferred = true, + isStrategicPartner = true, ), ) 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 e42df87aba..d3890582f5 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 @@ -86,6 +86,7 @@ internal object InitialStakingStatePreview { stakedBalance = null, votingPower = null, preferred = false, + isStrategicPartner = false, ), pendingActions = persistentListOf(), isClickable = true, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorListContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorListContent.kt index c86ebb7e95..9ddda86f32 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorListContent.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorListContent.kt @@ -5,16 +5,22 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.inputrow.InputRowImageSelector import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.extensions.* @@ -78,6 +84,9 @@ internal fun StakingValidatorListContent( false, ) .background(TangemTheme.colors.background.action), + subtitleExtraContent = { + ValidatorLabel(item.isStrategicPartner) + }, selectorContent = { checked, _, _ -> AnimatedVisibility( modifier = Modifier, @@ -99,6 +108,23 @@ internal fun StakingValidatorListContent( } } +@Composable +private fun RowScope.ValidatorLabel(isStrategicPartner: Boolean) { + if (isStrategicPartner) { + Text( + text = stringResource(R.string.staking_validators_label), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.icon.constant, + modifier = Modifier + .align(Alignment.CenterVertically) + .padding(horizontal = 6.dp) + .clip(RoundedCornerShape(6.dp)) + .background(TangemTheme.colors.text.accent) + .padding(horizontal = 8.dp), + ) + } +} + // region Preview @Preview(showBackground = true, widthDp = 360) @Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)