diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ApiResponseError.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ApiResponseError.kt index 5e88155058..e342289f0c 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ApiResponseError.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ApiResponseError.kt @@ -12,7 +12,11 @@ sealed class ApiResponseError : Exception() { * @property code The HTTP status code. * @property message A human-readable message describing the error. */ - data class HttpException(val code: Code, override val message: String?) : ApiResponseError() { + data class HttpException( + val code: Code, + override val message: String?, + val errorBody: String?, + ) : ApiResponseError() { // region Error Codes enum class Code(val code: Int) { diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ResponseExt.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ResponseExt.kt index 0ddb0ffa36..718392b0d9 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ResponseExt.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ResponseExt.kt @@ -16,7 +16,7 @@ internal fun Response.toSafeApiResponse(): ApiResponse { val e = if (code == null) { ApiResponseError.UnknownException(IllegalArgumentException("Unknown error status code: ${code()}")) } else { - ApiResponseError.HttpException(code, message()) + ApiResponseError.HttpException(code, message(), errorBody()?.string()) } apiError(e) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExpressErrorResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExpressErrorResponse.kt new file mode 100644 index 0000000000..c0c39b0fb2 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/express/models/response/ExpressErrorResponse.kt @@ -0,0 +1,37 @@ +package com.tangem.datasource.api.express.models.response + +import com.squareup.moshi.Json +import java.math.BigDecimal + +data class ExpressErrorResponse( + @Json(name = "error") + val error: ExpressError, +) + +data class ExpressError( + @Json(name = "code") + val code: Int, + + @Json(name = "description") + val description: String?, + + @Json(name = "value") + val value: ExpressErrorValue?, +) + +data class ExpressErrorValue( + @Json(name = "minAmount") + val minAmount: BigDecimal?, + + @Json(name = "decimals") + val decimals: Int?, + + @Json(name = "currentAllowance") + val currentAllowance: BigDecimal?, + + @Json(name = "receivedFromDecimals") + val receivedFromDecimals: Int?, + + @Json(name = "expressFromDecimals") + val expressFromDecimals: Int?, +) \ No newline at end of file diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt index 43010d791d..9ea783fc6b 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt @@ -6,6 +6,7 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.blockchain.extensions.Result import com.tangem.data.tokens.utils.CryptoCurrencyFactory +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.request.PairsRequestBody @@ -23,10 +24,10 @@ import com.tangem.domain.wallets.legacy.WalletsStateHolder import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.swap.converters.* import com.tangem.feature.swap.domain.SwapRepository +import com.tangem.feature.swap.domain.models.DataError import com.tangem.feature.swap.domain.models.createFromAmountWithOffset import com.tangem.feature.swap.domain.models.data.AggregatedSwapDataModel import com.tangem.feature.swap.domain.models.domain.* -import com.tangem.feature.swap.domain.models.mapErrors import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.async import kotlinx.coroutines.withContext @@ -43,6 +44,7 @@ internal class SwapRepositoryImpl @Inject constructor( private val configManager: ConfigManager, private val walletManagersFacade: WalletManagersFacade, private val walletsStateHolder: WalletsStateHolder, + private val errorsDataConverter: ErrorsDataConverter, ) : SwapRepository { private val tokensConverter = TokensConverter() @@ -165,7 +167,7 @@ internal class SwapRepositoryImpl @Inject constructor( ), ) } catch (ex: Exception) { - AggregatedSwapDataModel(null, mapErrors(ex.message)) + AggregatedSwapDataModel(null, getDataError(ex)) } } } @@ -204,7 +206,7 @@ internal class SwapRepositoryImpl @Inject constructor( dataModel = expressDataConverter.convert(response), ) } catch (ex: Exception) { - AggregatedSwapDataModel(null, mapErrors(ex.message)) + AggregatedSwapDataModel(null, getDataError(ex)) } } } @@ -301,6 +303,14 @@ internal class SwapRepositoryImpl @Inject constructor( ) } + private fun getDataError(ex: Exception): DataError { + return if (ex is ApiResponseError.HttpException) { + errorsDataConverter.convert(ex.errorBody ?: "") + } else { + DataError.UnknownError + } + } + companion object { // TODO("get this ids from blockchain enum later") private const val OPTIMISM_ID = "optimistic-ethereum" diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt new file mode 100644 index 0000000000..5330219b1e --- /dev/null +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt @@ -0,0 +1,48 @@ +package com.tangem.feature.swap.converters + +import com.squareup.moshi.JsonAdapter +import com.tangem.datasource.api.express.models.response.ExpressErrorResponse +import com.tangem.feature.swap.domain.models.DataError +import com.tangem.feature.swap.domain.models.SwapAmount +import com.tangem.utils.converter.Converter + +internal class ErrorsDataConverter( + private val jsonAdapter: JsonAdapter, +) : Converter { + + @Suppress("MagicNumber") + override fun convert(value: String): DataError { + try { + val error = jsonAdapter.fromJson(value)?.error ?: return DataError.UnknownError + + return when (error.code) { + 2010 -> DataError.BadRequest(code = error.code) + 2210 -> DataError.ExchangeProviderNotFoundError(code = error.code) + 2220 -> DataError.ExchangeProviderNotActiveError(code = error.code) + 2230 -> DataError.ExchangeProviderNotAvailableError(code = error.code) + 2240 -> DataError.ExchangeNotPossibleError(code = error.code) + 2250 -> DataError.ExchangeTooSmallAmountError( + code = error.code, + amount = SwapAmount( + requireNotNull(error.value?.minAmount), + requireNotNull(error.value?.decimals), + ), + ) + 2260 -> DataError.ExchangeNotEnoughAllowanceError( + code = error.code, + currentAllowance = requireNotNull(error.value?.currentAllowance), + ) + 2270 -> DataError.ExchangeNotEnoughBalanceError(code = error.code) + 2280 -> DataError.ExchangeInvalidAddressError(code = error.code) + 2290 -> DataError.ExchangeInvalidFromDecimalsError( + code = error.code, + receivedFromDecimals = requireNotNull(error.value?.receivedFromDecimals), + expressFromDecimals = requireNotNull(error.value?.expressFromDecimals), + ) + else -> DataError.UnknownError + } + } catch (e: Exception) { + return DataError.UnknownError + } + } +} \ No newline at end of file diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt index 261dcbb069..4c34db7b17 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt @@ -1,12 +1,16 @@ package com.tangem.feature.swap.di +import com.squareup.moshi.Moshi import com.tangem.datasource.api.express.TangemExpressApi +import com.tangem.datasource.api.express.models.response.ExpressErrorResponse import com.tangem.datasource.api.oneinch.OneInchApiFactory import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.config.ConfigManager +import com.tangem.datasource.di.NetworkMoshi import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.WalletsStateHolder import com.tangem.feature.swap.SwapRepositoryImpl +import com.tangem.feature.swap.converters.ErrorsDataConverter import com.tangem.feature.swap.domain.SwapRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module @@ -17,11 +21,11 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -class SwapDataModule { +internal class SwapDataModule { @Provides @Singleton - fun provideSwapRepository( + internal fun provideSwapRepository( tangemTechApi: TangemTechApi, tangemExpressApi: TangemExpressApi, oneInchApiFactory: OneInchApiFactory, @@ -29,6 +33,7 @@ class SwapDataModule { configManager: ConfigManager, walletManagerFacade: WalletManagersFacade, walletsStateHolder: WalletsStateHolder, + errorsDataConverter: ErrorsDataConverter, ): SwapRepository { return SwapRepositoryImpl( tangemTechApi = tangemTechApi, @@ -38,6 +43,14 @@ class SwapDataModule { configManager = configManager, walletManagersFacade = walletManagerFacade, walletsStateHolder = walletsStateHolder, + errorsDataConverter = errorsDataConverter, ) } + + @Provides + @Singleton + internal fun provideErrorsConverter(@NetworkMoshi moshi: Moshi): ErrorsDataConverter { + val jsonAdapter = moshi.adapter(ExpressErrorResponse::class.java) + return ErrorsDataConverter(jsonAdapter) + } } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt index 734efb5f0d..dd81f44d66 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt @@ -14,7 +14,6 @@ import com.tangem.domain.wallets.legacy.WalletsStateHolder import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.feature.swap.domain.* import com.tangem.feature.swap.domain.cache.SwapDataCacheImpl -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.UserWalletManager import com.tangem.utils.coroutines.CoroutineDispatcherProvider diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/DataError.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/DataError.kt index a00be2a2bc..2c3a3e6af6 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/DataError.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/DataError.kt @@ -1,10 +1,36 @@ package com.tangem.feature.swap.domain.models -sealed class DataError { - object UnknownError : DataError() - data class Error(val message: String) : DataError() -} +import java.math.BigDecimal -fun mapErrors(error: String?): DataError { - return error?.let { DataError.Error(it) } ?: DataError.UnknownError +sealed class DataError { + + abstract val code: Int + + data class BadRequest(override val code: Int) : DataError() + + data class ExchangeProviderNotFoundError(override val code: Int) : DataError() + + data class ExchangeProviderNotActiveError(override val code: Int) : DataError() + + data class ExchangeProviderNotAvailableError(override val code: Int) : DataError() + + data class ExchangeNotPossibleError(override val code: Int) : DataError() + + data class ExchangeTooSmallAmountError(override val code: Int, val amount: SwapAmount) : DataError() + + data class ExchangeNotEnoughAllowanceError(override val code: Int, val currentAllowance: BigDecimal) : DataError() + + data class ExchangeNotEnoughBalanceError(override val code: Int) : DataError() + + data class ExchangeInvalidAddressError(override val code: Int) : DataError() + + data class ExchangeInvalidFromDecimalsError( + override val code: Int, + val receivedFromDecimals: Int, + val expressFromDecimals: Int, + ) : DataError() + + object UnknownError : DataError() { + override val code: Int = -1 + } } \ No newline at end of file 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 index 43cb364d2c..7d965aa10f 100644 --- 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 @@ -515,7 +515,7 @@ internal class StateBuilder( // todo use if needed later // DataError.InsufficientLiquidity -> TODO() // DataError.NoError -> TODO() - is DataError.Error -> addWarning(uiState, error.message, true, onClick) + is DataError.ExchangeTooSmallAmountError -> addWarning(uiState, error.amount.toString(), true, onClick) else -> addWarning(uiState, null, false) {} } }