From 22ba475aae997aa6470615ac85b8e2576963e367 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 8 Oct 2024 15:40:17 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../data/staking/DefaultStakingRepository.kt | 4 ++++ .../staking/PendingTransactionItemConverter.kt | 1 + .../staking/converters/YieldBalanceConverter.kt | 9 +++++++-- .../domain/staking/model/PendingTransaction.kt | 2 ++ .../domain/staking/model/stakekit/Yield.kt | 2 ++ .../staking/model/stakekit/YieldBalance.kt | 1 + .../staking/model/stakekit/YieldBalanceList.kt | 4 ++-- .../GetConstructedStakingTransactionUseCase.kt | 4 +++- .../InvalidatePendingTransactionsUseCase.kt | 1 + .../staking/repositories/StakingRepository.kt | 2 ++ .../operations/CurrencyStatusOperations.kt | 17 ++++++++++++++--- .../tokens/repository/MockStakingRepository.kt | 2 ++ .../helpers/StakingFeeTransactionLoader.kt | 5 +++-- .../state/helpers/StakingTransactionSender.kt | 13 +++++++++++-- .../SetPossiblePendingTransactionTransformer.kt | 11 +++++++++-- .../presentation/viewmodel/StakingViewModel.kt | 8 ++++++-- gradle/dependencies.toml | 2 +- .../tangem/blockchainsdk/utils/Blockchain.kt | 9 +++++++++ 18 files changed, 80 insertions(+), 17 deletions(-) diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index a5b1e7b030..d7f207506b 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 @@ -4,12 +4,14 @@ import android.util.Base64 import arrow.core.getOrElse import arrow.core.raise.catch import com.squareup.moshi.Moshi +import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.TransactionStatus import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.utils.toCoinId +import com.tangem.blockchainsdk.utils.toMigratedCointId import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toCompressedPublicKey import com.tangem.data.common.cache.CacheRegistry @@ -269,6 +271,7 @@ internal class DefaultStakingRepository( override suspend fun constructTransaction( networkId: String, fee: Fee, + amount: Amount, transactionId: String, ): Pair { return withContext(dispatchers.io) { @@ -282,6 +285,7 @@ internal class DefaultStakingRepository( val transactionData = TransactionData.Compiled( value = getTransactionDataType(networkId, unsignedTransaction), fee = fee, + amount = amount, status = TransactionStatus.Unconfirmed, ) diff --git a/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt b/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt index 6169fc2b96..026c1435bd 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/PendingTransactionItemConverter.kt @@ -10,6 +10,7 @@ internal object PendingTransactionItemConverter : Converter { private val pendingActionConverter by lazy(LazyThreadSafetyMode.NONE) { PendingActionConverter() } - + private val networkTypeConverter by lazy(LazyThreadSafetyMode.NONE) { StakingNetworkTypeConverter() } + private val tokenConverter by lazy(LazyThreadSafetyMode.NONE) { TokenConverter(networkTypeConverter) } override fun convert(value: YieldBalanceWrapperDTO): YieldBalance { return if (value.balances.isEmpty()) { YieldBalance.Empty @@ -19,6 +23,7 @@ internal class YieldBalanceConverter : Converter BalanceItem( groupId = item.groupId, + token = tokenConverter.convert(item.tokenDTO), type = BalanceType.valueOf(item.type.name), amount = item.amount, rawCurrencyId = item.tokenDTO.coinGeckoId, diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt index b01eab990b..408de7dc0d 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/PendingTransaction.kt @@ -1,11 +1,13 @@ package com.tangem.domain.staking.model import com.tangem.domain.staking.model.stakekit.BalanceType +import com.tangem.domain.staking.model.stakekit.Token import com.tangem.domain.staking.model.stakekit.Yield import java.math.BigDecimal data class PendingTransaction( val groupId: String?, + val token: Token, val type: BalanceType, val amount: BigDecimal, val rawCurrencyId: String?, diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt index 768d65cbf4..a2a0a6b7fe 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/Yield.kt @@ -130,6 +130,8 @@ data class Yield( APR, // simple rate UNKNOWN, } + + fun getCurrentToken(rawCurrencyId: String?) = tokens.firstOrNull { rawCurrencyId == it.coinGeckoId } ?: token } @Serializable diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt index 01ac5388c9..d1f0f3a3f3 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalance.kt @@ -54,6 +54,7 @@ data class YieldBalanceItem( data class BalanceItem( val groupId: String, + val token: Token, val type: BalanceType, val amount: BigDecimal, val rawCurrencyId: String?, diff --git a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalanceList.kt b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalanceList.kt index 7b8e993734..c1f4ec5e38 100644 --- a/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalanceList.kt +++ b/domain/staking/models/src/main/kotlin/com/tangem/domain/staking/model/stakekit/YieldBalanceList.kt @@ -11,8 +11,8 @@ sealed class YieldBalanceList { val data = yieldBalance as? YieldBalance.Data val balance = data?.balance - val isCorrectAddress = balance?.items?.any { address == data.address } == true - val isCorrectIntegration = balance?.integrationId == integrationId + val isCorrectAddress = address != null && address == data?.address + val isCorrectIntegration = integrationId != null && balance?.integrationId == integrationId isCorrectIntegration && isCorrectAddress } ?: YieldBalance.Error diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/GetConstructedStakingTransactionUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/GetConstructedStakingTransactionUseCase.kt index 9e31da097f..38ae3bf571 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/GetConstructedStakingTransactionUseCase.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/GetConstructedStakingTransactionUseCase.kt @@ -1,6 +1,7 @@ package com.tangem.domain.staking import arrow.core.Either +import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.Fee import com.tangem.domain.staking.model.stakekit.StakingError @@ -16,9 +17,10 @@ class GetConstructedStakingTransactionUseCase( suspend operator fun invoke( networkId: String, fee: Fee, + amount: Amount, transactionId: String, ): Either> = Either.catch { - stakingRepository.constructTransaction(networkId, fee, transactionId) + stakingRepository.constructTransaction(networkId, fee, amount, transactionId) }.mapLeft { stakingErrorResolver.resolve(it) } diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt b/domain/staking/src/main/java/com/tangem/domain/staking/InvalidatePendingTransactionsUseCase.kt index 3ed9de1336..ca5a993891 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 @@ -67,6 +67,7 @@ class InvalidatePendingTransactionsUseCase( validatorAddress = pendingTransaction.validator?.address, date = null, pendingActions = emptyList(), + token = pendingTransaction.token, isPending = true, ) } 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 e82d0fb40a..dd28e33b38 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 @@ -1,5 +1,6 @@ package com.tangem.domain.staking.repositories +import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.Fee import com.tangem.domain.core.lce.LceFlow @@ -71,6 +72,7 @@ interface StakingRepository { suspend fun constructTransaction( networkId: String, fee: Fee, + amount: Amount, transactionId: String, ): Pair diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt index abdf3f94fd..6cbaf22b37 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt @@ -57,9 +57,20 @@ internal class CurrencyStatusOperations( val hasCurrentNetworkTransactions = status.pendingTransactions.isNotEmpty() val currentTransactions = status.pendingTransactions.getOrElse(currency.id, ::emptySet) - val isCurrentAddressStaking = - (yieldBalance as? YieldBalance.Data)?.address == status.address.defaultAddress.value - val currentYieldBalance = yieldBalance.takeIf { isCurrentAddressStaking } + val yieldBalanceData = yieldBalance as? YieldBalance.Data + val isCurrentAddressStaking = yieldBalanceData?.address == status.address.defaultAddress.value + val filteredTokenBalances = yieldBalanceData?.balance?.items?.filter { + it.token.coinGeckoId == currency.id.rawCurrencyId + } + val currentYieldBalance = if (isCurrentAddressStaking && filteredTokenBalances?.isNotEmpty() == true) { + yieldBalanceData.copy( + balance = yieldBalanceData.balance.copy( + items = filteredTokenBalances, + ), + ) + } else { + null + } return when { ignoreQuote -> CryptoCurrencyStatus.NoQuote( amount = amount, 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 647a1a411d..0a3071805b 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 @@ -1,5 +1,6 @@ package com.tangem.domain.tokens.repository +import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.TransactionStatus import com.tangem.blockchain.common.transaction.Fee @@ -217,6 +218,7 @@ class MockStakingRepository : StakingRepository { override suspend fun constructTransaction( networkId: String, fee: Fee, + amount: Amount, transactionId: String, ): Pair = StakingTransaction( id = "id", diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt index 4eb21da85c..33559d6211 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingFeeTransactionLoader.kt @@ -12,6 +12,7 @@ import com.tangem.domain.staking.model.StakingApproval import com.tangem.domain.staking.model.stakekit.PendingAction import com.tangem.domain.staking.model.stakekit.StakingError import com.tangem.domain.staking.model.stakekit.Yield +import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.domain.staking.model.stakekit.transaction.ActionParams import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -66,7 +67,7 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor( val validatorAddress = validatorState.chosenValidator.address val approval = stakingApproval as? StakingApproval.Needed - if (approval != null) { + if (approval != null && state.actionType == StakingActionCommonType.ENTER) { val allowance = getAllowanceUseCase( userWalletId = userWallet.walletId, cryptoCurrency = cryptoCurrencyStatus.currency, @@ -182,7 +183,7 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor( amount = amount, address = sourceAddress, validatorAddress = validatorAddress, - token = yield.token, + token = yield.getCurrentToken(cryptoCurrencyStatus.currency.id.rawCurrencyId), passthrough = action?.passthrough, type = action?.type, ), diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt index 4b1bdd6992..f334c448f4 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingTransactionSender.kt @@ -18,6 +18,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.transaction.usecase.SendTransactionUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase +import com.tangem.domain.utils.convertToSdkAmount import com.tangem.domain.wallets.models.UserWallet import com.tangem.features.staking.impl.analytics.StakingAnalyticsEvents import com.tangem.features.staking.impl.presentation.state.* @@ -67,6 +68,7 @@ internal class StakingTransactionSender @AssistedInject constructor( ?: error("No confirmation state") val fee = (confirmationState.feeState as? FeeState.Content)?.fee ?: error("No fee provided") + val amountState = state.amountState as? AmountState.Data ?: error("No amount state") val stakingTransactions = getStakingTransactions( state = state, @@ -77,6 +79,7 @@ internal class StakingTransactionSender @AssistedInject constructor( val fullTransactionsData = getConstructedTransactions( stakingTransactions = stakingTransactions, fee = fee, + amount = amountState.amountTextField.cryptoAmount.value.orZero(), onConstructError = onConstructError, ) @@ -131,6 +134,7 @@ internal class StakingTransactionSender @AssistedInject constructor( private suspend fun getConstructedTransactions( stakingTransactions: List?, fee: Fee, + amount: BigDecimal, onConstructError: (StakingError) -> Unit, ) = coroutineScope { stakingTransactions @@ -142,6 +146,7 @@ internal class StakingTransactionSender @AssistedInject constructor( getConstructedStakingTransactionUseCase( networkId = cryptoCurrencyStatus.currency.network.id.value, fee = fee, + amount = amount.convertToSdkAmount(cryptoCurrencyStatus.currency), transactionId = transaction.id, ).fold( ifRight = { (constructedTransaction, transactionData) -> @@ -188,7 +193,7 @@ internal class StakingTransactionSender @AssistedInject constructor( amount = amount, address = defaultAddress, validatorAddress = validatorAddress, - token = yield.token, + token = yield.getCurrentToken(cryptoCurrencyStatus.currency.id.rawCurrencyId), passthrough = action?.passthrough, type = action?.type, ), @@ -287,13 +292,17 @@ internal class StakingTransactionSender @AssistedInject constructor( private fun composeStakeTransaction(confirmationState: StakingStates.ConfirmationState.Data): PendingTransaction { val state = stateController.value + val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data + val token = yield.getCurrentToken(cryptoCurrencyStatus.currency.id.rawCurrencyId) + return PendingTransaction( groupId = UUID.randomUUID().toString(), + token = token, type = BalanceType.STAKED, amount = (state.amountState as? AmountState.Data)?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO, rawCurrencyId = cryptoCurrencyStatus.currency.id.rawCurrencyId, validator = (confirmationState.validatorState as? ValidatorState.Content)?.chosenValidator, - balancesId = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.getBalancesUniqueId() ?: 0, + balancesId = yieldBalance?.getBalancesUniqueId() ?: 0, ) } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt index a176330f37..26508e49b5 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetPossiblePendingTransactionTransformer.kt @@ -1,12 +1,16 @@ package com.tangem.features.staking.impl.presentation.state.transformers import com.tangem.domain.staking.model.PendingTransaction +import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.features.staking.impl.presentation.state.* +import com.tangem.features.staking.impl.presentation.state.BalanceState +import com.tangem.features.staking.impl.presentation.state.StakingStates +import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.utils.transformer.Transformer internal class SetPossiblePendingTransactionTransformer( + private val yield: Yield, private val balanceState: BalanceState, private val cryptoCurrencyStatus: CryptoCurrencyStatus, ) : Transformer { @@ -14,13 +18,16 @@ internal class SetPossiblePendingTransactionTransformer( override fun transform(prevState: StakingUiState): StakingUiState { val possibleConfirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState + val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data - val balancesId = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.getBalancesUniqueId() ?: 0 + val balancesId = yieldBalance?.getBalancesUniqueId() ?: 0 + val token = yield.getCurrentToken(cryptoCurrencyStatus.currency.id.rawCurrencyId) return prevState.copy( confirmationState = possibleConfirmationState.copy( possiblePendingTransaction = PendingTransaction( groupId = balanceState.groupId, + token = token, type = balanceState.type, amount = balanceState.cryptoDecimal, rawCurrencyId = balanceState.rawCurrencyId, 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 610fb00eac..a58ede27d2 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 @@ -232,7 +232,9 @@ internal class StakingViewModel @Inject constructor( ), ) if (balanceState != null) { - stateController.update(SetPossiblePendingTransactionTransformer(balanceState, cryptoCurrencyStatus)) + stateController.update( + SetPossiblePendingTransactionTransformer(yield, balanceState, cryptoCurrencyStatus), + ) } stateController.update( @@ -252,7 +254,9 @@ internal class StakingViewModel @Inject constructor( isAssentState() -> { getFee(pendingAction, pendingActions) if (balanceState != null) { - stateController.update(SetPossiblePendingTransactionTransformer(balanceState, cryptoCurrencyStatus)) + stateController.update( + SetPossiblePendingTransactionTransformer(yield, balanceState, cryptoCurrencyStatus), + ) } val amountState = value.amountState as? AmountState.Data if (amountState?.amountTextField?.isWarning == true) { diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index f47d1ba1dd..22f0f9ac29 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -89,7 +89,7 @@ markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-816" +tangemBlockchainSdk = "develop-817" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-387" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt index 126cc48617..c8e9fca637 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 @@ -366,6 +366,15 @@ fun Blockchain.toCoinId(): String { } } +/** + * New CoinId to existing coin "id" field. + * To support both old and new coin id. + */ +fun Blockchain.toMigratedCointId(): String = when (this) { + Blockchain.Polygon, Blockchain.PolygonTestnet -> "polygon-ecosystem-token" + else -> toCoinId() +} + fun Blockchain.isSupportedInApp(): Boolean { return !excludedBlockchains.contains(this) }