Updated on 2026-08-14
This commit is contained in:
parent
defd947695
commit
f53ffa9a64
14 changed files with 206 additions and 13 deletions
|
|
@ -102,4 +102,10 @@ internal object OnrampDomainModule {
|
|||
fun provideGetOnrampPaymentMethodsUseCase(onrampRepository: OnrampRepository): GetOnrampPaymentMethodsUseCase {
|
||||
return GetOnrampPaymentMethodsUseCase(onrampRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideClearOnrampCacheUseCase(onrampRepository: OnrampRepository): ClearOnrampCacheUseCase {
|
||||
return ClearOnrampCacheUseCase(onrampRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,12 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.local.datastore.RuntimeDataStore
|
||||
import com.tangem.datasource.local.onramp.DefaultOnrampPaymentMethodsStore
|
||||
import com.tangem.datasource.local.onramp.OnrampPaymentMethodsStore
|
||||
import com.tangem.datasource.local.onramp.pairs.DefaultOnrampPairsStore
|
||||
import com.tangem.datasource.local.onramp.pairs.OnrampPairsStore
|
||||
import com.tangem.datasource.local.onramp.paymentmethods.DefaultOnrampPaymentMethodsStore
|
||||
import com.tangem.datasource.local.onramp.paymentmethods.OnrampPaymentMethodsStore
|
||||
import com.tangem.datasource.local.onramp.quotes.DefaultOnrampQuotesStore
|
||||
import com.tangem.datasource.local.onramp.quotes.OnrampQuotesStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -18,4 +22,16 @@ internal object OnrampStoreModule {
|
|||
fun provideOnrampPaymentMethodsStore(): OnrampPaymentMethodsStore {
|
||||
return DefaultOnrampPaymentMethodsStore(dataStore = RuntimeDataStore())
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOnrampPairsStore(): OnrampPairsStore {
|
||||
return DefaultOnrampPairsStore(dataStore = RuntimeDataStore())
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOnrampQuotesStore(): OnrampQuotesStore {
|
||||
return DefaultOnrampQuotesStore(dataStore = RuntimeDataStore())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +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
|
||||
|
||||
internal class DefaultOnrampPairsStore(
|
||||
dataStore: StringKeyDataStore<List<OnrampPairDTO>>,
|
||||
) : OnrampPairsStore, StringKeyDataStoreDecorator<String, List<OnrampPairDTO>>(dataStore) {
|
||||
override fun provideStringKey(key: String): String = key
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.datasource.local.onramp.pairs
|
||||
|
||||
import com.tangem.datasource.api.onramp.models.response.model.OnrampPairDTO
|
||||
|
||||
interface OnrampPairsStore {
|
||||
suspend fun getSyncOrNull(key: String): List<OnrampPairDTO>?
|
||||
suspend fun store(key: String, value: List<OnrampPairDTO>)
|
||||
suspend fun clear()
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.datasource.local.onramp
|
||||
package com.tangem.datasource.local.onramp.paymentmethods
|
||||
|
||||
import com.tangem.datasource.api.onramp.models.response.model.PaymentMethodDTO
|
||||
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.datasource.local.onramp
|
||||
package com.tangem.datasource.local.onramp.paymentmethods
|
||||
|
||||
import com.tangem.datasource.api.onramp.models.response.model.PaymentMethodDTO
|
||||
|
||||
|
|
@ -9,4 +9,6 @@ interface OnrampPaymentMethodsStore {
|
|||
suspend fun store(key: String, value: List<PaymentMethodDTO>)
|
||||
|
||||
suspend fun contains(key: String): Boolean
|
||||
|
||||
suspend fun clear()
|
||||
}
|
||||
|
|
@ -0,0 +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
|
||||
|
||||
internal class DefaultOnrampQuotesStore(
|
||||
dataStore: StringKeyDataStore<List<OnrampQuoteResponse>>,
|
||||
) : OnrampQuotesStore, StringKeyDataStoreDecorator<String, List<OnrampQuoteResponse>>(dataStore) {
|
||||
override fun provideStringKey(key: String): String = key
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.datasource.local.onramp.quotes
|
||||
|
||||
import com.tangem.datasource.api.onramp.models.response.OnrampQuoteResponse
|
||||
|
||||
interface OnrampQuotesStore {
|
||||
suspend fun getSyncOrNull(key: String): List<OnrampQuoteResponse>?
|
||||
suspend fun store(key: String, value: List<OnrampQuoteResponse>)
|
||||
suspend fun clear()
|
||||
}
|
||||
|
|
@ -3,13 +3,18 @@ package com.tangem.data.onramp
|
|||
import com.tangem.data.common.api.safeApiCall
|
||||
import com.tangem.data.onramp.converters.CountryConverter
|
||||
import com.tangem.data.onramp.converters.CurrencyConverter
|
||||
import com.tangem.data.onramp.converters.StatusConverter
|
||||
import com.tangem.data.onramp.converters.PaymentMethodConverter
|
||||
import com.tangem.data.onramp.converters.StatusConverter
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.express.models.TangemExpressValues
|
||||
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.local.onramp.OnrampPaymentMethodsStore
|
||||
import com.tangem.datasource.local.onramp.pairs.OnrampPairsStore
|
||||
import com.tangem.datasource.local.onramp.paymentmethods.OnrampPaymentMethodsStore
|
||||
import com.tangem.datasource.local.onramp.quotes.OnrampQuotesStore
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObject
|
||||
|
|
@ -17,10 +22,13 @@ 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.OnrampStatus
|
||||
import com.tangem.domain.onramp.model.OnrampPaymentMethod
|
||||
import com.tangem.domain.onramp.model.OnrampStatus
|
||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -31,6 +39,8 @@ internal class DefaultOnrampRepository(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val paymentMethodsStore: OnrampPaymentMethodsStore,
|
||||
private val pairsStore: OnrampPairsStore,
|
||||
private val quotesStore: OnrampQuotesStore,
|
||||
) : OnrampRepository {
|
||||
|
||||
private val currencyConverter = CurrencyConverter()
|
||||
|
|
@ -100,8 +110,8 @@ internal class DefaultOnrampRepository(
|
|||
.map { it?.let(countryConverter::convert) }
|
||||
}
|
||||
|
||||
override suspend fun fetchPaymentMethodsIfAbsent() {
|
||||
if (paymentMethodsStore.contains(PAYMENT_METHODS_KEY)) return
|
||||
override suspend fun fetchPaymentMethodsIfAbsent() = withContext(dispatchers.io) {
|
||||
if (paymentMethodsStore.contains(PAYMENT_METHODS_KEY)) return@withContext
|
||||
|
||||
val response = safeApiCall(
|
||||
call = { onrampApi.getPaymentMethods().bind() },
|
||||
|
|
@ -113,6 +123,74 @@ internal class DefaultOnrampRepository(
|
|||
paymentMethodsStore.store(PAYMENT_METHODS_KEY, response)
|
||||
}
|
||||
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
).bind()
|
||||
},
|
||||
onError = {
|
||||
Timber.w(it, "Unable to fetch onramp pairs")
|
||||
throw it
|
||||
},
|
||||
)
|
||||
pairsStore.store(PAIRS_KEY, pairs)
|
||||
}
|
||||
|
||||
override suspend fun fetchQuotes(
|
||||
currency: OnrampCurrency,
|
||||
country: OnrampCountry,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
fromAmount: String,
|
||||
) = 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 ->
|
||||
pair.providers.flatMap { provider ->
|
||||
provider.paymentMethods.map { paymentMethod ->
|
||||
async {
|
||||
safeApiCall(
|
||||
call = {
|
||||
onrampApi.getQuote(
|
||||
fromCurrencyCode = currency.code,
|
||||
toContractAddress = cryptoCurrency.getContractAddress(),
|
||||
toNetwork = cryptoCurrency.network.backendId,
|
||||
paymentMethod = paymentMethod,
|
||||
countryCode = country.code,
|
||||
fromAmount = fromAmount,
|
||||
toDecimals = cryptoCurrency.decimals,
|
||||
providerId = provider.providerId,
|
||||
).bind()
|
||||
},
|
||||
onError = {
|
||||
Timber.w(
|
||||
it,
|
||||
"Unable to fetch onramp quotes for ${provider.providerId} -> $paymentMethod",
|
||||
)
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
.filterNotNull()
|
||||
quotesStore.store(QUOTES_KEY, quotes)
|
||||
}
|
||||
|
||||
override suspend fun getPaymentMethods(): List<OnrampPaymentMethod> {
|
||||
val paymentMethods = requireNotNull(paymentMethodsStore.getSyncOrNull(PAYMENT_METHODS_KEY)) {
|
||||
"Onramp payment methods is absent in storage"
|
||||
|
|
@ -120,7 +198,20 @@ internal class DefaultOnrampRepository(
|
|||
return paymentMethodsConverter.convertList(paymentMethods)
|
||||
}
|
||||
|
||||
override suspend fun clearCache() {
|
||||
paymentMethodsStore.clear()
|
||||
pairsStore.clear()
|
||||
quotesStore.clear()
|
||||
}
|
||||
|
||||
private fun CryptoCurrency.getContractAddress(): String = when (this) {
|
||||
is CryptoCurrency.Coin -> TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
||||
is CryptoCurrency.Token -> this.contractAddress
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val PAYMENT_METHODS_KEY = "onramp_payment_methods"
|
||||
const val PAIRS_KEY = "onramp_pairs"
|
||||
const val QUOTES_KEY = "onramp_quotes"
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,9 @@ import com.tangem.data.onramp.DefaultOnrampErrorResolver
|
|||
import com.tangem.data.onramp.DefaultOnrampRepository
|
||||
import com.tangem.data.onramp.DefaultOnrampTransactionRepository
|
||||
import com.tangem.datasource.api.onramp.OnrampApi
|
||||
import com.tangem.datasource.local.onramp.OnrampPaymentMethodsStore
|
||||
import com.tangem.datasource.local.onramp.pairs.OnrampPairsStore
|
||||
import com.tangem.datasource.local.onramp.paymentmethods.OnrampPaymentMethodsStore
|
||||
import com.tangem.datasource.local.onramp.quotes.OnrampQuotesStore
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||
|
|
@ -27,12 +29,16 @@ internal object OnrampDataModule {
|
|||
dispatchers: CoroutineDispatcherProvider,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
paymentMethodsStore: OnrampPaymentMethodsStore,
|
||||
pairsStore: OnrampPairsStore,
|
||||
quotesStore: OnrampQuotesStore,
|
||||
): OnrampRepository {
|
||||
return DefaultOnrampRepository(
|
||||
onrampApi = onrampApi,
|
||||
dispatchers = dispatchers,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
paymentMethodsStore = paymentMethodsStore,
|
||||
pairsStore = pairsStore,
|
||||
quotesStore = quotesStore,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ package com.tangem.domain.onramp
|
|||
import arrow.core.Either
|
||||
import com.tangem.domain.onramp.model.OnrampAvailability
|
||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
||||
class CheckOnrampAvailabilityUseCase(private val repository: OnrampRepository) {
|
||||
|
||||
suspend operator fun invoke(): Either<Throwable, OnrampAvailability> {
|
||||
suspend operator fun invoke(cryptoCurrency: CryptoCurrency): Either<Throwable, OnrampAvailability> {
|
||||
return Either.catch {
|
||||
repository.fetchPaymentMethodsIfAbsent()
|
||||
repository.getDefaultCountrySync()?.let { savedCountry ->
|
||||
|
|
@ -15,6 +16,11 @@ class CheckOnrampAvailabilityUseCase(private val repository: OnrampRepository) {
|
|||
repository.saveDefaultCurrency(savedCountry.defaultCurrency)
|
||||
savedCountry.defaultCurrency
|
||||
}
|
||||
repository.fetchPairs(
|
||||
currency = currency,
|
||||
country = savedCountry,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
)
|
||||
OnrampAvailability.Available(country = savedCountry, currency = currency)
|
||||
} else {
|
||||
OnrampAvailability.NotSupported(savedCountry)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.domain.onramp
|
||||
|
||||
import com.tangem.domain.onramp.repositories.OnrampRepository
|
||||
|
||||
class ClearOnrampCacheUseCase(private val repository: OnrampRepository) {
|
||||
|
||||
suspend operator fun invoke() {
|
||||
repository.clearCache()
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,9 @@ 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.OnrampStatus
|
||||
import com.tangem.domain.onramp.model.OnrampPaymentMethod
|
||||
import com.tangem.domain.onramp.model.OnrampStatus
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface OnrampRepository {
|
||||
|
|
@ -13,6 +14,13 @@ interface OnrampRepository {
|
|||
suspend fun getCountryByIp(): OnrampCountry
|
||||
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,
|
||||
)
|
||||
|
||||
// cache
|
||||
suspend fun saveDefaultCurrency(currency: OnrampCurrency)
|
||||
|
|
@ -22,4 +30,5 @@ interface OnrampRepository {
|
|||
suspend fun getDefaultCountrySync(): OnrampCountry?
|
||||
fun getDefaultCountry(): Flow<OnrampCountry?>
|
||||
suspend fun getPaymentMethods(): List<OnrampPaymentMethod>
|
||||
suspend fun clearCache()
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ 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.model.OnrampAvailability
|
||||
import com.tangem.domain.onramp.model.OnrampCurrency
|
||||
|
|
@ -27,6 +28,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
private val router: Router,
|
||||
private val checkOnrampAvailabilityUseCase: CheckOnrampAvailabilityUseCase,
|
||||
private val getOnrampCurrencyUseCase: GetOnrampCurrencyUseCase,
|
||||
private val clearOnrampCacheUseCase: ClearOnrampCacheUseCase,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model(), OnrampIntents {
|
||||
|
||||
|
|
@ -53,7 +55,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
|
||||
private fun checkResidenceCountry() {
|
||||
modelScope.launch {
|
||||
checkOnrampAvailabilityUseCase.invoke()
|
||||
checkOnrampAvailabilityUseCase.invoke(params.cryptoCurrency)
|
||||
.onRight(::handleOnrampAvailability)
|
||||
.onLeft { Timber.e(it) }
|
||||
}
|
||||
|
|
@ -99,4 +101,9 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
override fun openCurrenciesList() {
|
||||
bottomSheetNavigation.activate(OnrampMainBottomSheetConfig.CurrenciesList)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
modelScope.launch { clearOnrampCacheUseCase.invoke() }
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue