Updated on 2026-08-14
This commit is contained in:
parent
50fa269746
commit
4cb147b27e
7 changed files with 345 additions and 10 deletions
|
|
@ -364,6 +364,8 @@ internal class SwapTransferStateBuilder @Inject constructor(
|
|||
fee = fee,
|
||||
tokenSwapInfo = transferState.fromTokenInfo,
|
||||
appCurrency = transferState.appCurrency,
|
||||
isFeeSubtractedFromAmount = isFeeSubtractedFromAmount(transferState, fee),
|
||||
isFeeExceedingBalance = isFeeExceedingBalance(transferState, fee),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -384,11 +386,34 @@ internal class SwapTransferStateBuilder @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun isFeeSubtractedFromAmount(transferState: SwapState.Transfer, fee: Fee?): Boolean {
|
||||
if (!transferState.isAmountSubtractAvailable || fee == null) return false
|
||||
val swapCurrencyStatus = transferState.fromTokenInfo.swapCurrencyStatus
|
||||
val balance = swapCurrencyStatus.status.value.amount.orZero()
|
||||
val amountValue = transferState.fromTokenInfo.tokenAmount.value
|
||||
return amountValue + fee.amount.value.orZero() > balance
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the fee alone exceeds the balance: nothing can be sent (not even enough to cover the fee), so
|
||||
* the footer must show $0 instead of a positive total. Only meaningful when the fee is paid from the same
|
||||
* balance (subtraction available).
|
||||
*/
|
||||
private fun isFeeExceedingBalance(transferState: SwapState.Transfer, fee: Fee?): Boolean {
|
||||
if (!transferState.isAmountSubtractAvailable || fee == null) return false
|
||||
val swapCurrencyStatus = transferState.fromTokenInfo.swapCurrencyStatus
|
||||
val balance = swapCurrencyStatus.status.value.amount.orZero()
|
||||
return fee.amount.value.orZero() > balance
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
private fun getSendingFooterText(
|
||||
dataState: SwapProcessDataState,
|
||||
fee: Fee?,
|
||||
tokenSwapInfo: TokenSwapInfo,
|
||||
appCurrency: AppCurrency,
|
||||
isFeeSubtractedFromAmount: Boolean,
|
||||
isFeeExceedingBalance: Boolean,
|
||||
): TextReference? {
|
||||
if (fee == null) return null
|
||||
|
||||
|
|
@ -398,10 +423,13 @@ internal class SwapTransferStateBuilder @Inject constructor(
|
|||
val fiatFeeValue = value?.fiatRate?.multiply(fee.amount.value)
|
||||
val isFeeConvertibleToFiat = status.currency.network.hasFiatFeeRate
|
||||
|
||||
val fiatSendingValue = if (isFeeConvertibleToFiat) {
|
||||
fiatFeeValue?.let { fiatAmountValue.plus(it) }
|
||||
} else {
|
||||
fiatAmountValue
|
||||
val fiatSendingValue = when {
|
||||
!isFeeConvertibleToFiat -> fiatAmountValue
|
||||
// Fee alone exceeds the balance → the transaction can't go through, nothing is sent.
|
||||
isFeeExceedingBalance -> BigDecimal.ZERO
|
||||
// Fee is taken out of the entered amount → it already includes the fee, don't add it again.
|
||||
isFeeSubtractedFromAmount -> fiatAmountValue
|
||||
else -> fiatFeeValue?.let { fiatAmountValue.plus(it) }
|
||||
}
|
||||
|
||||
val fiatSending = fiatSendingValue.format {
|
||||
|
|
|
|||
|
|
@ -590,6 +590,104 @@ internal class SwapTransferStateBuilderTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN fee subtracted from amount WHEN updateTransferButtonEnableState THEN footer sending excludes the fee`() =
|
||||
runTest {
|
||||
// Arrange: subtraction available + amount + fee exceeds balance (1.0), but fee (0.5) <= balance.
|
||||
// The entered amount is the gross that already includes the fee, so the footer must not add it again.
|
||||
val fromAmount = BigDecimal("1.0")
|
||||
val transferState = buildTransferState(
|
||||
fromAmount = fromAmount,
|
||||
toAmount = fromAmount,
|
||||
isAccountsMode = false,
|
||||
isAmountSubtractAvailable = true,
|
||||
)
|
||||
val feePaidStatus = buildSwapCurrencyStatus(coldWallet)
|
||||
val feePaidRate = feePaidStatus.status.value.fiatRate!!
|
||||
val dataState = SwapProcessDataState(
|
||||
fromSwapCurrencyStatus = buildStatusWithNetwork(hasFiatFeeRate = true),
|
||||
feePaidCryptoCurrency = feePaidStatus.status,
|
||||
)
|
||||
val feeValue = BigDecimal("0.5")
|
||||
val fee = Fee.Common(amount = Amount(currencySymbol = "ETH", value = feeValue, decimals = 18))
|
||||
val appCurrency = transferState.appCurrency
|
||||
// Sending is the entered amount only — the fee is NOT added on top.
|
||||
val expectedFiatSending = (fromAmount * QUOTE).format {
|
||||
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
|
||||
}
|
||||
val expectedFiatFee = feePaidRate.multiply(feeValue).format {
|
||||
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
|
||||
}
|
||||
|
||||
// Act
|
||||
val result = sut.updateTransferButtonEnableState(
|
||||
dataState = dataState,
|
||||
transferState = transferState,
|
||||
actions = actions,
|
||||
uiStateHolder = baseStateHolder(),
|
||||
feePaidCryptoCurrencyStatus = null,
|
||||
fee = fee,
|
||||
isTangemPayWithdrawal = false,
|
||||
feeSelectorUM = null,
|
||||
)
|
||||
|
||||
// Assert
|
||||
assertThat(result.transferFooter).isEqualTo(
|
||||
resourceReference(
|
||||
id = com.tangem.features.send.impl.R.string.send_summary_transaction_description,
|
||||
formatArgs = wrappedList(expectedFiatSending, expectedFiatFee),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN fee exceeds balance WHEN updateTransferButtonEnableState THEN footer sending is zero`() =
|
||||
runTest {
|
||||
// Arrange: subtraction available + fee (2.0) exceeds balance (1.0) → nothing can be sent.
|
||||
val fromAmount = BigDecimal("1.0")
|
||||
val transferState = buildTransferState(
|
||||
fromAmount = fromAmount,
|
||||
toAmount = fromAmount,
|
||||
isAccountsMode = false,
|
||||
isAmountSubtractAvailable = true,
|
||||
)
|
||||
val feePaidStatus = buildSwapCurrencyStatus(coldWallet)
|
||||
val feePaidRate = feePaidStatus.status.value.fiatRate!!
|
||||
val dataState = SwapProcessDataState(
|
||||
fromSwapCurrencyStatus = buildStatusWithNetwork(hasFiatFeeRate = true),
|
||||
feePaidCryptoCurrency = feePaidStatus.status,
|
||||
)
|
||||
val feeValue = BigDecimal("2.0")
|
||||
val fee = Fee.Common(amount = Amount(currencySymbol = "ETH", value = feeValue, decimals = 18))
|
||||
val appCurrency = transferState.appCurrency
|
||||
val expectedFiatSending = BigDecimal.ZERO.format {
|
||||
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
|
||||
}
|
||||
val expectedFiatFee = feePaidRate.multiply(feeValue).format {
|
||||
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
|
||||
}
|
||||
|
||||
// Act
|
||||
val result = sut.updateTransferButtonEnableState(
|
||||
dataState = dataState,
|
||||
transferState = transferState,
|
||||
actions = actions,
|
||||
uiStateHolder = baseStateHolder(),
|
||||
feePaidCryptoCurrencyStatus = null,
|
||||
fee = fee,
|
||||
isTangemPayWithdrawal = false,
|
||||
feeSelectorUM = null,
|
||||
)
|
||||
|
||||
// Assert
|
||||
assertThat(result.transferFooter).isEqualTo(
|
||||
resourceReference(
|
||||
id = com.tangem.features.send.impl.R.string.send_summary_transaction_description,
|
||||
formatArgs = wrappedList(expectedFiatSending, expectedFiatFee),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN non-Tron fee and non-fiat-convertible network WHEN updateTransferButtonEnableState THEN transferFooter uses no-fiat-fee description`() =
|
||||
runTest {
|
||||
|
|
@ -919,6 +1017,7 @@ internal class SwapTransferStateBuilderTest {
|
|||
isInsufficientBalance: Boolean = false,
|
||||
isFeeCoverage: Boolean = false,
|
||||
isSendingAmountLoading: Boolean = false,
|
||||
isAmountSubtractAvailable: Boolean = false,
|
||||
): SwapState.Transfer {
|
||||
val fromInfo = TokenSwapInfo(
|
||||
tokenAmount = SwapAmount(value = fromAmount, decimals = fromCurrencyStatus.currency.decimals),
|
||||
|
|
@ -942,7 +1041,7 @@ internal class SwapTransferStateBuilderTest {
|
|||
isFeeCoverage = isFeeCoverage,
|
||||
sendingAmount = toAmount,
|
||||
tronFeeNotificationShowCount = 0,
|
||||
isAmountSubtractAvailable = false,
|
||||
isAmountSubtractAvailable = isAmountSubtractAvailable,
|
||||
isSendingAmountLoading = isSendingAmountLoading,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue