Updated on 2026-08-14
This commit is contained in:
parent
e50716f834
commit
2bb8415d4d
9 changed files with 92 additions and 59 deletions
|
|
@ -2,7 +2,7 @@ package com.tangem.feature.swap.domain
|
|||
|
||||
import com.tangem.feature.swap.domain.models.SwapAmount
|
||||
import com.tangem.feature.swap.domain.models.domain.Currency
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapApproveType
|
||||
import com.tangem.feature.swap.domain.models.domain.PermissionOptions
|
||||
import com.tangem.feature.swap.domain.models.ui.*
|
||||
|
||||
interface SwapInteractor {
|
||||
|
|
@ -40,19 +40,10 @@ interface SwapInteractor {
|
|||
* Gives permission to swap, this starts scan card process
|
||||
*
|
||||
* @param networkId network in which selected token
|
||||
* @param approveData tx data to give approve, it loaded from 1inch in findBestQuote if needed
|
||||
* @param forTokenContractAddress token contract address for which needs permission
|
||||
* @param fromToken which token will be swapping
|
||||
* @param approveType unlimited or tx amount approve
|
||||
* @param permissionOptions data to give permissions
|
||||
*/
|
||||
@Throws(IllegalStateException::class)
|
||||
suspend fun givePermissionToSwap(
|
||||
networkId: String,
|
||||
approveData: RequestApproveStateData,
|
||||
forTokenContractAddress: String,
|
||||
fromToken: Currency,
|
||||
approveType: SwapApproveType,
|
||||
): TxState
|
||||
suspend fun givePermissionToSwap(networkId: String, permissionOptions: PermissionOptions): TxState
|
||||
|
||||
/**
|
||||
* Find best quote for given tokens to swap
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.feature.swap.domain.converters.CryptoCurrencyConverter
|
|||
import com.tangem.feature.swap.domain.models.DataError
|
||||
import com.tangem.feature.swap.domain.models.SwapAmount
|
||||
import com.tangem.feature.swap.domain.models.domain.Currency
|
||||
import com.tangem.feature.swap.domain.models.domain.PermissionOptions
|
||||
import com.tangem.feature.swap.domain.models.domain.PreparedSwapConfigState
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapApproveType
|
||||
import com.tangem.feature.swap.domain.models.toStringWithRightOffset
|
||||
|
|
@ -107,29 +108,23 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
?: loadedTokens.firstOrNull { it.token.id == id }?.token
|
||||
}
|
||||
|
||||
override suspend fun givePermissionToSwap(
|
||||
networkId: String,
|
||||
approveData: RequestApproveStateData,
|
||||
forTokenContractAddress: String,
|
||||
fromToken: Currency,
|
||||
approveType: SwapApproveType,
|
||||
): TxState {
|
||||
val dataToSign = if (approveType == SwapApproveType.UNLIMITED) {
|
||||
repository.dataToApprove(networkId, getTokenAddress(fromToken)).data
|
||||
override suspend fun givePermissionToSwap(networkId: String, permissionOptions: PermissionOptions): TxState {
|
||||
val dataToSign = if (permissionOptions.approveType == SwapApproveType.UNLIMITED) {
|
||||
repository.dataToApprove(networkId, getTokenAddress(permissionOptions.fromToken)).data
|
||||
} else {
|
||||
approveData.approveModel.data
|
||||
permissionOptions.approveData.approveModel.data
|
||||
}
|
||||
val result = transactionManager.sendApproveTransaction(
|
||||
networkId = networkId,
|
||||
feeAmount = approveData.fee,
|
||||
gasLimit = approveData.gasLimit,
|
||||
destinationAddress = approveData.approveModel.toAddress,
|
||||
feeAmount = permissionOptions.txFee.feeValue,
|
||||
gasLimit = permissionOptions.txFee.gasLimit,
|
||||
destinationAddress = permissionOptions.approveData.approveModel.toAddress,
|
||||
dataToSign = dataToSign,
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
return when (result) {
|
||||
is SendTxResult.Success -> {
|
||||
allowPermissionsHandler.addAddressToInProgress(forTokenContractAddress)
|
||||
allowPermissionsHandler.addAddressToInProgress(permissionOptions.forTokenContractAddress)
|
||||
TxState.TxSent(txAddress = userWalletManager.getLastTransactionHash(networkId, derivationPath) ?: "")
|
||||
}
|
||||
SendTxResult.UserCancelledError -> TxState.UserCancelled
|
||||
|
|
@ -414,7 +409,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
val isBalanceIncludeFeeEnough =
|
||||
isBalanceEnough(networkId, fromToken, amount, txFeeState.priorityFee.feeValue)
|
||||
val isFeeEnough = checkFeeIsEnough(
|
||||
fee = txFeeState.priorityFee.feeValue,
|
||||
fee = txFeeState.normalFee.feeValue,
|
||||
spendAmount = amount,
|
||||
networkId = networkId,
|
||||
fromToken = fromToken,
|
||||
|
|
@ -527,12 +522,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
data = transactionData.data,
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
val feeFiat = getFormattedFiatFees(networkId, feeData.normalFee.fee.value)
|
||||
val formattedFee = amountFormatter.formatBigDecimalAmountToUI(
|
||||
amount = feeData.normalFee.fee.value,
|
||||
decimals = transactionManager.getNativeTokenDecimals(networkId),
|
||||
currency = userWalletManager.getNetworkCurrency(networkId),
|
||||
) + (feeFiat.firstOrNull() ?: "")
|
||||
val feeState = proxyFeesToFeeState(networkId, feeData)
|
||||
val isFeeEnough = checkFeeIsEnough(
|
||||
fee = feeData.normalFee.fee.value,
|
||||
spendAmount = SwapAmount.zeroSwapAmount(),
|
||||
|
|
@ -545,10 +535,8 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
amount = INFINITY_SYMBOL,
|
||||
walletAddress = getWalletAddress(networkId),
|
||||
spenderAddress = transactionData.toAddress,
|
||||
fee = formattedFee,
|
||||
requestApproveData = RequestApproveStateData(
|
||||
fee = feeData.normalFee.fee.value,
|
||||
gasLimit = feeData.normalFee.gasLimit.toInt(),
|
||||
fee = feeState,
|
||||
approveModel = transactionData,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.feature.swap.domain.models.domain
|
||||
|
||||
import com.tangem.feature.swap.domain.models.ui.RequestApproveStateData
|
||||
import com.tangem.feature.swap.domain.models.ui.TxFee
|
||||
|
||||
/**
|
||||
* Permission options
|
||||
*
|
||||
* @param approveData tx data to give approve, it loaded from 1inch in findBestQuote if needed
|
||||
* @param forTokenContractAddress token contract address for which needs permission
|
||||
* @param fromToken which token will be swapping
|
||||
* @param approveType unlimited or tx amount approve
|
||||
* @param txFee fee for tx
|
||||
*/
|
||||
data class PermissionOptions(
|
||||
val approveData: RequestApproveStateData,
|
||||
val forTokenContractAddress: String,
|
||||
val fromToken: Currency,
|
||||
val approveType: SwapApproveType,
|
||||
val txFee: TxFee,
|
||||
)
|
||||
|
|
@ -40,7 +40,6 @@ sealed class PermissionDataState {
|
|||
val amount: String,
|
||||
val walletAddress: String,
|
||||
val spenderAddress: String,
|
||||
val fee: String,
|
||||
val requestApproveData: RequestApproveStateData,
|
||||
) : PermissionDataState()
|
||||
|
||||
|
|
@ -59,8 +58,7 @@ data class TokenSwapInfo(
|
|||
)
|
||||
|
||||
data class RequestApproveStateData(
|
||||
val fee: BigDecimal,
|
||||
val gasLimit: Int,
|
||||
val fee: TxFeeState,
|
||||
val approveModel: ApproveModel,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.swap.models
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapApproveType
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -15,7 +16,7 @@ sealed class SwapPermissionState {
|
|||
val amount: String,
|
||||
val walletAddress: String,
|
||||
val spenderAddress: String,
|
||||
val fee: String,
|
||||
val fee: TextReference,
|
||||
val approveType: ApproveType,
|
||||
val approveItems: ImmutableList<ApproveType> = ApproveType.values().toList().toImmutableList(),
|
||||
val approveButton: ApprovePermissionButton,
|
||||
|
|
|
|||
|
|
@ -57,17 +57,21 @@ sealed class FeeState(open val tangemFee: Double) {
|
|||
|
||||
data class Loaded(
|
||||
override val tangemFee: Double,
|
||||
val state: SelectableItemsState<TxFee>?,
|
||||
override val state: SelectableItemsState<TxFee>?,
|
||||
val onSelectItem: (Item<TxFee>) -> Unit,
|
||||
) : FeeState(tangemFee)
|
||||
) : FeeState(tangemFee), FeeSelectState
|
||||
|
||||
object Loading : FeeState(0.0)
|
||||
|
||||
data class NotEnoughFundsWarning(
|
||||
override val tangemFee: Double,
|
||||
val state: SelectableItemsState<TxFee>?,
|
||||
override val state: SelectableItemsState<TxFee>?,
|
||||
val onSelectItem: (Item<TxFee>) -> Unit,
|
||||
) : FeeState(tangemFee)
|
||||
) : FeeState(tangemFee), FeeSelectState
|
||||
}
|
||||
|
||||
sealed interface FeeSelectState {
|
||||
val state: SelectableItemsState<TxFee>?
|
||||
}
|
||||
|
||||
sealed interface TransactionCardType {
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
permissionState = convertPermissionState(
|
||||
lastPermissionState = uiStateHolder.permissionState,
|
||||
permissionDataState = quoteModel.permissionState,
|
||||
feeState = feeState,
|
||||
onGivePermissionClick = actions.onGivePermissionClick,
|
||||
onChangeApproveType = actions.onChangeApproveType,
|
||||
),
|
||||
|
|
@ -269,6 +270,14 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
val newSelectedItem = item.copy(
|
||||
startText = TextReference.Res(R.string.send_network_fee_title),
|
||||
)
|
||||
val permissionState = uiState.permissionState
|
||||
val newPermissionState = if (permissionState is SwapPermissionState.ReadyForRequest) {
|
||||
permissionState.copy(
|
||||
fee = newSelectedItem.endText,
|
||||
)
|
||||
} else {
|
||||
permissionState
|
||||
}
|
||||
return when (val fee = uiState.fee) {
|
||||
is FeeState.Loaded -> {
|
||||
val newState = fee.state?.copy(
|
||||
|
|
@ -277,6 +286,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
)
|
||||
uiState.copy(
|
||||
fee = fee.copy(state = newState),
|
||||
permissionState = newPermissionState,
|
||||
)
|
||||
}
|
||||
is FeeState.NotEnoughFundsWarning -> {
|
||||
|
|
@ -286,6 +296,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
)
|
||||
uiState.copy(
|
||||
fee = fee.copy(state = newState),
|
||||
permissionState = newPermissionState,
|
||||
)
|
||||
}
|
||||
else -> uiState
|
||||
|
|
@ -302,8 +313,14 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
is FeeState.NotEnoughFundsWarning -> stateFee.state
|
||||
else -> null
|
||||
}
|
||||
val permissionState = quoteModel.permissionState
|
||||
val feeState = if (permissionState is PermissionDataState.PermissionReadyForRequest) {
|
||||
permissionState.requestApproveData.fee
|
||||
} else {
|
||||
quoteModel.swapDataModel?.fee
|
||||
}
|
||||
val selectFeeState = createSelectFeeState(
|
||||
fee = quoteModel.swapDataModel?.fee,
|
||||
fee = feeState,
|
||||
previousState = previousFeeState,
|
||||
onFeeSetup = onFeeSetup,
|
||||
)
|
||||
|
|
@ -407,6 +424,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
private fun convertPermissionState(
|
||||
lastPermissionState: SwapPermissionState,
|
||||
permissionDataState: PermissionDataState,
|
||||
feeState: FeeState,
|
||||
onGivePermissionClick: () -> Unit,
|
||||
onChangeApproveType: (ApproveType) -> Unit,
|
||||
): SwapPermissionState {
|
||||
|
|
@ -415,6 +433,10 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
} else {
|
||||
ApproveType.UNLIMITED
|
||||
}
|
||||
val fee = when (feeState) {
|
||||
is FeeSelectState -> feeState.state?.selectedItem?.endText
|
||||
else -> null
|
||||
}
|
||||
return when (permissionDataState) {
|
||||
PermissionDataState.Empty -> SwapPermissionState.Empty
|
||||
PermissionDataState.PermissionFailed -> SwapPermissionState.Empty
|
||||
|
|
@ -425,7 +447,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
approveType = approveType,
|
||||
walletAddress = getShortAddressValue(permissionDataState.walletAddress),
|
||||
spenderAddress = getShortAddressValue(permissionDataState.spenderAddress),
|
||||
fee = permissionDataState.fee,
|
||||
fee = fee ?: TextReference.Str(""),
|
||||
approveButton = ApprovePermissionButton(
|
||||
enabled = true,
|
||||
onClick = onGivePermissionClick,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.atoms.Hand
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.ApprovePermissionButton
|
||||
import com.tangem.feature.swap.models.ApproveType
|
||||
|
|
@ -123,7 +125,7 @@ private fun ApprovalBottomSheetInfo(data: SwapPermissionState.ReadyForRequest) {
|
|||
)
|
||||
SpacerH24()
|
||||
DividerBottomSheet()
|
||||
FeeItem(fee = data.fee)
|
||||
FeeItem(fee = data.fee.resolveReference())
|
||||
SubtitleItem(
|
||||
subtitle = stringResource(id = R.string.swapping_permission_fee_footer),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
|
@ -303,7 +305,7 @@ private val previewData = SwapPermissionState.ReadyForRequest(
|
|||
amount = "∞",
|
||||
walletAddress = "",
|
||||
spenderAddress = "",
|
||||
fee = "2,14$",
|
||||
fee = TextReference.Str("2,14$"),
|
||||
approveType = ApproveType.UNLIMITED,
|
||||
approveButton = ApprovePermissionButton(true) {},
|
||||
cancelButton = CancelPermissionButton(true),
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.feature.swap.analytics.SwapEvents
|
|||
import com.tangem.feature.swap.domain.BlockchainInteractor
|
||||
import com.tangem.feature.swap.domain.SwapInteractor
|
||||
import com.tangem.feature.swap.domain.models.domain.Currency
|
||||
import com.tangem.feature.swap.domain.models.domain.PermissionOptions
|
||||
import com.tangem.feature.swap.domain.models.formatToUIRepresentation
|
||||
import com.tangem.feature.swap.domain.models.ui.*
|
||||
import com.tangem.feature.swap.models.SwapPermissionState
|
||||
|
|
@ -276,17 +277,22 @@ internal class SwapViewModel @Inject constructor(
|
|||
runCatching(dispatchers.io) {
|
||||
swapInteractor.givePermissionToSwap(
|
||||
networkId = dataState.networkId,
|
||||
approveData = requireNotNull(dataState.approveDataModel) {
|
||||
Timber.e("dataState.approveDataModel might not be null")
|
||||
},
|
||||
forTokenContractAddress = (dataState.fromCurrency as? Currency.NonNativeToken)?.contractAddress
|
||||
?: "",
|
||||
fromToken = requireNotNull(dataState.fromCurrency) {
|
||||
Timber.e("dataState.fromCurrency might not be null")
|
||||
},
|
||||
approveType = requireNotNull(uiState.permissionState as? SwapPermissionState.ReadyForRequest) {
|
||||
Timber.e("uiState.permissionState should be SwapPermissionState.ReadyForRequest")
|
||||
}.approveType.toDomainApproveType(),
|
||||
permissionOptions = PermissionOptions(
|
||||
approveData = requireNotNull(dataState.approveDataModel) {
|
||||
"dataState.approveDataModel might not be null"
|
||||
},
|
||||
forTokenContractAddress = (dataState.fromCurrency as? Currency.NonNativeToken)?.contractAddress
|
||||
?: "",
|
||||
fromToken = requireNotNull(dataState.fromCurrency) {
|
||||
"dataState.fromCurrency might not be null"
|
||||
},
|
||||
approveType = requireNotNull(uiState.permissionState as? SwapPermissionState.ReadyForRequest) {
|
||||
"uiState.permissionState should be SwapPermissionState.ReadyForRequest"
|
||||
}.approveType.toDomainApproveType(),
|
||||
txFee = requireNotNull(dataState.selectedFee) {
|
||||
"dataState.selectedFee shouldn't be null"
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
.onSuccess {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue