From 00107432cf91325fe0c3d41f940d4b431665530a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 1 Nov 2024 19:29:07 +0300 Subject: [PATCH 01/40] Updated on 2026-08-14 --- gradle/dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index ecf6a68855..d78017f8a3 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -88,9 +88,9 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-848" +tangemBlockchainSdk = "release-app_5.18-849" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-395" +tangemCardSdk = "release-app_5.18-396" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem17" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ From ad6f031c7316ec12e231ccc793bac01ac10d4168 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 3 Nov 2024 20:41:00 +0300 Subject: [PATCH 02/40] 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 2f4b0bdc30..8a6315a3d3 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -88,7 +88,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.17-837" +tangemBlockchainSdk = "release-app_5.17-851" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.17-392" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 8bc33fe272a6663715a2afe07220850304fb2450 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Nov 2024 12:32:43 +0500 Subject: [PATCH 03/40] Updated on 2026-08-14 --- .../src/main/assets/contract_methods.json | 40 +++++++++++++++++++ .../utils/SdkTransactionTypeConverter.kt | 22 +++++++--- .../domain/txhistory/models/TxHistoryItem.kt | 12 +++--- ...tailsTxHistoryTransactionStateConverter.kt | 26 ++++++------ .../converter/TxHistoryItemStateConverter.kt | 26 ++++++------ 5 files changed, 89 insertions(+), 37 deletions(-) diff --git a/domain/legacy/src/main/assets/contract_methods.json b/domain/legacy/src/main/assets/contract_methods.json index f7bad0ed28..6a8d6dafc4 100644 --- a/domain/legacy/src/main/assets/contract_methods.json +++ b/domain/legacy/src/main/assets/contract_methods.json @@ -141,5 +141,45 @@ "info":"swapTokensForExactETC", "source":"https://etc.blockscout.com/tx/0xd79e03ca6b71529c94d576ff78c55b4371e2ef3ff8f9a47007995e5b9e77f879", "name":"swap" + }, + "0x6ab15071": { + "info": "stakePOL", + "source": "https://etherscan.io/tx/0x490d2f31faa3f2fdb1a9505888761d89aba1dd3dcfeb65f08651d094c1ee5021", + "name": "buyVoucher" + }, + "0xe4457a8a": { + "info": "stakePOL", + "source": "https://etherscan.io/tx/0xb7b67ea0261fe2067e285eb942be6883ef34b3d313160f3ce122cad3e91f95f7", + "name": "buyVoucherPOL" + }, + "0xc83ec04d": { + "info": "stakePOL", + "source": "https://etherscan.io/tx/0x0f7fa7154be5ae9bbe229963e3c0d3eea16b74311c50c4a35f3fcffc9519baed", + "name": "sellVoucher_new" + }, + "0xe570b78b": { + "info": "stakePOL", + "source": "https://etherscan.io/tx/0xde1dda9ff3aed8b1d5feff53f72032cbd106a57c1ae2e7fa80016349e2e68341", + "name": "sellVoucher_newPOL" + }, + "0xe97fddc2": { + "info": "stakePOL", + "source": "https://etherscan.io/tx/0x0478bc1229722c049eed737ef5a56baee6d7bde7ae21afad7a10e029700b378c", + "name": "unstakeClaimTokens_new" + }, + "0x8759c234": { + "info": "stakePOL", + "source": "https://etherscan.io/tx/0x79075778b470358bef4cfc25a74a43e7ae60f1bc292e0bca3ebc163fee8bf0c7", + "name": "unstakeClaimTokens_newPOL" + }, + "0xc7b8981c": { + "info": "stakePOL", + "source": "https://etherscan.io/tx/0xc2df9b41f7b3780a2dd06f0dfff72ecd5d3a5bde3f1d3f0eb926e14ff975e957", + "name": "withdrawRewards" + }, + "0xe0db556b": { + "info": "stakePOL", + "source": "https://etherscan.io/tx/0x96ee64ad31a045982a4776336d852af3c25dc39b6d01bea987aefd460b816773", + "name": "withdrawRewardsPOL" } } diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt index 306c18cf6a..fa74187f5a 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt @@ -21,19 +21,19 @@ internal class SdkTransactionTypeConverter( TxHistoryItem.TransactionType.Transfer } is TransactionType.TronStakingTransactionType.FreezeBalanceV2Contract -> { - TxHistoryItem.TransactionType.TronStakingTransactionType.Stake + TxHistoryItem.TransactionType.StakingTransactionType.Stake } is TransactionType.TronStakingTransactionType.UnfreezeBalanceV2Contract -> { - TxHistoryItem.TransactionType.TronStakingTransactionType.Unstake + TxHistoryItem.TransactionType.StakingTransactionType.Unstake } is TransactionType.TronStakingTransactionType.VoteWitnessContract -> { - TxHistoryItem.TransactionType.TronStakingTransactionType.Vote(value.validatorAddress) + TxHistoryItem.TransactionType.StakingTransactionType.Vote(value.validatorAddress) } is TransactionType.TronStakingTransactionType.WithdrawBalanceContract -> { - TxHistoryItem.TransactionType.TronStakingTransactionType.ClaimRewards + TxHistoryItem.TransactionType.StakingTransactionType.ClaimRewards } is TransactionType.TronStakingTransactionType.WithdrawExpireUnfreezeContract -> { - TxHistoryItem.TransactionType.TronStakingTransactionType.Withdraw + TxHistoryItem.TransactionType.StakingTransactionType.Withdraw } } } @@ -43,6 +43,18 @@ internal class SdkTransactionTypeConverter( "transfer" -> TxHistoryItem.TransactionType.Transfer "approve" -> TxHistoryItem.TransactionType.Approve "swap" -> TxHistoryItem.TransactionType.Swap + "buyVoucher", + "buyVoucherPOL", + -> TxHistoryItem.TransactionType.StakingTransactionType.Stake + "sellVoucher_new", + "sellVoucher_newPOL", + -> TxHistoryItem.TransactionType.StakingTransactionType.Unstake + "unstakeClaimTokens_new", + "unstakeClaimTokens_newPOL", + -> TxHistoryItem.TransactionType.StakingTransactionType.Withdraw + "withdrawRewards", + "withdrawRewardsPOL", + -> TxHistoryItem.TransactionType.StakingTransactionType.ClaimRewards null -> TxHistoryItem.TransactionType.UnknownOperation else -> TxHistoryItem.TransactionType.Operation(name = methodName.replaceFirstChar { it.titlecase() }) } diff --git a/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt index f96905a1ed..526be8774c 100644 --- a/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt +++ b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt @@ -40,12 +40,12 @@ data class TxHistoryItem( data object UnknownOperation : TransactionType data class Operation(val name: String) : TransactionType - sealed interface TronStakingTransactionType : TransactionType { - data class Vote(val validatorAddress: String) : TronStakingTransactionType - data object ClaimRewards : TronStakingTransactionType - data object Stake : TronStakingTransactionType - data object Unstake : TronStakingTransactionType - data object Withdraw : TronStakingTransactionType + sealed interface StakingTransactionType : TransactionType { + data class Vote(val validatorAddress: String) : StakingTransactionType + data object ClaimRewards : StakingTransactionType + data object Stake : StakingTransactionType + data object Unstake : StakingTransactionType + data object Withdraw : StakingTransactionType } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt index 4756f994ca..91863e54cd 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt @@ -50,13 +50,13 @@ internal class TokenDetailsTxHistoryTransactionStateConverter( } else { when (type) { is TransactionType.Approve -> R.drawable.ic_doc_24 - is TransactionType.TronStakingTransactionType.Stake, - is TransactionType.TronStakingTransactionType.Vote, + is TransactionType.StakingTransactionType.Stake, + is TransactionType.StakingTransactionType.Vote, -> R.drawable.ic_transaction_history_staking_24 - is TransactionType.TronStakingTransactionType.ClaimRewards, + is TransactionType.StakingTransactionType.ClaimRewards, -> R.drawable.ic_transaction_history_claim_rewards_24 - is TransactionType.TronStakingTransactionType.Unstake, - is TransactionType.TronStakingTransactionType.Withdraw, + is TransactionType.StakingTransactionType.Unstake, + is TransactionType.StakingTransactionType.Withdraw, -> R.drawable.ic_transaction_history_unstaking_24 is TransactionType.Operation, is TransactionType.Swap, @@ -72,11 +72,11 @@ internal class TokenDetailsTxHistoryTransactionStateConverter( is TransactionType.Swap -> resourceReference(R.string.common_swap) is TransactionType.Transfer -> resourceReference(R.string.common_transfer) is TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation) - is TransactionType.TronStakingTransactionType.Stake -> resourceReference(R.string.common_stake) - is TransactionType.TronStakingTransactionType.Unstake -> resourceReference(R.string.common_unstake) - is TransactionType.TronStakingTransactionType.Vote -> resourceReference(R.string.staking_vote) - is TransactionType.TronStakingTransactionType.ClaimRewards -> resourceReference(R.string.common_claim_rewards) - is TransactionType.TronStakingTransactionType.Withdraw -> resourceReference(R.string.staking_withdraw) + is TransactionType.StakingTransactionType.Stake -> resourceReference(R.string.common_stake) + is TransactionType.StakingTransactionType.Unstake -> resourceReference(R.string.common_unstake) + is TransactionType.StakingTransactionType.Vote -> resourceReference(R.string.staking_vote) + is TransactionType.StakingTransactionType.ClaimRewards -> resourceReference(R.string.common_claim_rewards) + is TransactionType.StakingTransactionType.Withdraw -> resourceReference(R.string.staking_withdraw) } private fun TxHistoryItem.extractSubtitle(): TextReference = @@ -119,9 +119,9 @@ internal class TokenDetailsTxHistoryTransactionStateConverter( } private fun TxHistoryItem.getAmount(): String { - if (type is TransactionType.TronStakingTransactionType.Vote || - type == TransactionType.TronStakingTransactionType.ClaimRewards || - type == TransactionType.TronStakingTransactionType.Withdraw + if (type is TransactionType.StakingTransactionType.Vote || + type == TransactionType.StakingTransactionType.ClaimRewards || + type == TransactionType.StakingTransactionType.Withdraw ) { return "" } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt index 461f74999f..36ea12af34 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt @@ -49,13 +49,13 @@ internal class TxHistoryItemStateConverter( } else { when (type) { is TransactionType.Approve -> R.drawable.ic_doc_24 - is TransactionType.TronStakingTransactionType.Stake, - is TransactionType.TronStakingTransactionType.Vote, + is TransactionType.StakingTransactionType.Stake, + is TransactionType.StakingTransactionType.Vote, -> R.drawable.ic_transaction_history_staking_24 - is TransactionType.TronStakingTransactionType.ClaimRewards, + is TransactionType.StakingTransactionType.ClaimRewards, -> R.drawable.ic_transaction_history_claim_rewards_24 - is TransactionType.TronStakingTransactionType.Unstake, - is TransactionType.TronStakingTransactionType.Withdraw, + is TransactionType.StakingTransactionType.Unstake, + is TransactionType.StakingTransactionType.Withdraw, -> R.drawable.ic_transaction_history_unstaking_24 is TransactionType.Operation, is TransactionType.Swap, @@ -70,11 +70,11 @@ internal class TxHistoryItemStateConverter( is TransactionType.Operation -> stringReference(type.name) is TransactionType.Swap -> resourceReference(R.string.common_swap) is TransactionType.Transfer -> resourceReference(R.string.common_transfer) - is TransactionType.TronStakingTransactionType.Stake -> resourceReference(R.string.common_stake) - is TransactionType.TronStakingTransactionType.Unstake -> resourceReference(R.string.common_unstake) - is TransactionType.TronStakingTransactionType.Vote -> resourceReference(R.string.staking_vote) - is TransactionType.TronStakingTransactionType.ClaimRewards -> resourceReference(R.string.common_claim_rewards) - is TransactionType.TronStakingTransactionType.Withdraw -> { resourceReference(R.string.staking_withdraw) } + is TransactionType.StakingTransactionType.Stake -> resourceReference(R.string.common_stake) + is TransactionType.StakingTransactionType.Unstake -> resourceReference(R.string.common_unstake) + is TransactionType.StakingTransactionType.Vote -> resourceReference(R.string.staking_vote) + is TransactionType.StakingTransactionType.ClaimRewards -> resourceReference(R.string.common_claim_rewards) + is TransactionType.StakingTransactionType.Withdraw -> { resourceReference(R.string.staking_withdraw) } is TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation) } @@ -119,9 +119,9 @@ internal class TxHistoryItemStateConverter( } private fun TxHistoryItem.getAmount(): String { - if (type is TransactionType.TronStakingTransactionType.Vote || - type == TransactionType.TronStakingTransactionType.ClaimRewards || - type == TransactionType.TronStakingTransactionType.Withdraw + if (type is TransactionType.StakingTransactionType.Vote || + type == TransactionType.StakingTransactionType.ClaimRewards || + type == TransactionType.StakingTransactionType.Withdraw ) { return "" } From 90b795635a683d48b1e80e59a5bfd6c13bd677d4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Nov 2024 13:34:20 +0500 Subject: [PATCH 04/40] Updated on 2026-08-14 --- .../src/main/assets/contract_methods.json | 20 +++++++++++++++++++ .../utils/SdkTransactionTypeConverter.kt | 4 ++++ .../domain/txhistory/models/TxHistoryItem.kt | 1 + ...tailsTxHistoryTransactionStateConverter.kt | 2 ++ .../converter/TxHistoryItemStateConverter.kt | 4 +++- 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/domain/legacy/src/main/assets/contract_methods.json b/domain/legacy/src/main/assets/contract_methods.json index 6a8d6dafc4..671f0b7d6b 100644 --- a/domain/legacy/src/main/assets/contract_methods.json +++ b/domain/legacy/src/main/assets/contract_methods.json @@ -181,5 +181,25 @@ "info": "stakePOL", "source": "https://etherscan.io/tx/0x96ee64ad31a045982a4776336d852af3c25dc39b6d01bea987aefd460b816773", "name": "withdrawRewardsPOL" + }, + "0x982ef0a7": { + "info": "stakeBSC", + "source": "https://bscscan.com/tx/0xd5ab62ee525f3dc2821de44c31e278f2b49a0202f4e0279e6d0fced05a034595", + "name": "delegate" + }, + "0x4d99dd16": { + "info": "stakeBSC", + "source": "https://bscscan.com/tx/0x866ba857e96051d7cea271723fccde4113c9909dccb534202a2a87ad4c56cb67", + "name": "undelegate" + }, + "0x59491871": { + "info": "stakeBSC", + "source": "https://bscscan.com/tx/0x69615650be1e8a6d94258483f4e880fc6b4d4d9ae53d967c82d847691d91dd71", + "name": "redelegate" + }, + "0xaad3ec96": { + "info": "stakeBSC", + "source": "https://bscscan.com/tx/0xfcbeac6e0a4ba5768d97d14b5115d08dda92defa8a8deee8bba34e2f0655c52b", + "name": "claim" } } diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt index fa74187f5a..4b2fb14d78 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt @@ -45,16 +45,20 @@ internal class SdkTransactionTypeConverter( "swap" -> TxHistoryItem.TransactionType.Swap "buyVoucher", "buyVoucherPOL", + "delegate", -> TxHistoryItem.TransactionType.StakingTransactionType.Stake "sellVoucher_new", "sellVoucher_newPOL", + "undelegate", -> TxHistoryItem.TransactionType.StakingTransactionType.Unstake "unstakeClaimTokens_new", "unstakeClaimTokens_newPOL", + "claim", -> TxHistoryItem.TransactionType.StakingTransactionType.Withdraw "withdrawRewards", "withdrawRewardsPOL", -> TxHistoryItem.TransactionType.StakingTransactionType.ClaimRewards + "redelegate" -> TxHistoryItem.TransactionType.StakingTransactionType.Restake null -> TxHistoryItem.TransactionType.UnknownOperation else -> TxHistoryItem.TransactionType.Operation(name = methodName.replaceFirstChar { it.titlecase() }) } diff --git a/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt index 526be8774c..f331f6a2a1 100644 --- a/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt +++ b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt @@ -46,6 +46,7 @@ data class TxHistoryItem( data object Stake : StakingTransactionType data object Unstake : StakingTransactionType data object Withdraw : StakingTransactionType + data object Restake : StakingTransactionType } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt index 91863e54cd..4091224cdc 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt @@ -52,6 +52,7 @@ internal class TokenDetailsTxHistoryTransactionStateConverter( is TransactionType.Approve -> R.drawable.ic_doc_24 is TransactionType.StakingTransactionType.Stake, is TransactionType.StakingTransactionType.Vote, + is TransactionType.StakingTransactionType.Restake, -> R.drawable.ic_transaction_history_staking_24 is TransactionType.StakingTransactionType.ClaimRewards, -> R.drawable.ic_transaction_history_claim_rewards_24 @@ -77,6 +78,7 @@ internal class TokenDetailsTxHistoryTransactionStateConverter( is TransactionType.StakingTransactionType.Vote -> resourceReference(R.string.staking_vote) is TransactionType.StakingTransactionType.ClaimRewards -> resourceReference(R.string.common_claim_rewards) is TransactionType.StakingTransactionType.Withdraw -> resourceReference(R.string.staking_withdraw) + is TransactionType.StakingTransactionType.Restake -> resourceReference(R.string.staking_restake) } private fun TxHistoryItem.extractSubtitle(): TextReference = diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt index 36ea12af34..10e1cb4f76 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt @@ -51,6 +51,7 @@ internal class TxHistoryItemStateConverter( is TransactionType.Approve -> R.drawable.ic_doc_24 is TransactionType.StakingTransactionType.Stake, is TransactionType.StakingTransactionType.Vote, + is TransactionType.StakingTransactionType.Restake, -> R.drawable.ic_transaction_history_staking_24 is TransactionType.StakingTransactionType.ClaimRewards, -> R.drawable.ic_transaction_history_claim_rewards_24 @@ -74,7 +75,8 @@ internal class TxHistoryItemStateConverter( is TransactionType.StakingTransactionType.Unstake -> resourceReference(R.string.common_unstake) is TransactionType.StakingTransactionType.Vote -> resourceReference(R.string.staking_vote) is TransactionType.StakingTransactionType.ClaimRewards -> resourceReference(R.string.common_claim_rewards) - is TransactionType.StakingTransactionType.Withdraw -> { resourceReference(R.string.staking_withdraw) } + is TransactionType.StakingTransactionType.Withdraw -> resourceReference(R.string.staking_withdraw) + is TransactionType.StakingTransactionType.Restake -> resourceReference(R.string.staking_restake) is TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation) } From 2a26369c447306e430cb79be43ef2b87d76ee48d Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Nov 2024 14:08:15 +0500 Subject: [PATCH 05/40] 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 544358e464..adbdabe422 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 544358e4640ae25402dd4186e0f244f9509c8906 +Subproject commit adbdabe422b0513640d1750f276298683d342f61 From 970440d8fdf3d8bc72ace77bb4cd74a9a8d2c793 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Nov 2024 14:49:28 +0500 Subject: [PATCH 06/40] 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 d78017f8a3..00bc1b4f7c 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -88,7 +88,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.18-849" +tangemBlockchainSdk = "release-app_5.18-853" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.18-396" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 96f9b04c8dd999b93c4903c7c3b9c0e8a469e77c Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Nov 2024 13:41:29 +0300 Subject: [PATCH 07/40] 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 8a6315a3d3..875c134863 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -88,7 +88,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.17-851" +tangemBlockchainSdk = "release-app_5.17-854" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.17-392" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From ede7143a0396dcec7d940aa04d2c9a27a91d5c1b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Nov 2024 17:01:38 +0500 Subject: [PATCH 08/40] Updated on 2026-08-14 --- core/res/src/main/res/values-de/strings.xml | 1 + core/res/src/main/res/values-ja/strings.xml | 1 + core/res/src/main/res/values-ru/strings.xml | 4 +- core/res/src/main/res/values/strings.xml | 3 + .../utils/SdkTransactionTypeConverter.kt | 20 +++--- .../domain/txhistory/models/TxHistoryItem.kt | 14 ++--- .../presentation/state/StakingNotification.kt | 7 ++- .../AmountRequirementStateTransformer.kt | 63 +++++++++++-------- .../AddStakingNotificationsTransformer.kt | 1 + .../StakingInfoNotificationsFactory.kt | 28 +++++++++ ...tailsTxHistoryTransactionStateConverter.kt | 30 ++++----- .../converter/TxHistoryItemStateConverter.kt | 30 ++++----- 12 files changed, 126 insertions(+), 76 deletions(-) diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 4313234fb9..198db71cec 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -774,6 +774,7 @@ Belohnungen Stake gesperrt Mehr staken + Gestakeder Betrag Du setzt %1$s ein und ethältst Deine Belohnung %2$s Zum Entsperren antippen Tippe zum Entsperren oder Abstimmen diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 8cdaeffe4a..d64d288ba7 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -764,6 +764,7 @@ 報酬 ステーキングはロックされています もっとステーキングする + ステーキング中の金額 %1$sをステーキングし、報酬%2$sを受け取ります タップしてロック解除 タップしてロック解除もしくは投票 diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index c600ccc00e..eb0c4ce63d 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -714,6 +714,7 @@ Имя Сумма для стейкинга должна быть не менее %s Согласно правилам сети, сумма стейкинга будет округлена до %1$sTRX. + Сумма для вывода из стейкинга будет округлена до %1$s TRX ввиду особенностей сети. Забрать средства Комиссия за стейкинг аккаунт Стейкинг аккаунт — это специальный счет, на котором хранятся застейканные токены SOL. Он создается при делегировании ваших токенов валидатору для участия в подтверждении транзакций и получении наград. За создание стейкинг аккаунта взимается небольшая комиссия, которая возвращается после завершения стейкинга. @@ -756,6 +757,7 @@ Стейкинг дает возможность вам получать %1$s. Награда будет зачисляться каждую неделю. Стейкайте безопасно и начинайте получать еженедельные награды. Получите награду за стейкинг + Оставшийся застейканный баланс будет слишком мал для вывода. Вам потребуется застейкать больше средств, чтобы достичь минимальной суммы для вывода. Стейкинг временно недоступен из-за проблем в сети. Пожалуйста, попробуйте позже. Стейкинг в сети %1$s с новым валидатором автоматически переведет ваши текущие застейканные средства на него. Реинвестируйте свои заработанные награды в вашу застейканную сумму, увеличивая потенциальный доход @@ -798,7 +800,7 @@ Отзыв Разблокировать Разблокировка - Сумма для стейкинга должна быть не менее %s + Сумма для вывода из стейкинга должна быть не менее %s Сумма превышает застейканный баланс Вывод из стейкинга Завершение стейкинга diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 3a507e68ab..10d590ce52 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -741,6 +741,8 @@ Staking allows you to earn %1$s. Your staking rewards arrive every week. Stake securely and start earning weekly rewards Earn staking rewards + Your remaining staked balance will be too low to unstake. You’ll need to stake more to meet the minimum unstake amount. + Low staked balance Staking is currently unavailable due to network conditions. Please try again later. Staking in the %1$s network with a new validator will automatically transfer all previously staked funds to this validator Reinvests your earned rewards in your staked amount, increasing potential earnings. @@ -861,6 +863,7 @@ from: %s to: %s validator: %s + The minimum transaction amount is %1$s. Try again You\'ve scanned the same card. To create a twin wallet you need to scan the card with number %d You\'ve scanned wrong twin card. Please try another one diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt index 4b2fb14d78..bdfee927b3 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt @@ -21,19 +21,19 @@ internal class SdkTransactionTypeConverter( TxHistoryItem.TransactionType.Transfer } is TransactionType.TronStakingTransactionType.FreezeBalanceV2Contract -> { - TxHistoryItem.TransactionType.StakingTransactionType.Stake + TxHistoryItem.TransactionType.Staking.Stake } is TransactionType.TronStakingTransactionType.UnfreezeBalanceV2Contract -> { - TxHistoryItem.TransactionType.StakingTransactionType.Unstake + TxHistoryItem.TransactionType.Staking.Unstake } is TransactionType.TronStakingTransactionType.VoteWitnessContract -> { - TxHistoryItem.TransactionType.StakingTransactionType.Vote(value.validatorAddress) + TxHistoryItem.TransactionType.Staking.Vote(value.validatorAddress) } is TransactionType.TronStakingTransactionType.WithdrawBalanceContract -> { - TxHistoryItem.TransactionType.StakingTransactionType.ClaimRewards + TxHistoryItem.TransactionType.Staking.ClaimRewards } is TransactionType.TronStakingTransactionType.WithdrawExpireUnfreezeContract -> { - TxHistoryItem.TransactionType.StakingTransactionType.Withdraw + TxHistoryItem.TransactionType.Staking.Withdraw } } } @@ -46,19 +46,19 @@ internal class SdkTransactionTypeConverter( "buyVoucher", "buyVoucherPOL", "delegate", - -> TxHistoryItem.TransactionType.StakingTransactionType.Stake + -> TxHistoryItem.TransactionType.Staking.Stake "sellVoucher_new", "sellVoucher_newPOL", "undelegate", - -> TxHistoryItem.TransactionType.StakingTransactionType.Unstake + -> TxHistoryItem.TransactionType.Staking.Unstake "unstakeClaimTokens_new", "unstakeClaimTokens_newPOL", "claim", - -> TxHistoryItem.TransactionType.StakingTransactionType.Withdraw + -> TxHistoryItem.TransactionType.Staking.Withdraw "withdrawRewards", "withdrawRewardsPOL", - -> TxHistoryItem.TransactionType.StakingTransactionType.ClaimRewards - "redelegate" -> TxHistoryItem.TransactionType.StakingTransactionType.Restake + -> TxHistoryItem.TransactionType.Staking.ClaimRewards + "redelegate" -> TxHistoryItem.TransactionType.Staking.Restake null -> TxHistoryItem.TransactionType.UnknownOperation else -> TxHistoryItem.TransactionType.Operation(name = methodName.replaceFirstChar { it.titlecase() }) } diff --git a/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt index f331f6a2a1..61f2278715 100644 --- a/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt +++ b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt @@ -40,13 +40,13 @@ data class TxHistoryItem( data object UnknownOperation : TransactionType data class Operation(val name: String) : TransactionType - sealed interface StakingTransactionType : TransactionType { - data class Vote(val validatorAddress: String) : StakingTransactionType - data object ClaimRewards : StakingTransactionType - data object Stake : StakingTransactionType - data object Unstake : StakingTransactionType - data object Withdraw : StakingTransactionType - data object Restake : StakingTransactionType + sealed interface Staking : TransactionType { + data class Vote(val validatorAddress: String) : Staking + data object ClaimRewards : Staking + data object Stake : Staking + data object Unstake : Staking + data object Withdraw : Staking + data object Restake : Staking } } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt index 5b02449fa4..d9fd203280 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt @@ -39,7 +39,7 @@ internal object StakingNotification { ) : NotificationUM.Warning( title = title, subtitle = subtitle, - iconResId = R.drawable.ic_alert_circle_24, + iconResId = R.drawable.img_attention_20, buttonsState = buttonsState, onCloseClick = onCloseClick, ) { @@ -47,6 +47,11 @@ internal object StakingNotification { val title: TextReference, val description: TextReference, ) : StakingNotification.Warning(title = title, subtitle = description) + + data object LowStakedBalance : StakingNotification.Warning( + title = resourceReference(R.string.staking_notification_low_staked_balance_title), + subtitle = resourceReference(R.string.staking_notification_low_staked_balance_text), + ) } sealed class Info( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt index 199707ada1..2543480a3f 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt @@ -1,5 +1,6 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount +import androidx.annotation.StringRes import androidx.compose.ui.text.input.ImeAction import com.tangem.common.extensions.isZero import com.tangem.common.ui.amountScreen.models.AmountState @@ -26,25 +27,18 @@ internal class AmountRequirementStateTransformer( private val actionType: StakingActionCommonType, ) : Transformer { override fun transform(prevState: AmountState): AmountState { - val amountRequirements = yield.args.enter.args[Yield.Args.ArgType.AMOUNT] - - return if (prevState is AmountState.Data && amountRequirements != null) { + return if (prevState is AmountState.Data) { updateWithError( prevState, actionType, - amountRequirements, ) } else { prevState } } - private fun updateWithError( - amountState: AmountState.Data, - actionType: StakingActionCommonType, - amountRequirements: AddressArgument, - ): AmountState { - val isRequirementError = isRequirementError(amountState, amountRequirements) + private fun updateWithError(amountState: AmountState.Data, actionType: StakingActionCommonType): AmountState { + val requirementError = getRequirementError(amountState) val isIntegerOnlyError = isIntegerOnlyError(amountState, actionType) val cryptoAmount = amountState.amountTextField.cryptoAmount @@ -53,14 +47,7 @@ internal class AmountRequirementStateTransformer( val errorText = when { amountState.amountTextField.isError -> amountState.amountTextField.error - isRequirementError -> resourceReference( - R.string.staking_amount_requirement_error, - wrappedList( - amountRequirements.minimum.format { - crypto(cryptoCurrencyStatus.currency) - }, - ), - ) + requirementError != null -> requirementError isIntegerOnlyError -> when (actionType) { StakingActionCommonType.Enter -> resourceReference( R.string.staking_amount_tron_integer_error, @@ -70,11 +57,11 @@ internal class AmountRequirementStateTransformer( R.string.staking_amount_tron_integer_error_unstaking, wrappedList(value), ) - else -> TODO() + else -> TextReference.EMPTY } else -> TextReference.EMPTY } - val isError = amountState.amountTextField.isError || isRequirementError + val isError = amountState.amountTextField.isError || requirementError != null return if (!errorText.isNullOrEmpty()) { amountState.copy( isPrimaryButtonEnabled = !isError, @@ -92,17 +79,25 @@ internal class AmountRequirementStateTransformer( } } - private fun isRequirementError(prevState: AmountState.Data, amountRequirements: AddressArgument): Boolean { - val amountDecimal = prevState.amountTextField.cryptoAmount.value ?: return false + private fun getRequirementError(prevState: AmountState.Data): TextReference? { + val amountDecimal = prevState.amountTextField.cryptoAmount.value ?: return null val isAlreadyErrorState = prevState.amountTextField.isError - val isAmountRequired = amountRequirements.required val isAmountZero = amountDecimal.isZero() - val isExceedsRequirements = - amountRequirements.maximum?.compareTo(amountDecimal) == -1 || - amountRequirements.minimum?.compareTo(amountDecimal) == 1 - return !isAmountZero && isAmountRequired && isExceedsRequirements && !isAlreadyErrorState + if (isAlreadyErrorState || isAmountZero) return null + + return when (actionType) { + StakingActionCommonType.Enter -> { + val enterRequirements = yield.args.enter.args[Yield.Args.ArgType.AMOUNT] + enterRequirements?.getError(amountDecimal, R.string.staking_amount_requirement_error) + } + StakingActionCommonType.Exit -> { + val exitRequirements = yield.args.exit?.args?.get(Yield.Args.ArgType.AMOUNT) + exitRequirements?.getError(amountDecimal, R.string.staking_unstake_amount_requirement_error) + } + else -> null + } } private fun isIntegerOnlyError(amountState: AmountState.Data, actionType: StakingActionCommonType): Boolean { @@ -116,6 +111,20 @@ internal class AmountRequirementStateTransformer( return isEnterOrExit && isTron && !isIntegerOnly } + private fun AddressArgument.getError(amount: BigDecimal, @StringRes errorTextRes: Int): TextReference? { + val isExceedsRequirements = maximum?.compareTo(amount) == -1 || + minimum?.compareTo(amount) == 1 + + return resourceReference( + errorTextRes, + wrappedList( + minimum.format { + crypto(cryptoCurrencyStatus.currency) + }, + ), + ).takeIf { required && isExceedsRequirements } + } + data class Data( val amountState: AmountState, val actionType: StakingActionCommonType, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt index 2f086ac68c..530e3cc24b 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/AddStakingNotificationsTransformer.kt @@ -107,6 +107,7 @@ internal class AddStakingNotificationsTransformer( notifications = this, prevState = prevState, sendingAmount = sendingAmount, + actionAmount = amountValue, feeValue = feeValue, ) }.toImmutableList() diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt index f610a6b17b..36f7acec1c 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt @@ -17,6 +17,7 @@ import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.lib.crypto.BlockchainUtils.isCosmos import com.tangem.lib.crypto.BlockchainUtils.isTron import com.tangem.utils.Provider +import com.tangem.utils.extensions.isZero import com.tangem.utils.extensions.orZero import java.math.BigDecimal @@ -26,12 +27,22 @@ internal class StakingInfoNotificationsFactory( private val isSubtractAvailable: Boolean, ) { + /** + * @param notifications current notification to display + * @param prevState current screen state to update + * @param sendingAmount amount being transferred from user account + * @param actionAmount any amount being transferred or used action + * @param feeValue fee amount payed from user account + */ fun addInfoNotifications( notifications: MutableList, prevState: StakingUiState, sendingAmount: BigDecimal, + actionAmount: BigDecimal, feeValue: BigDecimal, ) = with(notifications) { + addStakingLowBalanceNotification(prevState, actionAmount) + when (prevState.actionType) { StakingActionCommonType.Enter -> addEnterInfoNotifications(sendingAmount, feeValue) StakingActionCommonType.Exit -> addExitInfoNotifications() @@ -149,4 +160,21 @@ internal class StakingInfoNotificationsFactory( add(StakingNotification.Info.StakeEntireBalance) } } + + private fun MutableList.addStakingLowBalanceNotification( + prevState: StakingUiState, + actionAmount: BigDecimal, + ) { + if (prevState.actionType != StakingActionCommonType.Exit) return + + val maxAmount = prevState.balanceState?.cryptoAmount ?: return + val exitRequirements = yield.args.exit?.args?.get(Yield.Args.ArgType.AMOUNT) ?: return + + val amountLeft = maxAmount - actionAmount + val isNotEnoughLeft = !amountLeft.isZero() && amountLeft < exitRequirements.minimum + + if (exitRequirements.required && isNotEnoughLeft) { + add(StakingNotification.Warning.LowStakedBalance) + } + } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt index 4091224cdc..360126d91d 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt @@ -50,14 +50,14 @@ internal class TokenDetailsTxHistoryTransactionStateConverter( } else { when (type) { is TransactionType.Approve -> R.drawable.ic_doc_24 - is TransactionType.StakingTransactionType.Stake, - is TransactionType.StakingTransactionType.Vote, - is TransactionType.StakingTransactionType.Restake, + is TransactionType.Staking.Stake, + is TransactionType.Staking.Vote, + is TransactionType.Staking.Restake, -> R.drawable.ic_transaction_history_staking_24 - is TransactionType.StakingTransactionType.ClaimRewards, + is TransactionType.Staking.ClaimRewards, -> R.drawable.ic_transaction_history_claim_rewards_24 - is TransactionType.StakingTransactionType.Unstake, - is TransactionType.StakingTransactionType.Withdraw, + is TransactionType.Staking.Unstake, + is TransactionType.Staking.Withdraw, -> R.drawable.ic_transaction_history_unstaking_24 is TransactionType.Operation, is TransactionType.Swap, @@ -73,12 +73,12 @@ internal class TokenDetailsTxHistoryTransactionStateConverter( is TransactionType.Swap -> resourceReference(R.string.common_swap) is TransactionType.Transfer -> resourceReference(R.string.common_transfer) is TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation) - is TransactionType.StakingTransactionType.Stake -> resourceReference(R.string.common_stake) - is TransactionType.StakingTransactionType.Unstake -> resourceReference(R.string.common_unstake) - is TransactionType.StakingTransactionType.Vote -> resourceReference(R.string.staking_vote) - is TransactionType.StakingTransactionType.ClaimRewards -> resourceReference(R.string.common_claim_rewards) - is TransactionType.StakingTransactionType.Withdraw -> resourceReference(R.string.staking_withdraw) - is TransactionType.StakingTransactionType.Restake -> resourceReference(R.string.staking_restake) + is TransactionType.Staking.Stake -> resourceReference(R.string.common_stake) + is TransactionType.Staking.Unstake -> resourceReference(R.string.common_unstake) + is TransactionType.Staking.Vote -> resourceReference(R.string.staking_vote) + is TransactionType.Staking.ClaimRewards -> resourceReference(R.string.common_claim_rewards) + is TransactionType.Staking.Withdraw -> resourceReference(R.string.staking_withdraw) + is TransactionType.Staking.Restake -> resourceReference(R.string.staking_restake) } private fun TxHistoryItem.extractSubtitle(): TextReference = @@ -121,9 +121,9 @@ internal class TokenDetailsTxHistoryTransactionStateConverter( } private fun TxHistoryItem.getAmount(): String { - if (type is TransactionType.StakingTransactionType.Vote || - type == TransactionType.StakingTransactionType.ClaimRewards || - type == TransactionType.StakingTransactionType.Withdraw + if (type is TransactionType.Staking.Vote || + type == TransactionType.Staking.ClaimRewards || + type == TransactionType.Staking.Withdraw ) { return "" } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt index 10e1cb4f76..a85ca99eab 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt @@ -49,14 +49,14 @@ internal class TxHistoryItemStateConverter( } else { when (type) { is TransactionType.Approve -> R.drawable.ic_doc_24 - is TransactionType.StakingTransactionType.Stake, - is TransactionType.StakingTransactionType.Vote, - is TransactionType.StakingTransactionType.Restake, + is TransactionType.Staking.Stake, + is TransactionType.Staking.Vote, + is TransactionType.Staking.Restake, -> R.drawable.ic_transaction_history_staking_24 - is TransactionType.StakingTransactionType.ClaimRewards, + is TransactionType.Staking.ClaimRewards, -> R.drawable.ic_transaction_history_claim_rewards_24 - is TransactionType.StakingTransactionType.Unstake, - is TransactionType.StakingTransactionType.Withdraw, + is TransactionType.Staking.Unstake, + is TransactionType.Staking.Withdraw, -> R.drawable.ic_transaction_history_unstaking_24 is TransactionType.Operation, is TransactionType.Swap, @@ -71,12 +71,12 @@ internal class TxHistoryItemStateConverter( is TransactionType.Operation -> stringReference(type.name) is TransactionType.Swap -> resourceReference(R.string.common_swap) is TransactionType.Transfer -> resourceReference(R.string.common_transfer) - is TransactionType.StakingTransactionType.Stake -> resourceReference(R.string.common_stake) - is TransactionType.StakingTransactionType.Unstake -> resourceReference(R.string.common_unstake) - is TransactionType.StakingTransactionType.Vote -> resourceReference(R.string.staking_vote) - is TransactionType.StakingTransactionType.ClaimRewards -> resourceReference(R.string.common_claim_rewards) - is TransactionType.StakingTransactionType.Withdraw -> resourceReference(R.string.staking_withdraw) - is TransactionType.StakingTransactionType.Restake -> resourceReference(R.string.staking_restake) + is TransactionType.Staking.Stake -> resourceReference(R.string.common_stake) + is TransactionType.Staking.Unstake -> resourceReference(R.string.common_unstake) + is TransactionType.Staking.Vote -> resourceReference(R.string.staking_vote) + is TransactionType.Staking.ClaimRewards -> resourceReference(R.string.common_claim_rewards) + is TransactionType.Staking.Withdraw -> resourceReference(R.string.staking_withdraw) + is TransactionType.Staking.Restake -> resourceReference(R.string.staking_restake) is TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation) } @@ -121,9 +121,9 @@ internal class TxHistoryItemStateConverter( } private fun TxHistoryItem.getAmount(): String { - if (type is TransactionType.StakingTransactionType.Vote || - type == TransactionType.StakingTransactionType.ClaimRewards || - type == TransactionType.StakingTransactionType.Withdraw + if (type is TransactionType.Staking.Vote || + type == TransactionType.Staking.ClaimRewards || + type == TransactionType.Staking.Withdraw ) { return "" } From 65957fa5fe605769d6366f618939922ebd414c93 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Nov 2024 16:26:05 +0300 Subject: [PATCH 09/40] 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 875c134863..d7fbe88d22 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -88,7 +88,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.17-854" +tangemBlockchainSdk = "release-app_5.17-855" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.17-392" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From c44c0b19a298842b2f4d7e5bbb9f41140df55c54 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Nov 2024 17:19:57 +0300 Subject: [PATCH 10/40] 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 00bc1b4f7c..2e813b0545 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -88,7 +88,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.18-853" +tangemBlockchainSdk = "release-app_5.18-856" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.18-396" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 0a70defb2b1d84a732a5a0ec9803666d85448ba3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 4 Nov 2024 15:10:19 +0400 Subject: [PATCH 11/40] Updated on 2026-08-14 --- .../feature/wallet/presentation/wallet/ui/WalletScreen.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 8dc7d7fd8f..c1d3d9cd26 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -179,7 +179,11 @@ private fun WalletContent( contentPadding = contentPadding, horizontalAlignment = Alignment.CenterHorizontally, ) { - item(key = "WalletsList" + state.selectedWalletIndex, contentType = "WalletsList") { + item( + // !!! Type of the key should be saveable via Bundle on Android !!! + key = state.wallets.map { it.walletCardState.id.stringValue }, + contentType = state.wallets.map { it.walletCardState.id }, + ) { WalletsList( lazyListState = walletsListState, wallets = state.wallets.map(WalletState::walletCardState).toImmutableList(), From d80dbf1d5b37d21664686697f7e95ddc0eb13a7d Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Nov 2024 11:44:22 +0500 Subject: [PATCH 12/40] Updated on 2026-08-14 --- .../presentation/state/StakingStateController.kt | 2 +- .../impl/presentation/state/StakingStateRouter.kt | 13 +++++++++---- .../impl/presentation/state/StakingUiState.kt | 2 +- .../transformers/SetInitialDataStateTransformer.kt | 2 +- .../presentation/ui/StakingConfirmationContent.kt | 9 ++++++--- .../staking/impl/presentation/ui/StakingScreen.kt | 2 ++ 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt index 71d3c67900..4150cef942 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt @@ -75,7 +75,7 @@ internal class StakingStateController @Inject constructor( walletName = "", cryptoCurrencyName = "", cryptoCurrencySymbol = "", - cryptoCurrencyNetworkId = "", + cryptoCurrencyBlockchainId = "", currentStep = StakingStep.InitialInfo, initialInfoState = StakingStates.InitialInfoState.Empty(), amountState = AmountState.Empty(), diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt index 48823d364f..d7e8d24c25 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt @@ -26,7 +26,7 @@ internal class StakingStateRouter( StakingStep.InitialInfo -> when (stateController.value.actionType) { StakingActionCommonType.Enter -> showAmount() // TODO staking [REDACTED_TASK_KEY] support solana multisize hashes signing - StakingActionCommonType.Exit -> if (isSolana(stateController.value.cryptoCurrencyNetworkId)) { + StakingActionCommonType.Exit -> if (isSolana(stateController.value.cryptoCurrencyBlockchainId)) { showConfirmation() } else { showAmount() @@ -54,10 +54,15 @@ internal class StakingStateRouter( StakingStep.Amount, -> showInitial() StakingStep.Confirmation -> { - if (uiState.actionType != StakingActionCommonType.Enter) { - showInitial() - } else { + val isEnter = uiState.actionType == StakingActionCommonType.Enter + val isExit = uiState.actionType == StakingActionCommonType.Exit + + // TODO staking [REDACTED_TASK_KEY] support solana multisize hashes signing + val isSolana = isSolana(uiState.cryptoCurrencyBlockchainId) + if (isEnter || isExit && !isSolana) { showAmount() + } else { + showInitial() } } StakingStep.Validators -> showConfirmation() 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 470048621d..c8c0be08e6 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 @@ -29,7 +29,7 @@ internal data class StakingUiState( val walletName: String, val cryptoCurrencyName: String, val cryptoCurrencySymbol: String, - val cryptoCurrencyNetworkId: String, + val cryptoCurrencyBlockchainId: String, val currentStep: StakingStep, val initialInfoState: StakingStates.InitialInfoState, val amountState: AmountState, 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 e4cc55d35c..7a17d070d9 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 @@ -75,7 +75,7 @@ internal class SetInitialDataStateTransformer( title = TextReference.EMPTY, cryptoCurrencyName = cryptoCurrency.name, cryptoCurrencySymbol = cryptoCurrency.symbol, - cryptoCurrencyNetworkId = cryptoCurrency.network.id.value, + cryptoCurrencyBlockchainId = cryptoCurrency.network.id.value, clickIntents = clickIntents, currentStep = StakingStep.InitialInfo, initialInfoState = createInitialInfoState(), 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 ad11c31650..2df8cdd525 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 @@ -32,6 +32,7 @@ import com.tangem.features.staking.impl.presentation.ui.block.StakingFeeBlock import com.tangem.features.staking.impl.presentation.ui.block.ValidatorBlock import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents +@Suppress("LongParameterList") @Composable internal fun StakingConfirmationContent( amountState: AmountState, @@ -39,9 +40,10 @@ internal fun StakingConfirmationContent( validatorState: StakingStates.ValidatorState, clickIntents: StakingClickIntents, type: StakingActionCommonType, + isSolana: Boolean, // TODO staking [REDACTED_TASK_KEY] support solana multisize hashes signing ) { if (state !is StakingStates.ConfirmationState.Data) return - val isEnterAction = type == StakingActionCommonType.Enter + val isAmountEditable = type == StakingActionCommonType.Enter || type == StakingActionCommonType.Exit && !isSolana val isTransactionSent = state.innerState == InnerConfirmationStakingState.COMPLETED val isTransactionInProgress = state.notifications.any { it is StakingNotification.Warning.TransactionInProgress } Column( @@ -63,8 +65,8 @@ internal fun StakingConfirmationContent( } AmountBlock( amountState = amountState, - isClickDisabled = !isEnterAction || isTransactionSent || isTransactionInProgress, - isEditingDisabled = !isEnterAction && state.innerState != InnerConfirmationStakingState.COMPLETED, + isClickDisabled = !isAmountEditable || isTransactionSent || isTransactionInProgress, + isEditingDisabled = !isAmountEditable && state.innerState != InnerConfirmationStakingState.COMPLETED, onClick = clickIntents::onPrevClick, ) ValidatorBlock( @@ -89,6 +91,7 @@ private fun Preview_StakingConfirmationContent() { validatorState = ValidatorStatePreviewData.validatorState, clickIntents = StakingClickIntentsStub, type = StakingActionCommonType.Enter, + isSolana = false, ) } } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt index 21be700364..8e6b03d04c 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt @@ -28,6 +28,7 @@ import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingAc import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingInfoBottomSheetConfig import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingActionSelectorBottomSheet import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingInfoBottomSheet +import com.tangem.lib.crypto.BlockchainUtils.isSolana import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map @@ -170,6 +171,7 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M validatorState = uiState.validatorState, clickIntents = uiState.clickIntents, type = uiState.actionType, + isSolana = isSolana(uiState.cryptoCurrencyBlockchainId), ) StakingStep.RestakeValidator, StakingStep.Validators, From 9544abbccefe53d07cd088866ef363706e8bc111 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Nov 2024 11:45:18 +0500 Subject: [PATCH 13/40] Updated on 2026-08-14 --- .../AmountRequirementStateTransformer.kt | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt index 2543480a3f..cb0b0e31e9 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountRequirementStateTransformer.kt @@ -5,7 +5,6 @@ import androidx.compose.ui.text.input.ImeAction import com.tangem.common.extensions.isZero import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.isNullOrEmpty import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.format.bigdecimal.crypto @@ -57,26 +56,22 @@ internal class AmountRequirementStateTransformer( R.string.staking_amount_tron_integer_error_unstaking, wrappedList(value), ) - else -> TextReference.EMPTY + else -> null } - else -> TextReference.EMPTY + else -> null } val isError = amountState.amountTextField.isError || requirementError != null - return if (!errorText.isNullOrEmpty()) { - amountState.copy( - isPrimaryButtonEnabled = !isError, - amountTextField = amountState.amountTextField.copy( - isError = isError, - isWarning = isIntegerOnlyError, - error = errorText, - keyboardOptions = amountState.amountTextField.keyboardOptions.copy( - imeAction = ImeAction.None, - ), + return amountState.copy( + isPrimaryButtonEnabled = !isError, + amountTextField = amountState.amountTextField.copy( + isError = isError, + isWarning = isIntegerOnlyError, + error = errorText ?: amountState.amountTextField.error, + keyboardOptions = amountState.amountTextField.keyboardOptions.copy( + imeAction = ImeAction.None, ), - ) - } else { - amountState - } + ), + ) } private fun getRequirementError(prevState: AmountState.Data): TextReference? { From fc70ce1462d1b4bf9d47088ee33e72ef77d0639f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Nov 2024 14:31:53 +0500 Subject: [PATCH 14/40] Updated on 2026-08-14 --- .../converters/AmountStateConverter.kt | 9 +++--- .../presentation/state/SendStateFactory.kt | 2 +- .../transformers/SetAmountDataTransformer.kt | 32 +++++++++++-------- .../SetInitialDataStateTransformer.kt | 25 ++++++++------- .../viewmodel/StakingViewModel.kt | 23 ------------- 5 files changed, 37 insertions(+), 54 deletions(-) diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt index e2adb46d11..d463a635bc 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt @@ -12,8 +12,8 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format -import com.tangem.core.ui.utils.BigDecimalFormatter.formatFiatAmount import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.Provider @@ -26,7 +26,7 @@ import kotlinx.collections.immutable.persistentListOf * * @property clickIntents amount screen clicks * @property appCurrencyProvider selected app currency provider - * @property maxEnterAmountProvider max enter amount data provider + * @property maxEnterAmount max enter amount data * @property cryptoCurrencyStatusProvider current cryptocurrency status provider * @property iconStateConverter currency icon converter */ @@ -34,7 +34,7 @@ class AmountStateConverter( private val clickIntents: AmountScreenClickIntents, private val appCurrencyProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, - private val maxEnterAmountProvider: Provider, + private val maxEnterAmount: MaxEnterAmount, private val iconStateConverter: CryptoCurrencyToIconStateConverter, ) : Converter { @@ -47,10 +47,9 @@ class AmountStateConverter( } override fun convert(value: AmountParameters): AmountState { - val maxEnterAmount = maxEnterAmountProvider() val appCurrency = appCurrencyProvider() val status = cryptoCurrencyStatusProvider() - val fiat = formatFiatAmount(maxEnterAmount.fiatAmount, appCurrency.code, appCurrency.symbol) + val fiat = maxEnterAmount.fiatAmount.format { fiat(appCurrency.code, appCurrency.symbol) } val crypto = maxEnterAmount.amount.format { crypto(status.currency) } val noFeeRate = status.value.fiatRate.isNullOrZero() diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt index 0b69329371..030e8e81c7 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt @@ -44,7 +44,7 @@ internal class SendStateFactory( appCurrencyProvider = appCurrencyProvider, iconStateConverter = iconStateConverter, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, - maxEnterAmountProvider = Provider { maxEnterAmountConverter.convert(cryptoCurrencyStatusProvider()) }, + maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatusProvider()), ) } private val recipientStateConverter by lazy(LazyThreadSafetyMode.NONE) { diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt index 434b40908c..b9226a4214 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt @@ -11,7 +11,7 @@ import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet import com.tangem.features.staking.impl.R -import com.tangem.features.staking.impl.presentation.state.* +import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents import com.tangem.utils.Provider import com.tangem.utils.transformer.Transformer @@ -21,30 +21,36 @@ internal class SetAmountDataTransformer( private val cryptoCurrencyStatusProvider: Provider, private val userWalletProvider: Provider, private val appCurrencyProvider: Provider, - private val maxEnterAmountProvider: Provider, ) : Transformer { private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter) - private val amountStateConverter by lazy(LazyThreadSafetyMode.NONE) { - AmountStateConverter( - clickIntents = clickIntents, - cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, - appCurrencyProvider = appCurrencyProvider, - iconStateConverter = iconStateConverter, - maxEnterAmountProvider = maxEnterAmountProvider, - ) - } - override fun transform(prevState: StakingUiState): StakingUiState { val title = if (prevState.actionType == StakingActionCommonType.Exit) { resourceReference(R.string.staking_staked_amount) } else { stringReference(userWalletProvider().name) } + val cryptoBalanceValue = cryptoCurrencyStatusProvider().value + val (amount, fiatAmount) = if (prevState.actionType != StakingActionCommonType.Enter) { + prevState.balanceState?.cryptoAmount to prevState.balanceState?.fiatAmount + } else { + cryptoBalanceValue.amount to cryptoBalanceValue.fiatAmount + } + val maxEnterAmount = MaxEnterAmount( + amount = amount, + fiatAmount = fiatAmount, + fiatRate = cryptoBalanceValue.fiatRate, + ) return prevState.copy( - amountState = amountStateConverter.convert( + amountState = AmountStateConverter( + clickIntents = clickIntents, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + appCurrencyProvider = appCurrencyProvider, + iconStateConverter = iconStateConverter, + maxEnterAmount = maxEnterAmount, + ).convert( AmountParameters( title = title, value = "", 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 7a17d070d9..ef2d4058db 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 @@ -41,21 +41,10 @@ internal class SetInitialDataStateTransformer( private val userWalletProvider: Provider, private val appCurrencyProvider: Provider, private val balancesToShowProvider: Provider>, - private val maxEnterAmountProvider: Provider, ) : Transformer { private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter) - private val amountStateConverter by lazy(LazyThreadSafetyMode.NONE) { - AmountStateConverter( - clickIntents = clickIntents, - cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, - appCurrencyProvider = appCurrencyProvider, - iconStateConverter = iconStateConverter, - maxEnterAmountProvider = maxEnterAmountProvider, - ) - } - private val rewardsValidatorStateConverter by lazy(LazyThreadSafetyMode.NONE) { RewardsValidatorStateConverter(cryptoCurrencyStatusProvider, appCurrencyProvider, yield) } @@ -211,7 +200,19 @@ internal class SetInitialDataStateTransformer( } private fun createInitialAmountState(): AmountState { - return amountStateConverter.convert( + val cryptoBalanceValue = cryptoCurrencyStatusProvider().value + val maxEnterAmount = MaxEnterAmount( + amount = cryptoBalanceValue.amount, + fiatAmount = cryptoBalanceValue.fiatAmount, + fiatRate = cryptoBalanceValue.fiatRate, + ) + return AmountStateConverter( + clickIntents = clickIntents, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + appCurrencyProvider = appCurrencyProvider, + iconStateConverter = iconStateConverter, + maxEnterAmount = maxEnterAmount, + ).convert( AmountParameters( title = stringReference(userWalletProvider().name), value = "", 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 a15947fe0e..f8dff3cf4a 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 @@ -11,7 +11,6 @@ import com.tangem.common.routing.AppRoute import com.tangem.common.routing.bundle.unbundle import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer import com.tangem.common.ui.amountScreen.models.AmountState -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount import com.tangem.common.ui.bottomsheet.permission.state.ApproveType import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig import com.tangem.common.ui.notifications.NotificationUM @@ -154,14 +153,6 @@ internal class StakingViewModel @Inject constructor( ).getOrElse { emptyList() } } - private val maxEnterAmount: MaxEnterAmount - get() = - MaxEnterAmount( - amount = uiState.value.balanceState?.cryptoAmount, - fiatAmount = uiState.value.balanceState?.fiatAmount, - fiatRate = cryptoCurrencyStatus.value.fiatRate, - ) - private var isInitialInfoAnalyticSent: Boolean = false private val balanceUpdater by lazy(LazyThreadSafetyMode.NONE) { @@ -233,18 +224,6 @@ internal class StakingViewModel @Inject constructor( } override fun onNextClick(balanceState: BalanceState?) { - if (value.currentStep == StakingStep.InitialInfo && balanceState == null) { - stateController.update( - SetConfirmationStateInitTransformer( - isEnter = true, - isExplicitExit = false, - balanceState = null, - cryptoCurrencyStatus = cryptoCurrencyStatus, - stakingApproval = stakingApproval, - stakingAllowance = stakingAllowance, - ), - ) - } stakingStateRouter.onNextClick() } @@ -913,7 +892,6 @@ internal class StakingViewModel @Inject constructor( userWalletProvider = Provider { userWallet }, appCurrencyProvider = Provider { appCurrency }, balancesToShowProvider = Provider { balancesToShow }, - maxEnterAmountProvider = Provider { maxEnterAmount }, ), SetConfirmationStateEmptyTransformer, ) @@ -947,7 +925,6 @@ internal class StakingViewModel @Inject constructor( cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, userWalletProvider = Provider { userWallet }, appCurrencyProvider = Provider { appCurrency }, - maxEnterAmountProvider = Provider { maxEnterAmount }, ), AmountChangeStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, From 2e984fce4c8a57b8f324152dbc40b0819a9a105f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Nov 2024 14:32:24 +0500 Subject: [PATCH 15/40] Updated on 2026-08-14 --- core/res/src/main/res/values-de/strings.xml | 2 ++ core/res/src/main/res/values-es/strings.xml | 3 ++ core/res/src/main/res/values-fr/strings.xml | 3 ++ 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 | 2 ++ core/res/src/main/res/values/strings.xml | 8 +++++ .../tangem/feature/swap/ui/StateBuilder.kt | 32 ++++++++++++------- 8 files changed, 44 insertions(+), 11 deletions(-) diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 198db71cec..ac5448db47 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -812,7 +812,9 @@ Tausche mehr Token zu besseren Kursen direkt in deiner Brieftasche. Neuer Swap-Anbieter verfügbar! Der Betrag umfasst:\n- Gebühr des Dienstanbieters\n- Netzgebühr für die Rücksendung von %s von der Vermittlungsstelle an die Adresse des Nutzers. + Der Betrag beinhaltet:\n• Honorar des Dienstleisters\n• Netzwerkgebühr für das Senden %1$s von der Börse zurück an die Adresse des Benutzers. \n\n Provider-Slippage kann bis zu %2$s Der Betrag enthält die Gebühren des Dienstleisters. + Der Betrag beinhaltet die Gebühr des Dienstleisters. \n\nProvider-Slippage kann bis zu %s Anbieter-Slippage kann bis zu %s ausfallen. Gebühren Alle dezentralen Börsen benötigen Genehmigungen, um zu verhindern, dass intelligente Verträge ohne Ihre Erlaubnis auf Ihre Geldbörse zugreifen. Smart Contracts können nicht auf Ihre Token zugreifen, wenn Sie nicht zustimmen. Indem Sie Ihre Token \"freischalten\", ermächtigen Sie den 1-Zoll-Smart-Contract, sie auszugeben. Die Miner des Netzwerks erhalten eine (von Ihnen bezahlte) Gasgebühr, um diese Aktion in der Blockchain aufzuzeichnen. Sie können Ihre Token tauschen, nachdem Sie Ihre Zustimmung gegeben haben. diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml index a8fc4365c8..ac89c7802b 100644 --- a/core/res/src/main/res/values-es/strings.xml +++ b/core/res/src/main/res/values-es/strings.xml @@ -808,7 +808,9 @@ Intercambie más tokens a mejores tasas directamente en su billetera. ¡Nuevo proveedor de intercambio disponible! El monto incluye:\n• Tarifas del proveedor de servicios\n• Tarifas de red por enviar %s desde el intercambio a la dirección del usuario. + El monto incluye:\n• tarifas del proveedor de servicios\n• tarifas de red por enviar %1$s desde el intercambio a la dirección del usuario. \n\nEl slippage del proveedor puede alcanzar el %2$s El importe incluye los honorarios del proveedor de servicios. + El importe incluye las tarifas del proveedor de servicios. \n\nEl slippage del proveedor puede alcanzar el %s Tarifa Todos los exchanges descentralizados requieren aprobaciones para evitar que los smart contracts accedan a su billetera sin su permiso. Por diseño, los smart contracts no pueden acceder a tus tokens a menos que lo apruebes. Al \"desbloquear\" sus tokens, autoriza al smart contract de 1-inch a gastarlos. Los mineros de la red reciben una tarifa de gas (pagada por voz) para registrar esta acción en la blockchain. Puede intercambiar su token después de dar la aprobación. Aprobar @@ -856,6 +858,7 @@ desde: %s a: %s validador: %s + El monto mínimo para realizar esta transacción es %1$s. Inténtelo de nuevo Ha escaneado la misma tarjeta. Para crear una billetera gemela, necesite escanear la tarjeta con no. %d Ha escaneado la tarjeta gemela incorrecta. Por favor, intente con otra diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 42eb2abac8..d2a3505ef9 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -808,7 +808,9 @@ Échangez plus de jetons à de meilleurs taux directement dans votre portefeuille. Nouveau fournisseur d\'échange disponible ! Le montant comprend :\n• les frais du fournisseur de services\n• les frais de réseau pour l\'envoi de %s depuis l\'échange vers l\'adresse de l\'utilisateur. + Le montant comprend :\n• les frais du fournisseur de services\n• les frais de réseau pour l\'envoi de %1$s depuis l\'échange vers l\'adresse de l\'utilisateur. \n\nLe slippage du fournisseur peut atteindre %2$s Le montant comprend les frais du fournisseur de services. + Le montant comprend les frais du fournisseur de services. \n\nLe slippage du fournisseur peut atteindre %s Frais Tous les échanges décentralisés nécessitent des approbations pour empêcher les smart contracts d\'accéder à votre portefeuille sans votre permission. Par conception, les smart contracts ne peuvent pas accéder à vos jetons sans votre approbation. En « déverrouillant » vos jetons, vous autorisez le smart contract 1-inch à les dépenser. Les mineurs du réseau reçoivent des frais de gaz (payés par vous) pour enregistrer cette action sur la blockchain. Vous pouvez échanger votre jeton après avoir donné votre approbation. Approuver @@ -856,6 +858,7 @@ de : %s à : %s validateur : %s + Le montant minimum pour effectuer cette transaction est %1$s. Réessayez Vous avez scanné la même carte. Pour créer un portefeuille jumeau, vous devez scanner la carte portant le numéro %d Vous avez scanné une mauvaise carte jumelle. S\'il vous plaît, essayez-en un autre diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index d64d288ba7..b2998a24d0 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -802,7 +802,9 @@ より多くのトークンをより良いレートで、ウォレット内にて直接交換します。 新しいスワッププロバイダーが利用可能になりました! この金額には以下が含まれます:\n- サービスプロバイダーの手数料\n- 取引所からユーザーのアドレスに%s を送り返すためのネットワーク手数料。 + 金額には以下が含まれます: \n • サービス プロバイダーの手数料\n • 取引所からユーザーのアドレスに%1$sを送金するためのネットワーク手数料。 \n\nプロバイダーのスリッページは最大%2$sです この金額には、サービスプロバイダーの手数料が含まれています。 + 金額にはサービスプロバイダーの手数料が含まれます。 \n\nプロバイダーのスリッページは最大%s です プロバイダーのスリッページは最大%sです。 手数料 すべての分散型取引所は、スマートコントラクトがあなたの許可なくウォレットにアクセスするのを防ぐために承認を必要とします。設計上、スマートコントラクトは承認なしでトークンにアクセスできません。トークンを「ロック解除」することで、あなたは1-inchのスマートコントラクトがトークンを使うことを承認します。ネットワークのマイナーは、このアクションをブロックチェーンに記録するためのガス料金(あなたが支払う)を受け取ります。承認後、トークンを交換することができます。 diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index eb0c4ce63d..e73b23b485 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -827,7 +827,9 @@ Обменивайте больше токенов по лучшим курсам прямо в вашем кошельке. Новый провайдер обмена! В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя. + В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %1$s от биржи обратно на адрес пользователя \n\nПроскальзывание провайдера составляет до %2$s В сумму включена комиссия провайдера сервиса. + В сумму включена комиссия провайдера сервиса. \n\nПроскальзывание провайдера составляет до %s Проскальзывание провайдера составляет до %s. Информация Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен. @@ -876,6 +878,7 @@ от: %s на: %s валидатор: %s + Минимальная сумма транзакции равна %1$s. Попробовать снова Вы отсканировали ту же карту. Для создания twin-кошелька вам необходимо отсканировать карту с номером %d Вы отсканировали не ту twin-карту. Пожалуйста, попробуйте отсканировать другую 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 18f9697aaa..2697aa11d3 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -827,7 +827,9 @@ Обмінюйте більше токенів за вигіднішим курсом прямо у своєму гаманці. З\'явився новий провайдер обмінів! Сума включає: \n• комісію постачальника послуг\n• комісію мережі за відправлення %s з біржі назад на адресу користувача. + У суму входить:\n- комісія провайдера\n- мережева комісія за відправку %1$s з біржі назад на адресу користувача. \n\nПроскакування провайдера становить до %2$s Сума включає комісію постачальника послуг. + Сума включає комісію провайдера послуг. \n\nПроскакування провайдера до %s Комісії Всі децентралізовані біржі вимагають схвалення, щоб запобігти доступу смарт-контрактів до вашого гаманця без вашого дозволу. За задумом смарт-контракти не можуть отримати доступ до ваших токенів без вашого схвалення. \"Розблоковуючи\" свої токени, ви дозволяєте смарт-контракту 1inch витрачати ваші активи. Майнери мережі отримують плату за газ (сплачену вами), щоб зафіксувати цю дію в блокчейні. Ви можете обміняти свій токен після того, як дасте дозвіл. Підтвердити diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 10d590ce52..c2f7e4c756 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -551,10 +551,16 @@ Access code restore Identical cards Access code + Search by country + Unavailable + Search by currency Pay with Our services are not available in this country Change or confirm it Your residence has been identified as + Residence + Please select the correct country to ensure accurate payment options and services. + Settings Via Group By balance @@ -814,7 +820,9 @@ Exchange more tokens at better rates directly in your wallet. New Swap Provider Available! The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address. + The amount includes:\n• service provider\'s fee\n• network fee for sending %1$s from the exchange back to the user\'s address. \n\nProvider slippage is up to %2$s The amount includes the service provider\'s fee. + The amount includes the service provider\'s fee. \n\nProvider slippage is up to %s Provider slippage is up to %s. Information All decentralized exchanges require approvals to prevent smart contracts from accessing your wallet without your permission. By design, smart contracts can\'t access your tokens unless you approve. By \"unlocking\" your tokens, you authorize the 1-inch smart contract to spend them. The network\'s miners receive a gas fee (paid by you) to record this action on the blockchain. You can swap your token after giving approval. diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index c270a8d837..d5f2d4d92b 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -1094,10 +1094,20 @@ internal class StateBuilder( provider: SwapProvider, onDismiss: () -> Unit, ): SwapStateHolder { + val slippage = provider.slippage?.let { "${it.parseBigDecimal(1)}$PERCENT" } val combinedMessage = buildList { when (provider.type) { ExchangeProviderType.CEX -> { - add(resourceReference(R.string.swapping_alert_cex_description, wrappedList(token))) + if (slippage != null) { + add( + resourceReference( + id = R.string.swapping_alert_cex_description_with_slippage, + formatArgs = wrappedList(token, slippage), + ), + ) + } else { + add(resourceReference(R.string.swapping_alert_cex_description, wrappedList(token))) + } } ExchangeProviderType.DEX, ExchangeProviderType.DEX_BRIDGE, @@ -1106,18 +1116,18 @@ internal class StateBuilder( add(resourceReference(R.string.swapping_high_price_impact_description)) add(stringReference("\n\n")) } - add(resourceReference(R.string.swapping_alert_dex_description)) + if (slippage != null) { + add( + resourceReference( + id = R.string.swapping_alert_dex_description_with_slippage, + formatArgs = wrappedList(token, slippage), + ), + ) + } else { + add(resourceReference(R.string.swapping_alert_dex_description, wrappedList(token))) + } } } - provider.slippage?.let { slippage -> - add(stringReference("\n\n")) - add( - resourceReference( - R.string.swapping_alert_slippage_description, - wrappedList("${slippage.parseBigDecimal(1)}$PERCENT"), - ), - ) - } } return uiState.copy( event = triggeredEvent( From 404bcc011abfac7511e21d92e9e4736726b9c7e3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Nov 2024 14:27:45 +0200 Subject: [PATCH 16/40] Updated on 2026-08-14 --- .../main/java/com/tangem/utils/extensions/BigDecimalExt.kt | 5 ++++- domain/staking/build.gradle.kts | 1 + .../domain/staking/InvalidatePendingTransactionsUseCase.kt | 3 ++- .../presentation/state/helpers/StakingBalanceUpdater.kt | 6 +++--- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/core/utils/src/main/java/com/tangem/utils/extensions/BigDecimalExt.kt b/core/utils/src/main/java/com/tangem/utils/extensions/BigDecimalExt.kt index 31a31d6704..45f2966872 100644 --- a/core/utils/src/main/java/com/tangem/utils/extensions/BigDecimalExt.kt +++ b/core/utils/src/main/java/com/tangem/utils/extensions/BigDecimalExt.kt @@ -12,4 +12,7 @@ fun BigDecimal.isZero(): Boolean = this.compareTo(BigDecimal.ZERO) == 0 fun BigDecimal.isPositive(): Boolean = this.signum() == 1 /** Removes trailing zeros and returns plain [String] */ -fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString() \ No newline at end of file +fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString() + +/** Compares two [BigDecimal] numbers */ +infix fun BigDecimal.isEqualTo(other: BigDecimal): Boolean = this.compareTo(other) == 0 \ No newline at end of file diff --git a/domain/staking/build.gradle.kts b/domain/staking/build.gradle.kts index 9832ec7010..a0caf393f8 100644 --- a/domain/staking/build.gradle.kts +++ b/domain/staking/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { api(projects.domain.staking.models) api(projects.domain.core) api(projects.core.analytics) + api(projects.core.utils) implementation(deps.kotlin.serialization) implementation(deps.jodatime) 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 1b0f6b9128..dbb7c578d4 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 @@ -7,6 +7,7 @@ import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.model.stakekit.action.StakingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionType import com.tangem.domain.staking.repositories.StakingErrorResolver +import com.tangem.utils.extensions.isEqualTo import java.util.UUID class InvalidatePendingTransactionsUseCase( @@ -75,7 +76,7 @@ class InvalidatePendingTransactionsUseCase( private fun modifyBalancesByStatus(balances: MutableList, action: StakingAction, type: BalanceType) { val index = balances.indexOfFirst { - !it.isPending && it.amount == action.amount && it.type == type + !it.isPending && it.amount isEqualTo action.amount && it.type == type } if (index != -1) { diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt index e10d32b8a1..c84db7f3ea 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt @@ -59,9 +59,6 @@ internal class StakingBalanceUpdater @AssistedInject constructor( suspend fun partialUpdate() { coroutineScope { listOf( - async { - updateStakeBalance() - }, async { updateNetworkStatuses(delay = 0) }, @@ -78,6 +75,9 @@ internal class StakingBalanceUpdater @AssistedInject constructor( async { updateStakeBalance() }, + async { + updateProcessingActions() + }, ).awaitAll() } } From 7ed060a37dac4cca27a623fdebf1f19426eefd23 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Nov 2024 00:41:17 +0500 Subject: [PATCH 17/40] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 12 ++++++ .../converters/AmountReduceByTransformer.kt | 29 +++++++++++-- .../converters/AmountReduceToTransformer.kt | 31 ++++++++++++-- .../converters/AmountStateConverter.kt | 4 +- .../converters/MaxEnterAmountConverter.kt | 10 ++--- .../field/AmountFieldChangeTransformer.kt | 34 +++++++++++---- .../AmountFieldSetMaxAmountTransformer.kt | 42 ++++++++++++++----- .../models/EnterAmountBoundary.kt | 22 ++++++++++ .../ui/amountScreen/models/MaxEnterAmount.kt | 9 ---- .../ui/amountScreen/utils/AmountUtils.kt | 13 +++--- .../common/ui/notifications/NotificationUM.kt | 8 ++++ .../ui/notifications/NotificationsFactory.kt | 14 +++++++ .../DefaultCurrencyChecksRepository.kt | 10 +++++ .../model/warnings/CryptoCurrencyCheck.kt | 1 + .../domain/tokens/GetCurrencyCheckUseCase.kt | 2 + .../GetMinimumTransactionAmountSyncUseCase.kt | 26 ++++++++++++ .../repository/CurrencyChecksRepository.kt | 3 ++ .../state/amount/AmountStateFactory.kt | 6 +++ .../amount/SendAmountReduceByConverter.kt | 8 +++- .../amount/SendAmountReduceToConverter.kt | 8 +++- .../state/confirm/SendNotificationFactory.kt | 6 +++ .../fields/SendAmountFieldChangeConverter.kt | 13 +++++- .../SendAmountFieldMaxAmountConverter.kt | 9 +++- .../presentation/viewmodel/SendViewModel.kt | 25 ++++++++++- .../transformers/SetAmountDataTransformer.kt | 4 +- .../SetInitialDataStateTransformer.kt | 4 +- .../amount/AmountChangeStateTransformer.kt | 12 ++++-- .../amount/AmountMaxValueStateTransformer.kt | 12 ++++-- .../amount/AmountReduceByStateTransformer.kt | 8 +++- .../amount/AmountReduceToStateTransformer.kt | 8 +++- .../viewmodel/StakingViewModel.kt | 24 ++++++++++- 31 files changed, 351 insertions(+), 66 deletions(-) create mode 100644 common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/EnterAmountBoundary.kt delete mode 100644 common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/MaxEnterAmount.kt create mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index 3b3adab9bf..0776b43400 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -273,6 +273,18 @@ internal object TokensDomainModule { ) } + @Provides + @Singleton + fun provideGetMinimumTransactionAmountSyncUseCase( + currencyChecksRepository: CurrencyChecksRepository, + dispatchers: CoroutineDispatcherProvider, + ): GetMinimumTransactionAmountSyncUseCase { + return GetMinimumTransactionAmountSyncUseCase( + currencyChecksRepository = currencyChecksRepository, + dispatchers = dispatchers, + ) + } + @Provides @Singleton fun provideIsCryptoCurrencyCoinCouldHideUseCase( diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt index 0b9f4c1a5d..4fbf0d991a 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceByTransformer.kt @@ -4,10 +4,15 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.KeyboardType import com.tangem.common.ui.R import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.amountScreen.utils.checkExceedBalance import com.tangem.common.ui.amountScreen.utils.getFiatValue import com.tangem.common.ui.amountScreen.utils.getKeyboardAction +import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.isNullOrZero @@ -22,6 +27,7 @@ import java.math.BigDecimal */ class AmountReduceByTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: ReduceByData, ) : Transformer { @@ -47,22 +53,37 @@ class AmountReduceByTransformer( val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue val isExceedBalance = checkValue.checkExceedBalance(maxEnterAmount, amountTextField) + val isLessThanMinimumIfProvided = minimumTransactionAmount?.amount?.let { decimalCryptoValue < it } ?: false val isZero = if (amountTextField.isFiatValue) { decimalFiatValue.isNullOrZero() } else { decimalCryptoValue.isNullOrZero() } + val isCheckFailed = isExceedBalance || isLessThanMinimumIfProvided return prevState.copy( - isPrimaryButtonEnabled = !isExceedBalance && !isZero, + isPrimaryButtonEnabled = !isZero && !isCheckFailed, amountTextField = amountTextField.copy( value = cryptoValue, fiatValue = fiatValue, - isError = isExceedBalance, - error = resourceReference(R.string.send_validation_amount_exceeds_balance), + isError = isCheckFailed, + error = when { + isExceedBalance -> resourceReference(R.string.send_validation_amount_exceeds_balance) + isLessThanMinimumIfProvided -> { + val minimumAmount = minimumTransactionAmount + ?.amount + ?.format { crypto(cryptoCurrencyStatus.currency) } + .orEmpty() + resourceReference( + R.string.transfer_notification_invalid_minimum_transaction_amount_text, + wrappedList(minimumAmount, minimumAmount), + ) + } + else -> TextReference.EMPTY + }, cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue), fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue), keyboardOptions = KeyboardOptions( - imeAction = getKeyboardAction(isExceedBalance, decimalCryptoValue), + imeAction = getKeyboardAction(isCheckFailed, decimalCryptoValue), keyboardType = KeyboardType.Number, ), ), diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt index 74f6e66852..7e5cf0ab8d 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountReduceToTransformer.kt @@ -4,11 +4,17 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.KeyboardType import com.tangem.common.ui.R import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.amountScreen.utils.checkExceedBalance import com.tangem.common.ui.amountScreen.utils.getFiatValue import com.tangem.common.ui.amountScreen.utils.getKeyboardAction +import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal +import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.isNullOrZero import com.tangem.utils.transformer.Transformer @@ -22,6 +28,7 @@ import java.math.BigDecimal */ class AmountReduceToTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: BigDecimal, ) : Transformer { private val maxEnterAmountConverter = MaxEnterAmountConverter() @@ -34,6 +41,7 @@ class AmountReduceToTransformer( val fiatDecimals = amountTextField.fiatAmount.decimals val cryptoValue = value.parseBigDecimal(cryptoDecimals) + val decimalCryptoValue = cryptoValue.parseToBigDecimal(cryptoDecimals) val (fiatValue, decimalFiatValue) = cryptoValue.getFiatValue( fiatRate = cryptoCurrencyStatus.value.fiatRate, isFiatValue = false, @@ -44,18 +52,33 @@ class AmountReduceToTransformer( val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue val isExceedBalance = checkValue.checkExceedBalance(maxEnterAmount, amountTextField) + val isLessThanMinimumIfProvided = minimumTransactionAmount?.amount?.let { decimalCryptoValue < it } ?: false val isZero = if (amountTextField.isFiatValue) decimalFiatValue.isNullOrZero() else value.isNullOrZero() + val isCheckFailed = isExceedBalance || isLessThanMinimumIfProvided return prevState.copy( - isPrimaryButtonEnabled = !isExceedBalance && !isZero, + isPrimaryButtonEnabled = !isZero && !isCheckFailed, amountTextField = amountTextField.copy( value = cryptoValue, fiatValue = fiatValue, - isError = isExceedBalance, - error = resourceReference(R.string.send_validation_amount_exceeds_balance), + isError = isCheckFailed, + error = when { + isExceedBalance -> resourceReference(R.string.send_validation_amount_exceeds_balance) + isLessThanMinimumIfProvided -> { + val minimumAmount = minimumTransactionAmount + ?.amount + ?.format { crypto(cryptoCurrencyStatus.currency) } + .orEmpty() + resourceReference( + R.string.transfer_notification_invalid_minimum_transaction_amount_text, + wrappedList(minimumAmount, minimumAmount), + ) + } + else -> TextReference.EMPTY + }, cryptoAmount = amountTextField.cryptoAmount.copy(value = value), fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue), keyboardOptions = KeyboardOptions( - imeAction = getKeyboardAction(isExceedBalance, value), + imeAction = getKeyboardAction(isCheckFailed, decimalCryptoValue), keyboardType = KeyboardType.Number, ), ), diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt index d463a635bc..f91822b7b7 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt @@ -6,7 +6,7 @@ import com.tangem.common.ui.amountScreen.converters.field.AmountFieldConverter import com.tangem.common.ui.amountScreen.models.AmountParameters import com.tangem.common.ui.amountScreen.models.AmountSegmentedButtonsConfig import com.tangem.common.ui.amountScreen.models.AmountState -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference @@ -34,7 +34,7 @@ class AmountStateConverter( private val clickIntents: AmountScreenClickIntents, private val appCurrencyProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, - private val maxEnterAmount: MaxEnterAmount, + private val maxEnterAmount: EnterAmountBoundary, private val iconStateConverter: CryptoCurrencyToIconStateConverter, ) : Converter { diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt index 1b23b64181..dce1957361 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/MaxEnterAmountConverter.kt @@ -1,16 +1,16 @@ package com.tangem.common.ui.amountScreen.converters -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.converter.Converter /** - * Converts [CryptoCurrencyStatus] to [MaxEnterAmount] + * Converts [CryptoCurrencyStatus] to [EnterAmountBoundary] */ -class MaxEnterAmountConverter : Converter { +class MaxEnterAmountConverter : Converter { - override fun convert(value: CryptoCurrencyStatus): MaxEnterAmount { - return MaxEnterAmount( + override fun convert(value: CryptoCurrencyStatus): EnterAmountBoundary { + return EnterAmountBoundary( amount = value.value.amount, fiatAmount = value.value.fiatAmount, fiatRate = value.value.fiatRate, diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt index b51fa49ad2..392fa817ba 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt @@ -5,14 +5,18 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import com.tangem.common.ui.R import com.tangem.common.ui.amountScreen.models.AmountState -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.amountScreen.utils.checkExceedBalance import com.tangem.common.ui.amountScreen.utils.getCryptoValue import com.tangem.common.ui.amountScreen.utils.getFiatValue import com.tangem.common.ui.amountScreen.utils.getKeyboardAction import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseToBigDecimal +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.utils.isNullOrZero import com.tangem.utils.transformer.Transformer import java.math.BigDecimal @@ -24,7 +28,9 @@ import java.math.BigDecimal * @property value amount value */ class AmountFieldChangeTransformer( - private val maxEnterAmount: MaxEnterAmount, + private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val maxEnterAmount: EnterAmountBoundary, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: String, ) : Transformer { @@ -52,23 +58,37 @@ class AmountFieldChangeTransformer( val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue val isExceedBalance = checkValue.checkExceedBalance(maxEnterAmount, amountTextField) + val isLessThanMinimumIfProvided = minimumTransactionAmount?.amount?.let { decimalCryptoValue < it } ?: false val isZero = if (amountTextField.isFiatValue) { decimalFiatValue.isNullOrZero() } else { decimalCryptoValue.isNullOrZero() } + val isCheckFailed = isExceedBalance || isLessThanMinimumIfProvided return prevState.copy( - isPrimaryButtonEnabled = !isExceedBalance && !isZero, + isPrimaryButtonEnabled = !isZero && !isCheckFailed, amountTextField = amountTextField.copy( value = cryptoValue, fiatValue = fiatValue, - isError = isExceedBalance, - error = resourceReference(R.string.send_validation_amount_exceeds_balance).takeIf { isExceedBalance } - ?: TextReference.EMPTY, + isError = isCheckFailed, + error = when { + isExceedBalance -> resourceReference(R.string.send_validation_amount_exceeds_balance) + isLessThanMinimumIfProvided -> { + val minimumAmount = minimumTransactionAmount + ?.amount + ?.format { crypto(cryptoCurrencyStatus.currency) } + .orEmpty() + resourceReference( + R.string.transfer_notification_invalid_minimum_transaction_amount_text, + wrappedList(minimumAmount, minimumAmount), + ) + } + else -> TextReference.EMPTY + }, cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue), fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue), keyboardOptions = KeyboardOptions( - imeAction = getKeyboardAction(isExceedBalance, decimalCryptoValue), + imeAction = getKeyboardAction(isCheckFailed, decimalCryptoValue), keyboardType = KeyboardType.Number, ), ), diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt index 16c67d20f8..990e027cdf 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldSetMaxAmountTransformer.kt @@ -1,12 +1,19 @@ package com.tangem.common.ui.amountScreen.converters.field import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType +import com.tangem.common.ui.R import com.tangem.common.ui.amountScreen.models.AmountState -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary +import com.tangem.common.ui.amountScreen.utils.getKeyboardAction +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.utils.parseBigDecimal -import com.tangem.utils.isNullOrZero +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.utils.extensions.isZero import com.tangem.utils.transformer.Transformer import java.math.RoundingMode @@ -16,7 +23,9 @@ import java.math.RoundingMode * @property maxAmount maximum enter amount */ class AmountFieldSetMaxAmountTransformer( - private val maxAmount: MaxEnterAmount, + private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val maxAmount: EnterAmountBoundary, + private val minAmount: EnterAmountBoundary?, ) : Transformer { override fun transform(prevState: AmountState): AmountState { @@ -29,22 +38,35 @@ class AmountFieldSetMaxAmountTransformer( val decimalCryptoValue = maxAmount.amount val decimalFiatValue = maxAmount.fiatAmount - if (decimalCryptoValue.isNullOrZero()) return prevState + if (decimalCryptoValue == null || decimalCryptoValue.isZero()) return prevState - val isDoneActionEnabled = !decimalCryptoValue.isNullOrZero() - val cryptoValue = decimalCryptoValue?.parseBigDecimal(cryptoDecimals).orEmpty() + val cryptoValue = decimalCryptoValue.parseBigDecimal(cryptoDecimals) val fiatValue = decimalFiatValue?.parseBigDecimal(fiatDecimals, roundingMode = RoundingMode.HALF_UP).orEmpty() + val isLessThanMinimumIfProvided = minAmount?.amount?.let { decimalCryptoValue < it } ?: false return prevState.copy( - isPrimaryButtonEnabled = true, + isPrimaryButtonEnabled = !isLessThanMinimumIfProvided, amountTextField = amountTextField.copy( isValuePasted = true, value = cryptoValue, fiatValue = fiatValue, - isError = false, + isError = isLessThanMinimumIfProvided, + error = when { + isLessThanMinimumIfProvided -> { + val minimumAmount = minAmount + ?.amount + ?.format { crypto(cryptoCurrencyStatus.currency) } + .orEmpty() + resourceReference( + R.string.transfer_notification_invalid_minimum_transaction_amount_text, + wrappedList(minimumAmount, minimumAmount), + ) + } + else -> TextReference.EMPTY + }, cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue), fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue), keyboardOptions = KeyboardOptions( - imeAction = if (isDoneActionEnabled) ImeAction.Done else ImeAction.None, + imeAction = getKeyboardAction(isLessThanMinimumIfProvided, decimalCryptoValue), keyboardType = KeyboardType.Number, ), ), diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/EnterAmountBoundary.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/EnterAmountBoundary.kt new file mode 100644 index 0000000000..bc21ab5104 --- /dev/null +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/EnterAmountBoundary.kt @@ -0,0 +1,22 @@ +package com.tangem.common.ui.amountScreen.models + +import java.math.BigDecimal + +data class EnterAmountBoundary( + val amount: BigDecimal? = null, + val fiatAmount: BigDecimal? = null, + val fiatRate: BigDecimal? = null, +) { + constructor( + amount: BigDecimal? = null, + fiatRate: BigDecimal? = null, + ) : this( + amount = amount, + fiatAmount = if (amount != null && fiatRate != null) { + amount * fiatRate + } else { + null + }, + fiatRate = fiatRate, + ) +} \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/MaxEnterAmount.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/MaxEnterAmount.kt deleted file mode 100644 index e9a5f88238..0000000000 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/models/MaxEnterAmount.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.common.ui.amountScreen.models - -import java.math.BigDecimal - -data class MaxEnterAmount( - val amount: BigDecimal? = null, - val fiatAmount: BigDecimal? = null, - val fiatRate: BigDecimal? = null, -) \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/utils/AmountUtils.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/utils/AmountUtils.kt index 3f20ba474c..fe59dbf317 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/utils/AmountUtils.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/utils/AmountUtils.kt @@ -2,10 +2,10 @@ package com.tangem.common.ui.amountScreen.utils import androidx.compose.ui.text.input.ImeAction import com.tangem.common.ui.amountScreen.models.AmountFieldModel -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal -import com.tangem.utils.isNullOrZero +import com.tangem.utils.extensions.isZero import java.math.BigDecimal import java.math.RoundingMode @@ -36,7 +36,10 @@ internal fun String.getFiatValue( } } -internal fun String.checkExceedBalance(maxEnterAmount: MaxEnterAmount, amountTextField: AmountFieldModel): Boolean { +internal fun String.checkExceedBalance( + maxEnterAmount: EnterAmountBoundary, + amountTextField: AmountFieldModel, +): Boolean { val currencyCryptoAmount = maxEnterAmount.amount ?: BigDecimal.ZERO val currencyFiatAmount = maxEnterAmount.fiatAmount ?: BigDecimal.ZERO val fiatDecimal = parseToBigDecimal(amountTextField.fiatAmount.decimals) @@ -48,8 +51,8 @@ internal fun String.checkExceedBalance(maxEnterAmount: MaxEnterAmount, amountTex } } -internal fun getKeyboardAction(isExceedBalance: Boolean, decimalCryptoValue: BigDecimal) = - if (!isExceedBalance && !decimalCryptoValue.isNullOrZero()) { +internal fun getKeyboardAction(isCheckFailed: Boolean, decimalCryptoValue: BigDecimal) = + if (!isCheckFailed && !decimalCryptoValue.isZero()) { ImeAction.Done } else { ImeAction.None diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt index 8ca24539b5..858fdccb46 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationUM.kt @@ -47,6 +47,14 @@ sealed class NotificationUM(val config: NotificationConfig) { ), ) + data class MinimumSendAmountError(val amount: String) : Error( + title = resourceReference(R.string.send_notification_invalid_amount_title), + subtitle = resourceReference( + R.string.transfer_notification_invalid_minimum_transaction_amount_text, + wrappedList(amount, amount), + ), + ) + data class TransactionLimitError( val cryptoCurrency: String, val utxoLimit: String, diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt index 5ba4b5f528..359d6dd9c0 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt @@ -75,6 +75,20 @@ object NotificationsFactory { } } + fun MutableList.addMinimumAmountErrorNotification( + minimumSendAmount: BigDecimal?, + sendingAmount: BigDecimal, + cryptoCurrency: CryptoCurrency, + ) { + if (minimumSendAmount != null && minimumSendAmount > sendingAmount) { + add( + NotificationUM.Error.MinimumSendAmountError( + amount = minimumSendAmount.format { crypto(cryptoCurrency) }, + ), + ) + } + } + fun MutableList.addTransactionLimitErrorNotification( utxoLimit: UtxoAmountLimit?, cryptoCurrency: CryptoCurrency, diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt index ea56a8adbf..b156d85caf 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt @@ -2,6 +2,7 @@ package com.tangem.data.tokens.repository import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider import com.tangem.blockchain.common.FeeResourceAmountProvider +import com.tangem.blockchain.common.MinimumSendAmountProvider import com.tangem.blockchain.common.ReserveAmountProvider import com.tangem.blockchain.common.UtxoAmountLimitProvider import com.tangem.data.tokens.converters.UtxoConverter @@ -43,6 +44,15 @@ internal class DefaultCurrencyChecksRepository( return if (manager is ReserveAmountProvider) manager.getReserveAmount() else null } + override suspend fun getMinimumSendAmount(userWalletId: UserWalletId, network: Network): BigDecimal? { + val manager = walletManagersFacade.getOrCreateWalletManager( + userWalletId = userWalletId, + network = network, + ) + + return if (manager is MinimumSendAmountProvider) manager.getMinimumSendAmount() else null + } + override suspend fun getFeeResourceAmount(userWalletId: UserWalletId, network: Network): CurrencyAmount? { val manager = walletManagersFacade.getOrCreateWalletManager( userWalletId = userWalletId, diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt index 780c6c48fe..dae8238b02 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyCheck.kt @@ -6,6 +6,7 @@ import java.math.BigDecimal data class CryptoCurrencyCheck( val dustValue: BigDecimal?, val reserveAmount: BigDecimal?, + val minimumSendAmount: BigDecimal?, val existentialDeposit: BigDecimal?, val utxoAmountLimit: UtxoAmountLimit?, val isAccountFunded: Boolean, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt index aa488b2671..ba9e837248 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyCheckUseCase.kt @@ -24,6 +24,7 @@ class GetCurrencyCheckUseCase( val network = currencyStatus.currency.network val dustValue = currencyChecksRepository.getDustValue(userWalletId, network) val reserveAmount = currencyChecksRepository.getReserveAmount(userWalletId, network) + val minimumSendAmount = currencyChecksRepository.getMinimumSendAmount(userWalletId, network) val existentialDeposit = currencyChecksRepository.getExistentialDeposit(userWalletId, network) val isAccountFunded = recipientAddress?.let { currencyChecksRepository.checkIfAccountFunded( @@ -46,6 +47,7 @@ class GetCurrencyCheckUseCase( CryptoCurrencyCheck( dustValue = dustValue, reserveAmount = reserveAmount, + minimumSendAmount = minimumSendAmount, existentialDeposit = existentialDeposit, utxoAmountLimit = utxoAmountLimit, isAccountFunded = isAccountFunded, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt new file mode 100644 index 0000000000..73aaeb8c0d --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt @@ -0,0 +1,26 @@ +package com.tangem.domain.tokens + +import arrow.core.Either +import arrow.core.raise.either +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.repository.CurrencyChecksRepository +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext +import java.math.BigDecimal + +class GetMinimumTransactionAmountSyncUseCase( + private val currencyChecksRepository: CurrencyChecksRepository, + private val dispatchers: CoroutineDispatcherProvider, +) { + + suspend operator fun invoke( + userWalletId: UserWalletId, + cryptoCurrencyStatus: CryptoCurrencyStatus, + ): Either = withContext(dispatchers.io) { + either { + val cryptoCurrency = cryptoCurrencyStatus.currency + currencyChecksRepository.getMinimumSendAmount(userWalletId, cryptoCurrency.network) + } + } +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt index 0b5745a983..4ff37c6420 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt @@ -20,6 +20,9 @@ interface CurrencyChecksRepository { /** Returns reserve amount which is required to create an account */ suspend fun getReserveAmount(userWalletId: UserWalletId, network: Network): BigDecimal? + /** Returns minimum send transaction amount */ + suspend fun getMinimumSendAmount(userWalletId: UserWalletId, network: Network): BigDecimal? + /** Returns a fee resource amount available and max for paying fees in several blockchains */ suspend fun getFeeResourceAmount(userWalletId: UserWalletId, network: Network): CurrencyAmount? diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt index 204918b99b..9b8d11fb41 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt @@ -1,6 +1,7 @@ package com.tangem.features.send.impl.presentation.state.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.StateRouter @@ -16,6 +17,7 @@ internal class AmountStateFactory( private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val minimumTransactionAmountProvider: Provider, ) { private val amountFieldChangeConverter by lazy(LazyThreadSafetyMode.NONE) { @@ -23,6 +25,7 @@ internal class AmountStateFactory( stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + minimumTransactionAmountProvider = minimumTransactionAmountProvider, ) } private val amountFieldMaxAmountConverter by lazy(LazyThreadSafetyMode.NONE) { @@ -30,6 +33,7 @@ internal class AmountStateFactory( stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + minimumTransactionAmountProvider = minimumTransactionAmountProvider, ) } @@ -51,6 +55,7 @@ internal class AmountStateFactory( stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + minimumTransactionAmountProvider = minimumTransactionAmountProvider, ) } private val amountReduceToConverter by lazy { @@ -58,6 +63,7 @@ internal class AmountStateFactory( stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + minimumTransactionAmountProvider = minimumTransactionAmountProvider, ) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceByConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceByConverter.kt index 0468cd8c82..c0e28102e7 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceByConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceByConverter.kt @@ -1,6 +1,7 @@ package com.tangem.features.send.impl.presentation.state.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.StateRouter @@ -11,6 +12,7 @@ internal class SendAmountReduceByConverter( private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val minimumTransactionAmountProvider: Provider, ) : Converter { override fun convert(value: AmountReduceByTransformer.ReduceByData): SendUiState { @@ -23,7 +25,11 @@ internal class SendAmountReduceByConverter( sendState = state.sendState?.copy( reduceAmountBy = value.reduceAmountBy, ), - amountState = AmountReduceByTransformer(cryptoCurrencyStatusProvider(), value).transform(amountState), + amountState = AmountReduceByTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatusProvider(), + minimumTransactionAmount = minimumTransactionAmountProvider(), + value = value, + ).transform(amountState), ) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt index 1bb5518063..ea1caaf180 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt @@ -1,6 +1,7 @@ package com.tangem.features.send.impl.presentation.state.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceToTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.StateRouter @@ -12,6 +13,7 @@ internal class SendAmountReduceToConverter( private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val minimumTransactionAmountProvider: Provider, ) : Converter { override fun convert(value: BigDecimal): SendUiState { @@ -21,7 +23,11 @@ internal class SendAmountReduceToConverter( return state.copyWrapped( isEditState = isEditState, - amountState = AmountReduceToTransformer(cryptoCurrencyStatusProvider(), value).transform(amountState), + amountState = AmountReduceToTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatusProvider(), + minimumTransactionAmount = minimumTransactionAmountProvider(), + value = value, + ).transform(amountState), ) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt index bfe558339a..9975712433 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt @@ -11,6 +11,7 @@ import com.tangem.common.ui.notifications.NotificationsFactory.addExceedsBalance import com.tangem.common.ui.notifications.NotificationsFactory.addExistentialWarningNotification import com.tangem.common.ui.notifications.NotificationsFactory.addFeeCoverageNotification import com.tangem.common.ui.notifications.NotificationsFactory.addFeeUnreachableNotification +import com.tangem.common.ui.notifications.NotificationsFactory.addMinimumAmountErrorNotification import com.tangem.common.ui.notifications.NotificationsFactory.addReserveAmountErrorNotification import com.tangem.common.ui.notifications.NotificationsFactory.addTransactionLimitErrorNotification import com.tangem.common.ui.notifications.NotificationsFactory.addValidateTransactionNotifications @@ -192,6 +193,11 @@ internal class SendNotificationFactory( cryptoCurrency = currency, isAccountFunded = currencyCheck.isAccountFunded, ) + addMinimumAmountErrorNotification( + minimumSendAmount = currencyCheck.minimumSendAmount, + sendingAmount = sendingAmount, + cryptoCurrency = currency, + ) } private suspend fun MutableList.addWarningNotifications( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt index c4f28c469e..ea7e502aec 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt @@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.fields import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.StateRouter @@ -12,21 +13,29 @@ internal class SendAmountFieldChangeConverter( private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val minimumTransactionAmountProvider: Provider, ) : Converter { private val maxEnterAmountConverter = MaxEnterAmountConverter() override fun convert(value: String): SendUiState { val state = currentStateProvider() + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() val isEditState = stateRouterProvider().isEditState val amountState = state.getAmountState(isEditState) - val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatusProvider()) + val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatus) + val minimumTransactionAmount = minimumTransactionAmountProvider() return state.copyWrapped( isEditState = isEditState, sendState = state.sendState?.copy(reduceAmountBy = null), - amountState = AmountFieldChangeTransformer(maxEnterAmount, value).transform(amountState), + amountState = AmountFieldChangeTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxEnterAmount = maxEnterAmount, + minimumTransactionAmount = minimumTransactionAmount, + value = value, + ).transform(amountState), ) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt index 6e071d34d8..882365d726 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt @@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.fields import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter import com.tangem.common.ui.amountScreen.converters.field.AmountFieldSetMaxAmountTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.StateRouter @@ -13,6 +14,7 @@ internal class SendAmountFieldMaxAmountConverter( private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val minimumTransactionAmountProvider: Provider, ) : Converter { private val maxEnterAmountConverter = MaxEnterAmountConverter() @@ -27,11 +29,16 @@ internal class SendAmountFieldMaxAmountConverter( if (decimalCryptoValue.isNullOrZero()) return state val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatus) + val minimumTransactionAmount = minimumTransactionAmountProvider() return state.copyWrapped( isEditState = isEditState, sendState = state.sendState?.copy(reduceAmountBy = null), - amountState = AmountFieldSetMaxAmountTransformer(maxEnterAmount).transform(amountState), + amountState = AmountFieldSetMaxAmountTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxAmount = maxEnterAmount, + minAmount = minimumTransactionAmount, + ).transform(amountState), ) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt index 468482bb95..14b94f1d08 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt @@ -12,6 +12,7 @@ import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.routing.AppRoute import com.tangem.common.routing.bundle.unbundle import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.utils.parseBigDecimal @@ -59,6 +60,7 @@ import com.tangem.features.send.impl.presentation.state.recipient.RecipientSendF import com.tangem.lib.crypto.BlockchainUtils import com.tangem.utils.Provider import com.tangem.utils.coroutines.* +import com.tangem.utils.extensions.orZero import com.tangem.utils.extensions.stripZeroPlainString import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.* @@ -78,6 +80,7 @@ internal class SendViewModel @Inject constructor( private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getWalletsUseCase: GetWalletsUseCase, private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase, + private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase, private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase, private val getNetworkAddressesUseCase: GetNetworkAddressesUseCase, private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, @@ -153,6 +156,7 @@ internal class SendViewModel @Inject constructor( stateRouterProvider = Provider { stateRouter }, currentStateProvider = Provider { uiState.value }, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, + minimumTransactionAmountProvider = Provider { minimumTransactionAmount }, ) private val feeStateFactory = FeeStateFactory( @@ -212,6 +216,7 @@ internal class SendViewModel @Inject constructor( private var isTapHelpPreviewEnabled: Boolean = false private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull() private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null + private var minimumTransactionAmount: EnterAmountBoundary? = null private var balanceJobHolder = JobHolder() private var balanceHidingJobHolder = JobHolder() @@ -295,6 +300,7 @@ internal class SendViewModel @Inject constructor( onDataLoaded( currencyStatus = cryptoCurrencyStatus, feeCurrencyStatus = getFeeCurrencyStatusSync(cryptoCurrencyStatus, isMultiCurrency), + minTransactionAmount = getMinimumTransactionAmount(cryptoCurrencyStatus), ) }, ifLeft = { showErrorAlert() }, @@ -336,6 +342,18 @@ internal class SendViewModel @Inject constructor( } } + private suspend fun getMinimumTransactionAmount(cryptoCurrencyStatus: CryptoCurrencyStatus): EnterAmountBoundary? { + return getMinimumTransactionAmountSyncUseCase( + userWalletId = userWalletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + ).getOrNull()?.let { + EnterAmountBoundary( + amount = it, + fiatRate = cryptoCurrencyStatus.value.fiatRate.orZero(), + ) + } + } + private fun createSelectedAppCurrencyFlow(): StateFlow { return getSelectedAppCurrencyUseCase() .map { maybeAppCurrency -> @@ -348,9 +366,14 @@ internal class SendViewModel @Inject constructor( ) } - private fun onDataLoaded(currencyStatus: CryptoCurrencyStatus, feeCurrencyStatus: CryptoCurrencyStatus?) { + private fun onDataLoaded( + currencyStatus: CryptoCurrencyStatus, + feeCurrencyStatus: CryptoCurrencyStatus?, + minTransactionAmount: EnterAmountBoundary?, + ) { cryptoCurrencyStatus = currencyStatus feeCryptoCurrencyStatus = feeCurrencyStatus + minimumTransactionAmount = minTransactionAmount subscribeOnQRScannerResult() when { uiState.value.sendState?.isSuccess == true -> return diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt index b9226a4214..532054c768 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetAmountDataTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers import com.tangem.common.ui.amountScreen.converters.AmountStateConverter import com.tangem.common.ui.amountScreen.models.AmountParameters -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference @@ -37,7 +37,7 @@ internal class SetAmountDataTransformer( } else { cryptoBalanceValue.amount to cryptoBalanceValue.fiatAmount } - val maxEnterAmount = MaxEnterAmount( + val maxEnterAmount = EnterAmountBoundary( amount = amount, fiatAmount = fiatAmount, fiatRate = cryptoBalanceValue.fiatRate, 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 ef2d4058db..6825aeb4f6 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 @@ -4,7 +4,7 @@ import com.tangem.common.extensions.remove import com.tangem.common.ui.amountScreen.converters.AmountStateConverter import com.tangem.common.ui.amountScreen.models.AmountParameters import com.tangem.common.ui.amountScreen.models.AmountState -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.components.list.RoundedListWithDividersItemData import com.tangem.core.ui.extensions.* @@ -201,7 +201,7 @@ internal class SetInitialDataStateTransformer( private fun createInitialAmountState(): AmountState { val cryptoBalanceValue = cryptoCurrencyStatusProvider().value - val maxEnterAmount = MaxEnterAmount( + val maxEnterAmount = EnterAmountBoundary( amount = cryptoBalanceValue.amount, fiatAmount = cryptoBalanceValue.fiatAmount, fiatRate = cryptoBalanceValue.fiatRate, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt index cd74d47116..41fdc707cf 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountChangeStateTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -11,6 +11,7 @@ import com.tangem.utils.transformer.Transformer internal class AmountChangeStateTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: String, private val yield: Yield, ) : Transformer { @@ -20,7 +21,7 @@ internal class AmountChangeStateTransformer( override fun transform(prevState: StakingUiState): StakingUiState { val actionType = prevState.actionType val maxEnterAmount = if (actionType == StakingActionCommonType.Exit) { - MaxEnterAmount( + EnterAmountBoundary( amount = prevState.balanceState?.cryptoAmount, fiatAmount = prevState.balanceState?.fiatAmount, fiatRate = cryptoCurrencyStatus.value.fiatRate, @@ -29,7 +30,12 @@ internal class AmountChangeStateTransformer( maxEnterAmountConverter.convert(cryptoCurrencyStatus) } - val updatedAmountState = AmountFieldChangeTransformer(maxEnterAmount, value).transform(prevState.amountState) + val updatedAmountState = AmountFieldChangeTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxEnterAmount = maxEnterAmount, + minimumTransactionAmount = minimumTransactionAmount, + value = value, + ).transform(prevState.amountState) return prevState.copy( amountState = AmountRequirementStateTransformer( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt index 034398c337..db2865b4ec 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountMaxValueStateTransformer.kt @@ -2,7 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter import com.tangem.common.ui.amountScreen.converters.field.AmountFieldSetMaxAmountTransformer -import com.tangem.common.ui.amountScreen.models.MaxEnterAmount +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -11,6 +11,7 @@ import com.tangem.utils.transformer.Transformer internal class AmountMaxValueStateTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val actionType: StakingActionCommonType, private val yield: Yield, ) : Transformer { @@ -19,7 +20,7 @@ internal class AmountMaxValueStateTransformer( override fun transform(prevState: StakingUiState): StakingUiState { val maxEnterAmount = if (actionType == StakingActionCommonType.Exit) { - MaxEnterAmount( + EnterAmountBoundary( amount = prevState.balanceState?.cryptoAmount, fiatAmount = prevState.balanceState?.fiatAmount, fiatRate = cryptoCurrencyStatus.value.fiatRate, @@ -28,8 +29,11 @@ internal class AmountMaxValueStateTransformer( maxEnterAmountConverter.convert(cryptoCurrencyStatus) } - val updatedAmountState = AmountFieldSetMaxAmountTransformer(maxEnterAmount) - .transform(prevState.amountState) + val updatedAmountState = AmountFieldSetMaxAmountTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxAmount = maxEnterAmount, + minAmount = minimumTransactionAmount, + ).transform(prevState.amountState) return prevState.copy( amountState = AmountRequirementStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt index 91468a21bd..f5e92f66ec 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceByStateTransformer.kt @@ -2,18 +2,24 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.transformer.Transformer internal class AmountReduceByStateTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: ReduceByData, ) : Transformer { override fun transform(prevState: StakingUiState): StakingUiState { return prevState.copy( - amountState = AmountReduceByTransformer(cryptoCurrencyStatus, value).transform(prevState.amountState), + amountState = AmountReduceByTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, + value = value, + ).transform(prevState.amountState), ) } } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt index b826aae59e..5f7b5e5e57 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/amount/AmountReduceToStateTransformer.kt @@ -1,6 +1,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount import com.tangem.common.ui.amountScreen.converters.AmountReduceToTransformer +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.transformer.Transformer @@ -8,11 +9,16 @@ import java.math.BigDecimal internal class AmountReduceToStateTransformer( private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val minimumTransactionAmount: EnterAmountBoundary?, private val value: BigDecimal, ) : Transformer { override fun transform(prevState: StakingUiState): StakingUiState { return prevState.copy( - amountState = AmountReduceToTransformer(cryptoCurrencyStatus, value).transform(prevState.amountState), + amountState = AmountReduceToTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, + value = value, + ).transform(prevState.amountState), ) } } \ 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 f8dff3cf4a..e958be8c31 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 @@ -11,6 +11,7 @@ import com.tangem.common.routing.AppRoute import com.tangem.common.routing.bundle.unbundle import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.bottomsheet.permission.state.ApproveType import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig import com.tangem.common.ui.notifications.NotificationUM @@ -72,6 +73,7 @@ import com.tangem.features.staking.impl.presentation.state.utils.withStubUnstake import com.tangem.utils.Provider import com.tangem.utils.coroutines.* import com.tangem.utils.extensions.isSingleItem +import com.tangem.utils.extensions.orZero import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -92,6 +94,7 @@ internal class StakingViewModel @Inject constructor( private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase, private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase, + private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getUserWalletUseCase: GetUserWalletUseCase, private val sendTransactionUseCase: SendTransactionUseCase, @@ -139,6 +142,7 @@ internal class StakingViewModel @Inject constructor( private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull() private var processingActions: List = emptyList() private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null + private var minimumTransactionAmount: EnterAmountBoundary? = null private var innerRouter: InnerStakingRouter by Delegates.notNull() private var userWallet: UserWallet by Delegates.notNull() @@ -366,7 +370,14 @@ internal class StakingViewModel @Inject constructor( } override fun onAmountValueChange(value: String) { - stateController.update(AmountChangeStateTransformer(cryptoCurrencyStatus, value, yield)) + stateController.update( + AmountChangeStateTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, + value = value, + yield = yield, + ), + ) } override fun onAmountPasteTriggerDismiss() { @@ -378,6 +389,7 @@ internal class StakingViewModel @Inject constructor( stateController.update( AmountMaxValueStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, actionType = uiState.value.actionType, yield = yield, ), @@ -621,6 +633,7 @@ internal class StakingViewModel @Inject constructor( ) { AmountReduceByStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, value = AmountReduceByTransformer.ReduceByData( reduceAmountBy = reduceAmountBy, reduceAmountByDiff = reduceAmountByDiff, @@ -633,6 +646,7 @@ internal class StakingViewModel @Inject constructor( stateController.update( AmountReduceToStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, + minimumTransactionAmount = minimumTransactionAmount, value = reduceAmountTo, ), ) @@ -796,6 +810,13 @@ internal class StakingViewModel @Inject constructor( feeCryptoCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, status).getOrNull() + minimumTransactionAmount = + getMinimumTransactionAmountSyncUseCase(userWalletId, status).getOrNull()?.let { + EnterAmountBoundary( + amount = it, + fiatRate = status.value.fiatRate.orZero(), + ) + } cryptoCurrencyStatus = status setupApprovalNeeded() @@ -929,6 +950,7 @@ internal class StakingViewModel @Inject constructor( AmountChangeStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, value = amountValue, + minimumTransactionAmount = minimumTransactionAmount, yield = yield, ), ) From cb9d1fc4631d89455c7fafa536c237e11c6c5697 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Nov 2024 16:43:47 +0200 Subject: [PATCH 18/40] Updated on 2026-08-14 --- .../impl/presentation/viewmodel/StakingViewModel.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 e958be8c31..bb47449575 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 @@ -228,6 +228,18 @@ internal class StakingViewModel @Inject constructor( } override fun onNextClick(balanceState: BalanceState?) { + if (value.currentStep == StakingStep.InitialInfo && balanceState == null) { + stateController.update( + SetConfirmationStateInitTransformer( + isEnter = true, + isExplicitExit = false, + balanceState = null, + cryptoCurrencyStatus = cryptoCurrencyStatus, + stakingApproval = stakingApproval, + stakingAllowance = stakingAllowance, + ), + ) + } stakingStateRouter.onNextClick() } From 1232de0a81fb18213d18cd54e5e8095aee5a574c Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Nov 2024 20:52:15 +0500 Subject: [PATCH 19/40] Updated on 2026-08-14 --- .../notifications/StakingInfoNotificationsFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt index 36f7acec1c..11b61e92b9 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt @@ -171,7 +171,7 @@ internal class StakingInfoNotificationsFactory( val exitRequirements = yield.args.exit?.args?.get(Yield.Args.ArgType.AMOUNT) ?: return val amountLeft = maxAmount - actionAmount - val isNotEnoughLeft = !amountLeft.isZero() && amountLeft < exitRequirements.minimum + val isNotEnoughLeft = !amountLeft.isZero() && amountLeft < exitRequirements.minimum.orZero() if (exitRequirements.required && isNotEnoughLeft) { add(StakingNotification.Warning.LowStakedBalance) From 0d30d25bad7dcfb56c8f44c55ca15b172c593a6c Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 7 Nov 2024 10:57:09 +0200 Subject: [PATCH 20/40] Updated on 2026-08-14 --- .../InvalidatePendingTransactionsUseCase.kt | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 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 dbb7c578d4..ce0b576bab 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 @@ -37,8 +37,12 @@ class InvalidatePendingTransactionsUseCase( processingActions.forEach { action -> when (action.type) { - StakingActionType.STAKE, StakingActionType.VOTE, StakingActionType.VOTE_LOCKED -> { - processEnterAction(balances, action) + StakingActionType.STAKE, StakingActionType.VOTE -> { + addStubStakedPendingTransaction(balances, action) + } + StakingActionType.VOTE_LOCKED -> { + addStubStakedPendingTransaction(balances, action) + removeLockedBalance(balances, action) } StakingActionType.WITHDRAW -> { modifyBalancesByStatus(balances, action, BalanceType.UNSTAKED) @@ -58,7 +62,15 @@ class InvalidatePendingTransactionsUseCase( return balances } - private fun processEnterAction(balances: MutableList, action: StakingAction) { + private fun removeLockedBalance(balances: MutableList, action: StakingAction) { + val index = findBalanceIndex(balances, action, BalanceType.LOCKED) + + if (index != -1) { + balances.removeAt(index) + } + } + + private fun addStubStakedPendingTransaction(balances: MutableList, action: StakingAction) { balances.add( BalanceItem( groupId = UUID.randomUUID().toString(), @@ -75,12 +87,16 @@ class InvalidatePendingTransactionsUseCase( } private fun modifyBalancesByStatus(balances: MutableList, action: StakingAction, type: BalanceType) { - val index = balances.indexOfFirst { - !it.isPending && it.amount isEqualTo action.amount && it.type == type - } + val index = findBalanceIndex(balances, action, type) if (index != -1) { balances[index] = balances[index].copy(isPending = true) } } + + private fun findBalanceIndex(balances: MutableList, action: StakingAction, type: BalanceType): Int { + return balances.indexOfFirst { + !it.isPending && it.amount isEqualTo action.amount && it.type == type + } + } } \ No newline at end of file From 2892c0b6a8e599cbaeb12b2af5a5ea52da2bc2cf Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 7 Nov 2024 10:57:19 +0200 Subject: [PATCH 21/40] Updated on 2026-08-14 --- .../analytics/utils/StakingAnalyticSender.kt | 2 +- .../state/InnerYieldBalanceState.kt | 2 +- .../converters/YieldBalancesConverter.kt | 2 +- .../previewdata/InitialStakingStatePreview.kt | 2 +- .../ui/StakingInitialInfoContent.kt | 30 +++++---- .../TokenDetailsLoadedBalanceConverter.kt | 66 +++++++++---------- .../state/factory/TokenDetailsStateFactory.kt | 3 - .../viewmodels/TokenDetailsViewModel.kt | 3 - 8 files changed, 51 insertions(+), 59 deletions(-) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt index 114d86d579..5decf65514 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/analytics/utils/StakingAnalyticSender.kt @@ -18,7 +18,7 @@ internal class StakingAnalyticSender( fun initialInfoScreen(value: StakingUiState) { val initialInfoState = value.initialInfoState as? StakingStates.InitialInfoState.Data val validatorState = initialInfoState?.yieldBalance as? InnerYieldBalanceState.Data - val validatorCount = validatorState?.balance + val validatorCount = validatorState?.balances ?.filterNot { it.validator?.address.isNullOrBlank() } ?.distinctBy { it.validator?.address } ?.size ?: 0 diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt index 7fa7d42377..94cd29f646 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt @@ -16,7 +16,7 @@ internal sealed class InnerYieldBalanceState { val rewardsFiat: String, val rewardBlockType: RewardBlockType, val isActionable: Boolean, - val balance: ImmutableList, + val balances: ImmutableList, ) : InnerYieldBalanceState() data object Empty : InnerYieldBalanceState() 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 29ccbc1d29..41c9881df8 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 @@ -53,7 +53,7 @@ internal class YieldBalancesConverter( }, rewardBlockType = type, isActionable = isActionable, - balance = balanceToShowItems.mapBalances(), + balances = balanceToShowItems.mapBalances(), ) } else { InnerYieldBalanceState.Empty 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 293ba68c01..9bc454ab92 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 @@ -68,7 +68,7 @@ internal object InitialStakingStatePreview { rewardsCrypto = "100 SOL", rewardBlockType = RewardBlockType.RewardUnavailable, isActionable = true, - balance = persistentListOf( + balances = persistentListOf( BalanceState( groupId = "groupId", title = stringReference("Binance"), diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt index d0d364184d..1fbbff9df2 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt @@ -128,18 +128,20 @@ private fun LazyListScope.activeStakingBlock( clickIntents: StakingClickIntents, isBalanceHidden: Boolean, ) { - val balances = (state.yieldBalance as? InnerYieldBalanceState.Data)?.balance - if (!balances.isNullOrEmpty()) { - item(key = STAKING_REWARD_BLOCK_KEY) { - Column(modifier = Modifier.animateItem()) { - StakingRewardBlock( - yieldBalanceState = state.yieldBalance, - onRewardsClick = clickIntents::openRewardsValidators, - isBalanceHidden = isBalanceHidden, - ) - SpacerH12() - } + val innerYieldBalanceState = state.yieldBalance as? InnerYieldBalanceState.Data ?: return + + item(key = STAKING_REWARD_BLOCK_KEY) { + Column(modifier = Modifier.animateItem()) { + StakingRewardBlock( + yieldBalanceState = state.yieldBalance, + onRewardsClick = clickIntents::openRewardsValidators, + isBalanceHidden = isBalanceHidden, + ) + SpacerH12() } + } + + if (innerYieldBalanceState.balances.isNotEmpty()) { item(ACTIVE_STAKING_BLOCK_KEY) { Text( text = stringResource(id = R.string.staking_your_stakes), @@ -148,7 +150,7 @@ private fun LazyListScope.activeStakingBlock( modifier = Modifier .roundedShapeItemDecoration( currentIndex = 0, - lastIndex = 1 + state.yieldBalance.balance.lastIndex, + lastIndex = 1 + state.yieldBalance.balances.lastIndex, addDefaultPadding = false, ) .fillMaxWidth() @@ -162,7 +164,7 @@ private fun LazyListScope.activeStakingBlock( ) } itemsIndexed( - items = state.yieldBalance.balance, + items = state.yieldBalance.balances, key = { _, balance -> // Staked balance does not have unique identifier. balance.toString() @@ -177,7 +179,7 @@ private fun LazyListScope.activeStakingBlock( .animateItem() .roundedShapeItemDecoration( currentIndex = index + 1, - lastIndex = state.yieldBalance.balance.lastIndex + 1, + lastIndex = state.yieldBalance.balances.lastIndex + 1, addDefaultPadding = false, ), ) 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 676cff23f2..e9d2706803 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 @@ -14,7 +14,6 @@ import com.tangem.core.ui.format.bigdecimal.* import com.tangem.domain.appcurrency.model.AppCurrency 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.RewardBlockType import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.tokens.error.CurrencyStatusError @@ -41,7 +40,6 @@ internal class TokenDetailsLoadedBalanceConverter( private val currentStateProvider: Provider, private val appCurrencyProvider: Provider, private val stakingEntryInfoProvider: Provider, - private val pendingBalancesProvider: Provider>, private val stakingAvailabilityProvider: Provider, private val symbol: String, private val decimals: Int, @@ -143,53 +141,51 @@ internal class TokenDetailsLoadedBalanceConverter( } private fun getYieldBalance(status: CryptoCurrencyStatus, state: TokenDetailsState): StakingBlockUM? { + return when (stakingAvailabilityProvider.invoke()) { + StakingAvailability.TemporaryUnavailable -> StakingBlockUM.TemporaryUnavailable + StakingAvailability.Unavailable -> null + is StakingAvailability.Available -> getStakingInfoBlock(status, state) + } + } + + private fun getStakingInfoBlock(status: CryptoCurrencyStatus, state: TokenDetailsState): StakingBlockUM? { val yieldBalance = status.value.yieldBalance as? YieldBalance.Data + val stakingCryptoAmount = yieldBalance?.getTotalStakingBalance() + val pendingBalances = yieldBalance?.balance?.items ?: emptyList() val stakingEntryInfo = stakingEntryInfoProvider.invoke() - val stakingAvailability = stakingAvailabilityProvider.invoke() val iconState = state.tokenInfoBlockState.iconState - val pendingBalances = pendingBalancesProvider.invoke() - val fiatRate = status.value.fiatRate return when { - stakingAvailability == StakingAvailability.TemporaryUnavailable -> { - StakingBlockUM.TemporaryUnavailable - } - stakingAvailability == StakingAvailability.Unavailable -> { - null - } - 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, - ) + stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null -> { + if (pendingBalances.isEmpty()) { + getStakeAvailableState(stakingEntryInfo, iconState) + } else { + getStakedBlockWithFiatAmount(status, pendingBalances.sumOf { it.amount }, null) + } } stakingCryptoAmount.isNullOrZero() && stakingEntryInfo == null -> { null } - else -> { - val stakingRewardAmount = yieldBalance?.getRewardStakingBalance()?.let { fiatRate?.multiply(it) } - val stakingFiatAmount = stakingCryptoAmount?.let { fiatRate?.multiply(it) } - - getStakedState( - status = status, - stakingCryptoAmount = stakingCryptoAmount, - stakingFiatAmount = stakingFiatAmount, - stakingRewardAmount = stakingRewardAmount, - ) - } + else -> getStakedBlockWithFiatAmount(status, stakingCryptoAmount, yieldBalance?.getRewardStakingBalance()) } } + private fun getStakedBlockWithFiatAmount( + status: CryptoCurrencyStatus, + stakingAmount: BigDecimal?, + rewardAmount: BigDecimal?, + ): StakingBlockUM.Staked { + val fiatRate = status.value.fiatRate + return getStakedState( + status = status, + stakingCryptoAmount = stakingAmount, + stakingFiatAmount = stakingAmount?.let { fiatRate?.multiply(it) }, + stakingRewardAmount = rewardAmount?.let { fiatRate?.multiply(it) }, + ) + } + private fun getMarketPriceState( status: CryptoCurrencyStatus.Value, currencySymbol: String, 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 80f8e8e1dc..08c2f45190 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 @@ -21,7 +21,6 @@ import com.tangem.domain.staking.GetStakingAvailabilityUseCase import com.tangem.domain.staking.GetStakingIntegrationIdUseCase 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.error.CurrencyStatusError import com.tangem.domain.tokens.model.* import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning @@ -54,7 +53,6 @@ internal class TokenDetailsStateFactory( private val stakingEntryInfoProvider: Provider, private val stakingAvailabilityProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, - private val pendingBalancesProvider: Provider>, private val clickIntents: TokenDetailsClickIntents, private val networkHasDerivationUseCase: NetworkHasDerivationUseCase, private val getUserWalletUseCase: GetUserWalletUseCase, @@ -87,7 +85,6 @@ internal class TokenDetailsStateFactory( appCurrencyProvider = appCurrencyProvider, stakingEntryInfoProvider = stakingEntryInfoProvider, stakingAvailabilityProvider = stakingAvailabilityProvider, - 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 5f5e518a6c..1427b63a24 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 @@ -38,7 +38,6 @@ import com.tangem.domain.staking.GetStakingIntegrationIdUseCase 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 @@ -149,7 +148,6 @@ internal class TokenDetailsViewModel @Inject constructor( private val warningsJobHolder = JobHolder() private val swapTxJobHolder = JobHolder() private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() - private val stakingPendingBalances: List = emptyList() // TODO staking private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null private var stakingEntryInfo: StakingEntryInfo? = null @@ -162,7 +160,6 @@ internal class TokenDetailsViewModel @Inject constructor( stakingEntryInfoProvider = Provider { stakingEntryInfo }, stakingAvailabilityProvider = Provider { stakingAvailability }, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, - pendingBalancesProvider = Provider { stakingPendingBalances }, clickIntents = this, networkHasDerivationUseCase = networkHasDerivationUseCase, getUserWalletUseCase = getUserWalletUseCase, From 58927597e9795d4c590980918ec5fc10801eb51a Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 7 Nov 2024 17:11:53 +0500 Subject: [PATCH 22/40] Updated on 2026-08-14 --- .../tangem/common/ui/notifications/NotificationsFactory.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt index 359d6dd9c0..13b0c42b88 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt @@ -193,7 +193,7 @@ object NotificationsFactory { if (isExceedsLimit) { add( NotificationUM.Error.MinimumAmountError( - amount = dustValue.parseBigDecimal(cryptoCurrencyStatus.currency.decimals), + amount = dustValue.format { crypto(cryptoCurrencyStatus.currency) }, ), ) } @@ -301,7 +301,7 @@ object NotificationsFactory { dustValue?.let { add( NotificationUM.Error.MinimumAmountError( - amount = it.parseBigDecimal(sendingCurrency.decimals), + amount = it.format { crypto(sendingCurrency) }, ), ) } From defc168c786e23f809233147dd66f6dfb55bc113 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 7 Nov 2024 11:24:53 +0500 Subject: [PATCH 23/40] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt | 2 -- .../src/main/java/com/tangem/core/analytics/models/Basic.kt | 2 -- 2 files changed, 4 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt index e9db3ee67e..444b2c6f89 100644 --- a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt @@ -6,7 +6,6 @@ import com.tangem.blockchainsdk.BlockchainSDKFactory import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.Analytics -import com.tangem.core.analytics.models.Basic import com.tangem.datasource.api.common.config.managers.ApiConfigsManager import com.tangem.domain.appcurrency.FetchAppCurrenciesUseCase import com.tangem.domain.balancehiding.BalanceHidingSettings @@ -103,7 +102,6 @@ internal class MainViewModel @Inject constructor( .distinctUntilChanged() .onEach { userWallet -> Analytics.setContext(userWallet.scanResponse) - Analytics.send(Basic.WalletOpened()) } .flowOn(dispatchers.io) .launchIn(viewModelScope) diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt index 3c1d48f13b..c623358a84 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt @@ -78,8 +78,6 @@ sealed class Basic( error = error, ) - class WalletOpened : Basic(event = "Wallet Opened") - class ButtonSupport(source: AnalyticsParam.ScreensSources) : Basic( event = "Request Support", params = mapOf( From 32d37fd571adc38f79405b0b62b7a30bbc134ff2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 7 Nov 2024 17:21:50 +0500 Subject: [PATCH 24/40] Updated on 2026-08-14 --- .../java/com/tangem/common/ui/amountScreen/ui/AmountField.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountField.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountField.kt index c74f183aa6..f2f8e078f9 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountField.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountField.kt @@ -138,7 +138,7 @@ private fun AmountFieldError( exit = fadeOut(), modifier = modifier, ) { - val errorText = remember(this) { error } + val errorText = remember(this, error) { error } val color = if (isError) TangemTheme.colors.text.warning else TangemTheme.colors.text.attention Text( text = errorText.resolveReference(), From cc81fb6d522b835b225ec78f173d36c4f9cf673a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 8 Nov 2024 12:59:31 +0200 Subject: [PATCH 25/40] Updated on 2026-08-14 --- .../domain/staking/InvalidatePendingTransactionsUseCase.kt | 2 +- .../impl/presentation/viewmodel/StakingViewModel.kt | 7 ++++++- 2 files changed, 7 insertions(+), 2 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 ce0b576bab..eb07d96ab9 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 @@ -50,7 +50,7 @@ class InvalidatePendingTransactionsUseCase( StakingActionType.UNLOCK_LOCKED -> { modifyBalancesByStatus(balances, action, BalanceType.LOCKED) } - StakingActionType.UNSTAKE -> { + StakingActionType.UNSTAKE, StakingActionType.RESTAKE -> { modifyBalancesByStatus(balances, action, BalanceType.STAKED) } else -> { 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 bb47449575..522d616f07 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 @@ -50,6 +50,7 @@ import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.staking.analytics.StakeScreenSource import com.tangem.domain.staking.analytics.StakingAnalyticsEvent import com.tangem.domain.staking.model.stakekit.* +import com.tangem.domain.staking.model.stakekit.action.StakingActionType import com.tangem.features.staking.impl.analytics.StakingParamsInterceptor import com.tangem.features.staking.impl.analytics.utils.StakingAnalyticSender import com.tangem.features.staking.impl.navigation.InnerStakingRouter @@ -941,7 +942,7 @@ internal class StakingViewModel @Inject constructor( stateController.updateAll( SetConfirmationStateInitTransformer( isEnter = false, - isExplicitExit = balanceType == BalanceType.STAKED, + isExplicitExit = isExplicitExit(balanceType, pendingAction), balanceState = balanceState, cryptoCurrencyStatus = cryptoCurrencyStatus, stakingApproval = stakingApproval, @@ -968,6 +969,10 @@ internal class StakingViewModel @Inject constructor( ) } + private fun isExplicitExit(balanceType: BalanceType, pendingAction: PendingAction?): Boolean { + return balanceType == BalanceType.STAKED && pendingAction?.type != StakingActionType.RESTAKE + } + private fun isAssentState(): Boolean { return value.currentStep == StakingStep.Confirmation && (value.confirmationState as? StakingStates.ConfirmationState.Data)?.innerState == From 3af77d53b89c9eea7c5f3369aac16c564989dc1b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Nov 2024 14:01:49 +0500 Subject: [PATCH 26/40] Updated on 2026-08-14 --- core/res/src/main/res/values-de/strings.xml | 30 ++++++++++++++++--- core/res/src/main/res/values-es/strings.xml | 26 +++++++++++++++- core/res/src/main/res/values-fr/strings.xml | 24 +++++++++++++++ core/res/src/main/res/values-ja/strings.xml | 25 +++++++++++++++- core/res/src/main/res/values-ru/strings.xml | 15 ++++++++-- .../src/main/res/values-uk-rUA/strings.xml | 26 ++++++++++++++-- core/res/src/main/res/values/strings.xml | 18 +++++++++-- 7 files changed, 152 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 ac5448db47..db671f2128 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -1,5 +1,11 @@ + Token nicht in Deinem Portfolio gefunden? Überprüfe die Märkte, um es zu finden und zum Kauf hinzuzufügen + Token nicht in Deinem Portfolio gefunden? Überprüfe die Märkte, um es zu finden und zum Verkauf hinzuzufügen + Wähle den Token + Token nicht in Deinem Portfolio gefunden? Überprüfe die Märkte, um es für Swaps zu finden und hinzuzufügen + Es gibt keine verfügbaren Token, die mit dem ausgewählten Token getauscht werden können. Bitte wähle einen anderen. + Kein verfügbares Paar Netzwerk wählen Token anlegen Token verwalten @@ -551,10 +557,21 @@ Zugangscode wiederherstellen Identische Karten oder Ring Zugangscode + Suche nach Land + Nicht verfügbar + Suche nach Währung + Der Kaufbetrag sollte nicht höher sein als %s + Der zu kaufende Betrag muss mindestens %s betragen + Keine verfügbaren Anbieter für diese Währung Bezahlen mit + Du kannst Deine Transaktion beim Drittanbieter %s abschließen. + Umleitung auf %s... Unsere Dienstleistungen sind in diesem Land nicht verfügbar Änder oder bestätige bitte Dein Wohnsitz wurde identifiziert als + Residenz + Bitte wähle das richtige Land aus, um korrekte Zahlungsoptionen und Dienstleistungen zu gewährleisten. + Einstellungen Über Gruppe erstellen Nach Guthaben @@ -741,6 +758,8 @@ Mit dem Staking kannst Du %1$s verdienen. Deine Staking-Belohnungen erhältst Du jede Woche. Sicher staken und wöchentliche Belohnungen verdienen. Verdiene Staking-Belohnungen + Dein verbleibender Stakingbetrag ist zu niedrig, um den Einsatz aufzuheben. Du musst mehr Staken, um den Mindestbetrag zum Aufheben des Einsatzes zu erreichen. + Niedriges Stakingguthaben Aufgrund von Netzwerkproblemen ist Staking derzeit nicht verfügbar. Bitte versuche es später erneut. Beim Staking im %1$s -Netzwerk mit einem neuen Validator werden alle zuvor eingesetzten Kryptos automatisch an diesen Validator übertragen Reinvestiert Deine verdienten Prämien in Deinen Einsatzbetrag und erhöht so den potenziellen Gewinn. @@ -785,7 +804,7 @@ Lösen der Bindungen Gelocktes unlocken Entsperren - Die Anzahl der zu stakenden Krypros muss mindesten %s betragen + Der unstaking-Betrag muss mindestens %s betragen. Der Betrag übersteigt das eingesetzte Guthaben Unstaken Staking beenden @@ -815,8 +834,7 @@ Der Betrag beinhaltet:\n• Honorar des Dienstleisters\n• Netzwerkgebühr für das Senden %1$s von der Börse zurück an die Adresse des Benutzers. \n\n Provider-Slippage kann bis zu %2$s Der Betrag enthält die Gebühren des Dienstleisters. Der Betrag beinhaltet die Gebühr des Dienstleisters. \n\nProvider-Slippage kann bis zu %s - Anbieter-Slippage kann bis zu %s ausfallen. - Gebühren + Information Alle dezentralen Börsen benötigen Genehmigungen, um zu verhindern, dass intelligente Verträge ohne Ihre Erlaubnis auf Ihre Geldbörse zugreifen. Smart Contracts können nicht auf Ihre Token zugreifen, wenn Sie nicht zustimmen. Indem Sie Ihre Token \"freischalten\", ermächtigen Sie den 1-Zoll-Smart-Contract, sie auszugeben. Die Miner des Netzwerks erhalten eine (von Ihnen bezahlte) Gasgebühr, um diese Aktion in der Blockchain aufzuzeichnen. Sie können Ihre Token tauschen, nachdem Sie Ihre Zustimmung gegeben haben. Genehmigen Fehler bei der Gebührenschätzung. Bitte sende dein Feedback an den Support. @@ -854,6 +872,9 @@ Tausche diesen Token gegen einen anderen zu %1$s Servicegebühren von Februar %2$s-%3$s. Tausche mit Changelly, %s Gebühren Jetzt tauschen + Nicht verfügbar zum Kauf + Nicht zum Verkauf verfügbar + Nicht verfügbar für Tausch von %s Vertrag: %s Du hast noch keine Transaktionen Der Transaktionsverlauf konnte nicht geladen werden.\nKlicke auf die Schaltfläche Neu laden, um die Informationen zu aktualisieren. @@ -863,6 +884,7 @@ von: %s zu: %s Validierer: %s + Der Mindesttransaktionsbetrag beträgt %1$s. Versuche es erneut Du hast dieselbe Karte oder Ring gescannt. Um ein Zwillings-Wallet zu erstellen, musst du die Karte oder Ring mit der Nummer %d scannen. Du hast die falsche Doppelkarte oder Ring gescannt. Bitte versuche eine andere Karte oder Ring @@ -998,7 +1020,7 @@ Das Solana-Netz ist überlastet. Wenn deine Transaktion nicht innerhalb von 2 Minuten bearbeitet wird, wiederhole bitte die Transaktion. Solana Netzwerkalarm Das Solana-Netzwerk erhebt alle 2 Tage eine Miete von %1$s. Konten, die sich die Miete nicht leisten können, werden aus dem Netzwerk gelöscht. Hinterlege deinem Konto mit mehr als %2$s, um es kostenlos zu nutzen. - Einige Netzwerke sind derzeit nicht erreichbar. Bitte versuche es später erneut. + Wischen Sie nach unten, um zu aktualisieren, oder versuchen Sie es später erneut. Einige Netzwerke sind nicht erreichbar Dies ist eine Testnet-Karte. Sie kann keine Transaktionen verarbeiten und sollte nur zu Test- und Entwicklungszwecken verwendet werden. Nur für Testzwecke diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml index ac89c7802b..8c35a676fa 100644 --- a/core/res/src/main/res/values-es/strings.xml +++ b/core/res/src/main/res/values-es/strings.xml @@ -1,5 +1,11 @@ + ¿No encuentra el token en su billetera? Consulte los mercados para encontrarlo y añadirlo a la compra + ¿No encuentra el token en su billetera? Consulte los mercados para encontrarlo y añadirlo a la venta + Elige el token + ¿No encuentras el token en tu billetera? Consulte los mercados para encontrarlo y agregarlo al intercambio + No hay tokens disponibles para intercambiar con el token seleccionado. Por favor elige otro. + No hay par disponible Elige red Agregue un token personalizado Gestionar tokens @@ -88,7 +94,7 @@ Reclamar Reclame recompensas Cerrar - confirme + Confirme Continuar Copiar Copiar la dirección @@ -551,9 +557,20 @@ Restaurar código de acceso Tarjetas idénticas Código de acceso + Buscar por país + Indisponible + Buscar por moneda + El monto de la compra no debe ser mayor a %s + La cantidad a comprar debe ser como mínimo %s + No hay proveedores disponibles para esta moneda + Pagar con Nuestros servicios no están disponibles en este país Cámbielo o confírmelo Su residencia ha sido identificada como + Residencia + Seleccione el país correcto para garantizar opciones de pago y servicios precisos. + Ajustes + Vía Agrupar Por saldo Organizar tokens @@ -696,6 +713,7 @@ Nombre El monto del staking debe ser al menos %s El monto del staking se redondeará a %1$s TRX debido a las reglas de la red. + El monto de cancelación del staking se redondeará a %1$s TRX debido a las reglas de la red. Unstaking de la reclamación Tarifa de staking de la cuenta Una cuenta de staking es una cuenta especial donde se almacenan los tokens SOL de staking. Se crea cuando delegas sus tokens a un validador para participar en la validación de transacciones y ganar recompensas. Se cobra una pequeña tarifa por crear la cuenta de staking, que se devuelve una vez que se completa el staking. @@ -738,6 +756,8 @@ El staking le permite ganar %1$s. Sus recompensas por apostar llegan todas las semanas. Haga staking de forma segura y comience a ganar recompensas semanales Gane recompensas por staking + Su saldo de staking será demasiado bajo para cancelar el staking. Necesitará hacer staking más para alcanzar la cantidad mínima para cancelarlo. + Saldo de staking bajo El staking no está disponible actualmente debido a las condiciones de la red. Inténtelo de nuevo más tarde. El staking en la red %1$s con un nuevo validador transferirá automáticamente todos los fondos previamente en staking a este validador. Reinvierta las recompensas obtenidas en el monto apostado, aumentando las ganancias potenciales. @@ -771,6 +791,7 @@ Recompensas El stake está bloqueado Hacer más staking + Monto del staking Ud hace el staking de %1$s y recibirá %2$s Toque para desbloquear Pulse para desbloquear o votar @@ -849,6 +870,9 @@ Cambie este token por otro por una tarifa de servicio de %1$s del %2$s al %3$s de febrero. Intercambio con Changelly, %s tarifa Intercambie ahora + No disponible para compra + No disponible para vender + No disponible para cambio desde %s contacto: %s Aún no tiene ninguna transacción Error al cargar el historial de transacciones.\nHaga clic en el botón de recarga para actualizar la información. diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index d2a3505ef9..cfb3abf10e 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -1,5 +1,11 @@ + Jeton non trouvé dans votre portefeuille ? Consultez les marchés pour le trouver et l\'ajouter à l\'achat + Jeton non trouvé dans votre portefeuille ? Consultez les marchés pour le trouver et l\'ajouter à la vente + Choisissez le jeton + Jeton non trouvé dans votre portefeuille ? Consultez les marchés pour le trouver et l\'ajouter à l\'échange + Il n\'y a pas de token disponible pour échanger avec le token sélectionné. Veuillez en choisir un autre. + Aucune paire disponible Choisissez le réseau Ajouter un jeton personnalisé Gérer les jetons @@ -551,9 +557,20 @@ Restauration du code d\'accès Cartes identiques Code d\'accès + Recherche par pays + Indisponible + Recherche par devise + Le montant de l\'achat ne doit pas dépasser %s + Le montant à acheter doit être au moins %s + Aucun fournisseur disponible pour cette devise + Payer avec Nos services ne sont pas disponibles dans ce pays Modifiez-le ou confirmez-le Votre résidence a été identifiée comme + Résidence + Veuillez sélectionner le bon pays pour garantir des options de paiement et des services précis. + Paramètres + Via Grouper Par solde Organiser les jetons @@ -696,6 +713,7 @@ Nom Le montant à staker doit être au moins %s Le montant du staking sera arrondi à %1$s TRX en raison des règles du réseau. + Le montant d\'annulation du staking sera arrondi à %1$s TRX en raison des règles du réseau. Réclamation déstakée Frais de staking du compte Un compte de staking est un compte spécial où sont stockés les jetons SOL stakés. Il est créé lorsque vous déléguez vos jetons à un validateur pour participer à la validation des transactions et gagner des récompenses. Des frais minimes sont facturés pour la création du compte de staking, qui sont restitués une fois le staking terminé. @@ -738,6 +756,8 @@ Le staking vous permet de gagner %1$s. Vos récompenses de staking arrivent toutes les semaines. Stakez en toute sécurité et commencez à gagner des récompenses hebdomadaires Gagnez des récompenses de staking + Votre solde stakée sera trop faible pour annuler votre staking. Vous devrez staker davantage pour atteindre le montant minimum pour annuler le staking. + Solde de staking faible L\'option de staking n\'est actuellement pas disponible en raison des conditions du réseau. Veuillez réessayer plus tard. Le staking dans le réseau %1$s avec un nouveau validateur transférera automatiquement tous les fonds précédemment stakés vers ce validateur Réinvestissez vos récompenses gagnées dans le montant que vous avez staké, augmentant ainsi vos gains potentiels. @@ -771,6 +791,7 @@ Récompenses Stake verrouillé Staker plus + Montant staké Vous stakez %1$s et recevrez %2$s Appuyez pour déverrouiller Appuyez pour déverrouiller ou voter @@ -849,6 +870,9 @@ Échangez ce jeton contre un autre moyennant des frais de service de %1$s du %2$s au %3$s février. Échangez avec Changelly, %s frais Échangez maintenant + Non disponible à l\'achat + Indisponible à la vente + Non disponible pour l\'échange de %s contrat : %s Vous n\'avez pas encore de transactions Échec du chargement de l\'historique des transactions.\nCliquez sur le bouton de rechargement pour mettre à jour les informations. diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index b2998a24d0..58687b9a15 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -1,5 +1,11 @@ + ポートフォリオにトークンが見つかりませんか?マーケットから見つけ、買付のために追加してください + ポートフォリオにトークンが見つかりませんか?マーケットから見つけ、売却のために追加してください + トークンを選択 + ポートフォリオにトークンが見つかりませんか?マーケットから見つけ、スワップのために追加してください + 選択したトークンと交換できるトークンがありません。別のトークンを選択してください。 + 利用可能なペアがありません ネットワークを選択 カスタムトークンの追加 トークンの管理 @@ -543,10 +549,21 @@ アクセスコードの復元 同一のカード アクセスコード + 国で検索 + 利用不可 + 通貨で検索 + 買付金額は%s以下にしてください + 買付金額は少なくとも%sである必要があります + この通貨で利用可能なプロバイダーはありません 支払う + サードパーティプロバイダー%sで取引を完了できます。 + %sにリダイレクトしています... この国では当社のサービスはご利用いただけません 変更または確認 あなたの住居は次のように識別されています + 住居 + 正確なお支払い方法とサービスを確保するため、正しい国を選択してください。 + 設定 経由 グループ 残高順 @@ -731,6 +748,8 @@ ステーキングにより%1$sを獲得できます。ステーキング報酬は毎週受け取れます。 安全にステーキングして、報酬を毎週獲得しましょう ステーキング報酬を獲得 + 残りのステーキング残高が少なすぎてステーキングを解除できません。最小のステーキング解除残高を満たすには、さらにステーキングする必要があります。 + ステーキング残高が低いです ネットワークの状態により、現在ステーキングはご利用いただけません。しばらくしてからもう一度お試しください。 新しいバリデーターで%1$sネットワークにステーキングすると、以前にステーキングされた資金はすべてこのバリデーターに自動的に転送されます。 獲得した報酬をステーキングに再投資し、潜在的な収益を増やします。 @@ -805,7 +824,6 @@ 金額には以下が含まれます: \n • サービス プロバイダーの手数料\n • 取引所からユーザーのアドレスに%1$sを送金するためのネットワーク手数料。 \n\nプロバイダーのスリッページは最大%2$sです この金額には、サービスプロバイダーの手数料が含まれています。 金額にはサービスプロバイダーの手数料が含まれます。 \n\nプロバイダーのスリッページは最大%s です - プロバイダーのスリッページは最大%sです。 手数料 すべての分散型取引所は、スマートコントラクトがあなたの許可なくウォレットにアクセスするのを防ぐために承認を必要とします。設計上、スマートコントラクトは承認なしでトークンにアクセスできません。トークンを「ロック解除」することで、あなたは1-inchのスマートコントラクトがトークンを使うことを承認します。ネットワークのマイナーは、このアクションをブロックチェーンに記録するためのガス料金(あなたが支払う)を受け取ります。承認後、トークンを交換することができます。 承認 @@ -844,6 +862,9 @@ このトークンは、2月%2$s-%3$s の間、%1$s のサービス手数料で別のトークンと交換できます。 Changellyでスワップ、手数料%s 今すぐスワップ + 買付できません + 売却できません + %sからのスワップは利用できません コントラクト: %s まだ取引はありません 取引履歴の読み込みに失敗しました。\n情報を更新するには、リロードボタンをクリックしてください。 @@ -853,6 +874,8 @@ 送金元: %s 送金先: %s バリデーター: %s + 最小%s + 最小取引金額は%1$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 e73b23b485..babcd2542e 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -1,5 +1,8 @@ + Выберите токен + Нет доступных токенов для обмена с выбранным токеном, пожалуйста, выберите другой. + Нет доступных пар Выберите сеть Добавить токен Валюты @@ -566,7 +569,14 @@ Восстановление кода доступа Идентичные карты Код доступа + Поиск по стране + Недоступно + Поиск по валюте + Сумма покупки не может быть больше, чем %s + Сумма покупки должна составлять минимум %s Оплата с + Наши сервисы недоступны в данной стране + Настройки Через Группы По балансу @@ -830,7 +840,6 @@ В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %1$s от биржи обратно на адрес пользователя \n\nПроскальзывание провайдера составляет до %2$s В сумму включена комиссия провайдера сервиса. В сумму включена комиссия провайдера сервиса. \n\nПроскальзывание провайдера составляет до %s - Проскальзывание провайдера составляет до %s. Информация Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен. Подтвердить @@ -869,6 +878,8 @@ Обменивайте этот токен на другие с %1$s комиссии за обслуживание с %2$s по %3$s февраля. Обмен с Changelly, %s комиссии Обменять + Недоступно для покупки + Недоступно для продажи контракт: %s У вас еще нет транзакций Не удалось загрузить историю транзакций.\nНажмите на кнопку перезагрузки, чтобы обновить информацию. @@ -1016,7 +1027,7 @@ Сеть Солана испытывает высокую нагрузку. Если Ваша транзакция не прошла в течение 2 минут, повторите её отправку. Оповещение сети Солана Сеть Solana взимает арендную плату в размере %1$s каждые 2 дня. Аккаунты, которые не могут позволить себе арендную плату, удаляются из сети. Пополните свой счет более чем на %2$s, чтобы не платить арендную плату. - Некоторые сети в настоящее время недоступны. Пожалуйста, повторите попытку позже. + Свайпните вниз для обновления или попробуйте позже. Некоторые сети недоступны Это Testnet карта. Он не может обрабатывать транзакции и используется только в целях тестирования и разработки. Только для целей тестирования 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 2697aa11d3..53de8082e3 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -1,5 +1,11 @@ + Токен не знайдено у вашому портфелі? Перевірте Ринки, щоб знайти та додати його для покупки + Токен не знайдено у вашому портфелі? Перевірте Ринки, щоб знайти та додати його для продажу + Оберіть токен + Токен не знайдено у вашому портфелі? Перевірте ринки, щоб знайти та додати його для обміну + Немає доступних токенів для обміну з обраним токеном. Будь ласка, оберіть інший. + Немає вільної пари Оберіть мережу Додати токен Токени @@ -520,7 +526,7 @@ Ваша seed-фраза - слово + %d слова %d слів %d слів @@ -567,9 +573,16 @@ Відновлення коду доступу Ідентичні картки Код доступу + Пошук за країною + Недоступно + Пошук по валюті + Оплата з Наші сервіси недоступні в цій країні Змінити або підтвердити Ваше місце проживання визначено як + Будь ласка, виберіть правильну країну, щоб забезпечити точні способи оплати та послуги. + Параметри + Через Групами За балансом Сортування токенів @@ -716,6 +729,7 @@ Ім\'я Сума для стейкінгу має бути не менше %s Сума стейкінгу буде округлена до %1$s TRX відповідно до правил мережі. + Сума зняття зі стейкінгу буде округлена до %1$s TRX через мережеві правила. Зняти кошти Комісія за стейкінг-акаунт Стейкінг-акаунт - це спеціальний рахунок, на якому зберігаються застейкані SOL токени. Він створюється, коли ви делегуєте свої токени валідатору для участі у перевірці транзакцій та отримання винагород. За створення стейкінг-акаунту стягується невелика комісія, яка повертається після завершення стейкінгу. @@ -758,10 +772,13 @@ Стейкінг дає змогу вам отримувати %1$s. Винагорода буде зараховуватися кожен тиждень. Стейкайте безпечно та почніть отримувати винагороди щотижня Отримуйте винагороду за стейкінг + Ваш баланс стейкінгу, що залишився, буде занадто низьким, щоб вивести його зі стейкінгу. Вам потрібно буде застейкати більше, щоб досягти мінімальної суми для виводу. + Низький баланс стейкінгу Стейкінг тимчасово недоступний через проблеми в мережі. Будь ласка, спробуйте пізніше. Стейкінг в мережі %1$s з новим валідатором автоматично переведе всі раніше застейкані кошти до цього валідатора Реінвестуйте зароблені винагороди у суму стейкінгу, щоб збільшити потенційний прибуток. Рестейк дозволяє вам перемістити ваші кошти від одного валідатора до іншого без необхідності виводити кошти зі стейкінгу + Ви збираєтеся застейкати весь свій баланс. Ми рекомендуємо залишити невелику суму, щоб покрити комісію мережі за зняття коштів або отримання винагороди. Розблокуйте свої кошти, щоб вивести їх зі стейкінгу. Розблокування займе %s. Ваші кошти будуть доступні для використання після закінчення 21-денного періоду розблокування. Винагорода буде отримана разом з вашими незастейканими коштами. Ваші кошти будуть доступні після %s періоду розблокування. @@ -790,6 +807,7 @@ Винагороди Стейкінг закрито Застейкати більше + Застейканий залишок Ви стейкаєте %1$s і будете отримуватиме винагороду %2$s Натисніть, щоб розблокувати Натисніть, щоб розблокувати або проголосувати @@ -868,6 +886,9 @@ Обмінюйте цей токен на інші з %1$s комісією за обслуговування з %2$s по %3$s лютого. Обмін із Changelly, %s комісії Обміняти + Недоступно для покупки + Недоступно для продажу + Недоступно для обміну з %s контракт: %s У вас ще немає транзакцій Не вдалося завантажити історію транзакцій.\nНатисніть кнопку нижче, щоб оновити інформацію @@ -877,6 +898,7 @@ від: %s до: %s валідатор: %s + Мінімальна сума транзакції становить %1$s. Спробуйте знову Ви відсканували одну й ту саму картку. Для створення twin-гаманця вам потрібно відсканувати картку з номером %d Ви відсканували не ту twin-картку. Будь ласка, спробуйте відсканувати іншу @@ -1014,7 +1036,7 @@ Мережа Солана зазнає високого навантаження. Якщо транзакція не пройшла протягом 2 хвилин, повторіть транзакцію. Оповіщення мережі Солана Мережа Solana стягує орендну плату у розмірі %1$s кожні 2 дні. Акаунти, які не можуть дозволити собі орендну плату, видаляються з мережі. Поповніть свій рахунок на суму понад %2$s, щоб не платити орендну плату. - Деякі мережі наразі недоступні. Будь ласка, спробуйте пізніше. + Проведіть пальцем вниз для оновлення або спробуйте пізніше. Деякі мережі недоступні Це картка Testnet. Вона не може обробляти транзакції і повинна використовуватися лише для тестування та розробки. Лише для цілей тестування diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index c2f7e4c756..ef0cd2f4f5 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1,5 +1,11 @@ + Token not found in your portfolio? Check the Markets to find and add it for purchase + Token not found in your portfolio? Check the Markets to find and add it for sell + Choose the Token + Token not found in your portfolio? Check the Markets to find and add it for swap + There are no available tokens to swap with the selected token. Please choose another one. + No available pair Choose network Add custom token Manage tokens @@ -554,7 +560,12 @@ Search by country Unavailable Search by currency + The purchase amount should be no more than %s + The amount to buy must be at least %s + No available providers for this currency Pay with + You will be able to complete your transaction on the third-party provider, %s + Redirecting to %s... Our services are not available in this country Change or confirm it Your residence has been identified as @@ -823,7 +834,6 @@ The amount includes:\n• service provider\'s fee\n• network fee for sending %1$s from the exchange back to the user\'s address. \n\nProvider slippage is up to %2$s The amount includes the service provider\'s fee. The amount includes the service provider\'s fee. \n\nProvider slippage is up to %s - Provider slippage is up to %s. Information All decentralized exchanges require approvals to prevent smart contracts from accessing your wallet without your permission. By design, smart contracts can\'t access your tokens unless you approve. By \"unlocking\" your tokens, you authorize the 1-inch smart contract to spend them. The network\'s miners receive a gas fee (paid by you) to record this action on the blockchain. You can swap your token after giving approval. Approve @@ -862,6 +872,9 @@ Exchange this token for another at %1$s service fees from February %2$s-%3$s. Swap with Changelly, %s fees Swap now + Unavailable to purchase + Unavailable to sell + Unavailable for swap from %s contract: %s You don\'t have any transactions yet Failed to load transaction history.\nClick on reload button to update the information. @@ -871,6 +884,7 @@ from: %s to: %s validator: %s + Minimum %s The minimum transaction amount is %1$s. Try again You\'ve scanned the same card. To create a twin wallet you need to scan the card with number %d @@ -1007,7 +1021,7 @@ The Solana network is congested. If your transaction is not processed within 2 minutes, please repeat the transaction. Solana Network Alert Solana network charges a rent of %1$s every 2 days. Accounts that can\'t afford the rent are purged from the network. Deposit your account with more than %2$s to use it for free. - Some networks currently are unreachable. Please try again later. + Swipe down to refresh or try again later. Some networks are unreachable This is a Testnet card. It cannot process transactions and should only be used for testing and development purposes. For testing purposes only From 1df2f17b7946d7062643a92ca899d43ad8fccb4a Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Nov 2024 11:40:19 +0200 Subject: [PATCH 27/40] Updated on 2026-08-14 --- .../InvalidatePendingTransactionsUseCase.kt | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 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 eb07d96ab9..29e7148ee2 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.action.StakingAction import com.tangem.domain.staking.model.stakekit.action.StakingActionType import com.tangem.domain.staking.repositories.StakingErrorResolver import com.tangem.utils.extensions.isEqualTo +import java.math.BigDecimal import java.util.UUID class InvalidatePendingTransactionsUseCase( @@ -50,9 +51,15 @@ class InvalidatePendingTransactionsUseCase( StakingActionType.UNLOCK_LOCKED -> { modifyBalancesByStatus(balances, action, BalanceType.LOCKED) } - StakingActionType.UNSTAKE, StakingActionType.RESTAKE -> { + StakingActionType.RESTAKE -> { modifyBalancesByStatus(balances, action, BalanceType.STAKED) } + StakingActionType.UNSTAKE -> { + val isFullUnstake = modifyBalancesByStatus(balances, action, BalanceType.STAKED) + if (!isFullUnstake) { + processPartialUnstake(balances, action) + } + } else -> { // intentionally do nothing } @@ -86,12 +93,19 @@ class InvalidatePendingTransactionsUseCase( ) } - private fun modifyBalancesByStatus(balances: MutableList, action: StakingAction, type: BalanceType) { + private fun modifyBalancesByStatus( + balances: MutableList, + action: StakingAction, + type: BalanceType, + ): Boolean { val index = findBalanceIndex(balances, action, type) if (index != -1) { balances[index] = balances[index].copy(isPending = true) + return true } + + return false } private fun findBalanceIndex(balances: MutableList, action: StakingAction, type: BalanceType): Int { @@ -99,4 +113,31 @@ class InvalidatePendingTransactionsUseCase( !it.isPending && it.amount isEqualTo action.amount && it.type == type } } + + private fun processPartialUnstake(balances: MutableList, action: StakingAction) { + val (index, pendingActionAmount) = findPartialUnstake(balances, action) + + if (index != -1) { + val amount = balances[index].amount + balances[index] = balances[index].copy( + amount = amount - pendingActionAmount, + ) // remnants of real one + + balances.add( + balances[index].copy( + amount = pendingActionAmount, + isPending = true, + ), + ) // pending with amount from action + } + } + + private fun findPartialUnstake(balances: MutableList, action: StakingAction): Pair { + val index = balances.indexOfFirst { + !it.isPending && action.amount < it.amount && + it.type == BalanceType.STAKED && + it.validatorAddress == action.validatorAddress + } + return index to action.amount + } } \ No newline at end of file From 1fea0c7c4848406fe28567700579b91dcb84c74b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Nov 2024 11:40:29 +0200 Subject: [PATCH 28/40] Updated on 2026-08-14 --- .../api/stakekit/models/response/model/YieldDTO.kt | 8 ++++---- .../com/tangem/data/staking/DefaultStakingRepository.kt | 2 +- .../com/tangem/data/staking/converters/YieldConverter.kt | 8 ++++---- 3 files changed, 9 insertions(+), 9 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 951d60ddf7..88cc922696 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 @@ -7,7 +7,7 @@ import java.math.BigDecimal @JsonClass(generateAdapter = true) data class YieldDTO( @Json(name = "id") - val id: String, + val id: String?, @Json(name = "token") val token: TokenDTO?, @Json(name = "tokens") @@ -67,9 +67,9 @@ data class YieldDTO( @Json(name = "address") val address: String?, @Json(name = "status") - val status: ValidatorStatusDTO, + val status: ValidatorStatusDTO?, @Json(name = "name") - val name: String, + val name: String?, @Json(name = "image") val image: String?, @Json(name = "website") @@ -83,7 +83,7 @@ data class YieldDTO( @Json(name = "votingPower") val votingPower: Double?, @Json(name = "preferred") - val preferred: Boolean, + val preferred: Boolean?, ) { @JsonClass(generateAdapter = true) enum class ValidatorStatusDTO { 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 1a40afb7ac..3261381510 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 @@ -589,7 +589,7 @@ internal class DefaultStakingRepository( private fun getEnabledYields(): List { return yieldConverter.convertListIgnoreErrors( input = stakingYieldsStore.get(), - onError = { Timber.e("Error converting enabled yields list: $it") }, + onError = { Timber.e("Error converting one of the items in enabled yields: $it") }, ) } 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 52993093c5..6d6b14b10b 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 @@ -118,16 +118,16 @@ class YieldConverter( val address = validatorDTO.address.asMandatory("address") return Yield.Validator( address = address, - status = convertValidatorStatus(validatorDTO.status), - name = validatorDTO.name, + status = convertValidatorStatus(validatorDTO.status.asMandatory("status")), + name = validatorDTO.name.asMandatory("name"), image = validatorDTO.image, website = validatorDTO.website, apr = validatorDTO.apr, commission = validatorDTO.commission, stakedBalance = validatorDTO.stakedBalance, votingPower = validatorDTO.votingPower, - preferred = validatorDTO.preferred, - isStrategicPartner = isStrategicPartner(validatorDTO.address, validatorDTO.name), + preferred = validatorDTO.preferred.asMandatory("preferred"), + isStrategicPartner = isStrategicPartner(validatorDTO.address, validatorDTO.name.asMandatory("name")), ) } From 3a1101ceadc2f48637ed27e20d6047215558da36 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Nov 2024 11:40:39 +0200 Subject: [PATCH 29/40] Updated on 2026-08-14 --- .../impl/presentation/ui/StakingInitialInfoContent.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt index 1fbbff9df2..95038b16d9 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt @@ -165,9 +165,13 @@ private fun LazyListScope.activeStakingBlock( } itemsIndexed( items = state.yieldBalance.balances, - key = { _, balance -> + key = { index, balance -> // Staked balance does not have unique identifier. - balance.toString() + buildString { + append(balance.hashCode()) + append("_") + append(index) + } }, ) { index, balance -> ActiveStakingBlock( From 25c73300fef1f865f91ef8663d8c06d8c95c122e Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Nov 2024 13:05:59 +0300 Subject: [PATCH 30/40] Updated on 2026-08-14 --- .../data/DefaultLegacyWalletConnectRepository.kt | 6 ++++-- gradle/dependencies.toml | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt index 8f80642201..9e980da2ba 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt @@ -207,6 +207,10 @@ internal class DefaultLegacyWalletConnectRepository( Timber.i("onSessionDelete: $sessionDelete") } + override fun onSessionExtend(session: Wallet.Model.Session) { + Timber.i("onSessionExtend: $session") + } + override fun onSessionSettleResponse(settleSessionResponse: Wallet.Model.SettledSessionResponse) { // Triggered when wallet receives the session settlement response from Dapp Timber.i("onSessionSettleResponse: $settleSessionResponse") @@ -264,8 +268,6 @@ internal class DefaultLegacyWalletConnectRepository( } override fun approve(userNamespaces: Map>) { - this.userNamespaces = userNamespaces - val sessionProposal: Wallet.Model.SessionProposal = requireNotNull(this.sessionProposal) val userChains = userNamespaces.flatMap { namespace -> diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 2e813b0545..ba3c3819e0 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -69,8 +69,8 @@ zxingQrCode = "3.5.1" mviCore = "1.3.1" kotlinSerialization = "1.4.1" arrow = "1.2.3" -walletConnectCore = "1.18.0" -walletConnectWeb3 = "1.11.0" +walletConnectCore = "1.35.2" +walletConnectWeb3 = "1.35.2" prettyLogger = "2.2.0" okHttp-prettyLogging = "3.1.0" chucker = "4.0.0" From 0ddd171bb273117aca71c84d681891b5ad893d87 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Nov 2024 12:42:36 +0300 Subject: [PATCH 31/40] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 2 - .../bigdecimal/BigDecimalCryptoFormat.kt | 37 ++++-- .../tangem/data/tokens/di/TokensDataModule.kt | 10 +- .../DefaultCurrencyChecksRepository.kt | 15 ++- .../GetMinimumTransactionAmountSyncUseCase.kt | 11 +- .../tangem/feature/swap/ui/StateBuilder.kt | 55 +++++++-- .../feature/swap/viewmodels/SwapViewModel.kt | 110 +++++++++++------- 7 files changed, 155 insertions(+), 85 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index 0776b43400..7e547182ce 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -277,11 +277,9 @@ internal object TokensDomainModule { @Singleton fun provideGetMinimumTransactionAmountSyncUseCase( currencyChecksRepository: CurrencyChecksRepository, - dispatchers: CoroutineDispatcherProvider, ): GetMinimumTransactionAmountSyncUseCase { return GetMinimumTransactionAmountSyncUseCase( currencyChecksRepository = currencyChecksRepository, - dispatchers = dispatchers, ) } diff --git a/core/ui/src/main/java/com/tangem/core/ui/format/bigdecimal/BigDecimalCryptoFormat.kt b/core/ui/src/main/java/com/tangem/core/ui/format/bigdecimal/BigDecimalCryptoFormat.kt index eea3130209..7d6849591c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/format/bigdecimal/BigDecimalCryptoFormat.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/format/bigdecimal/BigDecimalCryptoFormat.kt @@ -5,6 +5,7 @@ import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants.CRYPTO_FEE import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants.CURRENCY_SPACE import com.tangem.core.ui.format.bigdecimal.BigDecimalFormatConstants.FORMAT_THRESHOLD import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.utils.StringsSigns.NON_BREAKING_SPACE import com.tangem.utils.extensions.isNotWhitespace import java.math.BigDecimal import java.math.RoundingMode @@ -16,6 +17,7 @@ open class BigDecimalCryptoFormat( val symbol: String, val decimals: Int, val locale: Locale = Locale.getDefault(), + val ignoreSymbolPosition: Boolean = false, ) : BigDecimalFormat { override fun invoke(value: BigDecimal): String = defaultAmount()(value) @@ -48,11 +50,13 @@ fun BigDecimalFormatScope.crypto( fun BigDecimalFormatScope.crypto( cryptoCurrency: CryptoCurrency, + ignoreSymbolPosition: Boolean = false, locale: Locale = Locale.getDefault(), ): BigDecimalCryptoFormat { return BigDecimalCryptoFormat( symbol = cryptoCurrency.symbol, decimals = cryptoCurrency.decimals, + ignoreSymbolPosition = ignoreSymbolPosition, locale = locale, ) } @@ -60,19 +64,28 @@ fun BigDecimalFormatScope.crypto( // == Formatters == fun BigDecimalCryptoFormat.defaultAmount() = BigDecimalFormat { value -> - val formatter = NumberFormat.getCurrencyInstance(locale).apply { - currency = usdCurrency - maximumFractionDigits = decimals.coerceAtMost(maximumValue = 8) - minimumFractionDigits = 2 - isGroupingUsed = true - roundingMode = RoundingMode.HALF_UP + if (ignoreSymbolPosition) { + val formatter = NumberFormat.getInstance(locale).apply { + maximumFractionDigits = decimals.coerceAtMost(maximumValue = 8) + minimumFractionDigits = 2 + isGroupingUsed = true + roundingMode = RoundingMode.HALF_UP + } + formatter.format(value) + NON_BREAKING_SPACE + symbol + } else { + val formatter = NumberFormat.getCurrencyInstance(locale).apply { + currency = usdCurrency + maximumFractionDigits = decimals.coerceAtMost(maximumValue = 8) + minimumFractionDigits = 2 + isGroupingUsed = true + roundingMode = RoundingMode.HALF_UP + } + formatter.format(value) + .replaceFiatSymbolWithCrypto( + fiatCurrencySymbol = usdCurrency.getSymbol(locale), + cryptoCurrencySymbol = symbol, + ) } - - formatter.format(value) - .replaceFiatSymbolWithCrypto( - fiatCurrencySymbol = usdCurrency.getSymbol(locale), - cryptoCurrencySymbol = symbol, - ) } fun BigDecimalCryptoFormat.shorted() = BigDecimalFormat { value -> diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt index 3e5c70b106..cc7bb7a3fe 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt @@ -95,8 +95,14 @@ internal object TokensDataModule { @Provides @Singleton - fun provideCurrencyChecksRepository(walletManagersFacade: WalletManagersFacade): CurrencyChecksRepository { - return DefaultCurrencyChecksRepository(walletManagersFacade = walletManagersFacade) + fun provideCurrencyChecksRepository( + walletManagersFacade: WalletManagersFacade, + coroutineDispatcherProvider: CoroutineDispatcherProvider, + ): CurrencyChecksRepository { + return DefaultCurrencyChecksRepository( + walletManagersFacade = walletManagersFacade, + coroutineDispatchers = coroutineDispatcherProvider, + ) } @Provides diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt index b156d85caf..a62d70a3f8 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt @@ -12,10 +12,13 @@ import com.tangem.domain.tokens.model.blockchains.UtxoAmountLimit import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext import java.math.BigDecimal internal class DefaultCurrencyChecksRepository( private val walletManagersFacade: WalletManagersFacade, + private val coroutineDispatchers: CoroutineDispatcherProvider, ) : CurrencyChecksRepository { override suspend fun getExistentialDeposit(userWalletId: UserWalletId, network: Network): BigDecimal? { val manager = walletManagersFacade.getOrCreateWalletManager( @@ -45,12 +48,14 @@ internal class DefaultCurrencyChecksRepository( } override suspend fun getMinimumSendAmount(userWalletId: UserWalletId, network: Network): BigDecimal? { - val manager = walletManagersFacade.getOrCreateWalletManager( - userWalletId = userWalletId, - network = network, - ) + return withContext(coroutineDispatchers.io) { + val manager = walletManagersFacade.getOrCreateWalletManager( + userWalletId = userWalletId, + network = network, + ) - return if (manager is MinimumSendAmountProvider) manager.getMinimumSendAmount() else null + if (manager is MinimumSendAmountProvider) manager.getMinimumSendAmount() else null + } } override suspend fun getFeeResourceAmount(userWalletId: UserWalletId, network: Network): CurrencyAmount? { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt index 73aaeb8c0d..388b88d7e7 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetMinimumTransactionAmountSyncUseCase.kt @@ -5,22 +5,17 @@ import arrow.core.raise.either import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.withContext import java.math.BigDecimal class GetMinimumTransactionAmountSyncUseCase( private val currencyChecksRepository: CurrencyChecksRepository, - private val dispatchers: CoroutineDispatcherProvider, ) { suspend operator fun invoke( userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, - ): Either = withContext(dispatchers.io) { - either { - val cryptoCurrency = cryptoCurrencyStatus.currency - currencyChecksRepository.getMinimumSendAmount(userWalletId, cryptoCurrency.network) - } + ): Either = either { + val cryptoCurrency = cryptoCurrencyStatus.currency + currencyChecksRepository.getMinimumSendAmount(userWalletId, cryptoCurrency.network) } } \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index d5f2d4d92b..e841fb0bba 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -172,10 +172,15 @@ internal class StateBuilder( val canSelectReceiveToken = mainTokenId != toToken.id.value if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder - val sendInput = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable).copy( - isError = false, - header = TextReference.Res(R.string.swapping_from_title), - ) + val sendInputType = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable) + val sendInput = if (sendInputType.isError) { + sendInputType + } else { + sendInputType.copy( + isError = false, + header = TextReference.Res(R.string.swapping_from_title), + ) + } return uiStateHolder.copy( sendCardData = SwapCardState.SwapCardData( type = sendInput, @@ -252,10 +257,16 @@ internal class StateBuilder( } else { TextReference.Res(R.string.swapping_from_title) } - val sendInput = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable).copy( - isError = isInsufficientFunds, - header = insufficientFundsHeader, - ) + val sendCardType = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.Inputtable) + val sendInput = if (sendCardType.isError && !isInsufficientFunds) { + // if any error in inputField and funds enough -> show that error else show fund is not enough error + sendCardType + } else { + sendCardType.copy( + isError = isInsufficientFunds, + header = insufficientFundsHeader, + ) + } return uiStateHolder.copy( sendCardData = SwapCardState.SwapCardData( type = sendInput, @@ -863,14 +874,36 @@ internal class StateBuilder( ) } - fun updateSwapAmount(uiState: SwapStateHolder, amount: String): SwapStateHolder { + fun updateSwapAmount( + uiState: SwapStateHolder, + amountFormatted: String, + amountRaw: String, + fromToken: CryptoCurrency, + minTxAmount: BigDecimal?, + ): SwapStateHolder { if (uiState.sendCardData !is SwapCardState.SwapCardData) return uiState + val amountToSend = amountRaw.toBigDecimalOrNull() + val sendInput = if (minTxAmount != null && amountToSend != null && amountToSend < minTxAmount) { + val minAmountFormatted = minTxAmount.format { + crypto(cryptoCurrency = fromToken, ignoreSymbolPosition = true) + } + (uiState.sendCardData.type as? TransactionCardType.Inputtable)?.copy( + isError = true, + header = resourceReference(R.string.transfer_min_amount_error, wrappedList(minAmountFormatted)), + ) ?: uiState.sendCardData.type + } else { + (uiState.sendCardData.type as? TransactionCardType.Inputtable)?.copy( + isError = false, + header = TextReference.Res(R.string.swapping_from_title), + ) ?: uiState.sendCardData.type + } return uiState.copy( sendCardData = uiState.sendCardData.copy( amountTextFieldValue = TextFieldValue( - text = amount, - selection = TextRange(amount.length), + text = amountFormatted, + selection = TextRange(amountFormatted.length), ), + type = sendInput, ), ) } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index c4854ddb29..216f2860fa 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -26,6 +26,7 @@ import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase +import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -83,6 +84,7 @@ internal class SwapViewModel @Inject constructor( private val getCardInfoUseCase: GetCardInfoUseCase, private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase, private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, + private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase, swapInteractorFactory: SwapInteractor.Factory, savedStateHandle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver { @@ -842,60 +844,78 @@ internal class SwapViewModel @Inject constructor( } private fun onChangeCardsClicked() { - val newFromToken = dataState.toCryptoCurrency - val newToToken = dataState.fromCryptoCurrency + viewModelScope.launch { + val newFromToken = dataState.toCryptoCurrency + val newToToken = dataState.fromCryptoCurrency - if (newFromToken != null && newToToken != null) { - isAmountChangedByUser = true + if (newFromToken != null && newToToken != null) { + isAmountChangedByUser = true - dataState = dataState.copy( - fromCryptoCurrency = newFromToken, - toCryptoCurrency = newToToken, - ) - isOrderReversed = !isOrderReversed - dataState.tokensDataState?.let { - updateTokensState(it) + dataState = dataState.copy( + fromCryptoCurrency = newFromToken, + toCryptoCurrency = newToToken, + ) + isOrderReversed = !isOrderReversed + dataState.tokensDataState?.let { + updateTokensState(it) + } + + val minTxAmount = getMinimumTransactionAmountSyncUseCase( + userWalletId, + newFromToken, + ).getOrNull() + val decimals = newFromToken.currency.decimals + lastAmount.value = cutAmountWithDecimals(decimals, lastAmount.value) + uiState = stateBuilder.updateSwapAmount( + uiState = uiState, + amountFormatted = inputNumberFormatter.formatWithThousands(lastAmount.value, decimals), + amountRaw = lastAmount.value, + fromToken = newFromToken.currency, + minTxAmount = minTxAmount, + ) + startLoadingQuotes( + fromToken = newFromToken, + toToken = newToToken, + amount = lastAmount.value, + toProvidersList = findSwapProviders(newFromToken, newToToken), + ) } - - val decimals = newFromToken.currency.decimals - lastAmount.value = cutAmountWithDecimals(decimals, lastAmount.value) - uiState = stateBuilder.updateSwapAmount( - uiState, - inputNumberFormatter.formatWithThousands(lastAmount.value, decimals), - ) - startLoadingQuotes( - fromToken = newFromToken, - toToken = newToToken, - amount = lastAmount.value, - toProvidersList = findSwapProviders(newFromToken, newToToken), - ) } } private fun onAmountChanged(value: String) { - val fromToken = dataState.fromCryptoCurrency - val toToken = dataState.toCryptoCurrency - if (fromToken != null) { - val decimals = fromToken.currency.decimals - val cutValue = cutAmountWithDecimals(decimals, value) - lastAmount.value = cutValue - uiState = stateBuilder.updateSwapAmount( - uiState = uiState, - amount = inputNumberFormatter.formatWithThousands(cutValue, decimals), - ) + viewModelScope.launch { + val fromToken = dataState.fromCryptoCurrency + val toToken = dataState.toCryptoCurrency + if (fromToken != null) { + val decimals = fromToken.currency.decimals + val cutValue = cutAmountWithDecimals(decimals, value) + val minTxAmount = getMinimumTransactionAmountSyncUseCase( + userWalletId, + fromToken, + ).getOrNull() + lastAmount.value = cutValue + uiState = stateBuilder.updateSwapAmount( + uiState = uiState, + amountFormatted = inputNumberFormatter.formatWithThousands(cutValue, decimals), + amountRaw = lastAmount.value, + fromToken = fromToken.currency, + minTxAmount = minTxAmount, + ) - if (toToken != null) { - if (toToken.value.amount != null) { - isAmountChangedByUser = true - } + if (toToken != null) { + if (toToken.value.amount != null) { + isAmountChangedByUser = true + } - amountDebouncer.debounce(viewModelScope, DEBOUNCE_AMOUNT_DELAY) { - startLoadingQuotes( - fromToken = fromToken, - toToken = toToken, - amount = lastAmount.value, - toProvidersList = findSwapProviders(fromToken, toToken), - ) + amountDebouncer.debounce(viewModelScope, DEBOUNCE_AMOUNT_DELAY) { + startLoadingQuotes( + fromToken = fromToken, + toToken = toToken, + amount = lastAmount.value, + toProvidersList = findSwapProviders(fromToken, toToken), + ) + } } } } From fa206b842261e81f8257636d6093ad3ebf71595a Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Nov 2024 16:57:11 +0300 Subject: [PATCH 32/40] 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 ba3c3819e0..cf8b89cfd5 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -90,7 +90,7 @@ markdownComposeView = "0.5.4" # region Tangem tangemBlockchainSdk = "release-app_5.18-856" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "release-app_5.18-396" +tangemCardSdk = "release-app_5.18-398" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem17" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ From 27e3dd5747a052bc8ae7adc69d6fa28df6d7bfc1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Nov 2024 23:26:48 +0300 Subject: [PATCH 33/40] 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 d7fbe88d22..5322aef4fa 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -88,7 +88,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.17-855" +tangemBlockchainSdk = "release-app_5.17-858" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.17-392" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From dba36d934f191322442cb2e5965ae4af32c51ede Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 12 Nov 2024 10:11:07 +0300 Subject: [PATCH 34/40] 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 cf8b89cfd5..cb7a4dbffc 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -88,7 +88,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.18-856" +tangemBlockchainSdk = "release-app_5.18-859" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.18-398" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 92ad2b40a08725385904089d51a16902611b5c12 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 12 Nov 2024 11:33:28 +0200 Subject: [PATCH 35/40] Updated on 2026-08-14 --- .../tangem/data/staking/converters/YieldBalanceConverter.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 5edd7f05e7..c0807165b7 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 @@ -31,10 +31,12 @@ internal class YieldBalanceConverter : Converter Date: Tue, 12 Nov 2024 13:57:07 +0300 Subject: [PATCH 36/40] Updated on 2026-08-14 --- .../transaction/usecase/EstimateFeeUseCase.kt | 41 ++- .../feature/swap/domain/SwapInteractorImpl.kt | 302 ++++++++++-------- gradle/dependencies.toml | 1 - 3 files changed, 181 insertions(+), 163 deletions(-) diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/EstimateFeeUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/EstimateFeeUseCase.kt index 134cb9f3f5..8edb20c7a6 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/EstimateFeeUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/EstimateFeeUseCase.kt @@ -15,8 +15,6 @@ import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.error.mapToFeeError import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWallet -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow import java.math.BigDecimal /** @@ -30,28 +28,25 @@ class EstimateFeeUseCase( amount: BigDecimal, userWallet: UserWallet, cryptoCurrency: CryptoCurrency, - ): Flow> { - return flow { - val amountData = convertCryptoCurrencyToAmount(cryptoCurrency, amount) - val result = if (demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) { - demoTransactionSender(userWallet, cryptoCurrency).estimateFee( - amount = amountData, - destination = "", - ) - } else { - walletManagersFacade.estimateFee( - amount = amountData, - userWalletId = userWallet.walletId, - network = cryptoCurrency.network, - ) - } + ): Either { + val amountData = convertCryptoCurrencyToAmount(cryptoCurrency, amount) + val result = if (demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) { + demoTransactionSender(userWallet, cryptoCurrency).estimateFee( + amount = amountData, + destination = "", + ) + } else { + walletManagersFacade.estimateFee( + amount = amountData, + userWalletId = userWallet.walletId, + network = cryptoCurrency.network, + ) + } - val maybeFee = when (result) { - is Result.Success -> result.data.right() - is Result.Failure -> result.mapToFeeError().left() - null -> GetFeeError.UnknownError.left() - } - emit(maybeFee) + return when (result) { + is Result.Success -> result.data.right() + is Result.Failure -> result.mapToFeeError().left() + null -> GetFeeError.UnknownError.left() } } diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 77aa2d704c..1d5e225d91 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -5,6 +5,7 @@ import arrow.core.getOrElse import com.tangem.blockchain.common.* import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.Blockchain.* import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.blockchainsdk.utils.fromNetworkId @@ -45,7 +46,6 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.firstOrNull import timber.log.Timber import java.math.BigDecimal import java.math.BigInteger @@ -865,8 +865,8 @@ internal class SwapInteractorImpl @AssistedInject constructor( } else { when (blockchain) { // region Blockchains with their own fees - Blockchain.VeChain, - Blockchain.VeChainTestnet, + VeChain, + VeChainTestnet, -> { Fee.VeChain( amount = feeAmount, @@ -874,28 +874,28 @@ internal class SwapInteractorImpl @AssistedInject constructor( gasLimit = fee.gasLimit.toLong(), ) } - Blockchain.Aptos, - Blockchain.AptosTestnet, + Aptos, + AptosTestnet, -> { val gasUnitPrice = fee.feeValue.divide( fee.gasLimit.toBigDecimal(), - Blockchain.Aptos.decimals(), + Aptos.decimals(), RoundingMode.HALF_UP, ) Fee.Aptos( amount = feeAmount, gasUnitPrice = gasUnitPrice - .movePointRight(Blockchain.Aptos.decimals()) + .movePointRight(Aptos.decimals()) .toLong(), gasLimit = fee.gasLimit.toLong(), ) } - Blockchain.Filecoin, + Filecoin, -> { val gasUnitPrice = fee.feeValue.divide( BigDecimal(fee.gasLimit), - Blockchain.Filecoin.decimals(), + Filecoin.decimals(), RoundingMode.HALF_UP, ) val feeParams = requireNotNull(fee.params as? TxFee.Params.Filecoin) @@ -903,14 +903,14 @@ internal class SwapInteractorImpl @AssistedInject constructor( Fee.Filecoin( amount = feeAmount, gasUnitPrice = gasUnitPrice - .movePointRight(Blockchain.Filecoin.decimals()) + .movePointRight(Filecoin.decimals()) .toLong(), gasLimit = fee.gasLimit.toLong(), gasPremium = feeParams.gasPremium, ) } - Blockchain.Sui, - Blockchain.SuiTestnet, + Sui, + SuiTestnet, -> { val feeParams = requireNotNull(fee.params as? TxFee.Params.Sui) @@ -922,131 +922,131 @@ internal class SwapInteractorImpl @AssistedInject constructor( } // endregion // region Blockchains with common fees or EVM-like fees - Blockchain.Unknown, - Blockchain.Arbitrum, - Blockchain.ArbitrumTestnet, - Blockchain.Avalanche, - Blockchain.AvalancheTestnet, - Blockchain.Binance, - Blockchain.BinanceTestnet, - Blockchain.BSC, - Blockchain.BSCTestnet, - Blockchain.Bitcoin, - Blockchain.BitcoinTestnet, - Blockchain.BitcoinCash, - Blockchain.BitcoinCashTestnet, - Blockchain.Cardano, - Blockchain.Cosmos, - Blockchain.CosmosTestnet, - Blockchain.Dogecoin, - Blockchain.Ducatus, - Blockchain.Ethereum, - Blockchain.EthereumTestnet, - Blockchain.EthereumClassic, - Blockchain.EthereumClassicTestnet, - Blockchain.Fantom, - Blockchain.FantomTestnet, - Blockchain.Litecoin, - Blockchain.Near, - Blockchain.NearTestnet, - Blockchain.Polkadot, - Blockchain.PolkadotTestnet, - Blockchain.Kava, - Blockchain.KavaTestnet, - Blockchain.Kusama, - Blockchain.Polygon, - Blockchain.PolygonTestnet, - Blockchain.RSK, - Blockchain.Sei, - Blockchain.SeiTestnet, - Blockchain.Stellar, - Blockchain.StellarTestnet, - Blockchain.Solana, - Blockchain.SolanaTestnet, - Blockchain.Tezos, - Blockchain.Tron, - Blockchain.TronTestnet, - Blockchain.XRP, - Blockchain.Gnosis, - Blockchain.Dash, - Blockchain.Optimism, - Blockchain.OptimismTestnet, - Blockchain.Dischain, - Blockchain.EthereumPow, - Blockchain.EthereumPowTestnet, - Blockchain.Kaspa, - Blockchain.Telos, - Blockchain.TelosTestnet, - Blockchain.TON, - Blockchain.TONTestnet, - Blockchain.Ravencoin, - Blockchain.RavencoinTestnet, - Blockchain.TerraV1, - Blockchain.TerraV2, - Blockchain.Cronos, - Blockchain.AlephZero, - Blockchain.AlephZeroTestnet, - Blockchain.OctaSpace, - Blockchain.OctaSpaceTestnet, - Blockchain.Chia, - Blockchain.ChiaTestnet, - Blockchain.Decimal, - Blockchain.DecimalTestnet, - Blockchain.XDC, - Blockchain.XDCTestnet, - Blockchain.Playa3ull, - Blockchain.Shibarium, - Blockchain.ShibariumTestnet, - Blockchain.Algorand, - Blockchain.AlgorandTestnet, - Blockchain.Hedera, - Blockchain.HederaTestnet, - Blockchain.Aurora, - Blockchain.AuroraTestnet, - Blockchain.Areon, - Blockchain.AreonTestnet, - Blockchain.PulseChain, - Blockchain.PulseChainTestnet, - Blockchain.ZkSyncEra, - Blockchain.ZkSyncEraTestnet, - Blockchain.Nexa, - Blockchain.NexaTestnet, - Blockchain.Moonbeam, - Blockchain.MoonbeamTestnet, - Blockchain.Manta, - Blockchain.MantaTestnet, - Blockchain.PolygonZkEVM, - Blockchain.PolygonZkEVMTestnet, - Blockchain.Radiant, - Blockchain.Base, - Blockchain.BaseTestnet, - Blockchain.Moonriver, - Blockchain.MoonriverTestnet, - Blockchain.Mantle, - Blockchain.MantleTestnet, - Blockchain.Flare, - Blockchain.FlareTestnet, - Blockchain.Taraxa, - Blockchain.TaraxaTestnet, - Blockchain.Koinos, - Blockchain.KoinosTestnet, - Blockchain.Joystream, - Blockchain.Bittensor, - Blockchain.Blast, - Blockchain.BlastTestnet, - Blockchain.Cyber, - Blockchain.CyberTestnet, - Blockchain.InternetComputer, - Blockchain.EnergyWebChain, - Blockchain.EnergyWebChainTestnet, - Blockchain.EnergyWebX, - Blockchain.EnergyWebXTestnet, - Blockchain.Casper, - Blockchain.CasperTestnet, - Blockchain.Core, - Blockchain.CoreTestnet, - Blockchain.Xodex, - Blockchain.Canxium, + Unknown, + Arbitrum, + ArbitrumTestnet, + Avalanche, + AvalancheTestnet, + Binance, + BinanceTestnet, + BSC, + BSCTestnet, + Bitcoin, + BitcoinTestnet, + BitcoinCash, + BitcoinCashTestnet, + Cardano, + Cosmos, + CosmosTestnet, + Dogecoin, + Ducatus, + Ethereum, + EthereumTestnet, + EthereumClassic, + EthereumClassicTestnet, + Fantom, + FantomTestnet, + Litecoin, + Near, + NearTestnet, + Polkadot, + PolkadotTestnet, + Kava, + KavaTestnet, + Kusama, + Polygon, + PolygonTestnet, + RSK, + Sei, + SeiTestnet, + Stellar, + StellarTestnet, + Solana, + SolanaTestnet, + Tezos, + Tron, + TronTestnet, + XRP, + Gnosis, + Dash, + Optimism, + OptimismTestnet, + Dischain, + EthereumPow, + EthereumPowTestnet, + Kaspa, + Telos, + TelosTestnet, + TON, + TONTestnet, + Ravencoin, + RavencoinTestnet, + TerraV1, + TerraV2, + Cronos, + AlephZero, + AlephZeroTestnet, + OctaSpace, + OctaSpaceTestnet, + Chia, + ChiaTestnet, + Decimal, + DecimalTestnet, + XDC, + XDCTestnet, + Playa3ull, + Shibarium, + ShibariumTestnet, + Algorand, + AlgorandTestnet, + Hedera, + HederaTestnet, + Aurora, + AuroraTestnet, + Areon, + AreonTestnet, + PulseChain, + PulseChainTestnet, + ZkSyncEra, + ZkSyncEraTestnet, + Nexa, + NexaTestnet, + Moonbeam, + MoonbeamTestnet, + Manta, + MantaTestnet, + PolygonZkEVM, + PolygonZkEVMTestnet, + Radiant, + Base, + BaseTestnet, + Moonriver, + MoonriverTestnet, + Mantle, + MantleTestnet, + Flare, + FlareTestnet, + Taraxa, + TaraxaTestnet, + Koinos, + KoinosTestnet, + Joystream, + Bittensor, + Blast, + BlastTestnet, + Cyber, + CyberTestnet, + InternetComputer, + EnergyWebChain, + EnergyWebChainTestnet, + EnergyWebX, + EnergyWebXTestnet, + Casper, + CasperTestnet, + Core, + CoreTestnet, + Xodex, + Canxium, -> Fee.Common(feeAmount) // endregion } @@ -1592,12 +1592,25 @@ internal class SwapInteractorImpl @AssistedInject constructor( amount: BigDecimal, userWallet: UserWallet, cryptoCurrency: CryptoCurrency, - ): Either? { + ): Either { return estimateFeeUseCase( amount = amount, userWallet = userWallet, cryptoCurrency = cryptoCurrency, - ).firstOrNull() + ).mapFeeToForceSingleFeeIfNeeded(cryptoCurrency) + } + + private fun Either.mapFeeToForceSingleFeeIfNeeded( + cryptoCurrency: CryptoCurrency, + ): Either { + return this.map { + val blockchain = Blockchain.fromNetworkId(cryptoCurrency.network.backendId) + if (it is TransactionFee.Choosable && forceSingleFeeBlockchains.contains(blockchain)) { + TransactionFee.Single(normal = it.normal) + } else { + it + } + } } @Suppress("LongParameterList", "LongMethod") @@ -2211,6 +2224,17 @@ internal class SwapInteractorImpl @AssistedInject constructor( private val minDemoFee = "0.0001".toBigDecimal() private val normalDemoFee = "0.0002".toBigDecimal() private val priorityDemoFee = "0.0003".toBigDecimal() + + private val forceSingleFeeBlockchains = listOf( + Bitcoin, BitcoinTestnet, + BitcoinCash, BitcoinCashTestnet, + Litecoin, + Dogecoin, + Dash, + Kaspa, + Ravencoin, RavencoinTestnet, + Ducatus, + ) } @AssistedFactory diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index cb7a4dbffc..045a84b417 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -78,7 +78,6 @@ mlKit-barcodeScanning = "17.2.0" androidXCamera = "1.3.0" listenableFuture = "1.0" swipeRefreshLayout = "1.1.0" -spr-client = "3.6.2" web3j = "4.10.1" leakcanary = "2.13" decompose = "2.2.2" From c404846b0da9178b6c75eb0e20a8061ce085f1f1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 12 Nov 2024 13:30:25 +0500 Subject: [PATCH 37/40] Updated on 2026-08-14 --- .../staking/model/stakekit/action/StakingActionType.kt | 5 +++-- .../notifications/StakingInfoNotificationsFactory.kt | 4 +++- .../presentation/state/utils/StakingPendingActionUtils.kt | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/action/StakingActionType.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/action/StakingActionType.kt index 3d449ae82a..01e02cea2f 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/action/StakingActionType.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/action/StakingActionType.kt @@ -25,9 +25,10 @@ enum class StakingActionType { UNSTAKE -> "Unstake" CLAIM_REWARDS -> "Claim Rewards" RESTAKE_REWARDS -> "Restake Rewards" - WITHDRAW -> "Withdraw" + CLAIM_UNSTAKED, + WITHDRAW, + -> "Withdraw" RESTAKE -> "Restake" - CLAIM_UNSTAKED -> "Claim Unstaked" UNLOCK_LOCKED -> "Unlock Locked" STAKE_LOCKED -> "Stake Locked" VOTE -> "Vote" diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt index 11b61e92b9..1a527dd242 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/notifications/StakingInfoNotificationsFactory.kt @@ -86,7 +86,9 @@ internal class StakingInfoNotificationsFactory( resourceReference(R.string.staking_restake) to resourceReference(R.string.staking_notification_restake_rewards_text) } - StakingActionType.WITHDRAW -> { + StakingActionType.CLAIM_UNSTAKED, + StakingActionType.WITHDRAW, + -> { resourceReference(R.string.staking_withdraw) to resourceReference(R.string.staking_notification_withdraw_text) } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt index 558aa4252e..9287657626 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/utils/StakingPendingActionUtils.kt @@ -16,9 +16,10 @@ import kotlinx.collections.immutable.toPersistentList internal fun StakingActionType?.getPendingActionTitle(): TextReference = when (this) { StakingActionType.CLAIM_REWARDS -> resourceReference(R.string.common_claim_rewards) StakingActionType.RESTAKE_REWARDS -> resourceReference(R.string.staking_restake_rewards) - StakingActionType.WITHDRAW -> resourceReference(R.string.staking_withdraw) + StakingActionType.CLAIM_UNSTAKED, + StakingActionType.WITHDRAW, + -> resourceReference(R.string.staking_withdraw) StakingActionType.RESTAKE -> resourceReference(R.string.staking_restake) - StakingActionType.CLAIM_UNSTAKED -> resourceReference(R.string.staking_claim_unstaked) StakingActionType.UNLOCK_LOCKED -> resourceReference(R.string.staking_unlocked_locked) StakingActionType.STAKE_LOCKED -> resourceReference(R.string.staking_stake_locked) StakingActionType.VOTE -> resourceReference(R.string.staking_vote) From c49d680508db7833e78d41a3dbf1c33034f273ec Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Nov 2024 15:02:44 +0200 Subject: [PATCH 38/40] Updated on 2026-08-14 --- .../datasource/di/StakingStoreModule.kt | 2 +- .../local/token/DefaultStakingYieldsStore.kt | 28 +++++++++++++++---- .../local/token/StakingYieldsStore.kt | 7 +++-- .../data/staking/DefaultStakingRepository.kt | 27 +++++++++++++----- .../staking/GetStakingAvailabilityUseCase.kt | 2 +- .../staking/repositories/StakingRepository.kt | 2 +- .../repository/MockStakingRepository.kt | 2 +- .../TokenDetailsSkeletonStateConverter.kt | 11 +------- .../state/factory/TokenDetailsStateFactory.kt | 3 -- .../viewmodels/TokenDetailsViewModel.kt | 1 - 10 files changed, 52 insertions(+), 33 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/StakingStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/StakingStoreModule.kt index 3dda78cc57..34b84bebc9 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/StakingStoreModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/StakingStoreModule.kt @@ -17,7 +17,7 @@ internal object StakingStoreModule { @Provides @Singleton fun provideStakingTokensStore(): StakingYieldsStore { - return DefaultStakingYieldsStore() + return DefaultStakingYieldsStore(dataStore = RuntimeDataStore()) } @Provides diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultStakingYieldsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultStakingYieldsStore.kt index d30a83e4c3..eb27aad46b 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultStakingYieldsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/DefaultStakingYieldsStore.kt @@ -1,16 +1,32 @@ package com.tangem.datasource.local.token import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO +import com.tangem.datasource.local.datastore.core.StringKeyDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock -internal class DefaultStakingYieldsStore : StakingYieldsStore { +internal class DefaultStakingYieldsStore( + private val dataStore: StringKeyDataStore>, +) : StakingYieldsStore { - private var yields = listOf() + private val mutex = Mutex() - override fun get(): List { - return yields + override fun get(): Flow> { + return dataStore.get(KEY) } - override fun store(items: List) { - yields = items + override suspend fun getSync(): List { + return dataStore.getSyncOrNull(KEY) ?: emptyList() + } + + override suspend fun store(items: List) { + mutex.withLock { + dataStore.store(KEY, items) + } + } + + companion object { + private const val KEY = "DefaultStakingYieldsStore" } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/token/StakingYieldsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/token/StakingYieldsStore.kt index dc3379969d..428aa1986c 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/token/StakingYieldsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/token/StakingYieldsStore.kt @@ -1,10 +1,13 @@ package com.tangem.datasource.local.token import com.tangem.datasource.api.stakekit.models.response.model.YieldDTO +import kotlinx.coroutines.flow.Flow interface StakingYieldsStore { - fun get(): List + fun get(): Flow> - fun store(items: List) + suspend fun getSync(): List + + suspend fun store(items: List) } \ No newline at end of file diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index 3261381510..466e1346ff 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 @@ -56,7 +56,9 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.plus import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeoutOrNull import timber.log.Timber +import kotlin.time.Duration.Companion.seconds @Suppress("LargeClass", "LongParameterList", "TooManyFunctions") internal class DefaultStakingRepository( @@ -133,7 +135,7 @@ internal class DefaultStakingRepository( val rawCurrencyId = cryptoCurrencyId.rawCurrencyId ?: error("Staking custom tokens is not available") val prefetchedYield = findPrefetchedYield( - yields = getEnabledYields(), + yields = getEnabledYieldsSync(), currencyId = rawCurrencyId, symbol = symbol, ) @@ -188,7 +190,7 @@ internal class DefaultStakingRepository( } } - override fun getStakingAvailability( + override suspend fun getStakingAvailability( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, ): StakingAvailability { @@ -199,7 +201,7 @@ internal class DefaultStakingRepository( val isSupportedInMobileApp = getSupportedIntegrationId(cryptoCurrency.id).isNullOrEmpty().not() val prefetchedYield = findPrefetchedYield( - yields = getEnabledYields(), + yields = getEnabledYieldsSync(), currencyId = rawCurrencyId, symbol = cryptoCurrency.symbol, ) @@ -420,12 +422,22 @@ internal class DefaultStakingRepository( key = getYieldBalancesKey(userWalletId), skipCache = refresh, block = { - val yields = getEnabledYields().ifEmpty { + val yieldDTOs = withTimeoutOrNull(YIELDS_WATITING_TIMEOUT) { + runCatching { stakingYieldsStore.get().firstOrNull() }.getOrNull() + } + + if (yieldDTOs == null) { Timber.i("No enabled yields for $userWalletId") stakingBalanceStore.store(userWalletId, emptySet()) return@invokeOnExpire } + + val yields = yieldConverter.convertListIgnoreErrors( + input = yieldDTOs, + onError = { Timber.e("Error converting one of the items in enabled yields: $it") }, + ) + val availableCurrencies = cryptoCurrencies .mapNotNull { currency -> val addresses = walletManagersFacade.getAddresses(userWalletId, currency.network) @@ -442,7 +454,6 @@ internal class DefaultStakingRepository( } .map { getBalanceRequestData(it.first.value, it.second) } .ifEmpty { - Timber.i("No yield balances available for $userWalletId") stakingBalanceStore.store(userWalletId, emptySet()) cacheRegistry.invalidate(getYieldBalancesKey(userWalletId)) @@ -586,9 +597,9 @@ internal class DefaultStakingRepository( } } - private fun getEnabledYields(): List { + private suspend fun getEnabledYieldsSync(): List { return yieldConverter.convertListIgnoreErrors( - input = stakingYieldsStore.get(), + input = stakingYieldsStore.getSync(), onError = { Timber.e("Error converting one of the items in enabled yields: $it") }, ) } @@ -636,6 +647,8 @@ internal class DefaultStakingRepository( const val ETHEREUM_POLYGON_APPROVE_SPENDER = "0x5e3Ef299fDDf15eAa0432E6e66473ace8c13D908" + val YIELDS_WATITING_TIMEOUT = 15.seconds + val INVALID_BATCHES_FOR_SOLANA = listOf("AC01", "CB79") // uncomment items as implementation is ready diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingAvailabilityUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingAvailabilityUseCase.kt index b2ab9c2b1e..7133219a03 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingAvailabilityUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/GetStakingAvailabilityUseCase.kt @@ -16,7 +16,7 @@ class GetStakingAvailabilityUseCase( private val stakingErrorResolver: StakingErrorResolver, ) { - operator fun invoke( + suspend operator fun invoke( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, ): Either { diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt index e68adf73a9..cc53ade06f 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt @@ -33,7 +33,7 @@ interface StakingRepository { suspend fun getYield(cryptoCurrencyId: CryptoCurrency.ID, symbol: String): Yield - fun getStakingAvailability(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): StakingAvailability + suspend fun getStakingAvailability(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): StakingAvailability suspend fun getActions( userWalletId: UserWalletId, diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt index fa3e6119d7..b66b4ccaf9 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt @@ -112,7 +112,7 @@ class MockStakingRepository : StakingRepository { isAvailable = false, ) - override fun getStakingAvailability( + override suspend fun getStakingAvailability( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, ): StakingAvailability = StakingAvailability.Unavailable diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt index 0f92424ba2..98850f08f2 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt @@ -10,9 +10,7 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.card.NetworkHasDerivationUseCase -import com.tangem.domain.staking.GetStakingAvailabilityUseCase import com.tangem.domain.staking.GetStakingIntegrationIdUseCase -import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network import com.tangem.domain.wallets.models.UserWalletId @@ -32,7 +30,6 @@ internal class TokenDetailsSkeletonStateConverter( private val clickIntents: TokenDetailsClickIntents, private val networkHasDerivationUseCase: NetworkHasDerivationUseCase, private val getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase, - private val getStakingAvailabilityUseCase: GetStakingAvailabilityUseCase, private val getUserWalletUseCase: GetUserWalletUseCase, private val userWalletId: UserWalletId, ) : Converter { @@ -42,8 +39,6 @@ internal class TokenDetailsSkeletonStateConverter( override fun convert(value: CryptoCurrency): TokenDetailsState { val iconState = iconStateConverter.convert(value) val isSupportedInMobileApp = getStakingIntegrationIdUseCase(value.id).isNullOrBlank().not() - val stakingAvailability = getStakingAvailabilityUseCase(userWalletId, value) - .getOrElse { StakingAvailability.Unavailable } return TokenDetailsState( topAppBarConfig = TokenDetailsTopAppBarConfig( @@ -68,11 +63,7 @@ internal class TokenDetailsSkeletonStateConverter( selectedBalanceType = BalanceType.ALL, ), marketPriceBlockState = MarketPriceBlockState.Loading(value.symbol), - stakingBlocksState = if (stakingAvailability == StakingAvailability.TemporaryUnavailable) { - StakingBlockUM.TemporaryUnavailable - } else { - StakingBlockUM.Loading(iconState).takeIf { isSupportedInMobileApp } - }, + stakingBlocksState = StakingBlockUM.Loading(iconState).takeIf { isSupportedInMobileApp }, notifications = persistentListOf(), pendingTxs = persistentListOf(), swapTxs = persistentListOf(), 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 08c2f45190..c3bc29d4a4 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 @@ -17,7 +17,6 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.NetworkHasDerivationUseCase import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.staking.GetStakingAvailabilityUseCase import com.tangem.domain.staking.GetStakingIntegrationIdUseCase import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingEntryInfo @@ -59,7 +58,6 @@ internal class TokenDetailsStateFactory( private val userWalletId: UserWalletId, stakingFeatureToggles: StakingFeatureToggles, getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase, - getStakingAvailabilityUseCase: GetStakingAvailabilityUseCase, symbol: String, decimals: Int, ) { @@ -69,7 +67,6 @@ internal class TokenDetailsStateFactory( clickIntents = clickIntents, networkHasDerivationUseCase = networkHasDerivationUseCase, getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase, - getStakingAvailabilityUseCase = getStakingAvailabilityUseCase, getUserWalletUseCase = getUserWalletUseCase, userWalletId = userWalletId, ) 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 1427b63a24..34c9d9f591 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 @@ -166,7 +166,6 @@ internal class TokenDetailsViewModel @Inject constructor( userWalletId = userWalletId, stakingFeatureToggles = stakingFeatureToggles, getStakingIntegrationIdUseCase = getStakingIntegrationIdUseCase, - getStakingAvailabilityUseCase = getStakingAvailabilityUseCase, symbol = cryptoCurrency.symbol, decimals = cryptoCurrency.decimals, ) From 4892000161628143a2be89385b546a2df8a4b287 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Nov 2024 18:23:07 +0300 Subject: [PATCH 39/40] Updated on 2026-08-14 --- .../src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt | 1 + 1 file changed, 1 insertion(+) 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 41bc95a6f8..52bc74e7f3 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 @@ -411,4 +411,5 @@ private val excludedBlockchains = listOf( Blockchain.Unknown, Blockchain.Nexa, Blockchain.NexaTestnet, + Blockchain.Xodex, ) \ No newline at end of file From 623d795a393c1c08e8113985d6201a24a7fb6f7f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 14 Nov 2024 10:15:43 +0300 Subject: [PATCH 40/40] 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 045a84b417..31944d51c4 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -87,7 +87,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.18-859" +tangemBlockchainSdk = "release-app_5.18-862" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.18-398" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^