Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-18 18:21:03 +05:00
parent 41648e4ffc
commit 3dc6ff9548
12 changed files with 400 additions and 1 deletions

View file

@ -0,0 +1,36 @@
package com.tangem.data.express
import com.tangem.data.common.api.safeApiCall
import com.tangem.data.express.converter.ExpressProviderConverter
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.express.TangemExpressApi
import com.tangem.datasource.exchangeservice.swap.ExpressUtils
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.express.models.ExpressProvider
import com.tangem.domain.express.ExpressRepository
import com.tangem.domain.wallets.models.UserWallet
import timber.log.Timber
internal class DefaultExpressRepository(
private val tangemExpressApi: TangemExpressApi,
private val appPreferencesStore: AppPreferencesStore,
) : ExpressRepository {
override suspend fun getProviders(userWallet: UserWallet): List<ExpressProvider> {
return safeApiCall(
call = {
tangemExpressApi.getProviders(
userWalletId = userWallet.walletId.stringValue,
refCode = ExpressUtils.getRefCode(
userWallet = userWallet,
appPreferencesStore = appPreferencesStore,
),
).getOrThrow().map(ExpressProviderConverter()::convert)
},
onError = {
Timber.w(it, "Unable to fetch express providers")
throw it
},
)
}
}

View file

@ -0,0 +1,91 @@
package com.tangem.data.express.converter
import com.squareup.moshi.JsonAdapter
import com.tangem.datasource.api.express.models.response.ExpressErrorResponse
import com.tangem.domain.express.models.ExpressError
import com.tangem.utils.converter.Converter
import com.tangem.datasource.api.express.models.response.ExpressError as ExpressErrorDTO
class ExpressErrorConverter(
private val jsonAdapter: JsonAdapter<ExpressErrorResponse>,
) : Converter<String, ExpressError> {
@Suppress("MagicNumber", "CyclomaticComplexMethod")
override fun convert(value: String): ExpressError {
try {
val error: ExpressErrorDTO = jsonAdapter.fromJson(value)?.error ?: return ExpressError.UnknownError
return when (error.code) {
2010 -> ExpressError.BadRequest(code = error.code)
2020 -> ExpressError.Forbidden(code = error.code)
2200 -> ExpressError.InternalError(code = error.code)
2210 -> ExpressError.ProviderNotFoundError(code = error.code)
2220 -> ExpressError.ProviderNotActiveError(code = error.code)
2230 -> ExpressError.ProviderNotAvailableError(code = error.code)
2231 -> ExpressError.ProviderInternalError(code = error.code)
2240 -> ExpressError.ExchangeNotPossibleError(code = error.code)
2250 -> error.toTooSmallAmountError()
2251 -> error.toTooBigAmountError()
2260 -> error.toNotEnoughAllowanceError()
2270 -> ExpressError.NotEnoughBalanceError(code = error.code)
2280 -> ExpressError.InvalidAddressError(code = error.code)
2290 -> error.toInvalidFromDecimalsError()
2320 -> error.toProviderDifferentAmountError()
else -> ExpressError.DataError(error.code, error.description)
}
} catch (e: Exception) {
return ExpressError.UnknownError
}
}
private fun ExpressErrorDTO.toTooSmallAmountError(): ExpressError {
val minAmount = value?.minAmount ?: return ExpressError.DataError(code, description)
val decimals = value?.decimals ?: return ExpressError.DataError(code, description)
val requiredAmount = minAmount.toBigDecimalOrNull()?.movePointLeft(decimals)
?: return ExpressError.DataError(code, description)
return ExpressError.AmountError.TooSmallError(code = code, amount = requiredAmount)
}
private fun ExpressErrorDTO.toTooBigAmountError(): ExpressError {
val maxAmount = value?.maxAmount ?: return ExpressError.DataError(code, description)
val decimals = value?.decimals ?: return ExpressError.DataError(code, description)
val requiredAmount = maxAmount.toBigDecimalOrNull()?.movePointLeft(decimals)
?: return ExpressError.DataError(code, description)
return ExpressError.AmountError.TooBigError(code = code, amount = requiredAmount)
}
private fun ExpressErrorDTO.toNotEnoughAllowanceError(): ExpressError {
val currentAllowance = value?.currentAllowance ?: return ExpressError.DataError(code, description)
return ExpressError.AmountError.NotEnoughAllowanceError(code = code, amount = currentAllowance)
}
private fun ExpressErrorDTO.toInvalidFromDecimalsError(): ExpressError {
val receivedFromDecimals = value?.receivedFromDecimals ?: return ExpressError.DataError(code, description)
val expressFromDecimals = value?.expressFromDecimals ?: return ExpressError.DataError(code, description)
return ExpressError.InvalidFromDecimalsError(
code = code,
receivedFromDecimals = receivedFromDecimals,
expressFromDecimals = expressFromDecimals,
)
}
private fun ExpressErrorDTO.toProviderDifferentAmountError(): ExpressError {
val decimals = value?.decimals ?: return ExpressError.DataError(code, description)
val fromAmount = value?.fromAmount?.toBigDecimalOrNull() ?: return ExpressError.DataError(code, description)
val fromAmountProvider = value?.fromAmountProvider?.toBigDecimalOrNull()
?: return ExpressError.DataError(code, description)
return ExpressError.ProviderDifferentAmountError(
code = code,
decimals = decimals,
fromAmount = fromAmount.movePointLeft(decimals),
fromProviderAmount = fromAmountProvider.movePointLeft(decimals),
)
}
}

