diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt index 5ecb7dc55c..a175478e41 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt @@ -7,7 +7,6 @@ import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.FeeStateFactory import com.tangem.features.send.impl.presentation.state.fee.FeeType -import com.tangem.features.send.impl.presentation.state.fee.getFee import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.utils.Provider import java.math.BigDecimal @@ -58,7 +57,7 @@ internal class SendEventStateFactory( } val newFeeValue = newFee.amount.value ?: BigDecimal.ZERO - val oldFeeValue = feeSelector.getFee().amount.value ?: BigDecimal.ZERO + val oldFeeValue = feeStateFactory.feeConverter.convert(feeSelector).amount.value ?: BigDecimal.ZERO val updateFeeState = feeStateFactory.onFeeOnLoadedState(fee) return if (newFeeValue > oldFeeValue) { updateFeeState.copy( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt index 0e13e65c4f..16d0b6190a 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt @@ -43,6 +43,7 @@ internal sealed class SendStates { abstract val isPrimaryButtonEnabled: Boolean /** Amount state */ + @Stable data class AmountState( override val type: SendUiStateType = SendUiStateType.Amount, override val isPrimaryButtonEnabled: Boolean, @@ -54,6 +55,7 @@ internal sealed class SendStates { ) : SendStates() /** Recipient state */ + @Stable data class RecipientState( override val type: SendUiStateType = SendUiStateType.Recipient, override val isPrimaryButtonEnabled: Boolean, @@ -65,6 +67,7 @@ internal sealed class SendStates { ) : SendStates() /** Fee and speed state */ + @Stable data class FeeState( override val type: SendUiStateType = SendUiStateType.Fee, override val isPrimaryButtonEnabled: Boolean = false, @@ -82,6 +85,7 @@ internal sealed class SendStates { ) : SendStates() /** Send state */ + @Stable data class SendState( override val type: SendUiStateType = SendUiStateType.Send, override val isPrimaryButtonEnabled: Boolean = true, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt index cd0877210f..16d58d39a6 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt @@ -1,8 +1,6 @@ package com.tangem.features.send.impl.presentation.state.fee import com.tangem.blockchain.common.transaction.Fee -import com.tangem.blockchain.common.transaction.TransactionFee -import com.tangem.blockchain.extensions.toBigDecimalOrDefault import com.tangem.features.send.impl.presentation.state.SendUiState import java.math.BigDecimal @@ -13,28 +11,4 @@ internal fun calculateReceiveAmount(state: SendUiState, feeAmount: Fee): BigDeci val amountValue = state.amountState?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO val fee = feeAmount.amount.value ?: return BigDecimal.ZERO return amountValue.minus(fee) -} - -/** - * Returns fee amount depending on current state - */ -internal fun FeeSelectorState.Content.getFee(): Fee { - return when (fees) { - is TransactionFee.Choosable -> { - when (selectedFee) { - FeeType.SLOW -> fees.minimum - FeeType.MARKET -> fees.normal - FeeType.FAST -> fees.priority - FeeType.CUSTOM -> { - val feeAmount = customValues.firstOrNull()?.value.toBigDecimalOrDefault() - Fee.Common( - fees.normal.amount.copy( - value = feeAmount, - ), - ) - } - } - } - is TransactionFee.Single -> fees.normal - } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeConverter.kt new file mode 100644 index 0000000000..c14db17575 --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeConverter.kt @@ -0,0 +1,60 @@ +package com.tangem.features.send.impl.presentation.state.fee + +import com.tangem.blockchain.common.transaction.Fee +import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.core.ui.utils.parseToBigDecimal +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter +import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents +import com.tangem.utils.Provider +import com.tangem.utils.converter.Converter + +internal class FeeConverter( + private val clickIntents: SendClickIntents, + private val appCurrencyProvider: Provider, + private val cryptoCurrencyStatusProvider: Provider, +) : Converter { + + private val ethereumCustomFeeConverter by lazy { + EthereumCustomFeeConverter( + clickIntents = clickIntents, + appCurrencyProvider = appCurrencyProvider, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + ) + } + + override fun convert(value: FeeSelectorState.Content): Fee { + return when (val fees = value.fees) { + is TransactionFee.Choosable -> { + when (value.selectedFee) { + FeeType.SLOW -> fees.minimum + FeeType.MARKET -> fees.normal + FeeType.FAST -> fees.priority + FeeType.CUSTOM -> convertCustom(value, fees) + } + } + is TransactionFee.Single -> fees.normal + } + } + + private fun convertCustom(feeSelectorState: FeeSelectorState.Content, fees: TransactionFee): Fee { + val customValues = feeSelectorState.customValues + val normalFee = fees.normal + return if (customValues.isEmpty()) { + normalFee + } else { + when (normalFee) { + is Fee.Ethereum -> ethereumCustomFeeConverter.convertBack(normalFee = normalFee, value = customValues) + else -> { + val customFee = customValues.firstOrNull() + Fee.Common( + normalFee.amount.copy( + value = customFee?.value?.parseToBigDecimal(customFee.decimals), + ), + ) + } + } + } + } +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt index 822c7f43af..244ed17cdd 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt @@ -3,6 +3,7 @@ package com.tangem.features.send.impl.presentation.state.fee import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.core.ui.extensions.networkIconResId +import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.tokens.GetBalanceNotEnoughForFeeWarningUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning @@ -14,6 +15,7 @@ import com.tangem.features.send.impl.presentation.state.SendUiStateType import com.tangem.features.send.impl.presentation.state.fields.SendTextField import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.utils.Provider +import com.tangem.utils.toFormattedString import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.filter @@ -65,7 +67,8 @@ internal class FeeNotificationFactory( ) { val multipleFees = transactionFee as? TransactionFee.Choosable ?: return val minimumValue = multipleFees.minimum.amount.value ?: return - val customValue = customFee.firstOrNull()?.value?.toBigDecimalOrNull() ?: return + val customAmount = customFee.firstOrNull() ?: return + val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals) if (selectedFee == FeeType.CUSTOM && minimumValue > customValue) { add(SendFeeNotification.Informational.TooLow) } @@ -78,10 +81,11 @@ internal class FeeNotificationFactory( ) { val multipleFees = transactionFee as? TransactionFee.Choosable ?: return val highValue = multipleFees.priority.amount.value ?: return - val customValue = customFee.firstOrNull()?.value?.toBigDecimalOrNull() ?: return + val customAmount = customFee.firstOrNull() ?: return + val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals) val diff = customValue / highValue if (selectedFee == FeeType.CUSTOM && diff > FEE_MAX_DIFF) { - add(SendFeeNotification.Warning.TooHigh(diff.toInt().toString())) + add(SendFeeNotification.Warning.TooHigh(diff.toFormattedString(HIGH_FEE_DIFF_DECIMALS))) } } @@ -158,5 +162,6 @@ internal class FeeNotificationFactory( companion object { private val FEE_MAX_DIFF = BigDecimal(5) + private const val HIGH_FEE_DIFF_DECIMALS = 0 } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt index c03cfaef65..fcbfd66705 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt @@ -2,7 +2,9 @@ package com.tangem.features.send.impl.presentation.state.fee import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.common.extensions.isZero import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase @@ -10,10 +12,8 @@ import com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.utils.Provider -import com.tangem.utils.isNullOrZero import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList import java.math.BigDecimal /** @@ -31,6 +31,15 @@ internal class FeeStateFactory( SendFeeCustomFieldConverter( clickIntents = clickIntents, appCurrencyProvider = appCurrencyProvider, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + ) + } + + val feeConverter by lazy { + FeeConverter( + clickIntents = clickIntents, + appCurrencyProvider = appCurrencyProvider, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, ) } @@ -58,7 +67,7 @@ internal class FeeStateFactory( customValues = customFeeFieldConverter.convert(fees.normal), ) - val fee = feeSelectorState.getFee() + val fee = feeConverter.convert(feeSelectorState) val receivedAmount = calculateReceiveAmount(state, fee) return state.copy( feeState = feeState.copy( @@ -83,7 +92,7 @@ internal class FeeStateFactory( fees = fees, customValues = customFeeFieldConverter.convert(fees.normal), ) - val fee = updatedFeeSelector.getFee() + val fee = feeConverter.convert(updatedFeeSelector) val receivedAmount = calculateReceiveAmount(state, fee) return state.copy( feeState = feeState.copy( @@ -112,7 +121,7 @@ internal class FeeStateFactory( val balance = coinCryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO val updatedFeeSelectorState = feeSelectorState.copy(selectedFee = feeType) - val fee = updatedFeeSelectorState.getFee() + val fee = feeConverter.convert(updatedFeeSelectorState) val receivedAmount = calculateReceiveAmount(state, fee) return state.copy( feeState = feeState.copy( @@ -129,14 +138,10 @@ internal class FeeStateFactory( val state = currentStateProvider() val feeState = state.feeState ?: return state val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state - val updatedFeeSelectorState = feeSelectorState.copy( - customValues = feeSelectorState.customValues.toMutableList().apply { - set(index, feeSelectorState.customValues[index].copy(value = value)) - }.toImmutableList(), - ) + val updatedFeeSelectorState = customFeeFieldConverter.onValueChange(feeSelectorState, index, value) val balance = coinCryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO - val fee = updatedFeeSelectorState.getFee() + val fee = feeConverter.convert(updatedFeeSelectorState) val receivedAmount = calculateReceiveAmount(state, fee) return state.copy( feeState = feeState.copy( @@ -153,7 +158,7 @@ internal class FeeStateFactory( val state = currentStateProvider() val feeState = state.feeState ?: return state val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state - val fee = feeSelectorState.getFee() + val fee = feeConverter.convert(feeSelectorState) val receivedAmount = calculateReceiveAmount(state, fee) return state.copy( feeState = feeState.copy( @@ -181,13 +186,17 @@ internal class FeeStateFactory( notifications: ImmutableList, ): Boolean { val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return false - val customValue = feeSelectorState.customValues.firstOrNull()?.value?.toBigDecimalOrNull() + val customValue = feeSelectorState.customValues.firstOrNull() val balance = coinCryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO - val fee = feeSelectorState.getFee() + val fee = feeConverter.convert(feeSelectorState) val feeValue = fee.amount.value ?: BigDecimal.ZERO val isNotCustom = feeSelectorState.selectedFee != FeeType.CUSTOM - val isNotEmptyCustom = !customValue.isNullOrZero() && !isNotCustom + val isNotEmptyCustom = if (customValue != null) { + !customValue.value.parseToBigDecimal(customValue.decimals).isZero() && !isNotCustom + } else { + false + } val noErrors = notifications.none { it is SendFeeNotification.Error } val isSubtractRequired = when { !feeState.isSubtractAvailable -> true // current currency is not fee currency diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeCustomFieldConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeCustomFieldConverter.kt index d6d6168f37..24c85ee305 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeCustomFieldConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeCustomFieldConverter.kt @@ -1,79 +1,47 @@ package com.tangem.features.send.impl.presentation.state.fee -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType import com.tangem.blockchain.common.transaction.Fee -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringReference -import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.features.send.impl.R +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter import com.tangem.features.send.impl.presentation.state.fields.SendTextField import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.utils.Provider import com.tangem.utils.converter.Converter -import com.tangem.utils.toFormattedString import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf internal class SendFeeCustomFieldConverter( private val clickIntents: SendClickIntents, private val appCurrencyProvider: Provider, + private val cryptoCurrencyStatusProvider: Provider, ) : Converter> { + private val ethereumCustomFeeConverter by lazy { + EthereumCustomFeeConverter( + clickIntents = clickIntents, + appCurrencyProvider = appCurrencyProvider, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + ) + } + override fun convert(value: Fee): ImmutableList { - val ethereumFee = value as? Fee.Ethereum ?: return persistentListOf() - val appCurrency = appCurrencyProvider() - - val maxFeeFiat = BigDecimalFormatter.formatFiatAmount( - fiatAmount = ethereumFee.amount.value, - fiatCurrencyCode = appCurrency.code, - fiatCurrencySymbol = appCurrency.symbol, - ) - - return persistentListOf( - SendTextField.CustomFee( - value = ethereumFee.amount.value?.toFormattedString(ethereumFee.amount.decimals).orEmpty(), - decimals = ethereumFee.amount.decimals, - symbol = ethereumFee.amount.currencySymbol, - onValueChange = { clickIntents.onCustomFeeValueChange(0, it) }, - keyboardOptions = KeyboardOptions( - imeAction = ImeAction.Next, - keyboardType = KeyboardType.Number, - ), - title = resourceReference(R.string.send_max_fee), - footer = resourceReference(R.string.send_max_fee_footer), - label = stringReference(maxFeeFiat), - ), - SendTextField.CustomFee( - value = ethereumFee.gasPrice.toString(), - decimals = 0, - symbol = ETHEREUM_UNIT, - title = resourceReference(R.string.send_gas_price), - footer = resourceReference(R.string.send_gas_price_footer), - onValueChange = { clickIntents.onCustomFeeValueChange(1, it) }, - keyboardOptions = KeyboardOptions( - imeAction = ImeAction.Next, - keyboardType = KeyboardType.Number, - ), - ), - SendTextField.CustomFee( - value = ethereumFee.gasLimit.toString(), - decimals = 0, - symbol = null, - title = resourceReference(R.string.send_gas_limit), - footer = resourceReference(R.string.send_gas_limit_footer), - onValueChange = { clickIntents.onCustomFeeValueChange(2, it) }, - keyboardOptions = KeyboardOptions( - imeAction = ImeAction.Done, - keyboardType = KeyboardType.Number, - ), - ), - ) + return when (value) { + is Fee.Ethereum -> { + ethereumCustomFeeConverter.convert(value) + } + else -> persistentListOf() + } } - companion object { - private const val ETHEREUM_UNIT = "GWEI" - } + fun onValueChange(feeSelectorState: FeeSelectorState.Content, index: Int, value: String) = feeSelectorState.copy( + customValues = when (feeSelectorState.fees.normal) { + is Fee.Ethereum -> ethereumCustomFeeConverter.onValueChange( + customValues = feeSelectorState.customValues, + index = index, + value = value, + ) + else -> feeSelectorState.customValues + }, + ) } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/EthereumCustomFeeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/EthereumCustomFeeConverter.kt new file mode 100644 index 0000000000..4997139e4e --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/EthereumCustomFeeConverter.kt @@ -0,0 +1,159 @@ +package com.tangem.features.send.impl.presentation.state.fee.custom + +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import com.tangem.blockchain.common.transaction.Fee +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.core.ui.utils.parseBigDecimal +import com.tangem.core.ui.utils.parseToBigDecimal +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.features.send.impl.R +import com.tangem.features.send.impl.presentation.state.fields.SendTextField +import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents +import com.tangem.utils.Provider +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList +import java.math.BigDecimal +import java.math.RoundingMode + +internal class EthereumCustomFeeConverter( + private val clickIntents: SendClickIntents, + private val appCurrencyProvider: Provider, + private val cryptoCurrencyStatusProvider: Provider, +) : Converter> { + + override fun convert(value: Fee.Ethereum): ImmutableList { + return persistentListOf( + SendTextField.CustomFee( + value = value.amount.value?.parseBigDecimal(value.amount.decimals).orEmpty(), + decimals = value.amount.decimals, + symbol = value.amount.currencySymbol, + onValueChange = { clickIntents.onCustomFeeValueChange(FEE_AMOUNT, it) }, + keyboardOptions = KeyboardOptions( + imeAction = ImeAction.Next, + keyboardType = KeyboardType.Number, + ), + title = resourceReference(R.string.send_max_fee), + footer = resourceReference(R.string.send_max_fee_footer), + label = getFeeFormatted(value.amount.value), + ), + SendTextField.CustomFee( + value = value.gasPrice.toString(), + decimals = ETHEREUM_GAS_DECIMALS, + symbol = ETHEREUM_GAS_UNIT, + title = resourceReference(R.string.send_gas_price), + footer = resourceReference(R.string.send_gas_price_footer), + onValueChange = { clickIntents.onCustomFeeValueChange(GAS_PRICE, it) }, + keyboardOptions = KeyboardOptions( + imeAction = ImeAction.Next, + keyboardType = KeyboardType.Number, + ), + ), + SendTextField.CustomFee( + value = value.gasLimit.toString(), + decimals = GAS_DECIMALS, + symbol = null, + title = resourceReference(R.string.send_gas_limit), + footer = resourceReference(R.string.send_gas_limit_footer), + onValueChange = { clickIntents.onCustomFeeValueChange(GAS_LIMIT, it) }, + keyboardOptions = KeyboardOptions( + imeAction = ImeAction.Done, + keyboardType = KeyboardType.Number, + ), + ), + ) + } + + fun convertBack(normalFee: Fee.Ethereum, value: ImmutableList): Fee.Ethereum { + val feeAmount = value[FEE_AMOUNT].value.parseToBigDecimal(value[FEE_AMOUNT].decimals) + val gasPrice = value[GAS_PRICE].value.parseToBigDecimal(GAS_DECIMALS).toBigInteger() + val gasLimit = value[GAS_LIMIT].value.parseToBigDecimal(GAS_DECIMALS).toBigInteger() + return normalFee.copy( + amount = normalFee.amount.copy(value = feeAmount), + gasPrice = gasPrice, + gasLimit = gasLimit, + ) + } + + fun onValueChange( + customValues: ImmutableList, + index: Int, + value: String, + ): ImmutableList { + val mutableCustomValues = customValues.toMutableList() + return mutableCustomValues.apply { + val gasLimit = this[GAS_LIMIT].value.parseToBigDecimal(this[GAS_LIMIT].decimals) + when (index) { + FEE_AMOUNT -> { + val newFeeAmountDecimal = value.parseToBigDecimal(this[FEE_AMOUNT].decimals) + val newFeeAmount = newFeeAmountDecimal.movePointRight(this[FEE_AMOUNT].decimals) + val newGasPrice = newFeeAmount.divide(gasLimit, GAS_DECIMALS, RoundingMode.HALF_UP) + set(GAS_PRICE, this[GAS_PRICE].copy(value = newGasPrice.parseBigDecimal(GAS_DECIMALS))) + set( + index, + this[index].copy( + value = value, + label = getFeeFormatted(newFeeAmountDecimal), + ), + ) + } + GAS_PRICE -> { + val newGasPrice = value.parseToBigDecimal(this[GAS_PRICE].decimals) + .movePointLeft(this[GAS_PRICE].decimals) + val newFeeAmount = gasLimit * newGasPrice + set( + FEE_AMOUNT, + this[FEE_AMOUNT].copy( + value = newFeeAmount.parseBigDecimal(this[FEE_AMOUNT].decimals), + label = getFeeFormatted(newFeeAmount), + ), + ) + set(index, this[index].copy(value = value)) + } + else -> { + val newGasLimit = value.parseToBigDecimal(this[GAS_LIMIT].decimals) + val gasPrice = this[GAS_PRICE].value.parseToBigDecimal(this[GAS_PRICE].decimals) + .movePointLeft(this[FEE_AMOUNT].decimals) + val newFeeAmount = newGasLimit * gasPrice + set( + FEE_AMOUNT, + this[FEE_AMOUNT].copy( + value = newFeeAmount.parseBigDecimal(this[FEE_AMOUNT].decimals), + label = getFeeFormatted(newFeeAmount), + ), + ) + set(index, this[index].copy(value = value)) + } + } + }.toImmutableList() + } + + private fun getFeeFormatted(fee: BigDecimal?): TextReference { + val appCurrency = appCurrencyProvider() + val rate = cryptoCurrencyStatusProvider().value.fiatRate + val fiatFee = rate?.let { fee?.multiply(it) } + return stringReference( + BigDecimalFormatter.formatFiatAmount( + fiatAmount = fiatFee, + fiatCurrencyCode = appCurrency.code, + fiatCurrencySymbol = appCurrency.symbol, + ), + ) + } + + companion object { + private const val ETHEREUM_GAS_UNIT = "GWEI" + private const val ETHEREUM_GAS_DECIMALS = 18 + private const val FEE_AMOUNT = 0 + private const val GAS_PRICE = 1 + private const val GAS_LIMIT = 2 + private const val GAS_DECIMALS = 0 + } +} \ 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 80f5e20cac..b02ffb58d7 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 @@ -38,7 +38,10 @@ import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.send.impl.navigation.InnerSendRouter import com.tangem.features.send.impl.presentation.domain.AvailableWallet import com.tangem.features.send.impl.presentation.state.* -import com.tangem.features.send.impl.presentation.state.fee.* +import com.tangem.features.send.impl.presentation.state.fee.FeeNotificationFactory +import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState +import com.tangem.features.send.impl.presentation.state.fee.FeeStateFactory +import com.tangem.features.send.impl.presentation.state.fee.FeeType import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder @@ -71,7 +74,7 @@ internal class SendViewModel @Inject constructor( private val walletManagersFacade: WalletManagersFacade, private val reduxStateHolder: ReduxStateHolder, private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase, - private val isFeeApproximateUseCase: IsFeeApproximateUseCase, + isFeeApproximateUseCase: IsFeeApproximateUseCase, getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, validateWalletMemoUseCase: ValidateWalletMemoUseCase, getBalanceNotEnoughForFeeWarningUseCase: GetBalanceNotEnoughForFeeWarningUseCase, @@ -536,7 +539,7 @@ internal class SendViewModel @Inject constructor( val feeState = uiState.feeState ?: return val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return val memo = uiState.recipientState?.memoTextField?.value - val fee = feeSelectorState.getFee() + val fee = feeStateFactory.feeConverter.convert(feeSelectorState) val amountValue = uiState.amountState?.amountTextField?.cryptoAmount?.value ?: return val amountToSend = if (feeState.isSubtract && isAmountSubtractAvailable) { feeState.receivedAmountValue @@ -555,6 +558,7 @@ internal class SendViewModel @Inject constructor( ).fold( ifLeft = { Timber.e(it) + uiState = stateFactory.getSendingStateUpdate(isSending = false) uiState = eventStateFactory.getGenericErrorState( error = it, onConsume = { uiState = eventStateFactory.onConsumeEventState() }, diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 4f8cc49e6f..f6b9405dcf 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ spr-client = "3.6.2" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-470" +tangemBlockchainSdk = "develop-471" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-324" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^