Updated on 2026-08-14

This commit is contained in:
Tangem 2023-01-13 17:01:04 +03:00
parent cf4dc83d0d
commit 83571f3bd5
19 changed files with 360 additions and 57 deletions

View file

@ -66,6 +66,12 @@ class TransactionManagerImpl(
return Blockchain.fromNetworkId(networkId)?.decimals() ?: error("blockchain not found")
}
override fun calculateFee(networkId: String, gasPrice: String, estimatedGas: Int): BigDecimal {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val gasPriceValue = requireNotNull(gasPrice.toLongOrNull()) { "gasprice should be Long" }
return (gasPriceValue * estimatedGas).toBigDecimal().movePointLeft(blockchain.decimals())
}
@Throws(IllegalStateException::class)
override suspend fun getFee(
networkId: String,

View file

@ -131,6 +131,11 @@ class UserWalletManagerImpl(
)
}
override fun getCurrencyByNetworkId(networkId: String): String {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
return blockchain.currency
}
private fun addNativeTokenToWalletAction(token: NativeToken, card: CardDTO): Action {
val blockchain = Blockchain.fromNetworkId(token.networkId)
val scanResponse = appStateHolder.scanResponse

View file

@ -23,6 +23,7 @@ class OneInchApisModule {
val apiFactory = OneInchApiFactory()
apiFactory.putApi(ETH_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_ETH_PATH, moshi))
apiFactory.putApi(BSC_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_BSC_PATH, moshi))
apiFactory.putApi(POLYGON_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_POLYGON_PATH, moshi))
return apiFactory
}
@ -45,8 +46,10 @@ class OneInchApisModule {
private const val ONE_INCH_BASE_URL = "https://api.1inch.io/v5.0/"
private const val ONE_INCH_ETH_PATH = "1/"
private const val ONE_INCH_BSC_PATH = "56/"
private const val ONE_INCH_POLYGON_PATH = "137/"
private const val ETH_NETWORK = "ethereum"
private const val BSC_NETWORK = "binance-smart-chain"
private const val POLYGON_NETWORK = "polygon-pos"
}
}

View file

@ -4,6 +4,7 @@ import com.tangem.datasource.api.oneinch.models.SwapResponse
import com.tangem.datasource.api.oneinch.models.TransactionDto
import com.tangem.feature.swap.domain.models.SwapDataModel
import com.tangem.feature.swap.domain.models.TransactionModel
import com.tangem.feature.swap.domain.models.createFromAmountWithOffset
import com.tangem.utils.converter.Converter
import javax.inject.Inject
@ -13,8 +14,8 @@ class SwapConverter @Inject constructor() : Converter<SwapResponse, SwapDataMode
return SwapDataModel(
fromTokenAddress = value.fromToken.address,
toTokenAddress = value.toToken.address,
toTokenAmount = value.fromTokenAmount,
fromTokenAmount = value.toTokenAmount,
fromTokenAmount = createFromAmountWithOffset(value.fromTokenAmount, value.fromToken.decimals),
toTokenAmount = createFromAmountWithOffset(value.toTokenAmount, value.toToken.decimals),
transaction = convertTransaction(value.transaction),
)
}

View file

@ -0,0 +1,8 @@
package com.tangem.feature.swap.domain
interface AllowPermissionsHandler {
fun addAddressToInProgress(tokenAddress: String)
fun removeAddressFromProgress(tokenAddress: String)
fun isAddressAllowanceInProgress(tokenAddress: String): Boolean
}

View file

@ -0,0 +1,19 @@
package com.tangem.feature.swap.domain
class AllowPermissionsHandlerImpl : AllowPermissionsHandler {
//todo maybe need to save in store
private val allowPermissionsInProgress = mutableSetOf<String>()
override fun addAddressToInProgress(tokenAddress: String) {
allowPermissionsInProgress.add(tokenAddress)
}
override fun removeAddressFromProgress(tokenAddress: String) {
allowPermissionsInProgress.remove(tokenAddress)
}
override fun isAddressAllowanceInProgress(tokenAddress: String): Boolean {
return allowPermissionsInProgress.contains(tokenAddress)
}
}