View file

@ -0,0 +1,33 @@
package com.tangem.data.express.converter
import com.tangem.datasource.api.express.models.response.ExchangeProvider
import com.tangem.datasource.api.express.models.response.ExchangeProviderType
import com.tangem.domain.express.models.ExpressProvider
import com.tangem.domain.express.models.ExpressProviderType
import com.tangem.utils.converter.Converter
internal class ExpressProviderConverter : Converter<ExchangeProvider, ExpressProvider> {
override fun convert(value: ExchangeProvider): ExpressProvider {
return ExpressProvider(
providerId = value.id,
rateTypes = emptyList(),
name = value.name,
type = convertExchangeType(value.type),
imageLarge = value.imageLargeUrl,
termsOfUse = value.termsOfUse,
privacyPolicy = value.privacyPolicy,
isRecommended = value.isRecommended,
slippage = value.slippage,
)
}
private fun convertExchangeType(type: ExchangeProviderType): ExpressProviderType {
return when (type) {
ExchangeProviderType.DEX -> ExpressProviderType.DEX
ExchangeProviderType.CEX -> ExpressProviderType.CEX
ExchangeProviderType.DEX_BRIDGE -> ExpressProviderType.DEX_BRIDGE
ExchangeProviderType.ONRAMP -> ExpressProviderType.ONRAMP
}
}
}

View file

@ -0,0 +1,43 @@
package com.tangem.data.express.di
import com.squareup.moshi.Moshi
import com.tangem.data.express.DefaultExpressRepository
import com.tangem.data.express.converter.ExpressErrorConverter
import com.tangem.data.express.error.DefaultExpressErrorResolver
import com.tangem.datasource.api.express.TangemExpressApi
import com.tangem.datasource.api.express.models.response.ExpressErrorResponse
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.express.ExpressErrorResolver
import com.tangem.domain.express.ExpressRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object ExpressDataModule {
@Provides
@Singleton
fun provideExpressErrorResolver(@NetworkMoshi moshi: Moshi): ExpressErrorResolver {
val jsonAdapter = moshi.adapter(ExpressErrorResponse::class.java)
return DefaultExpressErrorResolver(
ExpressErrorConverter(jsonAdapter),
)
}
@Provides
@Singleton
fun provideExpressRepository(
tangemExpressApi: TangemExpressApi,
appPreferencesStore: AppPreferencesStore,
): ExpressRepository {
return DefaultExpressRepository(
tangemExpressApi = tangemExpressApi,
appPreferencesStore = appPreferencesStore,
)
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.data.express.error
import com.tangem.data.express.converter.ExpressErrorConverter
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.domain.express.ExpressErrorResolver
import com.tangem.domain.express.models.ExpressError
internal class DefaultExpressErrorResolver(
private val expressErrorConverter: ExpressErrorConverter,
) : ExpressErrorResolver {
override fun resolve(throwable: Throwable): ExpressError {
return when (throwable) {
is ApiResponseError.HttpException -> {
expressErrorConverter.convert(throwable.errorBody.orEmpty())
}
else -> ExpressError.UnknownError
}
}
}