Updated on 2026-08-14
This commit is contained in:
parent
930e6ffc72
commit
b75a9370ba
9 changed files with 70 additions and 55 deletions
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.demo.models.DemoConfig
|
|||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
|
||||
import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.GetViewedTokenReceiveWarningUseCase
|
||||
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
|
|
@ -14,10 +15,7 @@ import com.tangem.domain.transaction.GaslessTransactionRepository
|
|||
import com.tangem.domain.transaction.TransactionRepository
|
||||
import com.tangem.domain.transaction.WalletAddressServiceRepository
|
||||
import com.tangem.domain.transaction.usecase.*
|
||||
import com.tangem.domain.transaction.usecase.gasless.GetAvailableFeeTokensUseCase
|
||||
import com.tangem.domain.transaction.usecase.gasless.GetFeeForGaslessUseCase
|
||||
import com.tangem.domain.transaction.usecase.gasless.GetFeeForTokenUseCase
|
||||
import com.tangem.domain.transaction.usecase.gasless.IsGaslessFeeSupportedForNetwork
|
||||
import com.tangem.domain.transaction.usecase.gasless.*
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -330,4 +328,22 @@ internal object TransactionDomainModule {
|
|||
): IsGaslessFeeSupportedForNetwork {
|
||||
return IsGaslessFeeSupportedForNetwork(gaslessTransactionRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCreateAndSendGaslessTransactionUseCase(
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
gaslessTransactionRepository: GaslessTransactionRepository,
|
||||
getSingCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
|
||||
cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory,
|
||||
): CreateAndSendGaslessTransactionUseCase {
|
||||
return CreateAndSendGaslessTransactionUseCase(
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
getSingleCryptoCurrencyStatusUseCase = getSingCryptoCurrencyStatusUseCase,
|
||||
gaslessTransactionRepository = gaslessTransactionRepository,
|
||||
cardSdkConfigRepository = cardSdkConfigRepository,
|
||||
getHotWalletSigner = tangemHotWalletSignerFactory::create,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import com.tangem.domain.transaction.models.TransactionFeeExtended
|
||||
import com.tangem.features.send.v2.api.R
|
||||
import com.tangem.features.send.v2.api.entity.FeeItem.*
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -66,6 +67,7 @@ data class FeeExtraInfo(
|
|||
val isTronToken: Boolean,
|
||||
val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val availableFeeCurrencies: ImmutableList<CryptoCurrencyStatus>? = null,
|
||||
val transactionFeeExtended: TransactionFeeExtended? = null,
|
||||
)
|
||||
|
||||
sealed class FeeNonce {
|
||||
|
|
|
|||
|
|
@ -81,12 +81,14 @@ internal class DefaultFeeSelectorBlockComponent @AssistedInject constructor(
|
|||
|
||||
val isScreenSource = params.feeDisplaySource == FeeSelectorParams.FeeDisplaySource.Screen
|
||||
val isNotSingleFee = (state as? FeeSelectorUM.Content)?.feeItems?.isSingleItem() == false
|
||||
val isGaslessAvailable = state is FeeSelectorUM.Content &&
|
||||
(state as FeeSelectorUM.Content).feeExtraInfo.transactionFeeExtended != null
|
||||
FeeSelectorBlockContent(
|
||||
state = state,
|
||||
onReadMoreClick = model::onReadMoreClicked,
|
||||
isGaslessFeatureEnabled = sendFeatureToggles.isGaslessTransactionsEnabled,
|
||||
modifier = modifier
|
||||
.conditional(isScreenSource && isNotSingleFee) {
|
||||
.conditional(isScreenSource && (isNotSingleFee || isGaslessAvailable)) {
|
||||
Modifier.clickable {
|
||||
model.showFeeSelector()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,12 +32,10 @@ import com.tangem.utils.transformer.update
|
|||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -58,7 +56,6 @@ internal class FeeSelectorLogic @AssistedInject constructor(
|
|||
) : FeeSelectorIntents {
|
||||
|
||||
private var appCurrency: AppCurrency = AppCurrency.Default
|
||||
private var selectedToken: CryptoCurrencyStatus? = null
|
||||
val uiState = MutableStateFlow<FeeSelectorUM>(params.state)
|
||||
|
||||
init {
|
||||
|
|
@ -115,34 +112,7 @@ internal class FeeSelectorLogic @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun onTokenSelected(status: CryptoCurrencyStatus) {
|
||||
if (status.currency !is CryptoCurrency.Token ||
|
||||
status.currency.id == params.feeCryptoCurrencyStatus.currency.id) {
|
||||
selectedToken = null
|
||||
loadFee()
|
||||
return
|
||||
}
|
||||
uiState.update(FeeSelectorTokenSelectedTransformer(status))
|
||||
|
||||
uiState.update { state ->
|
||||
if (state is FeeSelectorUM.Content) {
|
||||
state.copy(
|
||||
selectedFeeItem = FeeItem.Loading,
|
||||
feeItems = persistentListOf(FeeItem.Loading),
|
||||
feeExtraInfo = state.feeExtraInfo.copy(
|
||||
feeCryptoCurrencyStatus = status,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
if (status.currency is CryptoCurrency.Token) {
|
||||
selectedToken = status
|
||||
} else {
|
||||
selectedToken = null
|
||||
}
|
||||
|
||||
loadFee()
|
||||
}
|
||||
|
||||
|
|
@ -240,6 +210,9 @@ internal class FeeSelectorLogic @AssistedInject constructor(
|
|||
private suspend fun callLoadFee(): Either<GetFeeError, LoadedFeeResult> {
|
||||
val extended = params.onLoadFeeExtended
|
||||
return if (extended != null && sendFeatureToggles.isGaslessTransactionsEnabled) {
|
||||
val selectedToken = (uiState.value as? FeeSelectorUM.Content)?.feeExtraInfo?.feeCryptoCurrencyStatus
|
||||
?.takeIf { it.currency is CryptoCurrency.Token }
|
||||
|
||||
extended(selectedToken).fold(
|
||||
ifLeft = { error ->
|
||||
if (error is GetFeeError.GaslessError) {
|
||||
|
|
|
|||
|
|
@ -89,14 +89,11 @@ internal class FeeSelectorModel @Inject constructor(
|
|||
}
|
||||
|
||||
fun getInitialRoute(): FeeSelectorRoute {
|
||||
if (sendFeatureToggles.isGaslessTransactionsEnabled.not()) {
|
||||
return FeeSelectorRoute.ChooseSpeed
|
||||
}
|
||||
|
||||
return if (isGaslessFeeSupportedForNetwork(params.feeCryptoCurrencyStatus.currency.network)) {
|
||||
FeeSelectorRoute.NetworkFee
|
||||
} else {
|
||||
FeeSelectorRoute.ChooseSpeed
|
||||
return when {
|
||||
sendFeatureToggles.isGaslessTransactionsEnabled && params.onLoadFeeExtended != null &&
|
||||
isGaslessFeeSupportedForNetwork(params.feeCryptoCurrencyStatus.currency.network) ->
|
||||
FeeSelectorRoute.NetworkFee
|
||||
else -> FeeSelectorRoute.ChooseSpeed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,13 +38,17 @@ internal class FeeSelectorLoadedTransformer(
|
|||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val feeItems: ImmutableList<FeeItem> = feeItemsConverter.convert(
|
||||
FeeItemConverter.Input(fees.transactionFee, prevCustomFee),
|
||||
)
|
||||
|
||||
val selectedFee = when (prevState) {
|
||||
is FeeSelectorUM.Content ->
|
||||
feeItems.firstOrNull { it.isSameClass(prevState.selectedFeeItem) } ?: FeeItem.Loading
|
||||
is FeeSelectorUM.Content -> if (prevState.selectedFeeItem is FeeItem.Loading) {
|
||||
feeItems.firstOrNull { it is FeeItem.Market }
|
||||
} else {
|
||||
feeItems.firstOrNull { it.isSameClass(prevState.selectedFeeItem) }
|
||||
} ?: FeeItem.Loading
|
||||
is FeeSelectorUM.Error,
|
||||
FeeSelectorUM.Loading,
|
||||
-> feeItems.find { it is FeeItem.Suggested } ?: feeItems.first { it is FeeItem.Market }
|
||||
|
|
@ -64,6 +68,7 @@ internal class FeeSelectorLoadedTransformer(
|
|||
isTron(cryptoCurrencyStatus.currency.network.rawId),
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
availableFeeCurrencies = getAvailableFeeCurrencies(),
|
||||
transactionFeeExtended = (fees as? LoadedFeeResult.Extended)?.fee,
|
||||
),
|
||||
feeFiatRateUM = feeCryptoCurrencyStatus.value.fiatRate?.let { rate ->
|
||||
FeeFiatRateUM(
|
||||
|
|
|
|||
|
|
@ -213,7 +213,10 @@ private fun FeeContent(state: FeeSelectorUM.Content, isGaslessFeatureEnabled: Bo
|
|||
.padding(start = TangemTheme.dimens.spacing4)
|
||||
.testTag(FeeSelectorBlockTestTags.FEE_AMOUNT),
|
||||
)
|
||||
if (!state.feeItems.isSingleItem()) {
|
||||
|
||||
val isGaslessAvailable = isGaslessFeatureEnabled && state.feeExtraInfo.transactionFeeExtended != null
|
||||
|
||||
if (!state.feeItems.isSingleItem() || isGaslessAvailable) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(width = 18.dp, height = 24.dp)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import com.tangem.domain.tokens.IsAmountSubtractAvailableUseCase
|
|||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase
|
||||
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
|
||||
import com.tangem.domain.transaction.usecase.gasless.CreateAndSendGaslessTransactionUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||
import com.tangem.domain.utils.convertToSdkAmount
|
||||
import com.tangem.features.send.v2.api.SendNotificationsComponent
|
||||
|
|
@ -111,6 +112,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val createAndSendGaslessTransactionUseCase: CreateAndSendGaslessTransactionUseCase,
|
||||
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
|
||||
) : Model(), SendConfirmClickIntents, FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback {
|
||||
|
||||
|
|
@ -388,11 +390,21 @@ internal class SendConfirmModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun sendTransaction(txData: TransactionData.Uncompiled) {
|
||||
val result = sendTransactionUseCase(
|
||||
txData = txData,
|
||||
userWallet = userWallet,
|
||||
network = cryptoCurrency.network,
|
||||
)
|
||||
val feeExtended = feeUMV2?.feeExtraInfo?.transactionFeeExtended
|
||||
|
||||
val result = if (feeExtended != null) {
|
||||
createAndSendGaslessTransactionUseCase(
|
||||
userWallet = userWallet,
|
||||
transactionData = txData,
|
||||
fee = feeExtended,
|
||||
)
|
||||
} else {
|
||||
sendTransactionUseCase(
|
||||
txData = txData,
|
||||
userWallet = userWallet,
|
||||
network = cryptoCurrency.network,
|
||||
)
|
||||
}
|
||||
|
||||
_uiState.update(SendConfirmSendingStateTransformer(isSending = false))
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
|
|||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkExceedBalance
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
internal class EthereumCustomFeeConverter(
|
||||
|
|
@ -38,12 +39,16 @@ internal class EthereumCustomFeeConverter(
|
|||
)
|
||||
|
||||
override fun convert(value: Fee.Ethereum): ImmutableList<CustomFeeFieldUM> {
|
||||
if (value is Fee.Ethereum.TokenCurrency) {
|
||||
return persistentListOf()
|
||||
}
|
||||
|
||||
return buildList {
|
||||
add(convertFeeValue(value))
|
||||
|
||||
addAll(
|
||||
when (value) {
|
||||
is Fee.Ethereum.TokenCurrency -> error("handle in [REDACTED_TASK_KEY]")
|
||||
is Fee.Ethereum.TokenCurrency -> error("unreachable")
|
||||
is Fee.Ethereum.EIP1559 -> eipFeeConverter.convert(value)
|
||||
is Fee.Ethereum.Legacy -> legacyFeeConverter.convert(value)
|
||||
},
|
||||
|
|
@ -56,7 +61,7 @@ internal class EthereumCustomFeeConverter(
|
|||
|
||||
override fun convertBack(normalFee: Fee.Ethereum, value: ImmutableList<CustomFeeFieldUM>): Fee.Ethereum {
|
||||
return when (normalFee) {
|
||||
is Fee.Ethereum.TokenCurrency -> error("handle in [REDACTED_TASK_KEY]")
|
||||
is Fee.Ethereum.TokenCurrency -> error("unreachable")
|
||||
is Fee.Ethereum.EIP1559 -> eipFeeConverter.convertBack(normalFee = normalFee, value = value)
|
||||
is Fee.Ethereum.Legacy -> legacyFeeConverter.convertBack(normalFee = normalFee, value = value)
|
||||
}
|
||||
|
|
@ -64,7 +69,7 @@ internal class EthereumCustomFeeConverter(
|
|||
|
||||
override fun getGasLimitIndex(feeValue: Fee.Ethereum): Int {
|
||||
return when (feeValue) {
|
||||
is Fee.Ethereum.TokenCurrency -> error("handle in [REDACTED_TASK_KEY]")
|
||||
is Fee.Ethereum.TokenCurrency -> error("unreachable")
|
||||
is Fee.Ethereum.EIP1559 -> eipFeeConverter.getGasLimitIndex(feeValue)
|
||||
is Fee.Ethereum.Legacy -> legacyFeeConverter.getGasLimitIndex(feeValue)
|
||||
}
|
||||
|
|
@ -77,7 +82,7 @@ internal class EthereumCustomFeeConverter(
|
|||
value: String,
|
||||
): ImmutableList<CustomFeeFieldUM> {
|
||||
return when (feeValue) {
|
||||
is Fee.Ethereum.TokenCurrency -> error("handle in [REDACTED_TASK_KEY]")
|
||||
is Fee.Ethereum.TokenCurrency -> error("unreachable")
|
||||
is Fee.Ethereum.EIP1559 -> eipFeeConverter.onValueChange(
|
||||
feeValue = feeValue,
|
||||
customValues = customValues,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue