Updated on 2026-08-14
This commit is contained in:
commit
0f83c529a7
9 changed files with 152 additions and 15 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ internal fun <T : Any> Response<T>.toSafeApiResponse(): ApiResponse<T> {
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -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?,
|
||||
)
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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<ExpressErrorResponse>,
|
||||
) : Converter<String, DataError> {
|
||||
|
||||
@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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -502,7 +502,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) {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue