diff --git a/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt index 4b3fb3f472..3db5dadfc6 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt @@ -108,4 +108,16 @@ internal object OnrampDomainModule { fun provideClearOnrampCacheUseCase(onrampRepository: OnrampRepository): ClearOnrampCacheUseCase { return ClearOnrampCacheUseCase(onrampRepository) } + + @Provides + @Singleton + fun provideOnrampFetchQuotesUseCase(onrampRepository: OnrampRepository): OnrampFetchQuotesUseCase { + return OnrampFetchQuotesUseCase(onrampRepository) + } + + @Provides + @Singleton + fun provideGetOnrampQuotesUseCase(onrampRepository: OnrampRepository): GetOnrampQuotesUseCase { + return GetOnrampQuotesUseCase(onrampRepository) + } } \ No newline at end of file diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 273078fac5..78f5b0e498 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -27,6 +27,7 @@ dependencies { implementation(projects.domain.balanceHiding.models) implementation(projects.domain.txhistory.models) implementation(projects.domain.staking.models) + implementation(projects.domain.onramp.models) /** Tangem libraries */ implementation(deps.tangem.blockchain) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeProvider.kt b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeProvider.kt index 453d1e99c0..2c962257ca 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeProvider.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExchangeProvider.kt @@ -41,4 +41,7 @@ enum class ExchangeProviderType { @Json(name = "dex-bridge") DEX_BRIDGE, + + @Json(name = "onramp") + ONRAMP, } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/onramp/pairs/DefaultOnrampPairsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/onramp/pairs/DefaultOnrampPairsStore.kt index 636106284b..ff997be404 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/onramp/pairs/DefaultOnrampPairsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/onramp/pairs/DefaultOnrampPairsStore.kt @@ -1,11 +1,11 @@ package com.tangem.datasource.local.onramp.pairs -import com.tangem.datasource.api.onramp.models.response.model.OnrampPairDTO import com.tangem.datasource.local.datastore.core.StringKeyDataStore import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator +import com.tangem.domain.onramp.model.OnrampPair internal class DefaultOnrampPairsStore( - dataStore: StringKeyDataStore>, -) : OnrampPairsStore, StringKeyDataStoreDecorator>(dataStore) { + dataStore: StringKeyDataStore>, +) : OnrampPairsStore, StringKeyDataStoreDecorator>(dataStore) { override fun provideStringKey(key: String): String = key } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/onramp/pairs/OnrampPairsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/onramp/pairs/OnrampPairsStore.kt index 1b32894636..0396549be9 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/onramp/pairs/OnrampPairsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/onramp/pairs/OnrampPairsStore.kt @@ -1,9 +1,9 @@ package com.tangem.datasource.local.onramp.pairs -import com.tangem.datasource.api.onramp.models.response.model.OnrampPairDTO +import com.tangem.domain.onramp.model.OnrampPair interface OnrampPairsStore { - suspend fun getSyncOrNull(key: String): List? - suspend fun store(key: String, value: List) + suspend fun getSyncOrNull(key: String): List? + suspend fun store(key: String, value: List) suspend fun clear() } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/onramp/quotes/DefaultOnrampQuotesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/onramp/quotes/DefaultOnrampQuotesStore.kt index fb7a157350..0bd7a609b6 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/onramp/quotes/DefaultOnrampQuotesStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/onramp/quotes/DefaultOnrampQuotesStore.kt @@ -1,11 +1,11 @@ package com.tangem.datasource.local.onramp.quotes -import com.tangem.datasource.api.onramp.models.response.OnrampQuoteResponse import com.tangem.datasource.local.datastore.core.StringKeyDataStore import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator +import com.tangem.domain.onramp.model.OnrampQuote internal class DefaultOnrampQuotesStore( - dataStore: StringKeyDataStore>, -) : OnrampQuotesStore, StringKeyDataStoreDecorator>(dataStore) { + dataStore: StringKeyDataStore>, +) : OnrampQuotesStore, StringKeyDataStoreDecorator>(dataStore) { override fun provideStringKey(key: String): String = key } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/onramp/quotes/OnrampQuotesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/onramp/quotes/OnrampQuotesStore.kt index 0e7034f3b2..cad798ee3f 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/onramp/quotes/OnrampQuotesStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/onramp/quotes/OnrampQuotesStore.kt @@ -1,9 +1,11 @@ package com.tangem.datasource.local.onramp.quotes -import com.tangem.datasource.api.onramp.models.response.OnrampQuoteResponse +import com.tangem.domain.onramp.model.OnrampQuote +import kotlinx.coroutines.flow.Flow interface OnrampQuotesStore { - suspend fun getSyncOrNull(key: String): List? - suspend fun store(key: String, value: List) + suspend fun getSyncOrNull(key: String): List? + fun get(key: String): Flow> + suspend fun store(key: String, value: List) suspend fun clear() } \ No newline at end of file diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampErrorResolver.kt b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampErrorResolver.kt index b03b5c6c69..7a837b1197 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampErrorResolver.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampErrorResolver.kt @@ -1,19 +1,12 @@ package com.tangem.data.onramp -import com.tangem.data.onramp.converters.error.OnrampErrorConverter -import com.tangem.datasource.api.common.response.ApiResponseError import com.tangem.domain.onramp.model.OnrampError import com.tangem.domain.onramp.repositories.OnrampErrorResolver +// TODO: [REDACTED_JIRA] internal class DefaultOnrampErrorResolver : OnrampErrorResolver { - private val onrampErrorConverter = OnrampErrorConverter() - override fun resolve(throwable: Throwable): OnrampError { - return if (throwable is ApiResponseError.HttpException) { - onrampErrorConverter.convert(throwable.errorBody.orEmpty()) - } else { - OnrampError.UnknownError - } + return OnrampError.UnknownError } } \ No newline at end of file diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt index 8225a6295c..d2bcad9d24 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt @@ -5,13 +5,19 @@ import com.tangem.data.onramp.converters.CountryConverter import com.tangem.data.onramp.converters.CurrencyConverter import com.tangem.data.onramp.converters.PaymentMethodConverter import com.tangem.data.onramp.converters.StatusConverter +import com.tangem.data.onramp.converters.error.OnrampQuotesErrorConverter +import com.tangem.datasource.api.common.response.ApiResponseError import com.tangem.datasource.api.common.response.getOrThrow +import com.tangem.datasource.api.express.TangemExpressApi import com.tangem.datasource.api.express.models.TangemExpressValues +import com.tangem.datasource.api.express.models.response.ExchangeProvider import com.tangem.datasource.api.onramp.OnrampApi import com.tangem.datasource.api.onramp.models.common.OnrampDestinationDTO import com.tangem.datasource.api.onramp.models.request.OnrampPairsRequest import com.tangem.datasource.api.onramp.models.response.model.OnrampCountryDTO import com.tangem.datasource.api.onramp.models.response.model.OnrampCurrencyDTO +import com.tangem.datasource.api.onramp.models.response.model.OnrampPairDTO +import com.tangem.datasource.api.onramp.models.response.model.PaymentMethodDTO import com.tangem.datasource.local.onramp.pairs.OnrampPairsStore import com.tangem.datasource.local.onramp.paymentmethods.OnrampPaymentMethodsStore import com.tangem.datasource.local.onramp.quotes.OnrampQuotesStore @@ -20,27 +26,30 @@ import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.getObject import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull import com.tangem.datasource.local.preferences.utils.storeObject -import com.tangem.domain.onramp.model.OnrampCountry -import com.tangem.domain.onramp.model.OnrampCurrency -import com.tangem.domain.onramp.model.OnrampPaymentMethod -import com.tangem.domain.onramp.model.OnrampStatus +import com.tangem.domain.onramp.model.* import com.tangem.domain.onramp.repositories.OnrampRepository +import com.tangem.domain.tokens.model.Amount import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import timber.log.Timber +import java.math.BigDecimal +@Suppress("LongParameterList") internal class DefaultOnrampRepository( private val onrampApi: OnrampApi, + private val expressApi: TangemExpressApi, private val dispatchers: CoroutineDispatcherProvider, private val appPreferencesStore: AppPreferencesStore, private val paymentMethodsStore: OnrampPaymentMethodsStore, private val pairsStore: OnrampPairsStore, private val quotesStore: OnrampQuotesStore, + private val quotesErrorConverter: OnrampQuotesErrorConverter, ) : OnrampRepository { private val currencyConverter = CurrencyConverter() @@ -120,77 +129,98 @@ internal class DefaultOnrampRepository( throw it }, ) - paymentMethodsStore.store(PAYMENT_METHODS_KEY, response) + paymentMethodsStore.store(PAYMENT_METHODS_KEY, response.removeApplePay()) } override suspend fun fetchPairs(currency: OnrampCurrency, country: OnrampCountry, cryptoCurrency: CryptoCurrency) = withContext(dispatchers.io) { - val pairs = safeApiCall( - call = { - onrampApi.getPairs( - body = OnrampPairsRequest( - fromCurrencyCode = currency.code, - countryCode = country.code, - to = listOf( - OnrampDestinationDTO( - contractAddress = cryptoCurrency.getContractAddress(), - network = cryptoCurrency.network.backendId, + val onrampPairs = async { + safeApiCall( + call = { + onrampApi.getPairs( + body = OnrampPairsRequest( + fromCurrencyCode = currency.code, + countryCode = country.code, + to = listOf( + OnrampDestinationDTO( + contractAddress = cryptoCurrency.getContractAddress(), + network = cryptoCurrency.network.backendId, + ), ), ), - ), - ).bind() - }, - onError = { - Timber.w(it, "Unable to fetch onramp pairs") - throw it - }, - ) - pairsStore.store(PAIRS_KEY, pairs) + ).bind() + }, + onError = { + Timber.w(it, "Unable to fetch onramp pairs") + throw it + }, + ) + } + val providers = async { + safeApiCall( + call = { expressApi.getProviders().bind() }, + onError = { + Timber.w(it, "Unable to fetch express providers") + throw it + }, + ) + } + storeOnrampPairs(pairs = onrampPairs.await(), providers = providers.await()) } - override suspend fun fetchQuotes( - currency: OnrampCurrency, - country: OnrampCountry, - cryptoCurrency: CryptoCurrency, - fromAmount: String, - ) = withContext(dispatchers.io) { + override suspend fun fetchQuotes(cryptoCurrency: CryptoCurrency, amount: Amount) = withContext(dispatchers.io) { val pairs = requireNotNull(pairsStore.getSyncOrNull(PAIRS_KEY)) { "Unable to get pairs. At this point they must not be null." } - val quotes = pairs.flatMap { pair -> + val amountValue = requireNotNull(amount.value) { "Amount value must not be null" } + val fromAmount = amountValue.movePointRight(amount.decimals).toString() + val currency = requireNotNull(getDefaultCurrencySync()) { "Default currency must not be null" } + val country = requireNotNull(getDefaultCountrySync()) { "Default country must not be null" } + val quotes: List = pairs.flatMap { pair -> pair.providers.flatMap { provider -> provider.paymentMethods.map { paymentMethod -> async { safeApiCall( call = { - onrampApi.getQuote( + val response = onrampApi.getQuote( fromCurrencyCode = currency.code, toContractAddress = cryptoCurrency.getContractAddress(), toNetwork = cryptoCurrency.network.backendId, - paymentMethod = paymentMethod, + paymentMethod = paymentMethod.id, countryCode = country.code, fromAmount = fromAmount, toDecimals = cryptoCurrency.decimals, - providerId = provider.providerId, + providerId = provider.id, ).bind() - }, - onError = { - Timber.w( - it, - "Unable to fetch onramp quotes for ${provider.providerId} -> $paymentMethod", + OnrampQuote.Content( + fromAmount = convertToBigDecimal(response.fromAmount), + toAmount = convertToBigDecimal(response.toAmount), + minFromAmount = convertToBigDecimal(response.minFromAmount), + maxFromAmount = convertToBigDecimal(response.maxFromAmount), + paymentMethodId = response.paymentMethod, + providerId = response.providerId, ) - null + }, + onError = { error -> + if (error is ApiResponseError.HttpException) { + quotesErrorConverter.convert(error.errorBody.orEmpty() to amount) + } else { + Timber.w(error, "Unable to fetch onramp quotes for ${provider.id}. $error") + null + } }, ) } } } - } - .awaitAll() - .filterNotNull() + }.awaitAll().filterNotNull() quotesStore.store(QUOTES_KEY, quotes) } + override fun getQuotes(): Flow> { + return quotesStore.get(QUOTES_KEY) + } + override suspend fun getPaymentMethods(): List { val paymentMethods = requireNotNull(paymentMethodsStore.getSyncOrNull(PAYMENT_METHODS_KEY)) { "Onramp payment methods is absent in storage" @@ -198,17 +228,43 @@ internal class DefaultOnrampRepository( return paymentMethodsConverter.convertList(paymentMethods) } - override suspend fun clearCache() { + override suspend fun clearCache() = withContext(NonCancellable) { paymentMethodsStore.clear() pairsStore.clear() quotesStore.clear() } + private suspend fun storeOnrampPairs(pairs: List, providers: List) { + val onrampPaymentMethods = getPaymentMethods() + val onrampPairs = pairs.map { pair -> + val onrampProviders = pair.providers.mapNotNull { onrampProviderDTO -> + val providerInfo = providers.find { provider -> + provider.id == onrampProviderDTO.providerId + } ?: return@mapNotNull null + + OnrampProvider( + id = onrampProviderDTO.providerId, + info = OnrampProviderInfo(name = providerInfo.name, imageLarge = providerInfo.imageLargeUrl), + paymentMethods = onrampPaymentMethods.filter { paymentMethod -> + onrampProviderDTO.paymentMethods.any { paymentMethod.id == it } + }, + ) + } + OnrampPair(onrampProviders) + } + pairsStore.store(PAIRS_KEY, onrampPairs) + } + private fun CryptoCurrency.getContractAddress(): String = when (this) { is CryptoCurrency.Coin -> TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE is CryptoCurrency.Token -> this.contractAddress } + private fun convertToBigDecimal(value: String): BigDecimal = + requireNotNull(value.toBigDecimalOrNull()) { "Can not parse $value to BigDecimal" } + + private fun List.removeApplePay(): List = filterNot { it.id == "apple-pay" } + private companion object { const val PAYMENT_METHODS_KEY = "onramp_payment_methods" const val PAIRS_KEY = "onramp_pairs" diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/converters/error/OnrampErrorConverter.kt b/data/onramp/src/main/java/com/tangem/data/onramp/converters/error/OnrampErrorConverter.kt deleted file mode 100644 index b676ed5006..0000000000 --- a/data/onramp/src/main/java/com/tangem/data/onramp/converters/error/OnrampErrorConverter.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.data.onramp.converters.error - -import com.tangem.domain.onramp.model.OnrampError -import com.tangem.utils.converter.Converter - -internal class OnrampErrorConverter : Converter { - - override fun convert(value: String): OnrampError { - return OnrampError.UnknownError - } -} \ No newline at end of file diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/converters/error/OnrampQuotesErrorConverter.kt b/data/onramp/src/main/java/com/tangem/data/onramp/converters/error/OnrampQuotesErrorConverter.kt new file mode 100644 index 0000000000..37fbddf76e --- /dev/null +++ b/data/onramp/src/main/java/com/tangem/data/onramp/converters/error/OnrampQuotesErrorConverter.kt @@ -0,0 +1,79 @@ +package com.tangem.data.onramp.converters.error + +import com.squareup.moshi.JsonAdapter +import com.tangem.datasource.api.express.models.response.ExpressError +import com.tangem.datasource.api.express.models.response.ExpressErrorResponse +import com.tangem.domain.onramp.model.OnrampQuote +import com.tangem.domain.tokens.model.Amount +import com.tangem.domain.tokens.model.AmountType +import com.tangem.utils.converter.Converter + +internal class OnrampQuotesErrorConverter( + private val jsonAdapter: JsonAdapter, +) : Converter, OnrampQuote.Error?> { + + @Suppress("MagicNumber") + override fun convert(value: Pair): OnrampQuote.Error? { + val (errorResponse, amount) = value + try { + val error = jsonAdapter.fromJson(errorResponse)?.error ?: return null + return when (error.code) { + 2250 -> tryParseExchangeTooSmallAmountError(error = error, amount = amount) + 2251 -> tryParseExchangeTooBigAmountError(error = error, amount = amount) + else -> null + } + } catch (e: Exception) { + return null + } + } + + private fun tryParseExchangeTooSmallAmountError( + error: ExpressError, + amount: Amount, + ): OnrampQuote.Error.AmountTooSmallError? { + val minAmount = error.value?.minAmount ?: return null + val decimals = error.value?.decimals ?: return null + + return OnrampQuote.Error.AmountTooSmallError( + amount = createFromAmountWithOffset( + amountWithOffset = minAmount, + decimals = decimals, + symbol = amount.currencySymbol, + type = amount.type, + ), + ) + } + + private fun tryParseExchangeTooBigAmountError( + error: ExpressError, + amount: Amount, + ): OnrampQuote.Error.AmountTooBigError? { + val maxAmount = error.value?.maxAmount ?: return null + val decimals = error.value?.decimals ?: return null + + return OnrampQuote.Error.AmountTooBigError( + amount = createFromAmountWithOffset( + amountWithOffset = maxAmount, + decimals = decimals, + symbol = amount.currencySymbol, + type = amount.type, + ), + ) + } + + private fun createFromAmountWithOffset( + amountWithOffset: String, + decimals: Int, + symbol: String, + type: AmountType, + ): Amount { + return Amount( + value = requireNotNull( + amountWithOffset.toBigDecimalOrNull()?.movePointLeft(decimals), + ) { "wrong amount format, use only digits" }, + decimals = decimals, + currencySymbol = symbol, + type = type, + ) + } +} \ No newline at end of file diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/di/OnrampDataModule.kt b/data/onramp/src/main/java/com/tangem/data/onramp/di/OnrampDataModule.kt index 1d19134538..03172d1352 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/di/OnrampDataModule.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/di/OnrampDataModule.kt @@ -1,9 +1,14 @@ package com.tangem.data.onramp.di +import com.squareup.moshi.Moshi import com.tangem.data.onramp.DefaultOnrampErrorResolver import com.tangem.data.onramp.DefaultOnrampRepository import com.tangem.data.onramp.DefaultOnrampTransactionRepository +import com.tangem.data.onramp.converters.error.OnrampQuotesErrorConverter +import com.tangem.datasource.api.express.TangemExpressApi +import com.tangem.datasource.api.express.models.response.ExpressErrorResponse import com.tangem.datasource.api.onramp.OnrampApi +import com.tangem.datasource.di.NetworkMoshi import com.tangem.datasource.local.onramp.pairs.OnrampPairsStore import com.tangem.datasource.local.onramp.paymentmethods.OnrampPaymentMethodsStore import com.tangem.datasource.local.onramp.quotes.OnrampQuotesStore @@ -26,19 +31,23 @@ internal object OnrampDataModule { @Singleton fun provideOnrampRepository( onrampApi: OnrampApi, + expressApi: TangemExpressApi, dispatchers: CoroutineDispatcherProvider, appPreferencesStore: AppPreferencesStore, paymentMethodsStore: OnrampPaymentMethodsStore, pairsStore: OnrampPairsStore, quotesStore: OnrampQuotesStore, + quotesErrorConverter: OnrampQuotesErrorConverter, ): OnrampRepository { return DefaultOnrampRepository( onrampApi = onrampApi, + expressApi = expressApi, dispatchers = dispatchers, appPreferencesStore = appPreferencesStore, paymentMethodsStore = paymentMethodsStore, pairsStore = pairsStore, quotesStore = quotesStore, + quotesErrorConverter = quotesErrorConverter, ) } @@ -59,4 +68,11 @@ internal object OnrampDataModule { fun provideOnrampErrorResolver(): OnrampErrorResolver { return DefaultOnrampErrorResolver() } + + @Provides + @Singleton + internal fun provideErrorsConverter(@NetworkMoshi moshi: Moshi): OnrampQuotesErrorConverter { + val jsonAdapter = moshi.adapter(ExpressErrorResponse::class.java) + return OnrampQuotesErrorConverter(jsonAdapter) + } } \ No newline at end of file diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampPair.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampPair.kt new file mode 100644 index 0000000000..2bea50aad3 --- /dev/null +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampPair.kt @@ -0,0 +1,3 @@ +package com.tangem.domain.onramp.model + +data class OnrampPair(val providers: List) \ No newline at end of file diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampProvider.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampProvider.kt new file mode 100644 index 0000000000..53e6d8848b --- /dev/null +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampProvider.kt @@ -0,0 +1,9 @@ +package com.tangem.domain.onramp.model + +data class OnrampProvider( + val id: String, + val info: OnrampProviderInfo, + val paymentMethods: List, +) + +data class OnrampProviderInfo(val name: String, val imageLarge: String) \ No newline at end of file diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt new file mode 100644 index 0000000000..f347b73e67 --- /dev/null +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt @@ -0,0 +1,23 @@ +package com.tangem.domain.onramp.model + +import com.tangem.domain.tokens.model.Amount +import java.math.BigDecimal + +sealed class OnrampQuote { + + data class Content( + val fromAmount: BigDecimal, + val toAmount: BigDecimal, + val minFromAmount: BigDecimal, + val maxFromAmount: BigDecimal, + val paymentMethodId: String, + val providerId: String, + ) : OnrampQuote() + + sealed class Error : OnrampQuote() { + + data class AmountTooSmallError(val amount: Amount) : Error() + + data class AmountTooBigError(val amount: Amount) : Error() + } +} \ No newline at end of file diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampQuotesUseCase.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampQuotesUseCase.kt new file mode 100644 index 0000000000..2cc55a2f46 --- /dev/null +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampQuotesUseCase.kt @@ -0,0 +1,19 @@ +package com.tangem.domain.onramp + +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import com.tangem.domain.onramp.model.OnrampQuote +import com.tangem.domain.onramp.repositories.OnrampRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.map + +class GetOnrampQuotesUseCase(private val repository: OnrampRepository) { + + operator fun invoke(): Flow>> { + return repository.getQuotes() + .map, Either>> { it.right() } + .catch { emit(it.left()) } + } +} \ No newline at end of file diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampFetchQuotesUseCase.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampFetchQuotesUseCase.kt new file mode 100644 index 0000000000..2d360513fa --- /dev/null +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/OnrampFetchQuotesUseCase.kt @@ -0,0 +1,15 @@ +package com.tangem.domain.onramp + +import com.tangem.domain.onramp.repositories.OnrampRepository +import com.tangem.domain.tokens.model.Amount +import com.tangem.domain.tokens.model.CryptoCurrency + +class OnrampFetchQuotesUseCase(private val repository: OnrampRepository) { + + suspend operator fun invoke(amount: Amount, cryptoCurrency: CryptoCurrency) { + repository.fetchQuotes( + cryptoCurrency = cryptoCurrency, + amount = amount, + ) + } +} \ No newline at end of file diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampRepository.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampRepository.kt index 6bd6e7466b..7ffb44d177 100644 --- a/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampRepository.kt +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/repositories/OnrampRepository.kt @@ -1,9 +1,7 @@ package com.tangem.domain.onramp.repositories -import com.tangem.domain.onramp.model.OnrampCountry -import com.tangem.domain.onramp.model.OnrampCurrency -import com.tangem.domain.onramp.model.OnrampPaymentMethod -import com.tangem.domain.onramp.model.OnrampStatus +import com.tangem.domain.onramp.model.* +import com.tangem.domain.tokens.model.Amount import com.tangem.domain.tokens.model.CryptoCurrency import kotlinx.coroutines.flow.Flow @@ -15,12 +13,7 @@ interface OnrampRepository { suspend fun getStatus(txId: String): OnrampStatus suspend fun fetchPaymentMethodsIfAbsent() suspend fun fetchPairs(currency: OnrampCurrency, country: OnrampCountry, cryptoCurrency: CryptoCurrency) - suspend fun fetchQuotes( - currency: OnrampCurrency, - country: OnrampCountry, - cryptoCurrency: CryptoCurrency, - fromAmount: String, - ) + suspend fun fetchQuotes(cryptoCurrency: CryptoCurrency, amount: Amount) // cache suspend fun saveDefaultCurrency(currency: OnrampCurrency) @@ -30,5 +23,6 @@ interface OnrampRepository { suspend fun getDefaultCountrySync(): OnrampCountry? fun getDefaultCountry(): Flow suspend fun getPaymentMethods(): List + fun getQuotes(): Flow> suspend fun clearCache() } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt index 6c40dafb39..9b078e114c 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/OnrampStateFactory.kt @@ -72,7 +72,7 @@ internal class OnrampStateFactory( isValuePasted = false, onValuePastedTriggerDismiss = {}, ), - secondaryFieldModel = OnrampAmountSecondaryFieldUM.Loading, + secondaryFieldModel = OnrampAmountSecondaryFieldUM.Content(TextReference.EMPTY), ) } diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountFieldChangeConverter.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountFieldChangeConverter.kt index 8a8c93fef6..72e7abff4b 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountFieldChangeConverter.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountFieldChangeConverter.kt @@ -3,6 +3,7 @@ package com.tangem.features.onramp.main.entity.factory.amount import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType +import com.tangem.core.ui.extensions.TextReference import com.tangem.features.onramp.main.entity.OnrampAmountSecondaryFieldUM import com.tangem.features.onramp.main.entity.OnrampMainComponentUM import com.tangem.utils.Provider @@ -56,7 +57,7 @@ internal class OnrampAmountFieldChangeConverter( return copy( amountBlockState = amountBlockState.copy( amountFieldModel = amountFieldModel, - secondaryFieldModel = OnrampAmountSecondaryFieldUM.Loading, + secondaryFieldModel = OnrampAmountSecondaryFieldUM.Content(TextReference.EMPTY), ), ) } diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountStateFactory.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountStateFactory.kt index fd4c03914f..42980cd4d5 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountStateFactory.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountStateFactory.kt @@ -1,7 +1,17 @@ package com.tangem.features.onramp.main.entity.factory.amount +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.fiat +import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.onramp.model.OnrampCurrency +import com.tangem.domain.onramp.model.OnrampQuote import com.tangem.domain.tokens.model.AmountType +import com.tangem.features.onramp.impl.R +import com.tangem.features.onramp.main.entity.OnrampAmountBlockUM +import com.tangem.features.onramp.main.entity.OnrampAmountSecondaryFieldUM import com.tangem.features.onramp.main.entity.OnrampMainComponentUM import com.tangem.utils.Provider @@ -35,4 +45,69 @@ internal class OnrampAmountStateFactory(private val currentStateProvider: Provid ), ) } + + fun getAmountSecondaryLoadingState(): OnrampMainComponentUM { + val currentState = currentStateProvider() + if (currentState !is OnrampMainComponentUM.Content) return currentState + + val amountState = currentState.amountBlockState + + return currentState.copy( + amountBlockState = amountState.copy(secondaryFieldModel = OnrampAmountSecondaryFieldUM.Loading), + ) + } + + fun getAmountSecondaryUpdatedState(quote: OnrampQuote): OnrampMainComponentUM { + val currentState = currentStateProvider() + if (currentState !is OnrampMainComponentUM.Content) return currentState + + val amountState = currentState.amountBlockState + return currentState.copy( + amountBlockState = amountState.copy( + secondaryFieldModel = quote.toUiModel(amountState), + ), + ) + } + + private fun OnrampQuote.toUiModel(amountState: OnrampAmountBlockUM): OnrampAmountSecondaryFieldUM { + return when (this) { + is OnrampQuote.Content -> { + val amount = toAmount.format { + crypto( + symbol = amountState.amountFieldModel.cryptoAmount.currencySymbol, + decimals = amountState.amountFieldModel.cryptoAmount.decimals, + ) + } + OnrampAmountSecondaryFieldUM.Content(stringReference(amount)) + } + is OnrampQuote.Error.AmountTooBigError -> { + val amount = this.amount.value.format { + fiat( + fiatCurrencyCode = amountState.amountFieldModel.fiatAmount.currencySymbol, + fiatCurrencySymbol = amountState.amountFieldModel.fiatAmount.currencySymbol, + ) + } + OnrampAmountSecondaryFieldUM.Error( + resourceReference( + R.string.onramp_max_amount_restriction, + wrappedList(amount), + ), + ) + } + is OnrampQuote.Error.AmountTooSmallError -> { + val amount = this.amount.value.format { + fiat( + fiatCurrencyCode = amountState.amountFieldModel.fiatAmount.currencySymbol, + fiatCurrencySymbol = amountState.amountFieldModel.fiatAmount.currencySymbol, + ) + } + OnrampAmountSecondaryFieldUM.Error( + resourceReference( + R.string.onramp_min_amount_restriction, + wrappedList(amount), + ), + ) + } + } + } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt index ad2d439de3..65794c66af 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt @@ -5,9 +5,7 @@ import com.arkivanov.decompose.router.slot.activate import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router -import com.tangem.domain.onramp.CheckOnrampAvailabilityUseCase -import com.tangem.domain.onramp.ClearOnrampCacheUseCase -import com.tangem.domain.onramp.GetOnrampCurrencyUseCase +import com.tangem.domain.onramp.* import com.tangem.domain.onramp.model.OnrampAvailability import com.tangem.domain.onramp.model.OnrampCurrency import com.tangem.features.onramp.main.OnrampMainComponent @@ -16,6 +14,7 @@ import com.tangem.features.onramp.main.entity.OnrampMainBottomSheetConfig import com.tangem.features.onramp.main.entity.OnrampMainComponentUM import com.tangem.features.onramp.main.entity.factory.OnrampStateFactory import com.tangem.features.onramp.main.entity.factory.amount.OnrampAmountStateFactory +import com.tangem.features.onramp.utils.InputManager import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* @@ -23,12 +22,16 @@ import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject +@Suppress("LongParameterList") internal class OnrampMainComponentModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, private val router: Router, private val checkOnrampAvailabilityUseCase: CheckOnrampAvailabilityUseCase, private val getOnrampCurrencyUseCase: GetOnrampCurrencyUseCase, private val clearOnrampCacheUseCase: ClearOnrampCacheUseCase, + private val fetchQuotesUseCase: OnrampFetchQuotesUseCase, + private val getOnrampQuotesUseCase: GetOnrampQuotesUseCase, + private val amountInputManager: InputManager, paramsContainer: ParamsContainer, ) : Model(), OnrampIntents { @@ -51,6 +54,7 @@ internal class OnrampMainComponentModel @Inject constructor( init { checkResidenceCountry() + subscribeToAmountChanges() } private fun checkResidenceCountry() { @@ -73,6 +77,7 @@ internal class OnrampMainComponentModel @Inject constructor( private fun handleOnrampAvailable(currency: OnrampCurrency) { _state.update { stateFactory.getReadyState(currency) } subscribeToCurrencyUpdates() + subscribeToQuotesUpdate() } private fun subscribeToCurrencyUpdates() { @@ -86,8 +91,31 @@ internal class OnrampMainComponentModel @Inject constructor( .launchIn(modelScope) } + private fun subscribeToAmountChanges() = modelScope.launch { + amountInputManager.query + .filter(String::isNotEmpty) + .collectLatest { _ -> + val content = state.value as? OnrampMainComponentUM.Content ?: return@collectLatest + _state.update { amountStateFactory.getAmountSecondaryLoadingState() } + fetchQuotesUseCase.invoke( + amount = content.amountBlockState.amountFieldModel.fiatAmount, + cryptoCurrency = params.cryptoCurrency, + ) + } + } + + private fun subscribeToQuotesUpdate() { + getOnrampQuotesUseCase.invoke() + .onEach { maybeQuotes -> + val quote = maybeQuotes.getOrNull()?.firstOrNull() ?: return@onEach + _state.update { amountStateFactory.getAmountSecondaryUpdatedState(quote) } + } + .launchIn(modelScope) + } + override fun onAmountValueChanged(value: String) { _state.update { amountStateFactory.getOnAmountValueChange(value) } + modelScope.launch { amountInputManager.update(value) } } override fun openSettings() { diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/model/OnrampSelectCountryModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/model/OnrampSelectCountryModel.kt index 6dbb2cf7d5..9047b28af5 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/model/OnrampSelectCountryModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/model/OnrampSelectCountryModel.kt @@ -16,7 +16,7 @@ import com.tangem.features.onramp.selectcountry.entity.CountryListUM import com.tangem.features.onramp.selectcountry.entity.CountryListUMController import com.tangem.features.onramp.selectcountry.entity.transformer.UpdateCountryItemsLoadingTransformer import com.tangem.features.onramp.selectcountry.entity.transformer.UpdateCountryItemsTransformer -import com.tangem.features.onramp.utils.SearchManager +import com.tangem.features.onramp.utils.InputManager import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer import com.tangem.features.onramp.utils.UpdateSearchQueryTransformer import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -30,7 +30,7 @@ import javax.inject.Inject @ComponentScoped internal class OnrampSelectCountryModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, - private val searchManager: SearchManager, + private val searchManager: InputManager, private val getOnrampCountriesUseCase: GetOnrampCountriesUseCase, private val saveDefaultCountryUseCase: OnrampSaveDefaultCountryUseCase, private val getOnrampCountryUseCase: GetOnrampCountryUseCase, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/model/OnrampSelectCurrencyModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/model/OnrampSelectCurrencyModel.kt index 35b364808d..83e0cbac8a 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/model/OnrampSelectCurrencyModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/model/OnrampSelectCurrencyModel.kt @@ -17,7 +17,7 @@ import com.tangem.features.onramp.selectcurrency.entity.CurrencyItemState import com.tangem.features.onramp.selectcurrency.entity.CurrencyListController import com.tangem.features.onramp.selectcurrency.entity.transformer.UpdateCurrencyItemsLoadingTransformer import com.tangem.features.onramp.selectcurrency.entity.transformer.UpdateCurrencyItemsTransformer -import com.tangem.features.onramp.utils.SearchManager +import com.tangem.features.onramp.utils.InputManager import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer import com.tangem.features.onramp.utils.UpdateSearchQueryTransformer import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -31,7 +31,7 @@ import javax.inject.Inject @ComponentScoped internal class OnrampSelectCurrencyModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, - private val searchManager: SearchManager, + private val searchManager: InputManager, private val getOnrampCurrenciesUseCase: GetOnrampCurrenciesUseCase, private val saveDefaultCurrencyUseCase: OnrampSaveDefaultCurrencyUseCase, paramsContainer: ParamsContainer, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt index 03a36792af..1c9f0ecae6 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/availablepairs/model/AvailableSwapPairsModel.kt @@ -23,7 +23,7 @@ import com.tangem.features.onramp.swap.availablepairs.entity.transformers.SetLoa import com.tangem.features.onramp.tokenlist.entity.TokenListUM import com.tangem.features.onramp.tokenlist.entity.TokenListUMController import com.tangem.features.onramp.tokenlist.entity.transformer.UpdateTokenItemsTransformer -import com.tangem.features.onramp.utils.SearchManager +import com.tangem.features.onramp.utils.InputManager import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer import com.tangem.features.onramp.utils.UpdateSearchQueryTransformer import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -37,7 +37,7 @@ internal class AvailableSwapPairsModel @Inject constructor( getTokenListUseCase: GetTokenListUseCase, override val dispatchers: CoroutineDispatcherProvider, private val tokenListUMController: TokenListUMController, - private val searchManager: SearchManager, + private val searchManager: InputManager, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val getAvailablePairsUseCase: GetAvailablePairsUseCase, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt index ffda60ba6a..97d7afd5be 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/model/OnrampTokenListModel.kt @@ -20,7 +20,7 @@ import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent import com.tangem.features.onramp.tokenlist.entity.TokenListUM import com.tangem.features.onramp.tokenlist.entity.TokenListUMController import com.tangem.features.onramp.tokenlist.entity.transformer.UpdateTokenItemsTransformer -import com.tangem.features.onramp.utils.SearchManager +import com.tangem.features.onramp.utils.InputManager import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer import com.tangem.features.onramp.utils.UpdateSearchQueryTransformer import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -33,7 +33,7 @@ internal class OnrampTokenListModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, private val tokenListUMController: TokenListUMController, - private val searchManager: SearchManager, + private val searchManager: InputManager, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val getTokenListUseCase: GetTokenListUseCase, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/utils/SearchManager.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/utils/InputManager.kt similarity index 93% rename from features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/utils/SearchManager.kt rename to features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/utils/InputManager.kt index a2f80e0f27..b3add7d7ee 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/utils/SearchManager.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/utils/InputManager.kt @@ -12,7 +12,7 @@ import javax.inject.Inject * [REDACTED_AUTHOR] */ -internal class SearchManager @Inject constructor() { +internal class InputManager @Inject constructor() { val query: Flow get() = _query diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt index 17850cb1ba..a64fc2cf07 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt @@ -32,16 +32,17 @@ class SwapPairInfoConverter : Converter, ): SwapPairProviderDomain? { val additionalProvider = providerAdditional[swapPairProvider.providerId] ?: return null + val type = convertExchangeType(additionalProvider.type) ?: return null return SwapPairProviderDomain( providerId = swapPairProvider.providerId, rateTypes = swapPairProvider.rateTypes.map { rateTypeConverter.convert(it) }, name = additionalProvider.name, - type = convertExchangeType(additionalProvider.type), + type = type, imageLarge = additionalProvider.imageLargeUrl, termsOfUse = additionalProvider.termsOfUse, privacyPolicy = additionalProvider.privacyPolicy, @@ -68,11 +70,12 @@ class SwapPairInfoConverter : Converter ExchangeProviderTypeDomain.DEX ExchangeProviderType.CEX -> ExchangeProviderTypeDomain.CEX ExchangeProviderType.DEX_BRIDGE -> ExchangeProviderTypeDomain.DEX_BRIDGE + ExchangeProviderType.ONRAMP -> null } } } \ No newline at end of file