View file

@ -8,6 +8,11 @@ interface SwapInteractor {
suspend fun getTokensToSwap(networkId: String): List<Currency>
/**
* Give permission to swap
*
* @param tokenToApprove use token which you want to swap
*/
@Throws(IllegalStateException::class)
suspend fun givePermissionToSwap(tokenToApprove: Currency)

View file

@ -2,15 +2,17 @@ package com.tangem.feature.swap.domain
import com.tangem.feature.swap.domain.cache.SwapDataCache
import com.tangem.feature.swap.domain.converters.CryptoCurrencyConverter
import com.tangem.feature.swap.domain.models.ApproveModel
import com.tangem.feature.swap.domain.models.Currency
import com.tangem.feature.swap.domain.models.DataError
import com.tangem.feature.swap.domain.models.QuoteModel
import com.tangem.feature.swap.domain.models.PermissionDataState
import com.tangem.feature.swap.domain.models.SwapAmount
import com.tangem.feature.swap.domain.models.SwapState
import com.tangem.feature.swap.domain.models.TransactionModel
import com.tangem.feature.swap.domain.models.toStringWithRightOffset
import com.tangem.lib.crypto.TransactionManager
import com.tangem.lib.crypto.UserWalletManager
import com.tangem.lib.crypto.models.transactions.SendTxResult
import com.tangem.utils.toFiatString
import com.tangem.utils.toFormattedString
import java.math.BigDecimal
@ -21,6 +23,7 @@ internal class SwapInteractorImpl @Inject constructor(
private val userWalletManager: UserWalletManager,
private val repository: SwapRepository,
private val cache: SwapDataCache,
private val allowPermissionsHandler: AllowPermissionsHandler,
) : SwapInteractor {
private val cryptoCurrencyConverter = CryptoCurrencyConverter()
@ -40,9 +43,10 @@ internal class SwapInteractorImpl @Inject constructor(
if (tokenToApprove is Currency.NonNativeToken) {
val estimatedGas =
requireNotNull(cache.getLastQuote()?.estimatedGas) { "estimatedGas not found call findBestQuote" }
val transactionData = repository.dataToApprove(networkId, tokenToApprove.contractAddress)
val transactionData =
requireNotNull(cache.getApproveTransactionData()) { "getApproveTransactionData not found, call findQuotes" }
val gasPrice = transactionData.gasPrice.toBigDecimalOrNull() ?: error("cannot parse gasPrice")
val fee = gasPrice.multiply(estimatedGas.toBigDecimal()).movePointLeft(tokenToApprove.decimalCount)
val fee = transactionManager.calculateFee(networkId, gasPrice.toPlainString(), estimatedGas)
val result = transactionManager.sendTransaction(
networkId = networkId,
amountToSend = BigDecimal.ZERO,
@ -51,43 +55,46 @@ internal class SwapInteractorImpl @Inject constructor(
destinationAddress = transactionData.toAddress,
dataToSign = transactionData.data,
)
when (result) {
SendTxResult.Success -> {
allowPermissionsHandler.addAddressToInProgress(tokenToApprove.contractAddress)
}
SendTxResult.UserCancelledError -> TODO()
is SendTxResult.BlockchainSdkError -> TODO()
is SendTxResult.TangemSdkError -> TODO()
is SendTxResult.UnknownError -> TODO()
}
}
}
}
override suspend fun findBestQuote(fromToken: Currency, toToken: Currency, amount: SwapAmount): SwapState {
val networkId = cache.getNetworkId()
val networkId = requireNotNull(cache.getNetworkId()) { "no networkId found, please call getTokensToSwap first" }
val fromTokenAddress = getTokenAddress(fromToken)
val toTokenAddress = getTokenAddress(toToken)
val isAllowedToSpend = if (networkId.isNullOrEmpty()) {
error("no networkId found, please call getTokensToSwap first")
} else {
val allowance =
repository.checkTokensSpendAllowance(
networkId = networkId,
tokenAddress = fromTokenAddress,
walletAddress = userWalletManager.getWalletAddress(networkId),
)
allowance.error == DataError.NO_ERROR && allowance.dataModel != "0"
}
repository.findBestQuote(
networkId = networkId,
fromTokenAddress = fromTokenAddress,
toTokenAddress = toTokenAddress,
amount = amount.toStringWithRightOffset(),
).let { quotes ->
val quoteDataModel = quotes.dataModel
if (quoteDataModel != null) {
cache.cacheSwapParams(
quoteModel = quoteDataModel.copy(isAllowedToSpend = isAllowedToSpend),
amount = amount,
fromCurrency = fromToken,
toCurrency = toToken,
)
return updateBalances(networkId, fromToken, toToken, quoteDataModel)
} else {
return SwapState.SwapError(quotes.error)
val isAllowedToSpend = checkAllowance(networkId, fromTokenAddress)
val isBalanceZero = isNotZeroBalance(fromToken, networkId)
return if (isAllowedToSpend && isBalanceZero) {
if (allowPermissionsHandler.isAddressAllowanceInProgress(fromTokenAddress)) {
allowPermissionsHandler.removeAddressFromProgress(toTokenAddress)
}
loadSwapData(
networkId = networkId,
fromTokenAddress = fromTokenAddress,
toTokenAddress = toTokenAddress,
fromToken = fromToken,
toToken = toToken,
amount = amount,
)
} else {
loadQuoteData(
networkId = networkId,
fromTokenAddress = fromTokenAddress,
toTokenAddress = toTokenAddress,
amount = amount,
fromToken = fromToken,
toToken = toToken,
)
}
}
@ -128,11 +135,120 @@ internal class SwapInteractorImpl @Inject constructor(
}
}
private suspend fun checkAllowance(networkId: String, fromTokenAddress: String): Boolean {
val allowance = repository.checkTokensSpendAllowance(
networkId = networkId,
tokenAddress = fromTokenAddress,
walletAddress = userWalletManager.getWalletAddress(networkId),
)
return allowance.error == DataError.NO_ERROR && allowance.dataModel != ZERO_BALANCE
}
/**
* Load quote data calls only if spend is not allowed for token contract address
*/
private suspend fun loadQuoteData(
networkId: String,
fromTokenAddress: String,
toTokenAddress: String,
amount: SwapAmount,
fromToken: Currency,
toToken: Currency,
): SwapState {
repository.findBestQuote(
networkId = networkId,
fromTokenAddress = fromTokenAddress,
toTokenAddress = toTokenAddress,
amount = amount.toStringWithRightOffset(),
).let { quotes ->
val quoteDataModel = quotes.dataModel
if (quoteDataModel != null) {
cache.cacheSwapParams(
quoteModel = quoteDataModel,
amount = amount,
fromCurrency = fromToken,
toCurrency = toToken,
)
val transactionData = repository.dataToApprove(networkId, getTokenAddress(fromToken))
cache.cacheApproveTransactionData(transactionData)
val swapState = updateBalances(
networkId = networkId,
fromToken = fromToken,
toToken = toToken,
fromTokenAmount = quoteDataModel.fromTokenAmount,
toTokenAmount = quoteDataModel.toTokenAmount,
fee = transactionManager.calculateFee(
networkId = networkId,
estimatedGas = quoteDataModel.estimatedGas,
gasPrice = transactionData.gasPrice,
),
isAllowedToSpend = false,
)
return updatePermissionState(
networkId = networkId,
fromToken = fromToken,
quotesLoadedState = swapState,
estimatedGas = quoteDataModel.estimatedGas,
transactionData = transactionData,
)
} else {
return SwapState.SwapError(quotes.error)
}
}
}
/**
* Load swap data calls only if spend is allowed for token contract address
*/
private suspend fun loadSwapData(
networkId: String,
fromTokenAddress: String,
toTokenAddress: String,
fromToken: Currency,
toToken: Currency,
amount: SwapAmount,
): SwapState {
repository.prepareSwapTransaction(
networkId = networkId,
fromTokenAddress = fromTokenAddress,
toTokenAddress = toTokenAddress,
amount = amount.toStringWithRightOffset(),
slippage = DEFAULT_SLIPPAGE,
fromWalletAddress = getWalletAddress(networkId),
).let {
val swapData = it.dataModel
if (swapData != null) {
cache.cacheSwapData(swapData)
val swapState = updateBalances(
networkId = networkId,
fromToken = fromToken,
toToken = toToken,
fromTokenAmount = swapData.fromTokenAmount,
toTokenAmount = swapData.toTokenAmount,
fee = transactionManager.calculateFee(
networkId = networkId,
estimatedGas = swapData.transaction.gas.toIntOrNull() ?: DEFAULT_GAS,
gasPrice = swapData.transaction.gasPrice,
),
isAllowedToSpend = false,
)
return swapState.copy(
permissionState = PermissionDataState.Empty,
)
} else {
return SwapState.SwapError(it.error)
}
}
}
private suspend fun updateBalances(
networkId: String,
fromToken: Currency,
toToken: Currency,
quoteModel: QuoteModel,
fromTokenAmount: SwapAmount,
toTokenAmount: SwapAmount,
fee: BigDecimal,
isAllowedToSpend: Boolean,
): SwapState.QuotesLoadedState {
val appCurrency = userWalletManager.getUserAppCurrency()
val rates = repository.getRates(appCurrency.code, listOf(fromToken.id, toToken.id))
@ -144,25 +260,58 @@ internal class SwapInteractorImpl @Inject constructor(
it.value.toFormattedString(it.decimals)
}
return SwapState.QuotesLoadedState(
fromTokenAmount = quoteModel.fromTokenAmount,
toTokenAmount = quoteModel.toTokenAmount,
fromTokenAddress = quoteModel.fromTokenAddress,
toTokenAddress = quoteModel.toTokenAddress,
estimatedGas = quoteModel.estimatedGas,
isAllowedToSpend = quoteModel.isAllowedToSpend,
fromTokenAmount = fromTokenAmount,
toTokenAmount = toTokenAmount,
fromTokenAddress = getTokenAddress(fromToken),
toTokenAddress = getTokenAddress(toToken),
fee = fee.toPlainString(),
isAllowedToSpend = isAllowedToSpend,
fromTokenWalletBalance = fromTokenBalance ?: ZERO_BALANCE,
fromTokenFiatBalance = quoteModel.fromTokenAmount.value.toFiatString(
fromTokenFiatBalance = fromTokenAmount.value.toFiatString(
rates[fromToken.id]?.toBigDecimal() ?: BigDecimal.ZERO,
appCurrency.symbol,
),
toTokenWalletBalance = toTokenBalance ?: ZERO_BALANCE,
toTokenFiatBalance = quoteModel.fromTokenAmount.value.toFiatString(
toTokenFiatBalance = fromTokenAmount.value.toFiatString(
rates[fromToken.id]?.toBigDecimal() ?: BigDecimal.ZERO,
appCurrency.symbol,
),
)
}
private fun updatePermissionState(
networkId: String,
fromToken: Currency,
quotesLoadedState: SwapState.QuotesLoadedState,
estimatedGas: Int,
transactionData: ApproveModel,
): SwapState.QuotesLoadedState {
if (allowPermissionsHandler.isAddressAllowanceInProgress(fromToken.networkId)) {
return quotesLoadedState.copy(
permissionState = PermissionDataState.PermissionLoading,
)
}
return quotesLoadedState.copy(
permissionState = PermissionDataState.PermissionReadyForRequest(
currency = userWalletManager.getCurrencyByNetworkId(networkId),
amount = "infinite", //FIXME
walletAddress = getWalletAddress(networkId),
spenderAddress = transactionData.toAddress,
fee = transactionManager.calculateFee(
networkId = networkId,
gasPrice = transactionData.gasPrice,
estimatedGas = estimatedGas,
).toPlainString(),
),
)
}
private fun isNotZeroBalance(fromToken: Currency, networkId: String): Boolean {
/** to compare [BigDecimal] use only comparator */
return (userWalletManager.getCurrentWalletTokensBalance(networkId)[fromToken.symbol]?.value
?: BigDecimal.ZERO).compareTo(BigDecimal.ZERO) != 0
}
private fun getWalletAddress(networkId: String): String {
return userWalletManager.getWalletAddress(networkId)
}
@ -185,5 +334,6 @@ internal class SwapInteractorImpl @Inject constructor(
companion object {
private const val DEFAULT_SLIPPAGE = 2
private const val ZERO_BALANCE = "0"
private const val DEFAULT_GAS = 300000
}
}

View file

@ -1,17 +1,23 @@
package com.tangem.feature.swap.domain.cache
import com.tangem.feature.swap.domain.models.ApproveModel
import com.tangem.feature.swap.domain.models.Currency
import com.tangem.feature.swap.domain.models.QuoteModel
import com.tangem.feature.swap.domain.models.SwapAmount
import com.tangem.feature.swap.domain.models.SwapDataModel
import com.tangem.feature.swap.domain.models.cache.ExchangeCurrencies
interface SwapDataCache {
fun cacheNetworkId(networkId: String)
fun cacheSwapParams(quoteModel: QuoteModel, amount: SwapAmount, fromCurrency: Currency, toCurrency: Currency)
fun cacheApproveTransactionData(approve: ApproveModel)
fun cacheSwapData(swapDataModel: SwapDataModel)
fun cacheAvailableToSwapTokens(networkId: String, tokens: List<Currency>)
fun getAvailableTokens(networkId: String): List<Currency>
fun getApproveTransactionData(): ApproveModel?
fun getLastQuote(): QuoteModel?
fun getLastSwapData(): SwapDataModel?
fun getAmountToSwap(): SwapAmount?
fun getNetworkId(): String?
fun getExchangeCurrencies(): ExchangeCurrencies?

View file

@ -1,8 +1,10 @@
package com.tangem.feature.swap.domain.cache
import com.tangem.feature.swap.domain.models.ApproveModel
import com.tangem.feature.swap.domain.models.Currency
import com.tangem.feature.swap.domain.models.QuoteModel
import com.tangem.feature.swap.domain.models.SwapAmount
import com.tangem.feature.swap.domain.models.SwapDataModel
import com.tangem.feature.swap.domain.models.cache.ExchangeCurrencies
import com.tangem.feature.swap.domain.models.cache.SwapDataHolder
@ -28,6 +30,18 @@ class SwapDataCacheImpl : SwapDataCache {
)
}
override fun cacheSwapData(swapDataModel: SwapDataModel) {
lastDataForSwap = lastDataForSwap.copy(swapModel = swapDataModel)
}
override fun getLastSwapData(): SwapDataModel? {
return lastDataForSwap.swapModel
}
override fun cacheApproveTransactionData(approve: ApproveModel) {
lastDataForSwap = lastDataForSwap.copy(approveTxModel = approve)
}
override fun getExchangeCurrencies(): ExchangeCurrencies? {
return lastDataForSwap.exchangeCurrencies
}
@ -48,6 +62,10 @@ class SwapDataCacheImpl : SwapDataCache {
return availableTokensForNetwork.getOrElse(networkId) { emptyList() }
}
override fun getApproveTransactionData(): ApproveModel? {
return lastDataForSwap.approveTxModel
}
override fun getLastQuote(): QuoteModel? {
return lastDataForSwap.quoteModel
}

View file

@ -1,5 +1,6 @@
package com.tangem.feature.swap.domain.di
import com.tangem.feature.swap.domain.AllowPermissionsHandlerImpl
import com.tangem.feature.swap.domain.SwapInteractor
import com.tangem.feature.swap.domain.SwapInteractorImpl
import com.tangem.feature.swap.domain.SwapRepository
@ -28,6 +29,7 @@ class SwapDomainModule {
userWalletManager = userWalletManager,
repository = swapRepository,
cache = SwapDataCacheImpl(),
allowPermissionsHandler = AllowPermissionsHandlerImpl(),
)
}
}

View file

@ -8,7 +8,6 @@ package com.tangem.feature.swap.domain.models
* @property fromTokenAddress address token you want to exchange
* @property toTokenAddress address token you want to receive
* @property estimatedGas fee
* @property isAllowedToSpend does spend allowed
*/
data class QuoteModel(
val fromTokenAmount: SwapAmount,
@ -16,5 +15,4 @@ data class QuoteModel(
val fromTokenAddress: String,
val toTokenAddress: String,
val estimatedGas: Int,
val isAllowedToSpend: Boolean = false,
)

View file

@ -11,7 +11,7 @@ package com.tangem.feature.swap.domain.models
data class SwapDataModel(
val fromTokenAddress: String,
val toTokenAddress: String,
val toTokenAmount: String,
val fromTokenAmount: String,
val transaction: TransactionModel
val toTokenAmount: SwapAmount,
val fromTokenAmount: SwapAmount,
val transaction: TransactionModel,
)

View file

@ -1,5 +1,7 @@
package com.tangem.feature.swap.domain.models
import java.math.BigDecimal
sealed interface SwapState {
data class QuotesLoadedState(
@ -11,8 +13,9 @@ sealed interface SwapState {
val fromTokenFiatBalance: String,
val toTokenWalletBalance: String,
val toTokenFiatBalance: String,
val estimatedGas: Int,
val fee: String,
val isAllowedToSpend: Boolean = false,
val permissionState: PermissionDataState = PermissionDataState.Empty,
) : SwapState
data class SwapSuccess(
@ -23,4 +26,21 @@ sealed interface SwapState {
data class SwapError(
val errorType: DataError,
) : SwapState
}
sealed class PermissionDataState {
data class PermissionReadyForRequest(
val currency: String,
val amount: String,
val walletAddress: String,
val spenderAddress: String,
val fee: String,
) : PermissionDataState()
object PermissionFailed : PermissionDataState()
object PermissionLoading : PermissionDataState()
object Empty : PermissionDataState()
}

View file

@ -1,11 +1,15 @@
package com.tangem.feature.swap.domain.models.cache
import com.tangem.feature.swap.domain.models.ApproveModel
import com.tangem.feature.swap.domain.models.Currency
import com.tangem.feature.swap.domain.models.QuoteModel
import com.tangem.feature.swap.domain.models.SwapAmount
import com.tangem.feature.swap.domain.models.SwapDataModel
data class SwapDataHolder(
val quoteModel: QuoteModel? = null,
val swapModel: SwapDataModel? = null,
val approveTxModel: ApproveModel? = null,
val amountToSwap: SwapAmount? = null,
val networkId: String? = null,
val exchangeCurrencies: ExchangeCurrencies? = null,

View file

@ -1,11 +1,15 @@
package com.tangem.feature.swap.ui
import com.tangem.feature.swap.domain.models.Currency
import com.tangem.feature.swap.domain.models.PermissionDataState
import com.tangem.feature.swap.domain.models.SwapState
import com.tangem.feature.swap.domain.models.formatToUIRepresentation
import com.tangem.feature.swap.models.ApprovePermissionButton
import com.tangem.feature.swap.models.CancelPermissionButton
import com.tangem.feature.swap.models.FeeState
import com.tangem.feature.swap.models.SwapButton
import com.tangem.feature.swap.models.SwapCardData
import com.tangem.feature.swap.models.SwapPermissionStateHolder
import com.tangem.feature.swap.models.SwapStateHolder
import com.tangem.feature.swap.models.SwapWarning
import com.tangem.feature.swap.models.TransactionCardType
@ -75,6 +79,7 @@ class StateBuilder {
quoteModel: SwapState.QuotesLoadedState,
fromToken: Currency,
onSwapClick: () -> Unit,
onGivePermissionClick: () -> Unit,
): SwapStateHolder {
return uiStateHolder.copy(
sendCardData = SwapCardData(
@ -102,7 +107,8 @@ class StateBuilder {
} else {
emptyList()
},
fee = FeeState.Loaded(quoteModel.estimatedGas.toString()),
permissionState = convertPermissionState(quoteModel.permissionState, onGivePermissionClick),
fee = FeeState.Loaded(quoteModel.fee),
swapButton = SwapButton(
enabled = quoteModel.isAllowedToSpend,
loading = false,
@ -110,4 +116,30 @@ class StateBuilder {
),
)
}
private fun convertPermissionState(
permissionDataState: PermissionDataState,
onGivePermissionClick: () -> Unit,
): SwapPermissionStateHolder? {
return when (permissionDataState) {
PermissionDataState.Empty -> null
PermissionDataState.PermissionFailed -> null
PermissionDataState.PermissionLoading -> null
is PermissionDataState.PermissionReadyForRequest -> SwapPermissionStateHolder(
currency = permissionDataState.currency,
amount = permissionDataState.amount,
walletAddress = permissionDataState.walletAddress,
spenderAddress = permissionDataState.spenderAddress,
fee = permissionDataState.fee,
approveButton = ApprovePermissionButton(
enabled = true,
onClick = onGivePermissionClick,
),
cancelButton = CancelPermissionButton(
enabled = true,
onClick = {},
),
)
}
}
}

View file

@ -98,9 +98,13 @@ internal class SwapViewModel @Inject constructor(
.onSuccess { swapState ->
when (swapState) {
is SwapState.QuotesLoadedState -> {
uiState = stateBuilder.createQuotesLoadedState(uiState, swapState, fromToken) {
onSwapClick(toToken, swapState)
}
uiState = stateBuilder.createQuotesLoadedState(
uiStateHolder = uiState,
quoteModel = swapState,
fromToken = fromToken,
onSwapClick = { onSwapClick(toToken, swapState) },
onGivePermissionClick = { givePermissionsToSwap(fromToken) },
)
}
is SwapState.SwapSuccess -> {
//todo implement
@ -116,10 +120,12 @@ internal class SwapViewModel @Inject constructor(
private fun onSwapClick(toToken: Currency, quoteModel: SwapState.QuotesLoadedState) {
viewModelScope.launch(dispatchers.main) {
runCatching {
if (!quoteModel.isAllowedToSpend) {
swapInteractor.givePermissionToSwap(toToken)
} else {
swapInteractor.onSwap()
withContext(dispatchers.io) {
if (!quoteModel.isAllowedToSpend) {
swapInteractor.givePermissionToSwap(toToken)
} else {
swapInteractor.onSwap()
}
}
}
.onSuccess {
@ -137,6 +143,18 @@ internal class SwapViewModel @Inject constructor(
}
}
private fun givePermissionsToSwap(tokenToApprove: Currency) {
viewModelScope.launch(dispatchers.main) {
runCatching {
withContext(dispatchers.io) {
swapInteractor.givePermissionToSwap(tokenToApprove)
}
}
.onSuccess { }
.onFailure { }
}
}
companion object {
private const val MIN_TOKENS_IN_LIST = 2
private const val INITIAL_AMOUNT = "1"

View file

@ -29,4 +29,6 @@ interface TransactionManager {
@Throws(IllegalStateException::class)
fun getNativeTokenDecimals(networkId: String): Int
fun calculateFee(networkId: String, gasPrice: String, estimatedGas: Int): BigDecimal
}

View file

@ -53,4 +53,10 @@ interface UserWalletManager {
* Returns selected app currency
*/
fun getUserAppCurrency(): ProxyFiatCurrency
/**
* @param networkId
* @return currency name
*/
fun getCurrencyByNetworkId(networkId: String): String
}