From 01e5b6d95a793ba7702966f377cb97ed94b44fa1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 9 Jan 2023 13:56:57 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tangem/tap/common/extensions/Specific.kt | 1 + .../redux/navigation/NavigationMiddleware.kt | 1 + .../tap/proxy/TransactionManagerImpl.kt | 5 +- .../java/com/tangem/utils/FormatExtensions.kt | 21 ++ .../swap/converters/QuotesConverter.kt | 7 +- .../feature/swap/domain/SwapInteractor.kt | 11 +- .../feature/swap/domain/SwapInteractorImpl.kt | 40 ++-- .../swap/domain/cache/SwapDataCache.kt | 6 +- .../swap/domain/cache/SwapDataCacheImpl.kt | 6 +- .../swap/domain/models/SwapDataHolder.kt | 3 +- .../swap/domain/models/data/SwapAmount.kt | 41 ++++ .../swap/domain/models/data/SwapState.kt | 10 +- .../tangem/feature/swap/ui/StateBuilder.kt | 110 +++++++++++ .../feature/swap/viewmodels/SwapViewModel.kt | 179 ++++++++++-------- .../tangem/lib/crypto/TransactionManager.kt | 5 + 15 files changed, 322 insertions(+), 124 deletions(-) create mode 100644 core/utils/src/main/java/com/tangem/utils/FormatExtensions.kt create mode 100644 features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/data/SwapAmount.kt create mode 100644 features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt index 795bc8e268..3f8e39c384 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt @@ -11,6 +11,7 @@ import java.text.DecimalFormat import java.text.DecimalFormatSymbols import java.util.* +//todo move extensions to utils fun BigDecimal.toFormattedString( decimals: Int, roundingMode: RoundingMode = RoundingMode.DOWN, locale: Locale = Locale.US ): String { diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt index 871ef1ea5c..d5712f07f5 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt @@ -25,6 +25,7 @@ val navigationMiddleware: Middleware = { _, state -> screen = action.screen, addToBackstack = action.addToBackstack, fgShareTransition = action.fragmentShareTransition, + bundle = action.bundle, ) } is NavigationAction.PopBackTo -> { diff --git a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt index 11b7525e9a..bec8c6eebe 100644 --- a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt @@ -33,7 +33,6 @@ class TransactionManagerImpl( private val appStateHolder: AppStateHolder, ) : TransactionManager { - @Throws(IllegalStateException::class) override suspend fun sendTransaction( networkId: String, amountToSend: BigDecimal, @@ -63,6 +62,10 @@ class TransactionManagerImpl( return handleSendResult(sendResult) } + override fun getNativeTokenDecimals(networkId: String): Int { + return Blockchain.fromNetworkId(networkId)?.decimals() ?: error("blockchain not found") + } + @Throws(IllegalStateException::class) override suspend fun getFee( networkId: String, diff --git a/core/utils/src/main/java/com/tangem/utils/FormatExtensions.kt b/core/utils/src/main/java/com/tangem/utils/FormatExtensions.kt new file mode 100644 index 0000000000..218d035d61 --- /dev/null +++ b/core/utils/src/main/java/com/tangem/utils/FormatExtensions.kt @@ -0,0 +1,21 @@ +package com.tangem.utils + +import java.math.BigDecimal +import java.math.RoundingMode +import java.text.DecimalFormat +import java.text.DecimalFormatSymbols +import java.util.* + +fun BigDecimal.toFormattedString( + decimals: Int, roundingMode: RoundingMode = RoundingMode.DOWN, locale: Locale = Locale.US, +): String { + val symbols = DecimalFormatSymbols(locale) + val df = DecimalFormat().apply { + decimalFormatSymbols = symbols + maximumFractionDigits = decimals + minimumFractionDigits = 0 + isGroupingUsed = false + this.roundingMode = roundingMode + } + return df.format(this) +} \ No newline at end of file diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/QuotesConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/QuotesConverter.kt index d6566ef6e3..4cb95d4c03 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/QuotesConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/QuotesConverter.kt @@ -2,6 +2,7 @@ package com.tangem.feature.swap.converters import com.tangem.datasource.api.oneinch.models.QuoteResponse import com.tangem.feature.swap.domain.models.data.SwapState.QuoteModel +import com.tangem.feature.swap.domain.models.data.createFromAmountWithOffset import com.tangem.utils.converter.Converter import javax.inject.Inject @@ -9,8 +10,10 @@ class QuotesConverter @Inject constructor() : Converter if (tokenToApprove is Currency.NonNativeToken) { @@ -71,11 +56,10 @@ internal class SwapInteractorImpl @Inject constructor( } } - override suspend fun findBestQuote(fromToken: Currency, toToken: Currency, amount: String): SwapState { + override suspend fun findBestQuote(fromToken: Currency, toToken: Currency, amount: SwapAmount): SwapState { val networkId = cache.getNetworkId() val fromTokenAddress = getTokenAddress(fromToken) val toTokenAddress = getTokenAddress(toToken) - val bigDecimalAmount = amount.toBigDecimalOrNull() ?: error("wrong amount format, use only digits") val isAllowedToSpend = if (networkId.isNullOrEmpty()) { error("no networkId found, please call getTokensToSwap first") } else { @@ -91,13 +75,13 @@ internal class SwapInteractorImpl @Inject constructor( networkId = networkId, fromTokenAddress = fromTokenAddress, toTokenAddress = toTokenAddress, - amount = amount, + amount = amount.toStringWithRightOffset(), ).let { quotes -> val quoteDataModel = quotes.dataModel if (quoteDataModel != null) { cache.cacheSwapParams( quoteModel = quoteDataModel.copy(isAllowedToSpend = isAllowedToSpend), - amount = bigDecimalAmount, + amount = amount, fromCurrency = fromToken, toCurrency = toToken, ) @@ -115,9 +99,9 @@ internal class SwapInteractorImpl @Inject constructor( if (quoteModel != null && amountToSwap != null && networkId != null) { repository.prepareSwapTransaction( networkId = networkId, - fromTokenAddress = quoteModel.fromTokenAmount, - toTokenAddress = quoteModel.toTokenAmount, - amount = amountToSwap.toPlainString(), + fromTokenAddress = quoteModel.fromTokenAddress, + toTokenAddress = quoteModel.toTokenAddress, + amount = amountToSwap.toStringWithRightOffset(), slippage = DEFAULT_SLIPPAGE, fromWalletAddress = getWalletAddress(networkId), ).let { @@ -137,6 +121,14 @@ internal class SwapInteractorImpl @Inject constructor( } } + override fun getTokenDecimals(token: Currency): Int { + return if (token is Currency.NonNativeToken) { + token.decimalCount + } else { + transactionManager.getNativeTokenDecimals(token.networkId) + } + } + private fun getWalletAddress(networkId: String): String { return userWalletManager.getWalletAddress(networkId) } diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCache.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCache.kt index 99002c3de9..4318e5dbfc 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCache.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCache.kt @@ -2,17 +2,17 @@ package com.tangem.feature.swap.domain.cache import com.tangem.feature.swap.domain.models.ExchangeCurrencies import com.tangem.feature.swap.domain.models.data.Currency +import com.tangem.feature.swap.domain.models.data.SwapAmount import com.tangem.feature.swap.domain.models.data.SwapState.QuoteModel -import java.math.BigDecimal interface SwapDataCache { fun cacheNetworkId(networkId: String) - fun cacheSwapParams(quoteModel: QuoteModel, amount: BigDecimal, fromCurrency: Currency, toCurrency: Currency) + fun cacheSwapParams(quoteModel: QuoteModel, amount: SwapAmount, fromCurrency: Currency, toCurrency: Currency) fun cacheAvailableToSwapTokens(networkId: String, tokens: List) fun getAvailableTokens(networkId: String): List fun getLastQuote(): QuoteModel? - fun getAmountToSwap(): BigDecimal? + fun getAmountToSwap(): SwapAmount? fun getNetworkId(): String? fun getExchangeCurrencies(): ExchangeCurrencies? } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCacheImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCacheImpl.kt index 953041e139..2ee0425726 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCacheImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCacheImpl.kt @@ -3,8 +3,8 @@ package com.tangem.feature.swap.domain.cache import com.tangem.feature.swap.domain.models.ExchangeCurrencies import com.tangem.feature.swap.domain.models.SwapDataHolder import com.tangem.feature.swap.domain.models.data.Currency +import com.tangem.feature.swap.domain.models.data.SwapAmount import com.tangem.feature.swap.domain.models.data.SwapState.QuoteModel -import java.math.BigDecimal class SwapDataCacheImpl : SwapDataCache { @@ -13,7 +13,7 @@ class SwapDataCacheImpl : SwapDataCache { override fun cacheSwapParams( quoteModel: QuoteModel, - amount: BigDecimal, + amount: SwapAmount, fromCurrency: Currency, toCurrency: Currency, ) { @@ -52,7 +52,7 @@ class SwapDataCacheImpl : SwapDataCache { return lastDataForSwap.quoteModel } - override fun getAmountToSwap(): BigDecimal? { + override fun getAmountToSwap(): SwapAmount? { return lastDataForSwap.amountToSwap } } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/SwapDataHolder.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/SwapDataHolder.kt index c2534d20fe..e974b51f15 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/SwapDataHolder.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/SwapDataHolder.kt @@ -1,12 +1,13 @@ package com.tangem.feature.swap.domain.models import com.tangem.feature.swap.domain.models.data.Currency +import com.tangem.feature.swap.domain.models.data.SwapAmount import com.tangem.feature.swap.domain.models.data.SwapState.QuoteModel import java.math.BigDecimal data class SwapDataHolder( val quoteModel: QuoteModel? = null, - val amountToSwap: BigDecimal? = null, + val amountToSwap: SwapAmount? = null, val networkId: String? = null, val exchangeCurrencies: ExchangeCurrencies? = null, ) diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/data/SwapAmount.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/data/SwapAmount.kt new file mode 100644 index 0000000000..9b5738306a --- /dev/null +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/data/SwapAmount.kt @@ -0,0 +1,41 @@ +package com.tangem.feature.swap.domain.models.data + +import com.tangem.utils.toFormattedString +import java.math.BigDecimal + +data class SwapAmount( + val value: BigDecimal, + val decimals: Int, +) + +fun SwapAmount.formatToUIRepresentation(): String { + return value.toFormattedString(decimals = decimals) +} + +fun createFromAmountWithoutOffset(amountWithoutOffset: String, decimals: Int): SwapAmount { + return SwapAmount( + value = requireNotNull( + amountWithoutOffset.toBigDecimalOrNull(), + ) { "wrong amount format, use only digits" }, + decimals = decimals, + ) +} + +fun createFromAmountWithOffset(amountWithOffset: String, decimals: Int): SwapAmount { + return SwapAmount( + value = requireNotNull( + amountWithOffset.toBigDecimalOrNull()?.movePointLeft(decimals), + ) { "wrong amount format, use only digits" }, + decimals = decimals, + ) +} + +@Throws(IllegalStateException::class) +fun SwapAmount.toStringWithRightOffset(): String { + return value.movePointRight(decimals).toPlainString() +} + +@Throws(IllegalStateException::class) +fun SwapAmount.toStringWithLeftOffset(): String { + return value.movePointLeft(decimals).toPlainString() +} \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/data/SwapState.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/data/SwapState.kt index 7c269ea158..6b00f55db1 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/data/SwapState.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/data/SwapState.kt @@ -3,15 +3,17 @@ package com.tangem.feature.swap.domain.models.data sealed interface SwapState { data class QuoteModel( - val fromTokenAmount: String, - val toTokenAmount: String, + val fromTokenAmount: SwapAmount, + val toTokenAmount: SwapAmount, + val fromTokenAddress: String, + val toTokenAddress: String, val estimatedGas: Int, val isAllowedToSpend: Boolean = false, ) : SwapState data class SwapSuccess( - val fromTokenAmount: String, - val toTokenAmount: String, + val fromTokenAmount: SwapAmount, + val toTokenAmount: SwapAmount, ) : SwapState data class SwapError( diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt new file mode 100644 index 0000000000..65a6894659 --- /dev/null +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -0,0 +1,110 @@ +package com.tangem.feature.swap.ui + +import com.tangem.feature.swap.domain.models.data.Currency +import com.tangem.feature.swap.domain.models.data.SwapState +import com.tangem.feature.swap.domain.models.data.formatToUIRepresentation +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.SwapStateHolder +import com.tangem.feature.swap.models.SwapWarning +import com.tangem.feature.swap.models.TransactionCardType + +/** + * State builder creates a specific states for SwapScreen + */ +class StateBuilder { + + fun createInitialLoadingState(networkCurrency: String, onAmountChanged: (String) -> Unit): SwapStateHolder { + return SwapStateHolder( + sendCardData = SwapCardData( + type = TransactionCardType.SendCard("", true, onAmountChanged), + amount = null, + amountEquivalent = null, + tokenIconUrl = "", + tokenCurrency = "", + canSelectAnotherToken = false, + ), + receiveCardData = SwapCardData( + type = TransactionCardType.ReceiveCard(), + amount = null, + amountEquivalent = null, + tokenIconUrl = "", + tokenCurrency = "", + canSelectAnotherToken = false, + ), + fee = FeeState.Loading, + networkCurrency = networkCurrency, + swapButton = SwapButton(enabled = false, loading = true, onClick = {}), + onRefresh = {}, + onBackClicked = {}, + onChangeCardsClicked = {}, + ) + } + + fun createQuotesLoadingState( + uiStateHolder: SwapStateHolder, + fromToken: Currency, + toToken: Currency, + mainTokenId: String, + ): SwapStateHolder { + return uiStateHolder.copy( + sendCardData = SwapCardData( + type = (uiStateHolder.sendCardData.type as TransactionCardType.SendCard), + amount = uiStateHolder.sendCardData.amount, + amountEquivalent = uiStateHolder.sendCardData.amountEquivalent, + tokenIconUrl = fromToken.logoUrl, + tokenCurrency = fromToken.symbol, + canSelectAnotherToken = mainTokenId != fromToken.id, + ), + receiveCardData = SwapCardData( + type = TransactionCardType.ReceiveCard(), + amount = uiStateHolder.receiveCardData.amount, + amountEquivalent = uiStateHolder.receiveCardData.amountEquivalent, + tokenIconUrl = toToken.logoUrl, + tokenCurrency = toToken.symbol, + canSelectAnotherToken = mainTokenId != toToken.id, + ), + fee = FeeState.Loading, + swapButton = SwapButton(enabled = false, loading = true, onClick = {}), + ) + } + + fun createQuotesLoadedState( + uiStateHolder: SwapStateHolder, + quoteModel: SwapState.QuoteModel, + fromToken: Currency, + onSwapClick: () -> Unit, + ): SwapStateHolder { + return uiStateHolder.copy( + sendCardData = SwapCardData( + type = (uiStateHolder.sendCardData.type as TransactionCardType.SendCard) + .copy(permissionIsGiven = quoteModel.isAllowedToSpend), + amount = quoteModel.fromTokenAmount.formatToUIRepresentation(), + amountEquivalent = uiStateHolder.sendCardData.amountEquivalent, + tokenIconUrl = uiStateHolder.sendCardData.tokenIconUrl, + tokenCurrency = uiStateHolder.sendCardData.tokenCurrency, + canSelectAnotherToken = uiStateHolder.sendCardData.canSelectAnotherToken, + ), + receiveCardData = SwapCardData( + type = TransactionCardType.ReceiveCard(), + amount = quoteModel.toTokenAmount.formatToUIRepresentation(), + amountEquivalent = uiStateHolder.receiveCardData.amountEquivalent, + tokenIconUrl = uiStateHolder.receiveCardData.tokenIconUrl, + tokenCurrency = uiStateHolder.receiveCardData.tokenCurrency, + canSelectAnotherToken = uiStateHolder.receiveCardData.canSelectAnotherToken, + ), + warnings = if (!quoteModel.isAllowedToSpend) { + listOf(SwapWarning.PermissionNeeded(fromToken.symbol)) + } else { + emptyList() + }, + fee = FeeState.Loaded(quoteModel.estimatedGas.toString()), + swapButton = SwapButton( + enabled = quoteModel.isAllowedToSpend, + loading = false, + onClick = onSwapClick, + ), + ) + } +} \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index 30004ba1ec..0609a4e8cf 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -4,38 +4,44 @@ import android.util.Log import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.tangem.feature.swap.domain.SwapInteractor -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.SwapSelectTokenStateHolder +import com.tangem.feature.swap.domain.models.data.Currency +import com.tangem.feature.swap.domain.models.data.SwapState +import com.tangem.feature.swap.domain.models.data.createFromAmountWithoutOffset import com.tangem.feature.swap.models.SwapStateHolder -import com.tangem.feature.swap.models.SwapSuccessStateHolder -import com.tangem.feature.swap.models.SwapWarning -import com.tangem.feature.swap.models.TokenBalanceData -import com.tangem.feature.swap.models.TokenToSelect -import com.tangem.feature.swap.models.TransactionCardType -import com.tangem.feature.swap.presentation.R +import com.tangem.feature.swap.presentation.SwapFragment import com.tangem.feature.swap.router.SwapRouter import com.tangem.feature.swap.router.SwapScreen +import com.tangem.feature.swap.ui.StateBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import javax.inject.Inject import kotlin.properties.Delegates @HiltViewModel internal class SwapViewModel @Inject constructor( - private val referralInteractor: SwapInteractor, + private val swapInteractor: SwapInteractor, private val dispatchers: CoroutineDispatcherProvider, + savedStateHandle: SavedStateHandle, ) : ViewModel() { - var uiState: SwapStateHolder by mutableStateOf(createInitialUiState()) + private val stateBuilder = StateBuilder() + private val currency = Json.decodeFromString( + savedStateHandle[SwapFragment.CURRENCY_BUNDLE_KEY] + ?: error("no expected parameter Currency found"), + ) + + var uiState: SwapStateHolder by mutableStateOf( + stateBuilder.createInitialLoadingState(currency.symbol) + { /** [REDACTED_TODO_COMMENT]*/ }, + ) private set private var swapRouter: SwapRouter by Delegates.notNull() @@ -52,78 +58,87 @@ internal class SwapViewModel @Inject constructor( ) } - private fun createInitialUiState(): SwapStateHolder { // TODO: create using real state - return createTestUiState() - } - - // region temp for test init { - viewModelScope.launch { + viewModelScope.launch(dispatchers.main) { runCatching { - val tokens = referralInteractor.getTokensToSwap("binance-smart-chain") - val token = tokens[1] - val token2 = tokens[2] - }.onFailure { - Log.e("SwapViewModel", it.message ?: it.cause.toString()) + withContext(dispatchers.io) { + swapInteractor.getTokensToSwap(currency.networkId) + } } + .onSuccess { tokens -> + if (tokens.size > MIN_TOKENS_IN_LIST) { + tokens.firstOrNull { token -> + token.id == currency.id + }?.let { + loadQuotes(it, tokens[1], INITIAL_AMOUNT) + } + } + } + .onFailure { + Log.e("SwapViewModel", it.message ?: it.cause.toString()) + } } } - private fun createTestUiState(): SwapStateHolder { - val sendCard = SwapCardData( - type = TransactionCardType.SendCard("123", false, {}), - amount = "1 000 000 000 000 000 000", - amountEquivalent = "1 000 000 000 000 000 000", - tokenIconUrl = "", - tokenCurrency = "DAI", - networkIconRes = R.drawable.img_polygon_22, - ) - - val receiveCard = SwapCardData( - type = TransactionCardType.ReceiveCard(), - amount = "1 000 000", - amountEquivalent = "1 000 000", - tokenIconUrl = "", - tokenCurrency = "DAI", - networkIconRes = R.drawable.img_polygon_22, - canSelectAnotherToken = true, - ) - - val permissionState = SwapPermissionStateHolder( - currency = "DAI", - amount = "∞", - walletAddress = "", - spenderAddress = "", - fee = "2,14$", - approveButton = ApprovePermissionButton(true) {}, - cancelButton = CancelPermissionButton(true) {}, - ) - - val token = TokenToSelect( - id = "USDC", - name = "USDC", - symbol = "USDC", - iconUrl = "", - addedTokenBalanceData = TokenBalanceData(amount = "15 000 $", amountEquivalent = "15 000 USDT"), - ) - - val selectTokenState = SwapSelectTokenStateHolder( - listOf(token, token, token, token, token, token, token, token, token), - listOf(token, token, token, token, token, token, token, token, token), {}, {}, - ) - - return SwapStateHolder( - sendCardData = sendCard, - receiveCardData = receiveCard, - fee = FeeState.Loaded(fee = "0.155 MATIC (0.14 $)"), - warnings = listOf(SwapWarning.PermissionNeeded("DAI")), - networkCurrency = "MATIC", - swapButton = SwapButton(enabled = true, loading = false, onClick = {}), - onRefresh = {}, onBackClicked = {}, onChangeCardsClicked = {}, - permissionState = permissionState, - successState = SwapSuccessStateHolder("Success"), - selectTokenState = selectTokenState, - ) + private fun loadQuotes(fromToken: Currency, toToken: Currency, amount: String) { + uiState = stateBuilder.createQuotesLoadingState(uiState, fromToken, toToken, currency.id) + viewModelScope.launch(dispatchers.main) { + runCatching { + withContext(dispatchers.io) { + swapInteractor.findBestQuote( + fromToken = fromToken, + toToken = toToken, + amount = createFromAmountWithoutOffset( + amountWithoutOffset = amount, + decimals = swapInteractor.getTokenDecimals(fromToken), + ), + ) + } + } + .onSuccess { swapState -> + when (swapState) { + is SwapState.QuoteModel -> { + uiState = stateBuilder.createQuotesLoadedState(uiState, swapState, fromToken) { + onSwapClick(toToken, swapState) + } + } + is SwapState.SwapSuccess -> { + //todo implement + } + is SwapState.SwapError -> { + } + } + } + .onFailure { } + } + } + + private fun onSwapClick(toToken: Currency, quoteModel: SwapState.QuoteModel) { + viewModelScope.launch(dispatchers.main) { + runCatching { + if (!quoteModel.isAllowedToSpend) { + swapInteractor.givePermissionToSwap(toToken) + } else { + swapInteractor.onSwap() + } + } + .onSuccess { + if (it is SwapState) { + when (it) { + is SwapState.SwapSuccess -> { + } + is SwapState.SwapError -> { + } + else -> {} + } + } + } + .onFailure { } + } + } + + companion object { + private const val MIN_TOKENS_IN_LIST = 2 + private const val INITIAL_AMOUNT = "1" } - // endregion temp for test } \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt index a1c8bd3631..6da184fb5a 100644 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt @@ -7,6 +7,7 @@ import java.math.BigDecimal interface TransactionManager { + @Throws(IllegalStateException::class) suspend fun sendTransaction( networkId: String, amountToSend: BigDecimal, @@ -16,6 +17,7 @@ interface TransactionManager { dataToSign: String, ): SendTxResult + @Throws(IllegalStateException::class) suspend fun getFee( networkId: String, amountToSend: BigDecimal, @@ -24,4 +26,7 @@ interface TransactionManager { ): ProxyAmount fun getNativeAddress(networkId: String): String + + @Throws(IllegalStateException::class) + fun getNativeTokenDecimals(networkId: String): Int } \ No newline at end of file