Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-05 12:43:58 +05:00
parent 3abed7a11c
commit 57ec369ab0
8 changed files with 86 additions and 136 deletions

View file

@ -36,15 +36,6 @@ internal object TransactionDomainModule {
return GetEthSpecificFeeUseCase(walletManagersFacade = walletManagersFacade)
}
@Provides
@Singleton
fun provideTransferGetFeeUseCase(walletManagersFacade: WalletManagersFacade): GetTransferFeeUseCase {
return GetTransferFeeUseCase(
walletManagersFacade = walletManagersFacade,
demoConfig = DemoConfig(),
)
}
@Provides
@Singleton
fun provideSendTransactionUseCase(

View file

@ -77,13 +77,18 @@ internal class DefaultTransactionRepository(
override suspend fun createTransferTransaction(
amount: Amount,
fee: Fee,
fee: Fee?,
memo: String?,
destination: String,
userWalletId: UserWalletId,
network: Network,
): TransactionData.Uncompiled = withContext(coroutineDispatcherProvider.io) {
val blockchain = Blockchain.fromId(network.id.value)
val walletManager = walletManagersFacade.getOrCreateWalletManager(
userWalletId = userWalletId,
blockchain = blockchain,
derivationPath = network.derivationPath.value,
) ?: error("Wallet manager not found")
val callData = SmartContractCallDataProviderFactory.getTokenTransferCallData(
destinationAddress = destination,
@ -102,15 +107,25 @@ internal class DefaultTransactionRepository(
null
}
return@withContext createTransaction(
amount = amount,
fee = fee,
memo = null,
destination = destination,
userWalletId = userWalletId,
network = network,
txExtras = getMemoExtras(networkId = network.id.value, memo = memo) ?: extras,
)
return@withContext if (fee != null) {
createTransaction(
amount = amount,
fee = fee,
memo = null,
destination = destination,
userWalletId = userWalletId,
network = network,
txExtras = getMemoExtras(networkId = network.id.value, memo = memo) ?: extras,
)
} else {
TransactionData.Uncompiled(
amount = amount,
sourceAddress = walletManager.wallet.address,
destinationAddress = destination,
extras = getMemoExtras(networkId = network.id.value, memo = memo) ?: extras,
fee = null,
)
}
}
override suspend fun createApprovalTransaction(

View file

@ -28,7 +28,7 @@ interface TransactionRepository {
@Suppress("LongParameterList")
suspend fun createTransferTransaction(
amount: Amount,
fee: Fee,
fee: Fee?,
memo: String?,
destination: String,
userWalletId: UserWalletId,

View file

@ -38,4 +38,25 @@ class CreateTransferTransactionUseCase(
network = network,
)
}
/**
* [REDACTED_TODO_COMMENT]
*/
@Suppress("LongParameterList")
suspend operator fun invoke(
amount: Amount,
memo: String?,
destination: String,
userWalletId: UserWalletId,
network: Network,
) = Either.catch {
transactionRepository.createTransferTransaction(
amount = amount,
memo = memo,
fee = null,
destination = destination,
userWalletId = userWalletId,
network = network,
)
}
}

View file

@ -1,102 +0,0 @@
package com.tangem.domain.transaction.usecase
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.smartcontract.SmartContractCallDataProviderFactory
import com.tangem.blockchain.extensions.Result
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.demo.DemoTransactionSender
import com.tangem.domain.tokens.model.CryptoCurrency
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 java.math.BigDecimal
/**
* Use case to get transfer transaction fee
*
* !!!IMPORTANT
* Use when transaction data is compiled by us using BlockchainSDK methods
*/
class GetTransferFeeUseCase(
private val walletManagersFacade: WalletManagersFacade,
private val demoConfig: DemoConfig,
) {
suspend operator fun invoke(
amount: BigDecimal,
destination: String,
userWallet: UserWallet,
cryptoCurrency: CryptoCurrency,
) = either {
catch(
block = {
val amountData = convertCryptoCurrencyToAmount(cryptoCurrency, amount)
val result = if (demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) {
demoTransactionSender(userWallet, cryptoCurrency).getFee(
amount = amountData,
destination = destination,
)
} else {
val walletManager = walletManagersFacade.getOrCreateWalletManager(
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
)
val smartContractCallData = if (amountData.type is AmountType.Token) {
SmartContractCallDataProviderFactory.getTokenTransferCallData(
amount = amountData,
destinationAddress = destination,
blockchain = Blockchain.fromId(cryptoCurrency.network.id.value),
)
} else {
null
}
(walletManager as? TransactionSender)?.getFee(
amount = amountData,
destination = destination,
callData = smartContractCallData,
) ?: error("Fee is null")
}
val maybeFee = when (result) {
is Result.Success -> result.data
is Result.Failure -> raise(result.mapToFeeError())
}
maybeFee
},
catch = {
raise(GetFeeError.DataError(it))
},
)
}
private suspend fun demoTransactionSender(
userWallet: UserWallet,
cryptoCurrency: CryptoCurrency,
): DemoTransactionSender {
return DemoTransactionSender(
walletManagersFacade
.getOrCreateWalletManager(userWallet.walletId, cryptoCurrency.network)
?: error("WalletManager is null"),
)
}
private fun convertCryptoCurrencyToAmount(cryptoCurrency: CryptoCurrency, amount: BigDecimal) = Amount(
currencySymbol = cryptoCurrency.symbol,
value = amount,
decimals = cryptoCurrency.decimals,
type = when (cryptoCurrency) {
is CryptoCurrency.Coin -> AmountType.Coin
is CryptoCurrency.Token -> AmountType.Token(
token = Token(
symbol = cryptoCurrency.symbol,
contractAddress = cryptoCurrency.contractAddress,
decimals = cryptoCurrency.decimals,
),
)
},
)
}

View file

@ -26,7 +26,7 @@ import com.tangem.domain.settings.NeverShowTapHelpUseCase
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.tokens.IsAmountSubtractAvailableUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.transaction.usecase.CreateTransactionUseCase
import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.utils.convertToSdkAmount
@ -74,7 +74,7 @@ internal class SendConfirmModel @Inject constructor(
private val router: Router,
private val isSendTapHelpEnabledUseCase: IsSendTapHelpEnabledUseCase,
private val neverShowTapHelpUseCase: NeverShowTapHelpUseCase,
private val createTransactionUseCase: CreateTransactionUseCase,
private val createTransferTransactionUseCase: CreateTransferTransactionUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
@ -326,7 +326,7 @@ internal class SendConfirmModel @Inject constructor(
)
modelScope.launch {
createTransactionUseCase(
createTransferTransactionUseCase(
amount = receivingAmount.convertToSdkAmount(cryptoCurrency),
fee = fee,
memo = memo,

View file

@ -3,6 +3,7 @@ package com.tangem.features.send.v2.send.model
import androidx.compose.runtime.Stable
import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.left
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.core.decompose.di.ModelScoped
@ -28,16 +29,18 @@ import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.GetTransferFeeUseCase
import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.v2.api.SendComponent
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.SendConfirmAlertFactory
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.common.ui.state.NavigationUM
import com.tangem.features.send.v2.send.confirm.SendConfirmComponent
import com.tangem.features.send.v2.common.SendConfirmAlertFactory
import com.tangem.features.send.v2.send.ui.state.SendUM
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent
import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateQRTrigger
@ -80,7 +83,8 @@ internal class SendModel @Inject constructor(
private val getCardInfoUseCase: GetCardInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getTransferFeeUseCase: GetTransferFeeUseCase,
private val createTransferTransactionUseCase: CreateTransferTransactionUseCase,
private val getFeeUseCase: GetFeeUseCase,
private val sendAmountUpdateQRTrigger: SendAmountUpdateQRTrigger,
) : Model(), SendComponentCallback {
@ -135,13 +139,23 @@ internal class SendModel @Inject constructor(
val destinationUM = uiState.value.destinationUM as? DestinationUM.Content ?: error("Invalid destination")
val amountUM = uiState.value.amountUM as? AmountState.Data ?: error("Invalid amount")
val enteredDestinationAddress = destinationUM.addressTextField.value
val enteredMemo = destinationUM.memoTextField?.value
val enteredAmount = amountUM.amountTextField.cryptoAmount.value ?: error("Invalid amount")
return getTransferFeeUseCase(
val transferTransaction = createTransferTransactionUseCase(
amount = enteredAmount.convertToSdkAmount(cryptoCurrency),
memo = enteredMemo,
destination = enteredDestinationAddress,
amount = enteredAmount,
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
).getOrElse {
return GetFeeError.DataError(it).left()
}
return getFeeUseCase(
transactionData = transferTransaction,
userWallet = userWallet,
cryptoCurrency = params.currency,
network = params.currency.network,
)
}

View file

@ -92,9 +92,9 @@ internal class SendModel @Inject constructor(
private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val getTxHistoryItemsUseCase: GetTxHistoryItemsUseCase,
private val getFixedTxHistoryItemsUseCase: GetFixedTxHistoryItemsUseCase,
private val getTransferFeeUseCase: GetTransferFeeUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
private val createTransferTransactionUseCase: CreateTransferTransactionUseCase,
private val getFeeUseCase: GetFeeUseCase,
private val validateWalletAddressUseCase: ValidateWalletAddressUseCase,
private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
@ -825,12 +825,23 @@ internal class SendModel @Inject constructor(
val amountState = uiState.value.getAmountState(isFromConfirmation) as? AmountState.Data ?: return null
val recipientState = uiState.value.getRecipientState(isFromConfirmation) ?: return null
val amount = amountState.amountTextField.cryptoAmount.value ?: return null
val destinationAddress = recipientState.addressTextField.value
val memo = recipientState.memoTextField?.value
return getTransferFeeUseCase.invoke(
amount = amount,
destination = recipientState.addressTextField.value,
val transferTransaction = createTransferTransactionUseCase(
amount = amount.convertToSdkAmount(cryptoCurrency),
memo = memo,
destination = destinationAddress,
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
).getOrElse {
return GetFeeError.DataError(it).left()
}
return getFeeUseCase(
transactionData = transferTransaction,
userWallet = userWallet,
cryptoCurrency = cryptoCurrencyStatus.currency,
network = params.currency.network,
)
}
// endregion