Updated on 2026-08-14
This commit is contained in:
parent
0bcfed032f
commit
7d087423e9
4 changed files with 39 additions and 44 deletions
|
|
@ -2,7 +2,6 @@ package com.tangem.feature.swap.domain
|
|||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.swap.domain.models.SwapAmount
|
||||
import com.tangem.feature.swap.domain.models.domain.IncludeFeeInAmount
|
||||
|
|
@ -15,8 +14,6 @@ interface SwapInteractor {
|
|||
|
||||
suspend fun getTokensDataState(currency: CryptoCurrency): TokensDataStateExpress
|
||||
|
||||
fun initDerivationPathAndNetwork(derivationPath: String?, network: Network)
|
||||
|
||||
/**
|
||||
* Gives permission to swap, this starts scan card process
|
||||
*
|
||||
|
|
@ -30,7 +27,6 @@ interface SwapInteractor {
|
|||
* Find best quote for given tokens to swap
|
||||
* under the hood calls different methods to receive data, depends on permission for given token
|
||||
*
|
||||
* @param networkId network for tokens
|
||||
* @param fromToken [Currency] from which want to swap
|
||||
* @param toToken [Currency] that receive after swap
|
||||
* @param amountToSwap amount you want to swap
|
||||
|
|
@ -39,7 +35,6 @@ interface SwapInteractor {
|
|||
*/
|
||||
@Throws(IllegalStateException::class)
|
||||
suspend fun findBestQuote(
|
||||
networkId: String,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
toToken: CryptoCurrencyStatus,
|
||||
providers: List<SwapProvider>,
|
||||
|
|
@ -62,7 +57,6 @@ interface SwapInteractor {
|
|||
@Throws(IllegalStateException::class)
|
||||
suspend fun onSwap(
|
||||
swapProvider: SwapProvider,
|
||||
networkId: String,
|
||||
swapData: SwapDataModel?,
|
||||
currencyToSend: CryptoCurrencyStatus,
|
||||
currencyToGet: CryptoCurrencyStatus,
|
||||
|
|
@ -76,7 +70,6 @@ interface SwapInteractor {
|
|||
selectedFee: FeeType,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
amountToSwap: String,
|
||||
networkId: String,
|
||||
): SwapState.QuotesLoadedState
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
|
||||
private val swapCurrencyConverter = SwapCurrencyConverter()
|
||||
private val amountFormatter = AmountFormatter()
|
||||
private var derivationPath: String? = null
|
||||
private var network: Network? = null
|
||||
|
||||
override suspend fun getTokensDataState(currency: CryptoCurrency): TokensDataStateExpress {
|
||||
|
|
@ -182,14 +181,9 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
return repository.getPairs(initialCurrency, currenciesList)
|
||||
}
|
||||
|
||||
@Deprecated("used in old swap mechanism")
|
||||
override fun initDerivationPathAndNetwork(derivationPath: String?, network: Network) {
|
||||
this.derivationPath = derivationPath
|
||||
this.network = network
|
||||
}
|
||||
|
||||
@Deprecated("used in old swap mechanism")
|
||||
override suspend fun givePermissionToSwap(networkId: String, permissionOptions: PermissionOptions): TxState {
|
||||
val derivationPath = permissionOptions.fromToken.network.derivationPath.value
|
||||
val dataToSign = if (permissionOptions.approveType == SwapApproveType.UNLIMITED) {
|
||||
getApproveData(
|
||||
networkId = networkId,
|
||||
|
|
@ -233,7 +227,6 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
|
||||
@Deprecated("used in old swap mechanism")
|
||||
override suspend fun findBestQuote(
|
||||
networkId: String,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
toToken: CryptoCurrencyStatus,
|
||||
providers: List<SwapProvider>,
|
||||
|
|
@ -249,7 +242,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
}
|
||||
val amount = SwapAmount(amountDecimal, getTokenDecimals(fromToken.currency))
|
||||
val isBalanceWithoutFeeEnough = isBalanceEnough(fromToken, amount, null)
|
||||
|
||||
val networkId = fromToken.currency.network.backendId
|
||||
when (provider.type) {
|
||||
ExchangeProviderType.DEX -> {
|
||||
manageDex(
|
||||
|
|
@ -310,7 +303,10 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
|
||||
if (isAllowedToSpend && allowPermissionsHandler.isAddressAllowanceInProgress(fromTokenAddress)) {
|
||||
allowPermissionsHandler.removeAddressFromProgress(fromTokenAddress)
|
||||
transactionManager.updateWalletManager(networkId, derivationPath)
|
||||
transactionManager.updateWalletManager(
|
||||
networkId,
|
||||
fromToken.currency.network.derivationPath.value,
|
||||
)
|
||||
}
|
||||
return if (isAllowedToSpend && isBalanceWithoutFeeEnough) {
|
||||
provider to loadDexSwapData(
|
||||
|
|
@ -361,7 +357,6 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
|
||||
override suspend fun onSwap(
|
||||
swapProvider: SwapProvider,
|
||||
networkId: String,
|
||||
swapData: SwapDataModel?,
|
||||
currencyToSend: CryptoCurrencyStatus,
|
||||
currencyToGet: CryptoCurrencyStatus,
|
||||
|
|
@ -389,7 +384,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
}
|
||||
ExchangeProviderType.DEX -> {
|
||||
onSwapDex(
|
||||
networkId = networkId,
|
||||
networkId = currencyToSend.currency.network.backendId,
|
||||
swapData = requireNotNull(swapData),
|
||||
currencyToSend = currencyToSend.currency,
|
||||
currencyToGet = currencyToGet.currency,
|
||||
|
|
@ -405,7 +400,6 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
selectedFee: FeeType,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
amountToSwap: String,
|
||||
networkId: String,
|
||||
): SwapState.QuotesLoadedState {
|
||||
val amountDecimal = toBigDecimalOrNull(amountToSwap)
|
||||
if (amountDecimal == null || amountDecimal.signum() == 0) {
|
||||
|
|
@ -413,7 +407,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
}
|
||||
val amount = SwapAmount(amountDecimal, getTokenDecimals(fromToken.currency))
|
||||
val includeFeeInAmount = getIncludeFeeInAmount(
|
||||
networkId = networkId,
|
||||
networkId = fromToken.currency.network.backendId,
|
||||
txFee = state.txFee,
|
||||
amount = amount,
|
||||
fromToken = fromToken.currency,
|
||||
|
|
@ -422,6 +416,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
return state.copy(
|
||||
permissionState = PermissionDataState.Empty,
|
||||
preparedSwapConfigState = state.preparedSwapConfigState.copy(
|
||||
isFeeEnough = includeFeeInAmount !is IncludeFeeInAmount.BalanceNotEnough,
|
||||
isBalanceEnough = includeFeeInAmount !is IncludeFeeInAmount.BalanceNotEnough,
|
||||
includeFeeInAmount = includeFeeInAmount,
|
||||
),
|
||||
|
|
@ -438,6 +433,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
): TxState {
|
||||
val amountDecimal = requireNotNull(toBigDecimalOrNull(amountToSwap)) { "wrong amount format" }
|
||||
val amount = SwapAmount(amountDecimal, getTokenDecimals(currencyToSend))
|
||||
val derivationPath = currencyToSend.network.derivationPath.value
|
||||
val result = transactionManager.sendTransaction(
|
||||
txData = SwapTxData(
|
||||
networkId = networkId,
|
||||
|
|
@ -481,6 +477,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private suspend fun onSwapCex(
|
||||
currencyToSend: CryptoCurrencyStatus,
|
||||
currencyToGet: CryptoCurrencyStatus,
|
||||
|
|
@ -519,6 +516,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
|
||||
val externalUrl = (exchangeData.transaction as? ExpressTransactionModel.CEX)?.externalTxUrl
|
||||
|
||||
val derivationPath = currencyToSend.currency.network.derivationPath.value
|
||||
return result.fold(
|
||||
ifLeft = {
|
||||
when (it) {
|
||||
|
|
@ -659,7 +657,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
val allowance = repository.getAllowance(
|
||||
userWalletId = userWallet.walletId,
|
||||
networkId = networkId,
|
||||
derivationPath = derivationPath,
|
||||
derivationPath = fromToken.network.derivationPath.value,
|
||||
tokenDecimalCount = getTokenDecimals(fromToken),
|
||||
tokenAddress = getTokenAddress(fromToken),
|
||||
spenderAddress = spenderAddress,
|
||||
|
|
@ -832,7 +830,10 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
}
|
||||
|
||||
val tokenForFeeBalance =
|
||||
userWalletManager.getNativeTokenBalance(networkId, derivationPath) ?: ProxyAmount.empty()
|
||||
userWalletManager.getNativeTokenBalance(
|
||||
networkId,
|
||||
fromToken.network.derivationPath.value,
|
||||
) ?: ProxyAmount.empty()
|
||||
|
||||
if (amount.value > tokenForFeeBalance.value) {
|
||||
return IncludeFeeInAmount.BalanceNotEnough
|
||||
|
|
@ -907,7 +908,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
destinationAddress = swapData.transaction.txTo,
|
||||
increaseBy = INCREASE_GAS_LIMIT_BY,
|
||||
data = (swapData.transaction as ExpressTransactionModel.DEX).txData,
|
||||
derivationPath = derivationPath,
|
||||
derivationPath = fromToken.currency.network.derivationPath.value,
|
||||
)
|
||||
val txFeeState = when (feeData) {
|
||||
is ProxyFees.MultipleFees -> feeData.proxyFeesToFeeState(networkId)
|
||||
|
|
@ -1048,6 +1049,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
permissionState = PermissionDataState.PermissionLoading,
|
||||
)
|
||||
}
|
||||
val derivationPath = fromToken.network.derivationPath.value
|
||||
// setting up amount for approve with given amount for swap [SwapApproveType.Limited]
|
||||
val transactionData = getApproveData(
|
||||
networkId = networkId,
|
||||
|
|
@ -1091,7 +1093,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
permissionState = PermissionDataState.PermissionReadyForRequest(
|
||||
currency = fromToken.symbol,
|
||||
amount = INFINITY_SYMBOL,
|
||||
walletAddress = getWalletAddress(networkId),
|
||||
walletAddress = getWalletAddress(networkId, derivationPath),
|
||||
spenderAddress = getTokenAddress(fromToken),
|
||||
requestApproveData = RequestApproveStateData(
|
||||
fee = feeState,
|
||||
|
|
@ -1262,7 +1264,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getWalletAddress(networkId: String): String {
|
||||
private suspend fun getWalletAddress(networkId: String, derivationPath: String?): String {
|
||||
return userWalletManager.getWalletAddress(networkId, derivationPath)
|
||||
}
|
||||
|
||||
|
|
@ -1286,7 +1288,10 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
if (fee == null) {
|
||||
return false
|
||||
}
|
||||
val nativeTokenBalance = userWalletManager.getNativeTokenBalance(networkId, derivationPath)
|
||||
val nativeTokenBalance = userWalletManager.getNativeTokenBalance(
|
||||
networkId,
|
||||
fromToken.network.derivationPath.value,
|
||||
)
|
||||
val percentsToFeeIncrease = BigDecimal.ONE
|
||||
return when (fromToken) {
|
||||
is CryptoCurrency.Coin -> {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import com.tangem.feature.swap.models.ApproveType
|
|||
|
||||
data class SwapProcessDataState(
|
||||
// Initial network id
|
||||
val networkId: String,
|
||||
val fromCryptoCurrency: CryptoCurrencyStatus? = null,
|
||||
val toCryptoCurrency: CryptoCurrencyStatus? = null,
|
||||
// Amount from input
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
private val amountDebouncer = Debouncer()
|
||||
private val singleTaskScheduler = SingleTaskScheduler<Map<SwapProvider, SwapState>>()
|
||||
|
||||
private var dataState by mutableStateOf(SwapProcessDataState(networkId = initialCryptoCurrency.network.backendId))
|
||||
private var dataState by mutableStateOf(SwapProcessDataState())
|
||||
|
||||
var uiState: SwapStateHolder by mutableStateOf(
|
||||
stateBuilder.createInitialLoadingState(
|
||||
|
|
@ -103,11 +103,6 @@ internal class SwapViewModel @Inject constructor(
|
|||
requireNotNull(getCryptoCurrencyStatusUseCase(it.walletId, initialCryptoCurrency.id).getOrNull()) {
|
||||
"Failed to get initial crypto currency status"
|
||||
}
|
||||
|
||||
swapInteractor.initDerivationPathAndNetwork(
|
||||
derivationPath = initialCryptoCurrency.network.derivationPath.value,
|
||||
network = initialCryptoCurrency.network,
|
||||
)
|
||||
initTokens()
|
||||
}
|
||||
}
|
||||
|
|
@ -278,7 +273,6 @@ internal class SwapViewModel @Inject constructor(
|
|||
approveDataModel = null,
|
||||
)
|
||||
swapInteractor.findBestQuote(
|
||||
networkId = dataState.networkId,
|
||||
fromToken = fromToken,
|
||||
toToken = toToken,
|
||||
providers = toProvidersList,
|
||||
|
|
@ -438,13 +432,13 @@ internal class SwapViewModel @Inject constructor(
|
|||
Timber.e("Last loaded quotes state is null")
|
||||
return
|
||||
}
|
||||
val fromCurrency = requireNotNull(dataState.fromCryptoCurrency)
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
runCatching(dispatchers.io) {
|
||||
swapInteractor.onSwap(
|
||||
swapProvider = provider,
|
||||
networkId = dataState.networkId,
|
||||
swapData = dataState.swapDataModel,
|
||||
currencyToSend = requireNotNull(dataState.fromCryptoCurrency),
|
||||
currencyToSend = fromCurrency,
|
||||
currencyToGet = requireNotNull(dataState.toCryptoCurrency),
|
||||
amountToSwap = requireNotNull(dataState.amount),
|
||||
includeFeeInAmount = lastLoadedQuotesState.preparedSwapConfigState.includeFeeInAmount,
|
||||
|
|
@ -454,7 +448,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
when (it) {
|
||||
is TxState.TxSent -> {
|
||||
val url = blockchainInteractor.getExplorerTransactionLink(
|
||||
networkId = dataState.networkId,
|
||||
networkId = fromCurrency.currency.network.backendId,
|
||||
txAddress = it.txAddress,
|
||||
)
|
||||
uiState = stateBuilder.createSuccessState(
|
||||
|
|
@ -528,17 +522,18 @@ internal class SwapViewModel @Inject constructor(
|
|||
is TxFeeState.SingleFeeState -> fee.fee
|
||||
null -> error("Fee should not be null")
|
||||
}
|
||||
val fromToken = requireNotNull(dataState.fromCryptoCurrency?.currency) {
|
||||
"dataState.fromCurrency might not be null"
|
||||
}
|
||||
swapInteractor.givePermissionToSwap(
|
||||
networkId = dataState.networkId,
|
||||
networkId = fromToken.network.backendId,
|
||||
permissionOptions = PermissionOptions(
|
||||
approveData = requireNotNull(dataState.approveDataModel) {
|
||||
"dataState.approveDataModel might not be null"
|
||||
},
|
||||
forTokenContractAddress = (dataState.fromCryptoCurrency?.currency as? CryptoCurrency.Token)
|
||||
?.contractAddress ?: "",
|
||||
fromToken = requireNotNull(dataState.fromCryptoCurrency?.currency) {
|
||||
"dataState.fromCurrency might not be null"
|
||||
},
|
||||
fromToken = fromToken,
|
||||
approveType = requireNotNull(dataState.approveType) {
|
||||
"uiState.permissionState should not be null"
|
||||
}.toDomainApproveType(),
|
||||
|
|
@ -805,7 +800,6 @@ internal class SwapViewModel @Inject constructor(
|
|||
selectedFee = it.feeType,
|
||||
fromToken = fromToken,
|
||||
amountToSwap = amountToSwap,
|
||||
networkId = dataState.networkId,
|
||||
)
|
||||
setupLoadedState(selectedProvider, updatedState, fromToken)
|
||||
}
|
||||
|
|
@ -839,7 +833,11 @@ internal class SwapViewModel @Inject constructor(
|
|||
},
|
||||
onBuyClick = {
|
||||
swapInteractor.getSelectedWallet()?.let {
|
||||
swapRouter.openTokenDetails(it.walletId, swapInteractor.getNativeToken(dataState.networkId))
|
||||
val fromToken = dataState.fromCryptoCurrency ?: return@let
|
||||
swapRouter.openTokenDetails(
|
||||
it.walletId,
|
||||
swapInteractor.getNativeToken(fromToken.currency.network.backendId),
|
||||
)
|
||||
}
|
||||
},
|
||||
onRetryClick = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue